diff options
author | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-20 15:29:13 +0000 |
---|---|---|
committer | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-20 15:29:13 +0000 |
commit | 92576792823018d937fc58e74267b0d30663e459 (patch) | |
tree | a80543169bea29b7fee7346387a1a0e1d6b34090 | |
parent | 5603a6cf5fb7197f509b894e45bdc733e720b28a (diff) | |
download | chromium_src-92576792823018d937fc58e74267b0d30663e459.zip chromium_src-92576792823018d937fc58e74267b0d30663e459.tar.gz chromium_src-92576792823018d937fc58e74267b0d30663e459.tar.bz2 |
PPB_TCPSocket: add support for TCP server socket operations.
BUG=262601
TEST=new tests in test_tcp_socket.
Review URL: https://chromiumcodereview.appspot.com/24195004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224383 0039d316-1c4b-4281-b951-d872f2087c98
35 files changed, 1897 insertions, 391 deletions
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc index 7dadd5c..debec82 100644 --- a/chrome/test/ppapi/ppapi_browsertest.cc +++ b/chrome/test/ppapi/ppapi_browsertest.cc @@ -324,6 +324,8 @@ IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, TCPSocket) { LIST_TEST(TCPSocket_Connect) LIST_TEST(TCPSocket_ReadWrite) LIST_TEST(TCPSocket_SetOption) + LIST_TEST(TCPSocket_Listen) + LIST_TEST(TCPSocket_Backlog) ); } IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, TCPSocket) { @@ -331,6 +333,8 @@ IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, TCPSocket) { LIST_TEST(TCPSocket_Connect) LIST_TEST(TCPSocket_ReadWrite) LIST_TEST(TCPSocket_SetOption) + LIST_TEST(TCPSocket_Listen) + LIST_TEST(TCPSocket_Backlog) ); } IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, MAYBE_GLIBC(TCPSocket)) { @@ -338,6 +342,8 @@ IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, MAYBE_GLIBC(TCPSocket)) { LIST_TEST(TCPSocket_Connect) LIST_TEST(TCPSocket_ReadWrite) LIST_TEST(TCPSocket_SetOption) + LIST_TEST(TCPSocket_Listen) + LIST_TEST(TCPSocket_Backlog) ); } IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClTest, TCPSocket) { @@ -345,6 +351,8 @@ IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClTest, TCPSocket) { LIST_TEST(TCPSocket_Connect) LIST_TEST(TCPSocket_ReadWrite) LIST_TEST(TCPSocket_SetOption) + LIST_TEST(TCPSocket_Listen) + LIST_TEST(TCPSocket_Backlog) ); } diff --git a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc index 46e8c13..39e778d 100644 --- a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc +++ b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc @@ -19,7 +19,6 @@ #include "content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h" #include "content/browser/renderer_host/pepper/pepper_truetype_font_list_host.h" #include "content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h" -#include "net/socket/stream_socket.h" #include "ppapi/host/message_filter_host.h" #include "ppapi/host/ppapi_host.h" #include "ppapi/host/resource_host.h" @@ -105,16 +104,13 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( host_, instance, params.pp_resource(), file_system, internal_path)); } case PpapiHostMsg_TCPSocket_Create::ID: { - if (CanCreateSocket()) { - scoped_refptr<ResourceMessageFilter> tcp_socket( - new PepperTCPSocketMessageFilter(host_, - instance, - false)); - return scoped_ptr<ResourceHost>(new MessageFilterHost( - host_->GetPpapiHost(), instance, params.pp_resource(), tcp_socket)); - } else { + ppapi::TCPSocketVersion version; + if (!UnpackMessage<PpapiHostMsg_TCPSocket_Create>(message, &version) || + version == ppapi::TCP_SOCKET_VERSION_PRIVATE) { return scoped_ptr<ResourceHost>(); } + + return CreateNewTCPSocket(instance, params.pp_resource(), version); } case PpapiHostMsg_UDPSocket_Create::ID: { if (CanCreateSocket()) { @@ -178,16 +174,8 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( } } if (message.type() == PpapiHostMsg_TCPSocket_CreatePrivate::ID) { - if (CanCreateSocket()) { - scoped_refptr<ResourceMessageFilter> tcp_socket( - new PepperTCPSocketMessageFilter(host_, - instance, - true)); - return scoped_ptr<ResourceHost>(new MessageFilterHost( - host_->GetPpapiHost(), instance, params.pp_resource(), tcp_socket)); - } else { - return scoped_ptr<ResourceHost>(); - } + return CreateNewTCPSocket(instance, params.pp_resource(), + ppapi::TCP_SOCKET_VERSION_PRIVATE); } if (message.type() == PpapiHostMsg_UDPSocket_CreatePrivate::ID) { if (CanCreateSocket()) { @@ -223,21 +211,34 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( scoped_ptr<ppapi::host::ResourceHost> ContentBrowserPepperHostFactory::CreateAcceptedTCPSocket( PP_Instance instance, - bool private_api, - net::StreamSocket* socket) { - scoped_ptr<net::StreamSocket> s(socket); - + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket) { if (!CanCreateSocket()) return scoped_ptr<ResourceHost>(); scoped_refptr<ResourceMessageFilter> tcp_socket( - new PepperTCPSocketMessageFilter(host_, - instance, - private_api, - s.release())); + new PepperTCPSocketMessageFilter(host_, instance, version, + socket.Pass())); return scoped_ptr<ResourceHost>(new MessageFilterHost( host_->GetPpapiHost(), instance, 0, tcp_socket)); } +scoped_ptr<ppapi::host::ResourceHost> +ContentBrowserPepperHostFactory::CreateNewTCPSocket( + PP_Instance instance, + PP_Resource resource, + ppapi::TCPSocketVersion version) { + if (!CanCreateSocket()) + return scoped_ptr<ResourceHost>(); + + scoped_refptr<ResourceMessageFilter> tcp_socket( + new PepperTCPSocketMessageFilter(this, host_, instance, version)); + if (!tcp_socket) + return scoped_ptr<ResourceHost>(); + + return scoped_ptr<ResourceHost>(new MessageFilterHost( + host_->GetPpapiHost(), instance, resource, tcp_socket)); +} + const ppapi::PpapiPermissions& ContentBrowserPepperHostFactory::GetPermissions() const { return host_->GetPpapiHost()->permissions(); diff --git a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h index 64c8b42..04267a3 100644 --- a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h +++ b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h @@ -8,11 +8,10 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "net/socket/tcp_socket.h" +#include "ppapi/c/pp_resource.h" #include "ppapi/host/host_factory.h" - -namespace net { -class StreamSocket; -} +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" namespace ppapi { class PpapiPermissions; @@ -35,15 +34,19 @@ class ContentBrowserPepperHostFactory : public ppapi::host::HostFactory { PP_Instance instance, const IPC::Message& message) OVERRIDE; - // Creates ResourceHost for already accepted TCP |socket|. Takes - // ownership of the |socket|. In the case of failure returns wrapped - // NULL. + // Creates ResourceHost for already accepted TCP |socket|. In the case of + // failure returns wrapped NULL. scoped_ptr<ppapi::host::ResourceHost> CreateAcceptedTCPSocket( PP_Instance instance, - bool private_api, - net::StreamSocket* socket); + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket); private: + scoped_ptr<ppapi::host::ResourceHost> CreateNewTCPSocket( + PP_Instance instance, + PP_Resource resource, + ppapi::TCPSocketVersion version); + const ppapi::PpapiPermissions& GetPermissions() const; // Non-owning pointer. diff --git a/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc b/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc index af2234d..e2912a7 100644 --- a/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc +++ b/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc @@ -15,8 +15,6 @@ #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" -#include "net/socket/tcp_client_socket.h" -#include "net/socket/tcp_server_socket.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/host/dispatch_host_message.h" @@ -25,6 +23,7 @@ #include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/api_id.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" #include "ppapi/shared_impl/private/net_address_private_impl.h" using ppapi::NetAddressPrivateImpl; @@ -134,7 +133,8 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept( ppapi::host::ReplyMessageContext reply_context( context->MakeReplyMessageContext()); int net_result = socket_->Accept( - &socket_buffer_, + &accepted_socket_, + &accepted_address_, base::Bind(&PepperTCPServerSocketMessageFilter::OnAcceptCompleted, base::Unretained(this), reply_context)); if (net_result != net::ERR_IO_PENDING) @@ -169,8 +169,22 @@ void PepperTCPServerSocketMessageFilter::DoListen( state_ = STATE_LISTEN_IN_PROGRESS; - socket_.reset(new net::TCPServerSocket(NULL, net::NetLog::Source())); - int net_result = socket_->Listen(net::IPEndPoint(address, port), backlog); + socket_.reset(new net::TCPSocket(NULL, net::NetLog::Source())); + int net_result = net::OK; + do { + net::IPEndPoint ip_end_point(address, port); + net_result = socket_->Open(ip_end_point.GetFamily()); + if (net_result != net::OK) + break; + net_result = socket_->SetDefaultOptionsForServer(); + if (net_result != net::OK) + break; + net_result = socket_->Bind(ip_end_point); + if (net_result != net::OK) + break; + net_result = socket_->Listen(backlog); + } while (false); + if (net_result != net::ERR_IO_PENDING) OnListenCompleted(context, net_result); } @@ -229,16 +243,15 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( return; } - DCHECK(socket_buffer_.get()); + DCHECK(accepted_socket_.get()); - scoped_ptr<net::StreamSocket> socket(socket_buffer_.release()); net::IPEndPoint ip_end_point_local; - net::IPEndPoint ip_end_point_remote; PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress; PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress; int32_t pp_result = - NetErrorToPepperError(socket->GetLocalAddress(&ip_end_point_local)); + NetErrorToPepperError(accepted_socket_->GetLocalAddress( + &ip_end_point_local)); if (pp_result != PP_OK) { SendAcceptError(context, pp_result); return; @@ -246,19 +259,10 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( if (!NetAddressPrivateImpl::IPEndPointToNetAddress( ip_end_point_local.address(), ip_end_point_local.port(), - &local_addr)) { - SendAcceptError(context, PP_ERROR_FAILED); - return; - } - pp_result = - NetErrorToPepperError(socket->GetPeerAddress(&ip_end_point_remote)); - if (pp_result != PP_OK) { - SendAcceptError(context, pp_result); - return; - } - if (!NetAddressPrivateImpl::IPEndPointToNetAddress( - ip_end_point_remote.address(), - ip_end_point_remote.port(), + &local_addr) || + !NetAddressPrivateImpl::IPEndPointToNetAddress( + accepted_address_.address(), + accepted_address_.port(), &remote_addr)) { SendAcceptError(context, PP_ERROR_FAILED); return; @@ -266,7 +270,8 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( scoped_ptr<ppapi::host::ResourceHost> host = factory_->CreateAcceptedTCPSocket( - instance_, true /* private_api */, socket.release()); + instance_, ppapi::TCP_SOCKET_VERSION_PRIVATE, + accepted_socket_.Pass()); if (!host) { SendAcceptError(context, PP_ERROR_NOSPACE); return; diff --git a/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h b/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h index d36b787..5d17333 100644 --- a/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h +++ b/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h @@ -10,16 +10,13 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "content/common/content_export.h" +#include "net/base/ip_endpoint.h" +#include "net/socket/tcp_socket.h" #include "ppapi/c/pp_instance.h" #include "ppapi/host/resource_message_filter.h" struct PP_NetAddress_Private; -namespace net { -class ServerSocket; -class StreamSocket; -} - namespace ppapi { namespace host { class PpapiHost; @@ -31,6 +28,8 @@ namespace content { class BrowserPpapiHostImpl; class ContentBrowserPepperHostFactory; +// TODO(yzshen): Remove this class entirely and let +// TCPServerSocketPrivateResource inherit TCPSocketResourceBase. class CONTENT_EXPORT PepperTCPServerSocketMessageFilter : public ppapi::host::ResourceMessageFilter { public: @@ -97,8 +96,9 @@ class CONTENT_EXPORT PepperTCPServerSocketMessageFilter PP_Instance instance_; State state_; - scoped_ptr<net::ServerSocket> socket_; - scoped_ptr<net::StreamSocket> socket_buffer_; + scoped_ptr<net::TCPSocket> socket_; + scoped_ptr<net::TCPSocket> accepted_socket_; + net::IPEndPoint accepted_address_; // Following fields are initialized on the IO thread but used only // on the UI thread. diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc index 2ec8c54..26c1b7c 100644 --- a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc +++ b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc @@ -4,15 +4,20 @@ #include "content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h" +#include <cstring> + #include "base/bind.h" #include "base/logging.h" +#include "build/build_config.h" #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h" #include "content/browser/renderer_host/pepper/pepper_socket_utils.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/resource_context.h" #include "content/public/common/socket_permission_request.h" +#include "net/base/address_family.h" #include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -21,9 +26,10 @@ #include "net/socket/client_socket_handle.h" #include "net/socket/ssl_client_socket.h" #include "net/socket/tcp_client_socket.h" -#include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/error_conversion.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/tcp_socket_resource_base.h" #include "ppapi/shared_impl/private/net_address_private_impl.h" @@ -31,6 +37,8 @@ using ppapi::NetAddressPrivateImpl; using ppapi::host::NetErrorToPepperError; using ppapi::proxy::TCPSocketResourceBase; +using ppapi::TCPSocketState; +using ppapi::TCPSocketVersion; namespace { @@ -41,16 +49,24 @@ size_t g_num_instances = 0; namespace content { PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, BrowserPpapiHostImpl* host, PP_Instance instance, - bool private_api) - : external_plugin_(host->external_plugin()), - private_api_(private_api), + TCPSocketVersion version) + : version_(version), + external_plugin_(host->external_plugin()), render_process_id_(0), render_view_id_(0), - state_(STATE_BEFORE_CONNECT), + ppapi_host_(host->GetPpapiHost()), + factory_(factory), + instance_(instance), + state_(TCPSocketState::INITIAL), end_of_file_reached_(false), - ssl_context_helper_(host->ssl_context_helper()) { + bind_input_addr_(NetAddressPrivateImpl::kInvalidNetAddress), + socket_(new net::TCPSocket(NULL, net::NetLog::Source())), + ssl_context_helper_(host->ssl_context_helper()), + pending_accept_(false), + allow_address_reuse_(false) { DCHECK(host); ++g_num_instances; if (!host->GetRenderViewIDsForInstance(instance, @@ -63,17 +79,25 @@ PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter( PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter( BrowserPpapiHostImpl* host, PP_Instance instance, - bool private_api, - net::StreamSocket* socket) - : external_plugin_(host->external_plugin()), - private_api_(private_api), + TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket) + : version_(version), + external_plugin_(host->external_plugin()), render_process_id_(0), render_view_id_(0), - state_(STATE_CONNECTED), + ppapi_host_(host->GetPpapiHost()), + factory_(NULL), + instance_(instance), + state_(TCPSocketState::CONNECTED), end_of_file_reached_(false), - socket_(socket), - ssl_context_helper_(host->ssl_context_helper()) { + bind_input_addr_(NetAddressPrivateImpl::kInvalidNetAddress), + socket_(socket.Pass()), + ssl_context_helper_(host->ssl_context_helper()), + pending_accept_(false), + allow_address_reuse_(false) { DCHECK(host); + DCHECK_NE(version, ppapi::TCP_SOCKET_VERSION_1_0); + ++g_num_instances; if (!host->GetRenderViewIDsForInstance(instance, &render_process_id_, @@ -84,7 +108,9 @@ PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter( PepperTCPSocketMessageFilter::~PepperTCPSocketMessageFilter() { if (socket_) - socket_->Disconnect(); + socket_->Close(); + if (ssl_socket_) + ssl_socket_->Disconnect(); --g_num_instances; } @@ -97,13 +123,16 @@ scoped_refptr<base::TaskRunner> PepperTCPSocketMessageFilter::OverrideTaskRunnerForMessage( const IPC::Message& message) { switch (message.type()) { + case PpapiHostMsg_TCPSocket_Bind::ID: case PpapiHostMsg_TCPSocket_Connect::ID: case PpapiHostMsg_TCPSocket_ConnectWithNetAddress::ID: + case PpapiHostMsg_TCPSocket_Listen::ID: return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); case PpapiHostMsg_TCPSocket_SSLHandshake::ID: case PpapiHostMsg_TCPSocket_Read::ID: case PpapiHostMsg_TCPSocket_Write::ID: - case PpapiHostMsg_TCPSocket_Disconnect::ID: + case PpapiHostMsg_TCPSocket_Accept::ID: + case PpapiHostMsg_TCPSocket_Close::ID: case PpapiHostMsg_TCPSocket_SetOption::ID: return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); } @@ -115,6 +144,8 @@ int32_t PepperTCPSocketMessageFilter::OnResourceMessageReceived( ppapi::host::HostMessageContext* context) { IPC_BEGIN_MESSAGE_MAP(PepperTCPSocketMessageFilter, msg) PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Bind, OnMsgBind) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( PpapiHostMsg_TCPSocket_Connect, OnMsgConnect) PPAPI_DISPATCH_HOST_RESOURCE_CALL( PpapiHostMsg_TCPSocket_ConnectWithNetAddress, @@ -125,14 +156,38 @@ int32_t PepperTCPSocketMessageFilter::OnResourceMessageReceived( PpapiHostMsg_TCPSocket_Read, OnMsgRead) PPAPI_DISPATCH_HOST_RESOURCE_CALL( PpapiHostMsg_TCPSocket_Write, OnMsgWrite) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Listen, OnMsgListen) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( + PpapiHostMsg_TCPSocket_Accept, OnMsgAccept) PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( - PpapiHostMsg_TCPSocket_Disconnect, OnMsgDisconnect) + PpapiHostMsg_TCPSocket_Close, OnMsgClose) PPAPI_DISPATCH_HOST_RESOURCE_CALL( PpapiHostMsg_TCPSocket_SetOption, OnMsgSetOption) IPC_END_MESSAGE_MAP() return PP_ERROR_FAILED; } +int32_t PepperTCPSocketMessageFilter::OnMsgBind( + const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This is only supported by PPB_TCPSocket v1.1 or above. + if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + NOTREACHED(); + return PP_ERROR_NOACCESS; + } + + bind_input_addr_ = net_addr; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoBind, this, + context->MakeReplyMessageContext(), net_addr)); + return PP_OK_COMPLETIONPENDING; +} + int32_t PepperTCPSocketMessageFilter::OnMsgConnect( const ppapi::host::HostMessageContext* context, const std::string& host, @@ -140,7 +195,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgConnect( DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // This is only supported by PPB_TCPSocket_Private. - if (!private_api_) { + if (!IsPrivateAPI()) { NOTREACHED(); return PP_ERROR_NOACCESS; } @@ -148,9 +203,9 @@ int32_t PepperTCPSocketMessageFilter::OnMsgConnect( SocketPermissionRequest request(SocketPermissionRequest::TCP_CONNECT, host, port); - if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, render_process_id_, - render_view_id_)) { + if (!pepper_socket_utils::CanUseSocketAPIs( + external_plugin_, true /* private_api */, request, render_process_id_, + render_view_id_)) { return PP_ERROR_NOACCESS; } @@ -178,7 +233,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgConnectWithNetAddress( content::SocketPermissionRequest request = pepper_socket_utils::CreateSocketPermissionRequest( content::SocketPermissionRequest::TCP_CONNECT, net_addr); - if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, + if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, IsPrivateAPI(), request, render_process_id_, render_view_id_)) { return PP_ERROR_NOACCESS; @@ -201,18 +256,19 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake( // Allow to do SSL handshake only if currently the socket has been connected // and there isn't pending read or write. - // IsConnected() includes the state that SSL handshake has been finished and - // therefore isn't suitable here. - if (state_ != STATE_CONNECTED || read_buffer_.get() || - write_buffer_base_.get() || write_buffer_.get()) { + if (!state_.IsValidTransition(TCPSocketState::SSL_CONNECT) || + read_buffer_.get() || write_buffer_base_.get() || write_buffer_.get()) { return PP_ERROR_FAILED; } - SetState(STATE_SSL_HANDSHAKE_IN_PROGRESS); // TODO(raymes,rsleevi): Use trusted/untrusted certificates when connecting. + net::IPEndPoint peer_address; + if (socket_->GetPeerAddress(&peer_address) != net::OK) + return PP_ERROR_FAILED; scoped_ptr<net::ClientSocketHandle> handle(new net::ClientSocketHandle()); - handle->SetSocket(socket_.Pass()); + handle->SetSocket(make_scoped_ptr<net::StreamSocket>( + new net::TCPClientSocket(socket_.Pass(), peer_address))); net::ClientSocketFactory* factory = net::ClientSocketFactory::GetDefaultFactory(); net::HostPortPair host_port_pair(server_name, server_port); @@ -220,17 +276,20 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake( ssl_context.cert_verifier = ssl_context_helper_->GetCertVerifier(); ssl_context.transport_security_state = ssl_context_helper_->GetTransportSecurityState(); - socket_ = factory->CreateSSLClientSocket( + ssl_socket_ = factory->CreateSSLClientSocket( handle.Pass(), host_port_pair, ssl_context_helper_->ssl_config(), ssl_context); - if (!socket_) { + if (!ssl_socket_) { LOG(WARNING) << "Failed to create an SSL client socket."; + state_.CompletePendingTransition(false); return PP_ERROR_FAILED; } + state_.SetPendingTransition(TCPSocketState::SSL_CONNECT); + const ppapi::host::ReplyMessageContext reply_context( context->MakeReplyMessageContext()); - int net_result = socket_->Connect( + int net_result = ssl_socket_->Connect( base::Bind(&PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted, base::Unretained(this), reply_context)); if (net_result != net::ERR_IO_PENDING) @@ -242,7 +301,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgRead( const ppapi::host::HostMessageContext* context, int32_t bytes_to_read) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!IsConnected() || end_of_file_reached_) + if (!state_.IsConnected() || end_of_file_reached_) return PP_ERROR_FAILED; if (read_buffer_.get()) return PP_ERROR_INPROGRESS; @@ -254,11 +313,23 @@ int32_t PepperTCPSocketMessageFilter::OnMsgRead( ppapi::host::ReplyMessageContext reply_context( context->MakeReplyMessageContext()); read_buffer_ = new net::IOBuffer(bytes_to_read); - int net_result = socket_->Read( - read_buffer_.get(), - bytes_to_read, - base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted, - base::Unretained(this), reply_context)); + + int net_result = net::ERR_FAILED; + if (socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED); + net_result = socket_->Read( + read_buffer_.get(), + bytes_to_read, + base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted, + base::Unretained(this), reply_context)); + } else if (ssl_socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED); + net_result = ssl_socket_->Read( + read_buffer_.get(), + bytes_to_read, + base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted, + base::Unretained(this), reply_context)); + } if (net_result != net::ERR_IO_PENDING) OnReadCompleted(reply_context, net_result); return PP_OK_COMPLETIONPENDING; @@ -269,7 +340,7 @@ int32_t PepperTCPSocketMessageFilter::OnMsgWrite( const std::string& data) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!IsConnected()) + if (!state_.IsConnected()) return PP_ERROR_FAILED; if (write_buffer_base_.get() || write_buffer_.get()) return PP_ERROR_INPROGRESS; @@ -288,10 +359,68 @@ int32_t PepperTCPSocketMessageFilter::OnMsgWrite( return PP_OK_COMPLETIONPENDING; } -int32_t PepperTCPSocketMessageFilter::OnMsgDisconnect( +int32_t PepperTCPSocketMessageFilter::OnMsgListen( + const ppapi::host::HostMessageContext* context, + int32_t backlog) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This is only supported by PPB_TCPSocket v1.1 or above. + if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + NOTREACHED(); + return PP_ERROR_NOACCESS; + } + + content::SocketPermissionRequest request = + pepper_socket_utils::CreateSocketPermissionRequest( + content::SocketPermissionRequest::TCP_LISTEN, bind_input_addr_); + if (!pepper_socket_utils::CanUseSocketAPIs( + external_plugin_, false /* private_api */, request, + render_process_id_, render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoListen, this, + context->MakeReplyMessageContext(), backlog)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgAccept( const ppapi::host::HostMessageContext* context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - SetState(STATE_CLOSED); + + if (pending_accept_) + return PP_ERROR_INPROGRESS; + if (state_.state() != TCPSocketState::LISTENING) + return PP_ERROR_FAILED; + + pending_accept_ = true; + ppapi::host::ReplyMessageContext reply_context( + context->MakeReplyMessageContext()); + int net_result = socket_->Accept( + &accepted_socket_, + &accepted_address_, + base::Bind(&PepperTCPSocketMessageFilter::OnAcceptCompleted, + base::Unretained(this), reply_context)); + if (net_result != net::ERR_IO_PENDING) + OnAcceptCompleted(reply_context, net_result); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgClose( + const ppapi::host::HostMessageContext* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (state_.state() == TCPSocketState::CLOSED) + return PP_OK; + + state_.DoTransition(TCPSocketState::CLOSE, true); + // Make sure we get no further callbacks from |socket_| or |ssl_socket_|. + if (socket_) { + socket_->Close(); + } else if (ssl_socket_) { + ssl_socket_->Disconnect(); + } return PP_OK; } @@ -301,22 +430,21 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSetOption( const ppapi::SocketOptionData& value) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!IsConnected() || IsSsl()) - return PP_ERROR_FAILED; - - net::TCPClientSocket* tcp_socket = - static_cast<net::TCPClientSocket*>(socket_.get()); - DCHECK(tcp_socket); - switch (name) { case PP_TCPSOCKET_OPTION_NO_DELAY: { + if (state_.state() != TCPSocketState::CONNECTED) + return PP_ERROR_FAILED; + bool boolean_value = false; if (!value.GetBool(&boolean_value)) return PP_ERROR_BADARGUMENT; - return tcp_socket->SetNoDelay(boolean_value) ? PP_OK : PP_ERROR_FAILED; + return socket_->SetNoDelay(boolean_value) ? PP_OK : PP_ERROR_FAILED; } case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE: case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: { + if (state_.state() != TCPSocketState::CONNECTED) + return PP_ERROR_FAILED; + int32_t integer_value = 0; if (!value.GetInt32(&integer_value) || integer_value <= 0) return PP_ERROR_BADARGUMENT; @@ -325,20 +453,91 @@ int32_t PepperTCPSocketMessageFilter::OnMsgSetOption( if (name == PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE) { if (integer_value > TCPSocketResourceBase::kMaxSendBufferSize) return PP_ERROR_BADARGUMENT; - result = tcp_socket->SetSendBufferSize(integer_value); + result = socket_->SetSendBufferSize(integer_value); } else { if (integer_value > TCPSocketResourceBase::kMaxReceiveBufferSize) return PP_ERROR_BADARGUMENT; - result = tcp_socket->SetReceiveBufferSize(integer_value); + result = socket_->SetReceiveBufferSize(integer_value); } return result ? PP_OK : PP_ERROR_FAILED; } + case PP_TCPSOCKET_OPTION_ADDRESS_REUSE: { + if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) + return PP_ERROR_NOTSUPPORTED; + if (state_.state() != TCPSocketState::INITIAL) + return PP_ERROR_FAILED; + + bool boolean_value = false; + if (!value.GetBool(&boolean_value)) + return PP_ERROR_BADARGUMENT; + allow_address_reuse_ = boolean_value; + return PP_OK; + } default: { NOTREACHED(); return PP_ERROR_BADARGUMENT; } } - return PP_ERROR_FAILED; +} + +void PepperTCPSocketMessageFilter::DoBind( + const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (state_.IsPending(TCPSocketState::BIND)) { + SendBindError(context, PP_ERROR_INPROGRESS); + return; + } + if (!state_.IsValidTransition(TCPSocketState::BIND)) { + SendBindError(context, PP_ERROR_FAILED); + return; + } + + int pp_result = PP_OK; + do { + net::IPAddressNumber address; + int port; + if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address, + &port)) { + pp_result = PP_ERROR_ADDRESS_INVALID; + break; + } + net::IPEndPoint bind_addr(address, port); + + DCHECK(!socket_->IsValid()); + pp_result = NetErrorToPepperError(OpenSocket(bind_addr.GetFamily())); + if (pp_result != PP_OK) + break; + + pp_result = NetErrorToPepperError(socket_->Bind(bind_addr)); + if (pp_result != PP_OK) + break; + + net::IPEndPoint ip_end_point_local; + pp_result = NetErrorToPepperError( + socket_->GetLocalAddress(&ip_end_point_local)); + if (pp_result != PP_OK) + break; + + PP_NetAddress_Private local_addr = + NetAddressPrivateImpl::kInvalidNetAddress; + if (!NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_local.address(), + ip_end_point_local.port(), + &local_addr)) { + pp_result = PP_ERROR_ADDRESS_INVALID; + break; + } + + SendBindReply(context, PP_OK, local_addr); + state_.DoTransition(TCPSocketState::BIND, true); + return; + } while (false); + if (socket_->IsValid()) + socket_->Close(); + SendBindError(context, pp_result); + state_.DoTransition(TCPSocketState::BIND, false); } void PepperTCPSocketMessageFilter::DoConnect( @@ -348,12 +547,12 @@ void PepperTCPSocketMessageFilter::DoConnect( ResourceContext* resource_context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (state_ != STATE_BEFORE_CONNECT) { + if (!state_.IsValidTransition(TCPSocketState::CONNECT)) { SendConnectError(context, PP_ERROR_FAILED); return; } - SetState(STATE_CONNECT_IN_PROGRESS); + state_.SetPendingTransition(TCPSocketState::CONNECT); net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port)); resolver_.reset(new net::SingleRequestHostResolver( resource_context->GetHostResolver())); @@ -373,15 +572,18 @@ void PepperTCPSocketMessageFilter::DoConnectWithNetAddress( const PP_NetAddress_Private& net_addr) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (state_ != STATE_BEFORE_CONNECT) { + if (!state_.IsValidTransition(TCPSocketState::CONNECT)) { SendConnectError(context, PP_ERROR_FAILED); return; } + state_.SetPendingTransition(TCPSocketState::CONNECT); + net::IPAddressNumber address; int port; if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address, &port)) { + state_.CompletePendingTransition(false); SendConnectError(context, PP_ERROR_ADDRESS_INVALID); return; } @@ -389,7 +591,6 @@ void PepperTCPSocketMessageFilter::DoConnectWithNetAddress( // Copy the single IPEndPoint to address_list_. address_list_.clear(); address_list_.push_back(net::IPEndPoint(address, port)); - SetState(STATE_CONNECT_IN_PROGRESS); StartConnect(context); } @@ -399,30 +600,61 @@ void PepperTCPSocketMessageFilter::DoWrite( DCHECK(write_buffer_base_.get()); DCHECK(write_buffer_.get()); DCHECK_GT(write_buffer_->BytesRemaining(), 0); - - int net_result = socket_->Write( - write_buffer_.get(), - write_buffer_->BytesRemaining(), - base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted, - base::Unretained(this), context)); + DCHECK(state_.IsConnected()); + + int net_result = net::ERR_FAILED; + if (socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED); + net_result = socket_->Write( + write_buffer_.get(), + write_buffer_->BytesRemaining(), + base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted, + base::Unretained(this), context)); + } else if (ssl_socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED); + net_result = ssl_socket_->Write( + write_buffer_.get(), + write_buffer_->BytesRemaining(), + base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted, + base::Unretained(this), context)); + } if (net_result != net::ERR_IO_PENDING) OnWriteCompleted(context, net_result); } +void PepperTCPSocketMessageFilter::DoListen( + const ppapi::host::ReplyMessageContext& context, + int32_t backlog) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (state_.IsPending(TCPSocketState::LISTEN)) { + SendListenReply(context, PP_ERROR_INPROGRESS); + return; + } + if (!state_.IsValidTransition(TCPSocketState::LISTEN)) { + SendListenReply(context, PP_ERROR_FAILED); + return; + } + + int32_t pp_result = NetErrorToPepperError(socket_->Listen(backlog)); + SendListenReply(context, pp_result); + state_.DoTransition(TCPSocketState::LISTEN, pp_result == PP_OK); +} + void PepperTCPSocketMessageFilter::OnResolveCompleted( const ppapi::host::ReplyMessageContext& context, int net_result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (state_ != STATE_CONNECT_IN_PROGRESS) { + if (!state_.IsPending(TCPSocketState::CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); SendConnectError(context, PP_ERROR_FAILED); - SetState(STATE_CLOSED); return; } if (net_result != net::OK) { SendConnectError(context, NetErrorToPepperError(net_result)); - SetState(STATE_BEFORE_CONNECT); + state_.CompletePendingTransition(false); return; } @@ -432,18 +664,18 @@ void PepperTCPSocketMessageFilter::OnResolveCompleted( void PepperTCPSocketMessageFilter::StartConnect( const ppapi::host::ReplyMessageContext& context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(state_.IsPending(TCPSocketState::CONNECT)); - if (state_ != STATE_CONNECT_IN_PROGRESS) { - SendConnectError(context, PP_ERROR_FAILED); - SetState(STATE_CLOSED); - return; - } + int net_result = net::OK; + if (!socket_->IsValid()) + net_result = OpenSocket(address_list_[0].GetFamily()); - socket_.reset(new net::TCPClientSocket(address_list_, NULL, - net::NetLog::Source())); - int net_result = socket_->Connect( - base::Bind(&PepperTCPSocketMessageFilter::OnConnectCompleted, - base::Unretained(this), context)); + if (net_result == net::OK) { + net_result = socket_->Connect( + address_list_[0], + base::Bind(&PepperTCPSocketMessageFilter::OnConnectCompleted, + base::Unretained(this), context)); + } if (net_result != net::ERR_IO_PENDING) OnConnectCompleted(context, net_result); } @@ -452,11 +684,10 @@ void PepperTCPSocketMessageFilter::OnConnectCompleted( const ppapi::host::ReplyMessageContext& context, int net_result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(socket_.get()); - if (state_ != STATE_CONNECT_IN_PROGRESS) { + if (!state_.IsPending(TCPSocketState::CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); SendConnectError(context, PP_ERROR_FAILED); - SetState(STATE_CLOSED); return; } @@ -492,13 +723,24 @@ void PepperTCPSocketMessageFilter::OnConnectCompleted( break; } + socket_->SetDefaultOptionsForClient(); SendConnectReply(context, PP_OK, local_addr, remote_addr); - SetState(STATE_CONNECTED); + state_.CompletePendingTransition(true); return; } while (false); SendConnectError(context, pp_result); - SetState(STATE_BEFORE_CONNECT); + if (version_ == ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + state_.CompletePendingTransition(false); + } else { + // In order to maintain backward compatibility, allow further attempts to + // connect the socket. + state_ = TCPSocketState(TCPSocketState::INITIAL); + // We have to recreate |socket_| because it doesn't allow a second connect + // attempt. We won't lose any state such as bound address or set options, + // because in the private or v1.0 API, connect must be the first operation. + socket_.reset(new net::TCPSocket(NULL, net::NetLog::Source())); + } } void PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted( @@ -506,15 +748,14 @@ void PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted( int net_result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (state_ != STATE_SSL_HANDSHAKE_IN_PROGRESS) { + if (!state_.IsPending(TCPSocketState::SSL_CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); SendSSLHandshakeReply(context, PP_ERROR_FAILED); - SetState(STATE_CLOSED); return; } + SendSSLHandshakeReply(context, NetErrorToPepperError(net_result)); - SetState(net_result == net::OK ? - STATE_SSL_CONNECTED : - STATE_SSL_HANDSHAKE_FAILED); + state_.CompletePendingTransition(net_result == net::OK); } void PepperTCPSocketMessageFilter::OnReadCompleted( @@ -547,7 +788,7 @@ void PepperTCPSocketMessageFilter::OnWriteCompleted( // likely infinite loop. if (net_result > 0) { write_buffer_->DidConsume(net_result); - if (write_buffer_->BytesRemaining() > 0) { + if (write_buffer_->BytesRemaining() > 0 && state_.IsConnected()) { DoWrite(context); return; } @@ -562,6 +803,76 @@ void PepperTCPSocketMessageFilter::OnWriteCompleted( write_buffer_base_ = NULL; } +void PepperTCPSocketMessageFilter::OnAcceptCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(pending_accept_); + + pending_accept_ = false; + + if (net_result != net::OK) { + SendAcceptError(context, NetErrorToPepperError(net_result)); + return; + } + + DCHECK(accepted_socket_.get()); + + net::IPEndPoint ip_end_point_local; + PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress; + PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress; + + int32_t pp_result = + NetErrorToPepperError(accepted_socket_->GetLocalAddress( + &ip_end_point_local)); + if (pp_result != PP_OK) { + SendAcceptError(context, pp_result); + return; + } + if (!NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_local.address(), + ip_end_point_local.port(), + &local_addr) || + !NetAddressPrivateImpl::IPEndPointToNetAddress( + accepted_address_.address(), + accepted_address_.port(), + &remote_addr)) { + SendAcceptError(context, PP_ERROR_ADDRESS_INVALID); + return; + } + + // |factory_| is guaranteed to be non-NULL here. Only those instances created + // in CONNECTED state have a NULL |factory_|, while getting here requires + // LISTENING state. + scoped_ptr<ppapi::host::ResourceHost> host = + factory_->CreateAcceptedTCPSocket( + instance_, version_, accepted_socket_.Pass()); + if (!host) { + SendAcceptError(context, PP_ERROR_NOSPACE); + return; + } + int pending_host_id = ppapi_host_->AddPendingResourceHost(host.Pass()); + if (pending_host_id) + SendAcceptReply(context, PP_OK, pending_host_id, local_addr, remote_addr); + else + SendAcceptError(context, PP_ERROR_NOSPACE); +} + +void PepperTCPSocketMessageFilter::SendBindReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_BindReply(local_addr)); +} + +void PepperTCPSocketMessageFilter::SendBindError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendBindReply(context, pp_error, NetAddressPrivateImpl::kInvalidNetAddress); +} + void PepperTCPSocketMessageFilter::SendConnectReply( const ppapi::host::ReplyMessageContext& context, int32_t pp_result, @@ -590,10 +901,8 @@ void PepperTCPSocketMessageFilter::SendSSLHandshakeReply( ppapi::PPB_X509Certificate_Fields certificate_fields; if (pp_result == PP_OK) { // Our socket is guaranteed to be an SSL socket if we get here. - net::SSLClientSocket* ssl_socket = - static_cast<net::SSLClientSocket*>(socket_.get()); net::SSLInfo ssl_info; - ssl_socket->GetSSLInfo(&ssl_info); + ssl_socket_->GetSSLInfo(&ssl_info); if (ssl_info.cert.get()) { pepper_socket_utils::GetCertificateFields(*ssl_info.cert.get(), &certificate_fields); @@ -626,23 +935,49 @@ void PepperTCPSocketMessageFilter::SendWriteReply( SendReply(reply_context, PpapiPluginMsg_TCPSocket_WriteReply()); } -bool PepperTCPSocketMessageFilter::IsConnected() const { - return state_ == STATE_CONNECTED || state_ == STATE_SSL_CONNECTED; +void PepperTCPSocketMessageFilter::SendListenReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_ListenReply()); +} + +void PepperTCPSocketMessageFilter::SendAcceptReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, + PpapiPluginMsg_TCPSocket_AcceptReply( + pending_host_id, local_addr, remote_addr)); } -bool PepperTCPSocketMessageFilter::IsSsl() const { - return state_ == STATE_SSL_HANDSHAKE_IN_PROGRESS || - state_ == STATE_SSL_CONNECTED || - state_ == STATE_SSL_HANDSHAKE_FAILED; +void PepperTCPSocketMessageFilter::SendAcceptError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendAcceptReply(context, pp_error, 0, + NetAddressPrivateImpl::kInvalidNetAddress, + NetAddressPrivateImpl::kInvalidNetAddress); } -void PepperTCPSocketMessageFilter::SetState(State state) { - state_ = state; - if (state_ == STATE_CLOSED && socket_) { - // Make sure no further callbacks from socket_. - socket_->Disconnect(); - socket_.reset(); +int PepperTCPSocketMessageFilter::OpenSocket(net::AddressFamily family) { + int net_error = socket_->Open(family); + if (net_error != net::OK) + return net_error; + + // TODO(yzshen): Handle it for Windows. +#if defined(POSIX) + if (allow_address_reuse_) { + net_error = socket_->SetAddressReuse(true); + LOG_IF(WARNING, net_error != net::OK) << "SetAddressReuse() failed."; } +#endif + + return net::OK; } } // namespace content diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h index 84e8a75..01947ec 100644 --- a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h +++ b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h @@ -15,23 +15,27 @@ #include "content/browser/renderer_host/pepper/ssl_context_helper.h" #include "content/common/content_export.h" #include "net/base/address_list.h" +#include "net/base/ip_endpoint.h" +#include "net/socket/tcp_socket.h" #include "ppapi/c/pp_instance.h" #include "ppapi/c/ppb_tcp_socket.h" +#include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/host/resource_message_filter.h" - -struct PP_NetAddress_Private; +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" namespace net { +enum AddressFamily; class DrainableIOBuffer; class IOBuffer; class SingleRequestHostResolver; -class StreamSocket; +class SSLClientSocket; } namespace ppapi { class SocketOptionData; namespace host { +class PpapiHost; struct ReplyMessageContext; } } @@ -39,47 +43,29 @@ struct ReplyMessageContext; namespace content { class BrowserPpapiHostImpl; +class ContentBrowserPepperHostFactory; class ResourceContext; class CONTENT_EXPORT PepperTCPSocketMessageFilter : public ppapi::host::ResourceMessageFilter { public: PepperTCPSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, BrowserPpapiHostImpl* host, PP_Instance instance, - bool private_api); + ppapi::TCPSocketVersion version); - // Used for creating already connected sockets. Takes ownership of - // |socket|. + // Used for creating already connected sockets. PepperTCPSocketMessageFilter( BrowserPpapiHostImpl* host, PP_Instance instance, - bool private_api, - net::StreamSocket* socket); + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket); static size_t GetNumInstances(); - protected: - virtual ~PepperTCPSocketMessageFilter(); - private: - enum State { - // Before a connection is successfully established (including a previous - // connect request failed). - STATE_BEFORE_CONNECT, - // There is a connect request that is pending. - STATE_CONNECT_IN_PROGRESS, - // A connection has been successfully established. - STATE_CONNECTED, - // There is an SSL handshake request that is pending. - STATE_SSL_HANDSHAKE_IN_PROGRESS, - // An SSL connection has been successfully established. - STATE_SSL_CONNECTED, - // An SSL handshake has failed. - STATE_SSL_HANDSHAKE_FAILED, - // Socket is closed. - STATE_CLOSED - }; + virtual ~PepperTCPSocketMessageFilter(); // ppapi::host::ResourceMessageFilter overrides. virtual scoped_refptr<base::TaskRunner> OverrideTaskRunnerForMessage( @@ -88,6 +74,8 @@ class CONTENT_EXPORT PepperTCPSocketMessageFilter const IPC::Message& msg, ppapi::host::HostMessageContext* context) OVERRIDE; + int32_t OnMsgBind(const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr); int32_t OnMsgConnect(const ppapi::host::HostMessageContext* context, const std::string& host, uint16_t port); @@ -104,11 +92,16 @@ class CONTENT_EXPORT PepperTCPSocketMessageFilter int32_t bytes_to_read); int32_t OnMsgWrite(const ppapi::host::HostMessageContext* context, const std::string& data); - int32_t OnMsgDisconnect(const ppapi::host::HostMessageContext* context); + int32_t OnMsgListen(const ppapi::host::HostMessageContext* context, + int32_t backlog); + int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context); + int32_t OnMsgClose(const ppapi::host::HostMessageContext* context); int32_t OnMsgSetOption(const ppapi::host::HostMessageContext* context, PP_TCPSocket_Option name, const ppapi::SocketOptionData& value); + void DoBind(const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr); void DoConnect(const ppapi::host::ReplyMessageContext& context, const std::string& host, uint16_t port, @@ -117,6 +110,8 @@ class CONTENT_EXPORT PepperTCPSocketMessageFilter const ppapi::host::ReplyMessageContext& context, const PP_NetAddress_Private& net_addr); void DoWrite(const ppapi::host::ReplyMessageContext& context); + void DoListen(const ppapi::host::ReplyMessageContext& context, + int32_t backlog); void OnResolveCompleted(const ppapi::host::ReplyMessageContext& context, int net_result); @@ -130,7 +125,14 @@ class CONTENT_EXPORT PepperTCPSocketMessageFilter int net_result); void OnWriteCompleted(const ppapi::host::ReplyMessageContext& context, int net_result); + void OnAcceptCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void SendBindReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr); + void SendBindError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); void SendConnectReply(const ppapi::host::ReplyMessageContext& context, int32_t pp_result, const PP_NetAddress_Private& local_addr, @@ -146,28 +148,57 @@ class CONTENT_EXPORT PepperTCPSocketMessageFilter int32_t pp_error); void SendWriteReply(const ppapi::host::ReplyMessageContext& context, int32_t pp_result); + void SendListenReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result); + void SendAcceptReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr); + void SendAcceptError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); - bool IsConnected() const; - bool IsSsl() const; - void SetState(State state); + int32_t OpenSocket(net::AddressFamily family); - bool external_plugin_; - bool private_api_; + bool IsPrivateAPI() const { + return version_ == ppapi::TCP_SOCKET_VERSION_PRIVATE; + } + + // The following fields are used on both the UI and IO thread. + const ppapi::TCPSocketVersion version_; + + // The following fields are used only on the UI thread. + const bool external_plugin_; int render_process_id_; int render_view_id_; - State state_; + // The following fields are used only on the IO thread. + // Non-owning ptr. + ppapi::host::PpapiHost* ppapi_host_; + // Non-owning ptr. + ContentBrowserPepperHostFactory* factory_; + PP_Instance instance_; + + ppapi::TCPSocketState state_; bool end_of_file_reached_; + // This is the address requested to bind. Please note that this is not the + // bound address. For example, |bind_input_addr_| may have port set to 0. + // It is used to check permission for listening. + PP_NetAddress_Private bind_input_addr_; + scoped_ptr<net::SingleRequestHostResolver> resolver_; net::AddressList address_list_; - scoped_ptr<net::StreamSocket> socket_; + // Non-null unless an SSL connection is requested. + scoped_ptr<net::TCPSocket> socket_; + // Non-null if an SSL connection is requested. + scoped_ptr<net::SSLClientSocket> ssl_socket_; scoped_refptr<net::IOBuffer> read_buffer_; - // StreamSocket::Write() may not always write the full buffer, but we would + // TCPSocket::Write() may not always write the full buffer, but we would // rather have our DoWrite() do so whenever possible. To do this, we may have // to call the former multiple times for each of the latter. This entails // using a DrainableIOBuffer, which requires an underlying base IOBuffer. @@ -175,6 +206,12 @@ class CONTENT_EXPORT PepperTCPSocketMessageFilter scoped_refptr<net::DrainableIOBuffer> write_buffer_; scoped_refptr<SSLContextHelper> ssl_context_helper_; + bool pending_accept_; + scoped_ptr<net::TCPSocket> accepted_socket_; + net::IPEndPoint accepted_address_; + + bool allow_address_reuse_; + DISALLOW_COPY_AND_ASSIGN(PepperTCPSocketMessageFilter); }; diff --git a/content/content_browser.gypi b/content/content_browser.gypi index f62174e..82b077b 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -1519,6 +1519,7 @@ ['enable_plugins==1', { 'dependencies': [ '../ppapi/ppapi_internal.gyp:ppapi_ipc', + '../ppapi/ppapi_internal.gyp:ppapi_shared', ], }, { # enable_plugins==0 'sources!': [ diff --git a/content/renderer/pepper/resource_creation_impl.cc b/content/renderer/pepper/resource_creation_impl.cc index 42fd413..cef6fd3 100644 --- a/content/renderer/pepper/resource_creation_impl.cc +++ b/content/renderer/pepper/resource_creation_impl.cc @@ -245,6 +245,10 @@ PP_Resource ResourceCreationImpl::CreateTCPServerSocketPrivate( return 0; // Not supported in-process. } +PP_Resource ResourceCreationImpl::CreateTCPSocket1_0(PP_Instance instance) { + return 0; // Not supported in-process. +} + PP_Resource ResourceCreationImpl::CreateTCPSocket(PP_Instance instance) { return 0; // Not supported in-process. } diff --git a/content/renderer/pepper/resource_creation_impl.h b/content/renderer/pepper/resource_creation_impl.h index ace9935..adb7e5c 100644 --- a/content/renderer/pepper/resource_creation_impl.h +++ b/content/renderer/pepper/resource_creation_impl.h @@ -114,6 +114,7 @@ class ResourceCreationImpl : public ppapi::thunk::ResourceCreationAPI { virtual PP_Resource CreateTalk(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateTCPServerSocketPrivate( PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateTCPSocket1_0(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateTCPSocket(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateUDPSocket(PP_Instance instance) OVERRIDE; diff --git a/ppapi/api/ppb_tcp_socket.idl b/ppapi/api/ppb_tcp_socket.idl index 833879c..6c43712 100644 --- a/ppapi/api/ppb_tcp_socket.idl +++ b/ppapi/api/ppb_tcp_socket.idl @@ -7,10 +7,9 @@ * This file defines the <code>PPB_TCPSocket</code> interface. */ -[generate_thunk] - label Chrome { - M29 = 1.0 + M29 = 1.0, + M31 = 1.1 }; /** @@ -45,14 +44,23 @@ enum PP_TCPSocket_Option { * size. Even if <code>SetOption()</code> succeeds, the browser doesn't * guarantee it will conform to the size. */ - PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE = 2 + PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE = 2, + + /** + * Allows the socket to share the local address to which it will be bound. + * Value's type should be <code>PP_VARTYPE_BOOL</code>. + * This option can only be set before calling <code>Bind()</code>. + * Supported since version 1.1. + */ + PP_TCPSOCKET_OPTION_ADDRESS_REUSE = 3 }; /** * The <code>PPB_TCPSocket</code> interface provides TCP socket operations. * * Permissions: Apps permission <code>socket</code> with subrule - * <code>tcp-connect</code> is required for <code>Connect()</code>. + * <code>tcp-connect</code> is required for <code>Connect()</code>; subrule + * <code>tcp-listen</code> is required for <code>Listen()</code>. * For more details about network communication permissions, please see: * http://developer.chrome.com/apps/app_network.html */ @@ -79,7 +87,27 @@ interface PPB_TCPSocket { PP_Bool IsTCPSocket([in] PP_Resource resource); /** - * Connects the socket to the given address. + * Binds the socket to the given address. The socket must not be bound. + * + * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP + * socket. + * @param[in] addr A <code>PPB_NetAddress</code> resource. + * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + * completion. + * + * @return An int32_t containing an error code from <code>pp_errors.h</code>, + * including (but not limited to): + * - <code>PP_ERROR_ADDRESS_IN_USE</code>: the address is already in use. + * - <code>PP_ERROR_ADDRESS_INVALID</code>: the address is invalid. + */ + [version=1.1] + int32_t Bind([in] PP_Resource tcp_socket, + [in] PP_Resource addr, + [in] PP_CompletionCallback callback); + + /** + * Connects the socket to the given address. The socket must not be listening. + * Binding the socket beforehand is optional. * * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP * socket. @@ -98,13 +126,18 @@ interface PPB_TCPSocket { * - <code>PP_ERROR_CONNECTION_FAILED</code>: the connection attempt failed. * - <code>PP_ERROR_CONNECTION_TIMEDOUT</code>: the connection attempt timed * out. + * + * If the socket is listening/connected or has a pending listen/connect + * request, <code>Connect()</code> will fail without starting a connection + * attempt. Otherwise, any failure during the connection attempt will cause + * the socket to be closed. */ int32_t Connect([in] PP_Resource tcp_socket, [in] PP_Resource addr, [in] PP_CompletionCallback callback); /** - * Gets the local address of the socket, if it is connected. + * Gets the local address of the socket, if it is bound. * * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP * socket. @@ -162,13 +195,53 @@ interface PPB_TCPSocket { [in] str_t buffer, [in] int32_t bytes_to_write, [in] PP_CompletionCallback callback); + /** + * Starts listening. The socket must be bound and not connected. + * + * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP + * socket. + * @param[in] backlog A hint to determine the maximum length to which the + * queue of pending connections may grow. + * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + * completion. + * + * @return An int32_t containing an error code from <code>pp_errors.h</code>, + * including (but not limited to): + * - <code>PP_ERROR_NOACCESS</code>: the caller doesn't have required + * permissions. + * - <code>PP_ERROR_ADDRESS_IN_USE</code>: Another socket is already listening + * on the same port. + */ + [version=1.1] + int32_t Listen([in] PP_Resource tcp_socket, + [in] int32_t backlog, + [in] PP_CompletionCallback callback); + + /** + * Accepts a connection. The socket must be listening. + * + * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP + * socket. + * @param[out] accepted_tcp_socket Stores the accepted TCP socket on success. + * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + * completion. + * + * @return An int32_t containing an error code from <code>pp_errors.h</code>, + * including (but not limited to): + * - <code>PP_ERROR_CONNECTION_ABORTED</code>: A connection has been aborted. + */ + [version=1.1] + int32_t Accept([in] PP_Resource tcp_socket, + [out] PP_Resource accepted_tcp_socket, + [in] PP_CompletionCallback callback); /** - * Cancels all pending reads and writes and disconnects the socket. Any - * pending callbacks will still run, reporting <code>PP_ERROR_ABORTED</code> - * if pending IO was interrupted. After a call to this method, no output - * buffer pointers passed into previous <code>Read()</code> calls will be - * accessed. It is not valid to call <code>Connect()</code> again. + * Cancels all pending operations and closes the socket. Any pending callbacks + * will still run, reporting <code>PP_ERROR_ABORTED</code> if pending IO was + * interrupted. After a call to this method, no output buffer pointers passed + * into previous <code>Read()</code> or <code>Accept()</code> calls will be + * accessed. It is not valid to call <code>Connect()</code> or + * <code>Listen()</code> again. * * The socket is implicitly closed if it is destroyed, so you are not required * to call this method. diff --git a/ppapi/c/ppb_tcp_socket.h b/ppapi/c/ppb_tcp_socket.h index 467656f..9a9b964 100644 --- a/ppapi/c/ppb_tcp_socket.h +++ b/ppapi/c/ppb_tcp_socket.h @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* From ppb_tcp_socket.idl modified Sat Jun 22 11:17:34 2013. */ +/* From ppb_tcp_socket.idl modified Thu Sep 19 14:01:43 2013. */ #ifndef PPAPI_C_PPB_TCP_SOCKET_H_ #define PPAPI_C_PPB_TCP_SOCKET_H_ @@ -17,7 +17,8 @@ #include "ppapi/c/pp_var.h" #define PPB_TCPSOCKET_INTERFACE_1_0 "PPB_TCPSocket;1.0" -#define PPB_TCPSOCKET_INTERFACE PPB_TCPSOCKET_INTERFACE_1_0 +#define PPB_TCPSOCKET_INTERFACE_1_1 "PPB_TCPSocket;1.1" +#define PPB_TCPSOCKET_INTERFACE PPB_TCPSOCKET_INTERFACE_1_1 /** * @file @@ -58,7 +59,14 @@ typedef enum { * size. Even if <code>SetOption()</code> succeeds, the browser doesn't * guarantee it will conform to the size. */ - PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE = 2 + PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE = 2, + /** + * Allows the socket to share the local address to which it will be bound. + * Value's type should be <code>PP_VARTYPE_BOOL</code>. + * This option can only be set before calling <code>Bind()</code>. + * Supported since version 1.1. + */ + PP_TCPSOCKET_OPTION_ADDRESS_REUSE = 3 } PP_TCPSocket_Option; PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_TCPSocket_Option, 4); /** @@ -73,11 +81,12 @@ PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_TCPSocket_Option, 4); * The <code>PPB_TCPSocket</code> interface provides TCP socket operations. * * Permissions: Apps permission <code>socket</code> with subrule - * <code>tcp-connect</code> is required for <code>Connect()</code>. + * <code>tcp-connect</code> is required for <code>Connect()</code>; subrule + * <code>tcp-listen</code> is required for <code>Listen()</code>. * For more details about network communication permissions, please see: * http://developer.chrome.com/apps/app_network.html */ -struct PPB_TCPSocket_1_0 { +struct PPB_TCPSocket_1_1 { /** * Creates a TCP socket resource. * @@ -98,7 +107,25 @@ struct PPB_TCPSocket_1_0 { */ PP_Bool (*IsTCPSocket)(PP_Resource resource); /** - * Connects the socket to the given address. + * Binds the socket to the given address. The socket must not be bound. + * + * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP + * socket. + * @param[in] addr A <code>PPB_NetAddress</code> resource. + * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + * completion. + * + * @return An int32_t containing an error code from <code>pp_errors.h</code>, + * including (but not limited to): + * - <code>PP_ERROR_ADDRESS_IN_USE</code>: the address is already in use. + * - <code>PP_ERROR_ADDRESS_INVALID</code>: the address is invalid. + */ + int32_t (*Bind)(PP_Resource tcp_socket, + PP_Resource addr, + struct PP_CompletionCallback callback); + /** + * Connects the socket to the given address. The socket must not be listening. + * Binding the socket beforehand is optional. * * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP * socket. @@ -117,12 +144,17 @@ struct PPB_TCPSocket_1_0 { * - <code>PP_ERROR_CONNECTION_FAILED</code>: the connection attempt failed. * - <code>PP_ERROR_CONNECTION_TIMEDOUT</code>: the connection attempt timed * out. + * + * If the socket is listening/connected or has a pending listen/connect + * request, <code>Connect()</code> will fail without starting a connection + * attempt. Otherwise, any failure during the connection attempt will cause + * the socket to be closed. */ int32_t (*Connect)(PP_Resource tcp_socket, PP_Resource addr, struct PP_CompletionCallback callback); /** - * Gets the local address of the socket, if it is connected. + * Gets the local address of the socket, if it is bound. * * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP * socket. @@ -178,11 +210,48 @@ struct PPB_TCPSocket_1_0 { int32_t bytes_to_write, struct PP_CompletionCallback callback); /** - * Cancels all pending reads and writes and disconnects the socket. Any - * pending callbacks will still run, reporting <code>PP_ERROR_ABORTED</code> - * if pending IO was interrupted. After a call to this method, no output - * buffer pointers passed into previous <code>Read()</code> calls will be - * accessed. It is not valid to call <code>Connect()</code> again. + * Starts listening. The socket must be bound and not connected. + * + * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP + * socket. + * @param[in] backlog A hint to determine the maximum length to which the + * queue of pending connections may grow. + * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + * completion. + * + * @return An int32_t containing an error code from <code>pp_errors.h</code>, + * including (but not limited to): + * - <code>PP_ERROR_NOACCESS</code>: the caller doesn't have required + * permissions. + * - <code>PP_ERROR_ADDRESS_IN_USE</code>: Another socket is already listening + * on the same port. + */ + int32_t (*Listen)(PP_Resource tcp_socket, + int32_t backlog, + struct PP_CompletionCallback callback); + /** + * Accepts a connection. The socket must be listening. + * + * @param[in] tcp_socket A <code>PP_Resource</code> corresponding to a TCP + * socket. + * @param[out] accepted_tcp_socket Stores the accepted TCP socket on success. + * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + * completion. + * + * @return An int32_t containing an error code from <code>pp_errors.h</code>, + * including (but not limited to): + * - <code>PP_ERROR_CONNECTION_ABORTED</code>: A connection has been aborted. + */ + int32_t (*Accept)(PP_Resource tcp_socket, + PP_Resource* accepted_tcp_socket, + struct PP_CompletionCallback callback); + /** + * Cancels all pending operations and closes the socket. Any pending callbacks + * will still run, reporting <code>PP_ERROR_ABORTED</code> if pending IO was + * interrupted. After a call to this method, no output buffer pointers passed + * into previous <code>Read()</code> or <code>Accept()</code> calls will be + * accessed. It is not valid to call <code>Connect()</code> or + * <code>Listen()</code> again. * * The socket is implicitly closed if it is destroyed, so you are not required * to call this method. @@ -211,7 +280,30 @@ struct PPB_TCPSocket_1_0 { struct PP_CompletionCallback callback); }; -typedef struct PPB_TCPSocket_1_0 PPB_TCPSocket; +typedef struct PPB_TCPSocket_1_1 PPB_TCPSocket; + +struct PPB_TCPSocket_1_0 { + PP_Resource (*Create)(PP_Instance instance); + PP_Bool (*IsTCPSocket)(PP_Resource resource); + int32_t (*Connect)(PP_Resource tcp_socket, + PP_Resource addr, + struct PP_CompletionCallback callback); + PP_Resource (*GetLocalAddress)(PP_Resource tcp_socket); + PP_Resource (*GetRemoteAddress)(PP_Resource tcp_socket); + int32_t (*Read)(PP_Resource tcp_socket, + char* buffer, + int32_t bytes_to_read, + struct PP_CompletionCallback callback); + int32_t (*Write)(PP_Resource tcp_socket, + const char* buffer, + int32_t bytes_to_write, + struct PP_CompletionCallback callback); + void (*Close)(PP_Resource tcp_socket); + int32_t (*SetOption)(PP_Resource tcp_socket, + PP_TCPSocket_Option name, + struct PP_Var value, + struct PP_CompletionCallback callback); +}; /** * @} */ diff --git a/ppapi/cpp/tcp_socket.cc b/ppapi/cpp/tcp_socket.cc index f0002fa..ab08ff3 100644 --- a/ppapi/cpp/tcp_socket.cc +++ b/ppapi/cpp/tcp_socket.cc @@ -17,13 +17,20 @@ template <> const char* interface_name<PPB_TCPSocket_1_0>() { return PPB_TCPSOCKET_INTERFACE_1_0; } +template <> const char* interface_name<PPB_TCPSocket_1_1>() { + return PPB_TCPSOCKET_INTERFACE_1_1; +} + } // namespace TCPSocket::TCPSocket() { } TCPSocket::TCPSocket(const InstanceHandle& instance) { - if (has_interface<PPB_TCPSocket_1_0>()) { + if (has_interface<PPB_TCPSocket_1_1>()) { + PassRefFromConstructor(get_interface<PPB_TCPSocket_1_1>()->Create( + instance.pp_instance())); + } else if (has_interface<PPB_TCPSocket_1_0>()) { PassRefFromConstructor(get_interface<PPB_TCPSocket_1_0>()->Create( instance.pp_instance())); } @@ -46,11 +53,25 @@ TCPSocket& TCPSocket::operator=(const TCPSocket& other) { // static bool TCPSocket::IsAvailable() { - return has_interface<PPB_TCPSocket_1_0>(); + return has_interface<PPB_TCPSocket_1_1>() || + has_interface<PPB_TCPSocket_1_0>(); +} + +int32_t TCPSocket::Bind(const NetAddress& addr, + const CompletionCallback& callback) { + if (has_interface<PPB_TCPSocket_1_1>()) { + return get_interface<PPB_TCPSocket_1_1>()->Bind( + pp_resource(), addr.pp_resource(), callback.pp_completion_callback()); + } + return callback.MayForce(PP_ERROR_NOINTERFACE); } int32_t TCPSocket::Connect(const NetAddress& addr, const CompletionCallback& callback) { + if (has_interface<PPB_TCPSocket_1_1>()) { + return get_interface<PPB_TCPSocket_1_1>()->Connect( + pp_resource(), addr.pp_resource(), callback.pp_completion_callback()); + } if (has_interface<PPB_TCPSocket_1_0>()) { return get_interface<PPB_TCPSocket_1_0>()->Connect( pp_resource(), addr.pp_resource(), callback.pp_completion_callback()); @@ -59,6 +80,11 @@ int32_t TCPSocket::Connect(const NetAddress& addr, } NetAddress TCPSocket::GetLocalAddress() const { + if (has_interface<PPB_TCPSocket_1_1>()) { + return NetAddress( + PASS_REF, + get_interface<PPB_TCPSocket_1_1>()->GetLocalAddress(pp_resource())); + } if (has_interface<PPB_TCPSocket_1_0>()) { return NetAddress( PASS_REF, @@ -68,6 +94,11 @@ NetAddress TCPSocket::GetLocalAddress() const { } NetAddress TCPSocket::GetRemoteAddress() const { + if (has_interface<PPB_TCPSocket_1_1>()) { + return NetAddress( + PASS_REF, + get_interface<PPB_TCPSocket_1_1>()->GetRemoteAddress(pp_resource())); + } if (has_interface<PPB_TCPSocket_1_0>()) { return NetAddress( PASS_REF, @@ -79,6 +110,11 @@ NetAddress TCPSocket::GetRemoteAddress() const { int32_t TCPSocket::Read(char* buffer, int32_t bytes_to_read, const CompletionCallback& callback) { + if (has_interface<PPB_TCPSocket_1_1>()) { + return get_interface<PPB_TCPSocket_1_1>()->Read( + pp_resource(), buffer, bytes_to_read, + callback.pp_completion_callback()); + } if (has_interface<PPB_TCPSocket_1_0>()) { return get_interface<PPB_TCPSocket_1_0>()->Read( pp_resource(), buffer, bytes_to_read, @@ -90,6 +126,11 @@ int32_t TCPSocket::Read(char* buffer, int32_t TCPSocket::Write(const char* buffer, int32_t bytes_to_write, const CompletionCallback& callback) { + if (has_interface<PPB_TCPSocket_1_1>()) { + return get_interface<PPB_TCPSocket_1_1>()->Write( + pp_resource(), buffer, bytes_to_write, + callback.pp_completion_callback()); + } if (has_interface<PPB_TCPSocket_1_0>()) { return get_interface<PPB_TCPSocket_1_0>()->Write( pp_resource(), buffer, bytes_to_write, @@ -98,14 +139,39 @@ int32_t TCPSocket::Write(const char* buffer, return callback.MayForce(PP_ERROR_NOINTERFACE); } +int32_t TCPSocket::Listen(int32_t backlog, + const CompletionCallback& callback) { + if (has_interface<PPB_TCPSocket_1_1>()) { + return get_interface<PPB_TCPSocket_1_1>()->Listen( + pp_resource(), backlog, callback.pp_completion_callback()); + } + return callback.MayForce(PP_ERROR_NOINTERFACE); +} + +int32_t TCPSocket::Accept( + const CompletionCallbackWithOutput<TCPSocket>& callback) { + if (has_interface<PPB_TCPSocket_1_1>()) { + return get_interface<PPB_TCPSocket_1_1>()->Accept( + pp_resource(), callback.output(), callback.pp_completion_callback()); + } + return callback.MayForce(PP_ERROR_NOINTERFACE); +} + void TCPSocket::Close() { - if (has_interface<PPB_TCPSocket_1_0>()) + if (has_interface<PPB_TCPSocket_1_1>()) { + get_interface<PPB_TCPSocket_1_1>()->Close(pp_resource()); + } else if (has_interface<PPB_TCPSocket_1_0>()) { get_interface<PPB_TCPSocket_1_0>()->Close(pp_resource()); + } } int32_t TCPSocket::SetOption(PP_TCPSocket_Option name, const Var& value, const CompletionCallback& callback) { + if (has_interface<PPB_TCPSocket_1_1>()) { + return get_interface<PPB_TCPSocket_1_1>()->SetOption( + pp_resource(), name, value.pp_var(), callback.pp_completion_callback()); + } if (has_interface<PPB_TCPSocket_1_0>()) { return get_interface<PPB_TCPSocket_1_0>()->SetOption( pp_resource(), name, value.pp_var(), callback.pp_completion_callback()); diff --git a/ppapi/cpp/tcp_socket.h b/ppapi/cpp/tcp_socket.h index 5a5e0f4..bf999b8 100644 --- a/ppapi/cpp/tcp_socket.h +++ b/ppapi/cpp/tcp_socket.h @@ -15,10 +15,13 @@ namespace pp { class CompletionCallback; class InstanceHandle; +template <typename T> class CompletionCallbackWithOutput; + /// The <code>TCPSocket</code> class provides TCP socket operations. /// /// Permissions: Apps permission <code>socket</code> with subrule -/// <code>tcp-connect</code> is required for <code>Connect()</code>. +/// <code>tcp-connect</code> is required for <code>Connect()</code>; subrule +/// <code>tcp-listen</code> is required for <code>Listen()</code>. /// For more details about network communication permissions, please see: /// http://developer.chrome.com/apps/app_network.html class TCPSocket : public Resource { @@ -60,7 +63,20 @@ class TCPSocket : public Resource { /// @return true if the interface is available, false otherwise. static bool IsAvailable(); - /// Connects the socket to the given address. + /// Binds the socket to the given address. The socket must not be bound. + /// + /// @param[in] addr A <code>NetAddress</code> object. + /// @param[in] callback A <code>CompletionCallback</code> to be called upon + /// completion. + /// + /// @return An int32_t containing an error code from <code>pp_errors.h</code>, + /// including (but not limited to): + /// - <code>PP_ERROR_ADDRESS_IN_USE</code>: the address is already in use. + /// - <code>PP_ERROR_ADDRESS_INVALID</code>: the address is invalid. + int32_t Bind(const NetAddress& addr, const CompletionCallback& callback); + + /// Connects the socket to the given address. The socket must not be + /// listening. Binding the socket beforehand is optional. /// /// @param[in] addr A <code>NetAddress</code> object. /// @param[in] callback A <code>CompletionCallback</code> to be called upon @@ -77,10 +93,14 @@ class TCPSocket : public Resource { /// - <code>PP_ERROR_CONNECTION_FAILED</code>: the connection attempt failed. /// - <code>PP_ERROR_CONNECTION_TIMEDOUT</code>: the connection attempt timed /// out. - int32_t Connect(const NetAddress& addr, - const CompletionCallback& callback); + /// + /// If the socket is listening/connected or has a pending listen/connect + /// request, <code>Connect()</code> will fail without starting a connection + /// attempt. Otherwise, any failure during the connection attempt will cause + /// the socket to be closed. + int32_t Connect(const NetAddress& addr, const CompletionCallback& callback); - /// Gets the local address of the socket, if it is connected. + /// Gets the local address of the socket, if it is bound. /// /// @return A <code>NetAddress</code> object. The object will be null /// (i.e., is_null() returns true) on failure. @@ -135,11 +155,38 @@ class TCPSocket : public Resource { int32_t bytes_to_write, const CompletionCallback& callback); - /// Cancels all pending reads and writes and disconnects the socket. Any - /// pending callbacks will still run, reporting <code>PP_ERROR_ABORTED</code> - /// if pending IO was interrupted. After a call to this method, no output - /// buffer pointers passed into previous <code>Read()</code> calls will be - /// accessed. It is not valid to call <code>Connect()</code> again. + /// Starts listening. The socket must be bound and not connected. + /// + /// @param[in] backlog A hint to determine the maximum length to which the + /// queue of pending connections may grow. + /// @param[in] callback A <code>CompletionCallback</code> to be called upon + /// completion. + /// + /// @return An int32_t containing an error code from <code>pp_errors.h</code>, + /// including (but not limited to): + /// - <code>PP_ERROR_NOACCESS</code>: the caller doesn't have required + /// permissions. + /// - <code>PP_ERROR_ADDRESS_IN_USE</code>: Another socket is already + /// listening on the same port. + int32_t Listen(int32_t backlog, + const CompletionCallback& callback); + + /// Accepts a connection. The socket must be listening. + /// + /// @param[in] callback A <code>CompletionCallbackWithOutput</code> to be + /// called upon completion. + /// + /// @return An int32_t containing an error code from <code>pp_errors.h</code>, + /// including (but not limited to): + /// - <code>PP_ERROR_CONNECTION_ABORTED</code>: A connection has been aborted. + int32_t Accept(const CompletionCallbackWithOutput<TCPSocket>& callback); + + /// Cancels all pending operations and closes the socket. Any pending + /// callbacks will still run, reporting <code>PP_ERROR_ABORTED</code> if + /// pending IO was interrupted. After a call to this method, no output buffer + /// pointers passed into previous <code>Read()</code> or <code>Accept()</code> + /// calls will be accessed. It is not valid to call <code>Connect()</code> or + /// <code>Listen()</code> again. /// /// The socket is implicitly closed if it is destroyed, so you are not /// required to call this method. @@ -155,7 +202,6 @@ class TCPSocket : public Resource { /// completion. /// /// @return An int32_t containing an error code from <code>pp_errors.h</code>. - //// int32_t SetOption(PP_TCPSocket_Option name, const Var& value, const CompletionCallback& callback); diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c index 91f9752..074bbbe 100644 --- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c +++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c @@ -158,6 +158,7 @@ static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_MouseLock_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_NetAddress_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_NetworkProxy_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_TCPSocket_1_0; +static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_TCPSocket_1_1; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_TextInputController_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_UDPSocket_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_URLLoader_1_0; @@ -1073,6 +1074,70 @@ static int32_t Pnacl_M29_PPB_TCPSocket_SetOption(PP_Resource tcp_socket, PP_TCPS /* End wrapper methods for PPB_TCPSocket_1_0 */ +/* Begin wrapper methods for PPB_TCPSocket_1_1 */ + +static PP_Resource Pnacl_M31_PPB_TCPSocket_Create(PP_Instance instance) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->Create(instance); +} + +static PP_Bool Pnacl_M31_PPB_TCPSocket_IsTCPSocket(PP_Resource resource) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->IsTCPSocket(resource); +} + +static int32_t Pnacl_M31_PPB_TCPSocket_Bind(PP_Resource tcp_socket, PP_Resource addr, struct PP_CompletionCallback* callback) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->Bind(tcp_socket, addr, *callback); +} + +static int32_t Pnacl_M31_PPB_TCPSocket_Connect(PP_Resource tcp_socket, PP_Resource addr, struct PP_CompletionCallback* callback) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->Connect(tcp_socket, addr, *callback); +} + +static PP_Resource Pnacl_M31_PPB_TCPSocket_GetLocalAddress(PP_Resource tcp_socket) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->GetLocalAddress(tcp_socket); +} + +static PP_Resource Pnacl_M31_PPB_TCPSocket_GetRemoteAddress(PP_Resource tcp_socket) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->GetRemoteAddress(tcp_socket); +} + +static int32_t Pnacl_M31_PPB_TCPSocket_Read(PP_Resource tcp_socket, char* buffer, int32_t bytes_to_read, struct PP_CompletionCallback* callback) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->Read(tcp_socket, buffer, bytes_to_read, *callback); +} + +static int32_t Pnacl_M31_PPB_TCPSocket_Write(PP_Resource tcp_socket, const char* buffer, int32_t bytes_to_write, struct PP_CompletionCallback* callback) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->Write(tcp_socket, buffer, bytes_to_write, *callback); +} + +static int32_t Pnacl_M31_PPB_TCPSocket_Listen(PP_Resource tcp_socket, int32_t backlog, struct PP_CompletionCallback* callback) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->Listen(tcp_socket, backlog, *callback); +} + +static int32_t Pnacl_M31_PPB_TCPSocket_Accept(PP_Resource tcp_socket, PP_Resource* accepted_tcp_socket, struct PP_CompletionCallback* callback) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->Accept(tcp_socket, accepted_tcp_socket, *callback); +} + +static void Pnacl_M31_PPB_TCPSocket_Close(PP_Resource tcp_socket) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + iface->Close(tcp_socket); +} + +static int32_t Pnacl_M31_PPB_TCPSocket_SetOption(PP_Resource tcp_socket, PP_TCPSocket_Option name, struct PP_Var* value, struct PP_CompletionCallback* callback) { + const struct PPB_TCPSocket_1_1 *iface = Pnacl_WrapperInfo_PPB_TCPSocket_1_1.real_iface; + return iface->SetOption(tcp_socket, name, *value, *callback); +} + +/* End wrapper methods for PPB_TCPSocket_1_1 */ + /* Begin wrapper methods for PPB_TextInputController_1_0 */ static void Pnacl_M30_PPB_TextInputController_SetTextInputType(PP_Instance instance, PP_TextInput_Type type) { @@ -4265,6 +4330,21 @@ struct PPB_TCPSocket_1_0 Pnacl_Wrappers_PPB_TCPSocket_1_0 = { .SetOption = (int32_t (*)(PP_Resource tcp_socket, PP_TCPSocket_Option name, struct PP_Var value, struct PP_CompletionCallback callback))&Pnacl_M29_PPB_TCPSocket_SetOption }; +struct PPB_TCPSocket_1_1 Pnacl_Wrappers_PPB_TCPSocket_1_1 = { + .Create = (PP_Resource (*)(PP_Instance instance))&Pnacl_M31_PPB_TCPSocket_Create, + .IsTCPSocket = (PP_Bool (*)(PP_Resource resource))&Pnacl_M31_PPB_TCPSocket_IsTCPSocket, + .Bind = (int32_t (*)(PP_Resource tcp_socket, PP_Resource addr, struct PP_CompletionCallback callback))&Pnacl_M31_PPB_TCPSocket_Bind, + .Connect = (int32_t (*)(PP_Resource tcp_socket, PP_Resource addr, struct PP_CompletionCallback callback))&Pnacl_M31_PPB_TCPSocket_Connect, + .GetLocalAddress = (PP_Resource (*)(PP_Resource tcp_socket))&Pnacl_M31_PPB_TCPSocket_GetLocalAddress, + .GetRemoteAddress = (PP_Resource (*)(PP_Resource tcp_socket))&Pnacl_M31_PPB_TCPSocket_GetRemoteAddress, + .Read = (int32_t (*)(PP_Resource tcp_socket, char* buffer, int32_t bytes_to_read, struct PP_CompletionCallback callback))&Pnacl_M31_PPB_TCPSocket_Read, + .Write = (int32_t (*)(PP_Resource tcp_socket, const char* buffer, int32_t bytes_to_write, struct PP_CompletionCallback callback))&Pnacl_M31_PPB_TCPSocket_Write, + .Listen = (int32_t (*)(PP_Resource tcp_socket, int32_t backlog, struct PP_CompletionCallback callback))&Pnacl_M31_PPB_TCPSocket_Listen, + .Accept = (int32_t (*)(PP_Resource tcp_socket, PP_Resource* accepted_tcp_socket, struct PP_CompletionCallback callback))&Pnacl_M31_PPB_TCPSocket_Accept, + .Close = (void (*)(PP_Resource tcp_socket))&Pnacl_M31_PPB_TCPSocket_Close, + .SetOption = (int32_t (*)(PP_Resource tcp_socket, PP_TCPSocket_Option name, struct PP_Var value, struct PP_CompletionCallback callback))&Pnacl_M31_PPB_TCPSocket_SetOption +}; + struct PPB_TextInputController_1_0 Pnacl_Wrappers_PPB_TextInputController_1_0 = { .SetTextInputType = (void (*)(PP_Instance instance, PP_TextInput_Type type))&Pnacl_M30_PPB_TextInputController_SetTextInputType, .UpdateCaretPosition = (void (*)(PP_Instance instance, const struct PP_Rect* caret))&Pnacl_M30_PPB_TextInputController_UpdateCaretPosition, @@ -5239,6 +5319,12 @@ static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_TCPSocket_1_0 = { .real_iface = NULL }; +static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_TCPSocket_1_1 = { + .iface_macro = PPB_TCPSOCKET_INTERFACE_1_1, + .wrapped_iface = (void *) &Pnacl_Wrappers_PPB_TCPSocket_1_1, + .real_iface = NULL +}; + static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_TextInputController_1_0 = { .iface_macro = PPB_TEXTINPUTCONTROLLER_INTERFACE_1_0, .wrapped_iface = (void *) &Pnacl_Wrappers_PPB_TextInputController_1_0, @@ -5719,6 +5805,7 @@ static struct __PnaclWrapperInfo *s_ppb_wrappers[] = { &Pnacl_WrapperInfo_PPB_NetAddress_1_0, &Pnacl_WrapperInfo_PPB_NetworkProxy_1_0, &Pnacl_WrapperInfo_PPB_TCPSocket_1_0, + &Pnacl_WrapperInfo_PPB_TCPSocket_1_1, &Pnacl_WrapperInfo_PPB_TextInputController_1_0, &Pnacl_WrapperInfo_PPB_UDPSocket_1_0, &Pnacl_WrapperInfo_PPB_URLLoader_1_0, diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi index 2ee964e..699f603 100644 --- a/ppapi/ppapi_shared.gypi +++ b/ppapi/ppapi_shared.gypi @@ -71,6 +71,8 @@ 'shared_impl/ppb_opengles2_shared.h', 'shared_impl/ppb_resource_array_shared.cc', 'shared_impl/ppb_resource_array_shared.h', + 'shared_impl/ppb_tcp_socket_shared.cc', + 'shared_impl/ppb_tcp_socket_shared.h', 'shared_impl/ppb_trace_event_impl.cc', 'shared_impl/ppb_trace_event_impl.h', 'shared_impl/ppb_url_util_shared.cc', diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 95bbd8b..24beb03 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -62,6 +62,7 @@ #include "ppapi/shared_impl/ppapi_preferences.h" #include "ppapi/shared_impl/ppb_device_ref_shared.h" #include "ppapi/shared_impl/ppb_input_event_shared.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" #include "ppapi/shared_impl/ppb_view_shared.h" #include "ppapi/shared_impl/ppp_flash_browser_operations_shared.h" #include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h" @@ -74,6 +75,8 @@ #define IPC_MESSAGE_START PpapiMsgStart +IPC_ENUM_TRAITS_MAX_VALUE(ppapi::TCPSocketVersion, + ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) IPC_ENUM_TRAITS(PP_AudioSampleRate) IPC_ENUM_TRAITS(PP_DeviceType_Dev) IPC_ENUM_TRAITS(PP_DecryptorStreamType) @@ -99,7 +102,7 @@ IPC_ENUM_TRAITS_MAX_VALUE(PP_TalkEvent, PP_TALKEVENT_NUM_EVENTS - 1) IPC_ENUM_TRAITS_MAX_VALUE(PP_TalkPermission, PP_TALKPERMISSION_NUM_PERMISSIONS - 1) IPC_ENUM_TRAITS_MAX_VALUE(PP_TCPSocket_Option, - PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE) + PP_TCPSOCKET_OPTION_ADDRESS_REUSE) IPC_ENUM_TRAITS(PP_TextInput_Type) IPC_ENUM_TRAITS(PP_TrueTypeFontFamily_Dev) IPC_ENUM_TRAITS(PP_TrueTypeFontStyle_Dev) @@ -1450,11 +1453,16 @@ IPC_MESSAGE_CONTROL1(PpapiPluginMsg_Printing_GetDefaultPrintSettingsReply, // TCP Socket ------------------------------------------------------------------ // Creates a PPB_TCPSocket resource. -IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Create) +IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Create, + ppapi::TCPSocketVersion /* version */) // Creates a PPB_TCPSocket_Private resource. IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_CreatePrivate) +IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Bind, + PP_NetAddress_Private /* net_addr */) +IPC_MESSAGE_CONTROL1(PpapiPluginMsg_TCPSocket_BindReply, + PP_NetAddress_Private /* local_addr */) IPC_MESSAGE_CONTROL2(PpapiHostMsg_TCPSocket_Connect, std::string /* host */, uint16_t /* port */) @@ -1477,7 +1485,15 @@ IPC_MESSAGE_CONTROL1(PpapiPluginMsg_TCPSocket_ReadReply, IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Write, std::string /* data */) IPC_MESSAGE_CONTROL0(PpapiPluginMsg_TCPSocket_WriteReply) -IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Disconnect) +IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Listen, + int32_t /* backlog */) +IPC_MESSAGE_CONTROL0(PpapiPluginMsg_TCPSocket_ListenReply) +IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Accept) +IPC_MESSAGE_CONTROL3(PpapiPluginMsg_TCPSocket_AcceptReply, + int /* pending_host_id*/, + PP_NetAddress_Private /* local_addr */, + PP_NetAddress_Private /* remote_addr */) +IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Close) IPC_MESSAGE_CONTROL2(PpapiHostMsg_TCPSocket_SetOption, PP_TCPSocket_Option /* name */, ppapi::SocketOptionData /* value */) diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc index 60841022..ea01f6e 100644 --- a/ppapi/proxy/resource_creation_proxy.cc +++ b/ppapi/proxy/resource_creation_proxy.cc @@ -323,9 +323,17 @@ PP_Resource ResourceCreationProxy::CreateTCPServerSocketPrivate( GetReference(); } +PP_Resource ResourceCreationProxy::CreateTCPSocket1_0( + PP_Instance instance) { + return (new TCPSocketResource(GetConnection(), instance, + TCP_SOCKET_VERSION_1_0))->GetReference(); +} + PP_Resource ResourceCreationProxy::CreateTCPSocket( PP_Instance instance) { - return (new TCPSocketResource(GetConnection(), instance))->GetReference(); + return (new TCPSocketResource( + GetConnection(), instance, TCP_SOCKET_VERSION_1_1_OR_ABOVE))-> + GetReference(); } PP_Resource ResourceCreationProxy::CreateTCPSocketPrivate( diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h index 210e23c..509b155 100644 --- a/ppapi/proxy/resource_creation_proxy.h +++ b/ppapi/proxy/resource_creation_proxy.h @@ -138,6 +138,7 @@ class ResourceCreationProxy : public InterfaceProxy, virtual PP_Resource CreatePrinting(PP_Instance) OVERRIDE; virtual PP_Resource CreateTCPServerSocketPrivate( PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateTCPSocket1_0(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateTCPSocket(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateUDPSocket(PP_Instance instance) OVERRIDE; diff --git a/ppapi/proxy/tcp_socket_private_resource.cc b/ppapi/proxy/tcp_socket_private_resource.cc index 0698e49..76ed4b9 100644 --- a/ppapi/proxy/tcp_socket_private_resource.cc +++ b/ppapi/proxy/tcp_socket_private_resource.cc @@ -5,13 +5,14 @@ #include "ppapi/proxy/tcp_socket_private_resource.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" namespace ppapi { namespace proxy { TCPSocketPrivateResource::TCPSocketPrivateResource(Connection connection, PP_Instance instance) - : TCPSocketResourceBase(connection, instance, true) { + : TCPSocketResourceBase(connection, instance, TCP_SOCKET_VERSION_PRIVATE) { SendCreate(BROWSER, PpapiHostMsg_TCPSocket_CreatePrivate()); } @@ -21,14 +22,12 @@ TCPSocketPrivateResource::TCPSocketPrivateResource( int pending_resource_id, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr) - : TCPSocketResourceBase(connection, instance, true, - local_addr, - remote_addr) { + : TCPSocketResourceBase(connection, instance, TCP_SOCKET_VERSION_PRIVATE, + local_addr, remote_addr) { AttachToPendingHost(BROWSER, pending_resource_id); } TCPSocketPrivateResource::~TCPSocketPrivateResource() { - DisconnectImpl(); } thunk::PPB_TCPSocket_Private_API* @@ -91,7 +90,7 @@ int32_t TCPSocketPrivateResource::Write( } void TCPSocketPrivateResource::Disconnect() { - DisconnectImpl(); + CloseImpl(); } int32_t TCPSocketPrivateResource::SetOption( @@ -109,5 +108,13 @@ int32_t TCPSocketPrivateResource::SetOption( } } +PP_Resource TCPSocketPrivateResource::CreateAcceptedSocket( + int /* pending_host_id */, + const PP_NetAddress_Private& /* local_addr */, + const PP_NetAddress_Private& /* remote_addr */) { + NOTREACHED(); + return 0; +} + } // namespace proxy } // namespace ppapi diff --git a/ppapi/proxy/tcp_socket_private_resource.h b/ppapi/proxy/tcp_socket_private_resource.h index f79ab9d..9ae9bb2 100644 --- a/ppapi/proxy/tcp_socket_private_resource.h +++ b/ppapi/proxy/tcp_socket_private_resource.h @@ -59,6 +59,12 @@ class PPAPI_PROXY_EXPORT TCPSocketPrivateResource const PP_Var& value, scoped_refptr<TrackedCallback> callback) OVERRIDE; + // TCPSocketResourceBase implementation. + virtual PP_Resource CreateAcceptedSocket( + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) OVERRIDE; + private: DISALLOW_COPY_AND_ASSIGN(TCPSocketPrivateResource); }; diff --git a/ppapi/proxy/tcp_socket_resource.cc b/ppapi/proxy/tcp_socket_resource.cc index 085aab3..f8f8f68 100644 --- a/ppapi/proxy/tcp_socket_resource.cc +++ b/ppapi/proxy/tcp_socket_resource.cc @@ -4,7 +4,9 @@ #include "ppapi/proxy/tcp_socket_resource.h" +#include "base/logging.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" #include "ppapi/thunk/enter.h" #include "ppapi/thunk/ppb_net_address_api.h" @@ -19,19 +21,40 @@ typedef thunk::EnterResourceNoLock<thunk::PPB_NetAddress_API> } // namespace TCPSocketResource::TCPSocketResource(Connection connection, - PP_Instance instance) - : TCPSocketResourceBase(connection, instance, false) { - SendCreate(BROWSER, PpapiHostMsg_TCPSocket_Create()); + PP_Instance instance, + TCPSocketVersion version) + : TCPSocketResourceBase(connection, instance, version) { + DCHECK_NE(version, TCP_SOCKET_VERSION_PRIVATE); + SendCreate(BROWSER, PpapiHostMsg_TCPSocket_Create(version)); +} + +TCPSocketResource::TCPSocketResource(Connection connection, + PP_Instance instance, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) + : TCPSocketResourceBase(connection, instance, + TCP_SOCKET_VERSION_1_1_OR_ABOVE, local_addr, + remote_addr) { + AttachToPendingHost(BROWSER, pending_host_id); } TCPSocketResource::~TCPSocketResource() { - DisconnectImpl(); } thunk::PPB_TCPSocket_API* TCPSocketResource::AsPPB_TCPSocket_API() { return this; } +int32_t TCPSocketResource::Bind(PP_Resource addr, + scoped_refptr<TrackedCallback> callback) { + EnterNetAddressNoLock enter(addr, true); + if (enter.failed()) + return PP_ERROR_BADARGUMENT; + + return BindImpl(&enter.object()->GetNetAddressPrivate(), callback); +} + int32_t TCPSocketResource::Connect(PP_Resource addr, scoped_refptr<TrackedCallback> callback) { EnterNetAddressNoLock enter(addr, true); @@ -78,8 +101,18 @@ int32_t TCPSocketResource::Write(const char* buffer, return WriteImpl(buffer, bytes_to_write, callback); } +int32_t TCPSocketResource::Listen(int32_t backlog, + scoped_refptr<TrackedCallback> callback) { + return ListenImpl(backlog, callback); +} + +int32_t TCPSocketResource::Accept(PP_Resource* accepted_tcp_socket, + scoped_refptr<TrackedCallback> callback) { + return AcceptImpl(accepted_tcp_socket, callback); +} + void TCPSocketResource::Close() { - DisconnectImpl(); + CloseImpl(); } int32_t TCPSocketResource::SetOption(PP_TCPSocket_Option name, @@ -88,5 +121,13 @@ int32_t TCPSocketResource::SetOption(PP_TCPSocket_Option name, return SetOptionImpl(name, value, callback); } +PP_Resource TCPSocketResource::CreateAcceptedSocket( + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + return (new TCPSocketResource(connection(), pp_instance(), pending_host_id, + local_addr, remote_addr))->GetReference(); +} + } // namespace proxy } // namespace ppapi diff --git a/ppapi/proxy/tcp_socket_resource.h b/ppapi/proxy/tcp_socket_resource.h index 05b4fe3..5dbfdd5 100644 --- a/ppapi/proxy/tcp_socket_resource.h +++ b/ppapi/proxy/tcp_socket_resource.h @@ -11,18 +11,27 @@ #include "ppapi/thunk/ppb_tcp_socket_api.h" namespace ppapi { + +enum TCPSocketVersion; + namespace proxy { class PPAPI_PROXY_EXPORT TCPSocketResource : public thunk::PPB_TCPSocket_API, public TCPSocketResourceBase { public: - TCPSocketResource(Connection connection, PP_Instance instance); + // C-tor used for new sockets created. + TCPSocketResource(Connection connection, + PP_Instance instance, + TCPSocketVersion version); + virtual ~TCPSocketResource(); // PluginResource overrides. virtual thunk::PPB_TCPSocket_API* AsPPB_TCPSocket_API() OVERRIDE; // thunk::PPB_TCPSocket_API implementation. + virtual int32_t Bind(PP_Resource addr, + scoped_refptr<TrackedCallback> callback) OVERRIDE; virtual int32_t Connect(PP_Resource addr, scoped_refptr<TrackedCallback> callback) OVERRIDE; virtual PP_Resource GetLocalAddress() OVERRIDE; @@ -33,12 +42,29 @@ class PPAPI_PROXY_EXPORT TCPSocketResource : public thunk::PPB_TCPSocket_API, virtual int32_t Write(const char* buffer, int32_t bytes_to_write, scoped_refptr<TrackedCallback> callback) OVERRIDE; + virtual int32_t Listen(int32_t backlog, + scoped_refptr<TrackedCallback> callback) OVERRIDE; + virtual int32_t Accept(PP_Resource* accepted_tcp_socket, + scoped_refptr<TrackedCallback> callback) OVERRIDE; virtual void Close() OVERRIDE; virtual int32_t SetOption(PP_TCPSocket_Option name, const PP_Var& value, scoped_refptr<TrackedCallback> callback) OVERRIDE; + // TCPSocketResourceBase implementation. + virtual PP_Resource CreateAcceptedSocket( + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) OVERRIDE; + private: + // C-tor used for accepted sockets. + TCPSocketResource(Connection connection, + PP_Instance instance, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr); + DISALLOW_COPY_AND_ASSIGN(TCPSocketResource); }; diff --git a/ppapi/proxy/tcp_socket_resource_base.cc b/ppapi/proxy/tcp_socket_resource_base.cc index f02895b..2590c41 100644 --- a/ppapi/proxy/tcp_socket_resource_base.cc +++ b/ppapi/proxy/tcp_socket_resource_base.cc @@ -32,12 +32,13 @@ const int32_t TCPSocketResourceBase::kMaxReceiveBufferSize = TCPSocketResourceBase::TCPSocketResourceBase(Connection connection, PP_Instance instance, - bool private_api) + TCPSocketVersion version) : PluginResource(connection, instance), - connection_state_(BEFORE_CONNECT), + state_(TCPSocketState::INITIAL), read_buffer_(NULL), bytes_to_read_(-1), - private_api_(private_api) { + accepted_tcp_socket_(NULL), + version_(version) { local_addr_.size = 0; memset(local_addr_.data, 0, arraysize(local_addr_.data) * sizeof(*local_addr_.data)); @@ -49,19 +50,42 @@ TCPSocketResourceBase::TCPSocketResourceBase(Connection connection, TCPSocketResourceBase::TCPSocketResourceBase( Connection connection, PP_Instance instance, - bool private_api, + TCPSocketVersion version, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr) : PluginResource(connection, instance), - connection_state_(CONNECTED), + state_(TCPSocketState::CONNECTED), read_buffer_(NULL), bytes_to_read_(-1), local_addr_(local_addr), remote_addr_(remote_addr), - private_api_(private_api) { + accepted_tcp_socket_(NULL), + version_(version) { } TCPSocketResourceBase::~TCPSocketResourceBase() { + CloseImpl(); +} + +int32_t TCPSocketResourceBase::BindImpl( + const PP_NetAddress_Private* addr, + scoped_refptr<TrackedCallback> callback) { + if (!addr) + return PP_ERROR_BADARGUMENT; + if (state_.IsPending(TCPSocketState::BIND)) + return PP_ERROR_INPROGRESS; + if (!state_.IsValidTransition(TCPSocketState::BIND)) + return PP_ERROR_FAILED; + + bind_callback_ = callback; + state_.SetPendingTransition(TCPSocketState::BIND); + + Call<PpapiPluginMsg_TCPSocket_BindReply>( + BROWSER, + PpapiHostMsg_TCPSocket_Bind(*addr), + base::Bind(&TCPSocketResourceBase::OnPluginMsgBindReply, + base::Unretained(this))); + return PP_OK_COMPLETIONPENDING; } int32_t TCPSocketResourceBase::ConnectImpl( @@ -70,12 +94,13 @@ int32_t TCPSocketResourceBase::ConnectImpl( scoped_refptr<TrackedCallback> callback) { if (!host) return PP_ERROR_BADARGUMENT; - if (connection_state_ != BEFORE_CONNECT) + if (state_.IsPending(TCPSocketState::CONNECT)) + return PP_ERROR_INPROGRESS; + if (!state_.IsValidTransition(TCPSocketState::CONNECT)) return PP_ERROR_FAILED; - if (TrackedCallback::IsPending(connect_callback_)) - return PP_ERROR_INPROGRESS; // Can only have one pending request. connect_callback_ = callback; + state_.SetPendingTransition(TCPSocketState::CONNECT); Call<PpapiPluginMsg_TCPSocket_ConnectReply>( BROWSER, @@ -90,12 +115,13 @@ int32_t TCPSocketResourceBase::ConnectWithNetAddressImpl( scoped_refptr<TrackedCallback> callback) { if (!addr) return PP_ERROR_BADARGUMENT; - if (connection_state_ != BEFORE_CONNECT) + if (state_.IsPending(TCPSocketState::CONNECT)) + return PP_ERROR_INPROGRESS; + if (!state_.IsValidTransition(TCPSocketState::CONNECT)) return PP_ERROR_FAILED; - if (TrackedCallback::IsPending(connect_callback_)) - return PP_ERROR_INPROGRESS; // Can only have one pending request. connect_callback_ = callback; + state_.SetPendingTransition(TCPSocketState::CONNECT); Call<PpapiPluginMsg_TCPSocket_ConnectReply>( BROWSER, @@ -107,7 +133,7 @@ int32_t TCPSocketResourceBase::ConnectWithNetAddressImpl( PP_Bool TCPSocketResourceBase::GetLocalAddressImpl( PP_NetAddress_Private* local_addr) { - if (!IsConnected() || !local_addr) + if (!state_.IsBound() || !local_addr) return PP_FALSE; *local_addr = local_addr_; return PP_TRUE; @@ -115,7 +141,7 @@ PP_Bool TCPSocketResourceBase::GetLocalAddressImpl( PP_Bool TCPSocketResourceBase::GetRemoteAddressImpl( PP_NetAddress_Private* remote_addr) { - if (!IsConnected() || !remote_addr) + if (!state_.IsConnected() || !remote_addr) return PP_FALSE; *remote_addr = remote_addr_; return PP_TRUE; @@ -128,15 +154,16 @@ int32_t TCPSocketResourceBase::SSLHandshakeImpl( if (!server_name) return PP_ERROR_BADARGUMENT; - if (connection_state_ != CONNECTED) - return PP_ERROR_FAILED; - if (TrackedCallback::IsPending(ssl_handshake_callback_) || + if (state_.IsPending(TCPSocketState::SSL_CONNECT) || TrackedCallback::IsPending(read_callback_) || TrackedCallback::IsPending(write_callback_)) { return PP_ERROR_INPROGRESS; } + if (!state_.IsValidTransition(TCPSocketState::SSL_CONNECT)) + return PP_ERROR_FAILED; ssl_handshake_callback_ = callback; + state_.SetPendingTransition(TCPSocketState::SSL_CONNECT); Call<PpapiPluginMsg_TCPSocket_SSLHandshakeReply>( BROWSER, @@ -193,10 +220,10 @@ int32_t TCPSocketResourceBase::ReadImpl( if (!buffer || bytes_to_read <= 0) return PP_ERROR_BADARGUMENT; - if (!IsConnected()) + if (!state_.IsConnected()) return PP_ERROR_FAILED; if (TrackedCallback::IsPending(read_callback_) || - TrackedCallback::IsPending(ssl_handshake_callback_)) + state_.IsPending(TCPSocketState::SSL_CONNECT)) return PP_ERROR_INPROGRESS; read_buffer_ = buffer; bytes_to_read_ = std::min(bytes_to_read, kMaxReadSize); @@ -217,10 +244,10 @@ int32_t TCPSocketResourceBase::WriteImpl( if (!buffer || bytes_to_write <= 0) return PP_ERROR_BADARGUMENT; - if (!IsConnected()) + if (!state_.IsConnected()) return PP_ERROR_FAILED; if (TrackedCallback::IsPending(write_callback_) || - TrackedCallback::IsPending(ssl_handshake_callback_)) + state_.IsPending(TCPSocketState::SSL_CONNECT)) return PP_ERROR_INPROGRESS; if (bytes_to_write > kMaxWriteSize) @@ -236,33 +263,79 @@ int32_t TCPSocketResourceBase::WriteImpl( return PP_OK_COMPLETIONPENDING; } -void TCPSocketResourceBase::DisconnectImpl() { - if (connection_state_ == DISCONNECTED) +int32_t TCPSocketResourceBase::ListenImpl( + int32_t backlog, + scoped_refptr<TrackedCallback> callback) { + if (backlog <= 0) + return PP_ERROR_BADARGUMENT; + if (state_.IsPending(TCPSocketState::LISTEN)) + return PP_ERROR_INPROGRESS; + if (!state_.IsValidTransition(TCPSocketState::LISTEN)) + return PP_ERROR_FAILED; + + listen_callback_ = callback; + state_.SetPendingTransition(TCPSocketState::LISTEN); + + Call<PpapiPluginMsg_TCPSocket_ListenReply>( + BROWSER, + PpapiHostMsg_TCPSocket_Listen(backlog), + base::Bind(&TCPSocketResourceBase::OnPluginMsgListenReply, + base::Unretained(this))); + return PP_OK_COMPLETIONPENDING; +} + +int32_t TCPSocketResourceBase::AcceptImpl( + PP_Resource* accepted_tcp_socket, + scoped_refptr<TrackedCallback> callback) { + if (!accepted_tcp_socket) + return PP_ERROR_BADARGUMENT; + if (TrackedCallback::IsPending(accept_callback_)) + return PP_ERROR_INPROGRESS; + if (state_.state() != TCPSocketState::LISTENING) + return PP_ERROR_FAILED; + + accept_callback_ = callback; + accepted_tcp_socket_ = accepted_tcp_socket; + + Call<PpapiPluginMsg_TCPSocket_AcceptReply>( + BROWSER, + PpapiHostMsg_TCPSocket_Accept(), + base::Bind(&TCPSocketResourceBase::OnPluginMsgAcceptReply, + base::Unretained(this))); + return PP_OK_COMPLETIONPENDING; +} + +void TCPSocketResourceBase::CloseImpl() { + if (state_.state() == TCPSocketState::CLOSED) return; - connection_state_ = DISCONNECTED; + state_.DoTransition(TCPSocketState::CLOSE, true); - Post(BROWSER, PpapiHostMsg_TCPSocket_Disconnect()); + Post(BROWSER, PpapiHostMsg_TCPSocket_Close()); + PostAbortIfNecessary(&bind_callback_); PostAbortIfNecessary(&connect_callback_); PostAbortIfNecessary(&ssl_handshake_callback_); PostAbortIfNecessary(&read_callback_); PostAbortIfNecessary(&write_callback_); + PostAbortIfNecessary(&listen_callback_); + PostAbortIfNecessary(&accept_callback_); read_buffer_ = NULL; bytes_to_read_ = -1; server_certificate_ = NULL; + accepted_tcp_socket_ = NULL; } int32_t TCPSocketResourceBase::SetOptionImpl( PP_TCPSocket_Option name, const PP_Var& value, scoped_refptr<TrackedCallback> callback) { - if (!IsConnected()) - return PP_ERROR_FAILED; - SocketOptionData option_data; switch (name) { case PP_TCPSOCKET_OPTION_NO_DELAY: { + if (!state_.IsConnected()) + return PP_ERROR_FAILED; + if (value.type != PP_VARTYPE_BOOL) return PP_ERROR_BADARGUMENT; option_data.SetBool(PP_ToBool(value.value.as_bool)); @@ -270,11 +343,25 @@ int32_t TCPSocketResourceBase::SetOptionImpl( } case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE: case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: { + if (!state_.IsConnected()) + return PP_ERROR_FAILED; + if (value.type != PP_VARTYPE_INT32) return PP_ERROR_BADARGUMENT; option_data.SetInt32(value.value.as_int); break; } + case PP_TCPSOCKET_OPTION_ADDRESS_REUSE: { + if (version_ != TCP_SOCKET_VERSION_1_1_OR_ABOVE) + return PP_ERROR_NOTSUPPORTED; + if (state_.state() != TCPSocketState::INITIAL) + return PP_ERROR_FAILED; + + if (value.type != PP_VARTYPE_BOOL) + return PP_ERROR_BADARGUMENT; + option_data.SetBool(PP_ToBool(value.value.as_bool)); + break; + } default: { NOTREACHED(); return PP_ERROR_BADARGUMENT; @@ -291,34 +378,52 @@ int32_t TCPSocketResourceBase::SetOptionImpl( return PP_OK_COMPLETIONPENDING; } -bool TCPSocketResourceBase::IsConnected() const { - return connection_state_ == CONNECTED || connection_state_ == SSL_CONNECTED; -} - void TCPSocketResourceBase::PostAbortIfNecessary( scoped_refptr<TrackedCallback>* callback) { if (TrackedCallback::IsPending(*callback)) (*callback)->PostAbort(); } +void TCPSocketResourceBase::OnPluginMsgBindReply( + const ResourceMessageReplyParams& params, + const PP_NetAddress_Private& local_addr) { + // It is possible that CloseImpl() has been called. We don't want to update + // class members in this case. + if (!state_.IsPending(TCPSocketState::BIND)) + return; + + DCHECK(TrackedCallback::IsPending(bind_callback_)); + if (params.result() == PP_OK) { + local_addr_ = local_addr; + state_.CompletePendingTransition(true); + } else { + state_.CompletePendingTransition(false); + } + RunCallback(bind_callback_, params.result()); +} + void TCPSocketResourceBase::OnPluginMsgConnectReply( const ResourceMessageReplyParams& params, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr) { - // It is possible that |connect_callback_| is pending while - // |connection_state_| is not BEFORE_CONNECT: DisconnectImpl() has been - // called, but a ConnectCompleted notification came earlier than the task to - // abort |connect_callback_|. We don't want to update |connection_state_| or - // other members in that case. - if (connection_state_ != BEFORE_CONNECT || - !TrackedCallback::IsPending(connect_callback_)) { + // It is possible that CloseImpl() has been called. We don't want to update + // class members in this case. + if (!state_.IsPending(TCPSocketState::CONNECT)) return; - } + DCHECK(TrackedCallback::IsPending(connect_callback_)); if (params.result() == PP_OK) { local_addr_ = local_addr; remote_addr_ = remote_addr; - connection_state_ = CONNECTED; + state_.CompletePendingTransition(true); + } else { + if (version_ == TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + state_.CompletePendingTransition(false); + } else { + // In order to maintain backward compatibility, allow to connect the + // socket again. + state_ = TCPSocketState(TCPSocketState::INITIAL); + } } RunCallback(connect_callback_, params.result()); } @@ -326,42 +431,33 @@ void TCPSocketResourceBase::OnPluginMsgConnectReply( void TCPSocketResourceBase::OnPluginMsgSSLHandshakeReply( const ResourceMessageReplyParams& params, const PPB_X509Certificate_Fields& certificate_fields) { - // It is possible that |ssl_handshake_callback_| is pending while - // |connection_state_| is not CONNECT: DisconnectImpl() has been - // called, but a SSLHandshakeCompleted notification came earlier than the task - // to abort |ssl_handshake_callback_|. We don't want to update - // |connection_state_| or other members in that case. - if (connection_state_ != CONNECTED || - !TrackedCallback::IsPending(ssl_handshake_callback_)) { + // It is possible that CloseImpl() has been called. We don't want to + // update class members in this case. + if (!state_.IsPending(TCPSocketState::SSL_CONNECT)) return; - } + DCHECK(TrackedCallback::IsPending(ssl_handshake_callback_)); if (params.result() == PP_OK) { - connection_state_ = SSL_CONNECTED; + state_.CompletePendingTransition(true); server_certificate_ = new PPB_X509Certificate_Private_Shared( OBJECT_IS_PROXY, pp_instance(), certificate_fields); - RunCallback(ssl_handshake_callback_, params.result()); } else { - // The resource might be released in the callback so we need to hold - // a reference so we can Disconnect() first. - AddRef(); - RunCallback(ssl_handshake_callback_, params.result()); - DisconnectImpl(); - Release(); + state_.CompletePendingTransition(false); } + RunCallback(ssl_handshake_callback_, params.result()); } void TCPSocketResourceBase::OnPluginMsgReadReply( const ResourceMessageReplyParams& params, const std::string& data) { - // It is possible that |read_callback_| is pending while |read_buffer_| is - // NULL: DisconnectImpl() has been called, but a ReadCompleted notification - // came earlier than the task to abort |read_callback_|. We shouldn't access - // the buffer in that case. The user may have released it. - if (!TrackedCallback::IsPending(read_callback_) || !read_buffer_) + // It is possible that CloseImpl() has been called. We shouldn't access the + // buffer in that case. The user may have released it. + if (!state_.IsConnected() || !TrackedCallback::IsPending(read_callback_) || + !read_buffer_) { return; + } const bool succeeded = params.result() == PP_OK; if (succeeded) { @@ -372,19 +468,48 @@ void TCPSocketResourceBase::OnPluginMsgReadReply( read_buffer_ = NULL; bytes_to_read_ = -1; - read_callback_->Run(succeeded ? - static_cast<int32_t>(data.size()) : - ConvertNetworkAPIErrorForCompatibility(params.result(), - private_api_)); + RunCallback(read_callback_, + succeeded ? static_cast<int32_t>(data.size()) : params.result()); } void TCPSocketResourceBase::OnPluginMsgWriteReply( const ResourceMessageReplyParams& params) { - if (!TrackedCallback::IsPending(write_callback_)) + if (!state_.IsConnected() || !TrackedCallback::IsPending(write_callback_)) return; RunCallback(write_callback_, params.result()); } +void TCPSocketResourceBase::OnPluginMsgListenReply( + const ResourceMessageReplyParams& params) { + if (!state_.IsPending(TCPSocketState::LISTEN)) + return; + + DCHECK(TrackedCallback::IsPending(listen_callback_)); + state_.CompletePendingTransition(params.result() == PP_OK); + + RunCallback(listen_callback_, params.result()); +} + +void TCPSocketResourceBase::OnPluginMsgAcceptReply( + const ResourceMessageReplyParams& params, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + // It is possible that CloseImpl() has been called. We shouldn't access the + // output parameter in that case. The user may have released it. + if (state_.state() != TCPSocketState::LISTENING || + !TrackedCallback::IsPending(accept_callback_) || !accepted_tcp_socket_) { + return; + } + + if (params.result() == PP_OK) { + *accepted_tcp_socket_ = CreateAcceptedSocket(pending_host_id, local_addr, + remote_addr); + } + accepted_tcp_socket_ = NULL; + RunCallback(accept_callback_, params.result()); +} + void TCPSocketResourceBase::OnPluginMsgSetOptionReply( const ResourceMessageReplyParams& params) { if (set_option_callbacks_.empty()) { @@ -399,8 +524,8 @@ void TCPSocketResourceBase::OnPluginMsgSetOptionReply( void TCPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback, int32_t pp_result) { - callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result, - private_api_)); + callback->Run(ConvertNetworkAPIErrorForCompatibility( + pp_result, version_ == TCP_SOCKET_VERSION_PRIVATE)); } } // namespace ppapi diff --git a/ppapi/proxy/tcp_socket_resource_base.h b/ppapi/proxy/tcp_socket_resource_base.h index 7e8a481..8835ab9 100644 --- a/ppapi/proxy/tcp_socket_resource_base.h +++ b/ppapi/proxy/tcp_socket_resource_base.h @@ -15,6 +15,7 @@ #include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/proxy/plugin_resource.h" #include "ppapi/proxy/ppapi_proxy_export.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" #include "ppapi/shared_impl/tracked_callback.h" namespace ppapi { @@ -27,6 +28,7 @@ namespace proxy { class PPAPI_PROXY_EXPORT TCPSocketResourceBase : public PluginResource { public: + // TODO(yzshen): Move these constants to ppb_tcp_socket_shared. // The maximum number of bytes that each PpapiHostMsg_PPBTCPSocket_Read // message is allowed to request. static const int32_t kMaxReadSize; @@ -46,33 +48,28 @@ class PPAPI_PROXY_EXPORT TCPSocketResourceBase : public PluginResource { static const int32_t kMaxReceiveBufferSize; protected: - enum ConnectionState { - // Before a connection is successfully established (including a connect - // request is pending or a previous connect request failed). - BEFORE_CONNECT, - // A connection has been successfully established (including a request of - // initiating SSL is pending). - CONNECTED, - // An SSL connection has been successfully established. - SSL_CONNECTED, - // The connection has been ended. - DISCONNECTED - }; - // C-tor used for new sockets. TCPSocketResourceBase(Connection connection, PP_Instance instance, - bool private_api); + TCPSocketVersion version); // C-tor used for already accepted sockets. TCPSocketResourceBase(Connection connection, PP_Instance instance, - bool private_api, + TCPSocketVersion version, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr); virtual ~TCPSocketResourceBase(); + // Implemented by subclasses to create resources for accepted sockets. + virtual PP_Resource CreateAcceptedSocket( + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) = 0; + + int32_t BindImpl(const PP_NetAddress_Private* addr, + scoped_refptr<TrackedCallback> callback); int32_t ConnectImpl(const char* host, uint16_t port, scoped_refptr<TrackedCallback> callback); @@ -92,15 +89,19 @@ class PPAPI_PROXY_EXPORT TCPSocketResourceBase : public PluginResource { int32_t WriteImpl(const char* buffer, int32_t bytes_to_write, scoped_refptr<TrackedCallback> callback); - void DisconnectImpl(); + int32_t ListenImpl(int32_t backlog, scoped_refptr<TrackedCallback> callback); + int32_t AcceptImpl(PP_Resource* accepted_tcp_socket, + scoped_refptr<TrackedCallback> callback); + void CloseImpl(); int32_t SetOptionImpl(PP_TCPSocket_Option name, const PP_Var& value, scoped_refptr<TrackedCallback> callback); - bool IsConnected() const; void PostAbortIfNecessary(scoped_refptr<TrackedCallback>* callback); // IPC message handlers. + void OnPluginMsgBindReply(const ResourceMessageReplyParams& params, + const PP_NetAddress_Private& local_addr); void OnPluginMsgConnectReply(const ResourceMessageReplyParams& params, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr); @@ -110,15 +111,23 @@ class PPAPI_PROXY_EXPORT TCPSocketResourceBase : public PluginResource { void OnPluginMsgReadReply(const ResourceMessageReplyParams& params, const std::string& data); void OnPluginMsgWriteReply(const ResourceMessageReplyParams& params); + void OnPluginMsgListenReply(const ResourceMessageReplyParams& params); + void OnPluginMsgAcceptReply(const ResourceMessageReplyParams& params, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr); void OnPluginMsgSetOptionReply(const ResourceMessageReplyParams& params); + scoped_refptr<TrackedCallback> bind_callback_; scoped_refptr<TrackedCallback> connect_callback_; scoped_refptr<TrackedCallback> ssl_handshake_callback_; scoped_refptr<TrackedCallback> read_callback_; scoped_refptr<TrackedCallback> write_callback_; + scoped_refptr<TrackedCallback> listen_callback_; + scoped_refptr<TrackedCallback> accept_callback_; std::queue<scoped_refptr<TrackedCallback> > set_option_callbacks_; - ConnectionState connection_state_; + TCPSocketState state_; char* read_buffer_; int32_t bytes_to_read_; @@ -130,10 +139,12 @@ class PPAPI_PROXY_EXPORT TCPSocketResourceBase : public PluginResource { std::vector<std::vector<char> > trusted_certificates_; std::vector<std::vector<char> > untrusted_certificates_; + PP_Resource* accepted_tcp_socket_; + private: void RunCallback(scoped_refptr<TrackedCallback> callback, int32_t pp_result); - bool private_api_; + TCPSocketVersion version_; DISALLOW_COPY_AND_ASSIGN(TCPSocketResourceBase); }; diff --git a/ppapi/shared_impl/ppb_tcp_socket_shared.cc b/ppapi/shared_impl/ppb_tcp_socket_shared.cc new file mode 100644 index 0000000..934c573 --- /dev/null +++ b/ppapi/shared_impl/ppb_tcp_socket_shared.cc @@ -0,0 +1,90 @@ +// Copyright 2013 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 "ppapi/shared_impl/ppb_tcp_socket_shared.h" + +#include "base/logging.h" + +namespace ppapi { + +TCPSocketState::TCPSocketState(StateType state) + : state_(state), + pending_transition_(NONE) { + DCHECK(state_ == INITIAL || state_ == CONNECTED); +} + +TCPSocketState::~TCPSocketState() { +} + +void TCPSocketState::SetPendingTransition(TransitionType pending_transition) { + DCHECK(IsValidTransition(pending_transition)); + pending_transition_ = pending_transition; +} + +void TCPSocketState::CompletePendingTransition(bool success) { + switch (pending_transition_) { + case NONE: + NOTREACHED(); + break; + case BIND: + if (success) + state_ = BOUND; + break; + case CONNECT: + state_ = success ? CONNECTED : CLOSED; + break; + case SSL_CONNECT: + state_ = success ? SSL_CONNECTED : CLOSED; + break; + case LISTEN: + if (success) + state_ = LISTENING; + break; + case CLOSE: + state_ = CLOSED; + break; + } + pending_transition_ = NONE; +} + +void TCPSocketState::DoTransition(TransitionType transition, bool success) { + SetPendingTransition(transition); + CompletePendingTransition(success); +} + +bool TCPSocketState::IsValidTransition(TransitionType transition) const { + if (pending_transition_ != NONE && transition != CLOSE) + return false; + + switch (transition) { + case NONE: + return false; + case BIND: + return state_ == INITIAL; + case CONNECT: + return state_ == INITIAL || state_ == BOUND; + case SSL_CONNECT: + return state_ == CONNECTED; + case LISTEN: + return state_ == BOUND; + case CLOSE: + return true; + } + NOTREACHED(); + return false; +} + +bool TCPSocketState::IsPending(TransitionType transition) const { + return pending_transition_ == transition; +} + +bool TCPSocketState::IsConnected() const { + return state_ == CONNECTED || state_ == SSL_CONNECTED; +} + +bool TCPSocketState::IsBound() const { + return state_ != INITIAL && state_ != CLOSED; +} + +} // namespace ppapi diff --git a/ppapi/shared_impl/ppb_tcp_socket_shared.h b/ppapi/shared_impl/ppb_tcp_socket_shared.h new file mode 100644 index 0000000..c723bdc --- /dev/null +++ b/ppapi/shared_impl/ppb_tcp_socket_shared.h @@ -0,0 +1,74 @@ +// Copyright 2013 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 PPAPI_SHARED_IMPL_PPB_TCP_SOCKET_SHARED_H_ +#define PPAPI_SHARED_IMPL_PPB_TCP_SOCKET_SHARED_H_ + +#include "ppapi/shared_impl/ppapi_shared_export.h" + +namespace ppapi { + +class PPAPI_SHARED_EXPORT TCPSocketState { + public: + enum StateType { + // The socket hasn't been bound or connected. + INITIAL, + // The socket has been bound. + BOUND, + // A connection has been established. + CONNECTED, + // An SSL connection has been established. + SSL_CONNECTED, + // The socket is listening. + LISTENING, + // The socket has been closed. + CLOSED + }; + + // Transitions that will change the socket state. Please note that + // read/write/accept are not included because they don't change the socket + // state. + enum TransitionType { + NONE, + BIND, + CONNECT, + SSL_CONNECT, + LISTEN, + CLOSE + }; + + explicit TCPSocketState(StateType state); + ~TCPSocketState(); + + StateType state() const { return state_; } + + void SetPendingTransition(TransitionType pending_transition); + void CompletePendingTransition(bool success); + + void DoTransition(TransitionType transition, bool success); + + bool IsValidTransition(TransitionType transition) const; + bool IsPending(TransitionType transition) const; + + bool IsConnected() const; + bool IsBound() const; + + private: + StateType state_; + TransitionType pending_transition_; +}; + +// TCP socket API versions. +enum PPAPI_SHARED_EXPORT TCPSocketVersion { + // PPB_TCPSocket_Private. + TCP_SOCKET_VERSION_PRIVATE, + // PPB_TCPSocket v1.0. + TCP_SOCKET_VERSION_1_0, + // PPB_TCPSocket v1.1 or above. + TCP_SOCKET_VERSION_1_1_OR_ABOVE +}; + +} // namespace ppapi + +#endif // PPAPI_SHARED_IMPL_PPB_TCP_SOCKET_SHARED_H_ diff --git a/ppapi/tests/test_tcp_socket.cc b/ppapi/tests/test_tcp_socket.cc index a4c56eb..90f6913 100644 --- a/ppapi/tests/test_tcp_socket.cc +++ b/ppapi/tests/test_tcp_socket.cc @@ -4,6 +4,9 @@ #include "ppapi/tests/test_tcp_socket.h" +#include <vector> + +#include "ppapi/cpp/message_loop.h" #include "ppapi/cpp/tcp_socket.h" #include "ppapi/tests/test_utils.h" #include "ppapi/tests/testing_instance.h" @@ -49,25 +52,58 @@ void TestTCPSocket::RunTests(const std::string& filter) { RUN_CALLBACK_TEST(TestTCPSocket, Connect, filter); RUN_CALLBACK_TEST(TestTCPSocket, ReadWrite, filter); RUN_CALLBACK_TEST(TestTCPSocket, SetOption, filter); + RUN_CALLBACK_TEST(TestTCPSocket, Listen, filter); + RUN_CALLBACK_TEST(TestTCPSocket, Backlog, filter); } std::string TestTCPSocket::TestConnect() { - pp::TCPSocket socket(instance_); - TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + { + // The basic case. + pp::TCPSocket socket(instance_); + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); - cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); - CHECK_CALLBACK_BEHAVIOR(cb); - ASSERT_EQ(PP_OK, cb.result()); + cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); + + pp::NetAddress local_addr, remote_addr; + local_addr = socket.GetLocalAddress(); + remote_addr = socket.GetRemoteAddress(); + + ASSERT_NE(0, local_addr.pp_resource()); + ASSERT_NE(0, remote_addr.pp_resource()); + ASSERT_TRUE(EqualNetAddress(addr_, remote_addr)); - pp::NetAddress local_addr, remote_addr; - local_addr = socket.GetLocalAddress(); - remote_addr = socket.GetRemoteAddress(); + socket.Close(); + } + + { + // Connect a bound socket. + pp::TCPSocket socket(instance_); + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + + pp::NetAddress any_port_address; + ASSERT_SUBTEST_SUCCESS(GetAddressToBind(&any_port_address)); + + cb.WaitForResult(socket.Bind(any_port_address, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); - ASSERT_NE(0, local_addr.pp_resource()); - ASSERT_NE(0, remote_addr.pp_resource()); - ASSERT_TRUE(EqualNetAddress(addr_, remote_addr)); + cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); - socket.Close(); + pp::NetAddress local_addr, remote_addr; + local_addr = socket.GetLocalAddress(); + remote_addr = socket.GetRemoteAddress(); + + ASSERT_NE(0, local_addr.pp_resource()); + ASSERT_NE(0, remote_addr.pp_resource()); + ASSERT_TRUE(EqualNetAddress(addr_, remote_addr)); + ASSERT_NE(0u, GetPort(local_addr)); + + socket.Close(); + } PASS(); } @@ -80,11 +116,11 @@ std::string TestTCPSocket::TestReadWrite() { CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); - ASSERT_EQ(PP_OK, WriteStringToSocket(&socket, "GET / HTTP/1.0\r\n\r\n")); + ASSERT_SUBTEST_SUCCESS(WriteToSocket(&socket, "GET / HTTP/1.0\r\n\r\n")); // Read up to the first \n and check that it looks like valid HTTP response. std::string s; - ASSERT_EQ(PP_OK, ReadFirstLineFromSocket(&socket, &s)); + ASSERT_SUBTEST_SUCCESS(ReadFirstLineFromSocket(&socket, &s)); ASSERT_TRUE(ValidateHttpResponse(s)); PASS(); @@ -95,6 +131,7 @@ std::string TestTCPSocket::TestSetOption() { TestCompletionCallback cb_1(instance_->pp_instance(), callback_type()); TestCompletionCallback cb_2(instance_->pp_instance(), callback_type()); TestCompletionCallback cb_3(instance_->pp_instance(), callback_type()); + TestCompletionCallback cb_4(instance_->pp_instance(), callback_type()); // These options cannot be set before the socket is connected. int32_t result_1 = socket.SetOption(PP_TCPSOCKET_OPTION_NO_DELAY, @@ -104,6 +141,10 @@ std::string TestTCPSocket::TestSetOption() { int32_t result_3 = socket.SetOption(PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE, 512, cb_3.GetCallback()); + // This option can only be set before the socket is bound. + int32_t result_4 = socket.SetOption(PP_TCPSOCKET_OPTION_ADDRESS_REUSE, + true, cb_4.GetCallback()); + cb_1.WaitForResult(result_1); CHECK_CALLBACK_BEHAVIOR(cb_1); ASSERT_EQ(PP_ERROR_FAILED, cb_1.result()); @@ -116,6 +157,10 @@ std::string TestTCPSocket::TestSetOption() { CHECK_CALLBACK_BEHAVIOR(cb_3); ASSERT_EQ(PP_ERROR_FAILED, cb_3.result()); + cb_4.WaitForResult(result_4); + CHECK_CALLBACK_BEHAVIOR(cb_4); + ASSERT_EQ(PP_OK, cb_4.result()); + cb_1.WaitForResult(socket.Connect(addr_, cb_1.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb_1); ASSERT_EQ(PP_OK, cb_1.result()); @@ -126,6 +171,8 @@ std::string TestTCPSocket::TestSetOption() { 512, cb_2.GetCallback()); result_3 = socket.SetOption(PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE, 1024, cb_3.GetCallback()); + result_4 = socket.SetOption(PP_TCPSOCKET_OPTION_ADDRESS_REUSE, + false, cb_4.GetCallback()); cb_1.WaitForResult(result_1); CHECK_CALLBACK_BEHAVIOR(cb_1); @@ -139,53 +186,221 @@ std::string TestTCPSocket::TestSetOption() { CHECK_CALLBACK_BEHAVIOR(cb_3); ASSERT_EQ(PP_OK, cb_3.result()); + cb_4.WaitForResult(result_4); + CHECK_CALLBACK_BEHAVIOR(cb_4); + ASSERT_EQ(PP_ERROR_FAILED, cb_4.result()); + + PASS(); +} + +std::string TestTCPSocket::TestListen() { + static const int kBacklog = 2; + + pp::TCPSocket server_socket(instance_); + ASSERT_SUBTEST_SUCCESS(StartListen(&server_socket, kBacklog)); + + // We can't use a blocking callback for Accept, because it will wait forever + // for the client to connect, since the client connects after. + TestCompletionCallbackWithOutput<pp::TCPSocket> + accept_callback(instance_->pp_instance(), PP_REQUIRED); + // We need to make sure there's a message loop to run accept_callback on. + pp::MessageLoop current_thread_loop(pp::MessageLoop::GetCurrent()); + if (current_thread_loop.is_null() && testing_interface_->IsOutOfProcess()) { + current_thread_loop = pp::MessageLoop(instance_); + current_thread_loop.AttachToCurrentThread(); + } + + int32_t accept_rv = server_socket.Accept(accept_callback.GetCallback()); + + pp::TCPSocket client_socket; + TestCompletionCallback callback(instance_->pp_instance(), callback_type()); + do { + client_socket = pp::TCPSocket(instance_); + + callback.WaitForResult(client_socket.Connect( + server_socket.GetLocalAddress(), callback.GetCallback())); + } while (callback.result() != PP_OK); + + pp::NetAddress client_local_addr = client_socket.GetLocalAddress(); + pp::NetAddress client_remote_addr = client_socket.GetRemoteAddress(); + ASSERT_FALSE(client_local_addr.is_null()); + ASSERT_FALSE(client_remote_addr.is_null()); + + accept_callback.WaitForResult(accept_rv); + CHECK_CALLBACK_BEHAVIOR(accept_callback); + ASSERT_EQ(PP_OK, accept_callback.result()); + + pp::TCPSocket accepted_socket(accept_callback.output()); + pp::NetAddress accepted_local_addr = accepted_socket.GetLocalAddress(); + pp::NetAddress accepted_remote_addr = accepted_socket.GetRemoteAddress(); + ASSERT_FALSE(accepted_local_addr.is_null()); + ASSERT_FALSE(accepted_remote_addr.is_null()); + + ASSERT_TRUE(EqualNetAddress(client_local_addr, accepted_remote_addr)); + + const char kSentByte = 'a'; + ASSERT_SUBTEST_SUCCESS(WriteToSocket(&client_socket, + std::string(1, kSentByte))); + + char received_byte; + ASSERT_SUBTEST_SUCCESS(ReadFromSocket(&accepted_socket, + &received_byte, + sizeof(received_byte))); + ASSERT_EQ(kSentByte, received_byte); + + accepted_socket.Close(); + client_socket.Close(); + server_socket.Close(); + PASS(); } -int32_t TestTCPSocket::ReadFirstLineFromSocket(pp::TCPSocket* socket, - std::string* s) { +std::string TestTCPSocket::TestBacklog() { + static const size_t kBacklog = 5; + + pp::TCPSocket server_socket(instance_); + ASSERT_SUBTEST_SUCCESS(StartListen(&server_socket, 2 * kBacklog)); + + std::vector<pp::TCPSocket*> client_sockets(kBacklog); + std::vector<TestCompletionCallback*> connect_callbacks(kBacklog); + std::vector<int32_t> connect_rv(kBacklog); + pp::NetAddress address = server_socket.GetLocalAddress(); + for (size_t i = 0; i < kBacklog; ++i) { + client_sockets[i] = new pp::TCPSocket(instance_); + connect_callbacks[i] = new TestCompletionCallback(instance_->pp_instance(), + callback_type()); + connect_rv[i] = client_sockets[i]->Connect( + address, connect_callbacks[i]->GetCallback()); + } + + std::vector<pp::TCPSocket*> accepted_sockets(kBacklog); + for (size_t i = 0; i < kBacklog; ++i) { + TestCompletionCallbackWithOutput<pp::TCPSocket> callback( + instance_->pp_instance(), callback_type()); + callback.WaitForResult(server_socket.Accept(callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + accepted_sockets[i] = new pp::TCPSocket(callback.output()); + ASSERT_FALSE(accepted_sockets[i]->is_null()); + } + + for (size_t i = 0; i < kBacklog; ++i) { + connect_callbacks[i]->WaitForResult(connect_rv[i]); + CHECK_CALLBACK_BEHAVIOR(*connect_callbacks[i]); + ASSERT_EQ(PP_OK, connect_callbacks[i]->result()); + } + + for (size_t i = 0; i < kBacklog; ++i) { + const char byte = 'a' + i; + ASSERT_SUBTEST_SUCCESS(WriteToSocket(client_sockets[i], + std::string(1, byte))); + } + + bool byte_received[kBacklog] = {}; + for (size_t i = 0; i < kBacklog; ++i) { + char byte; + ASSERT_SUBTEST_SUCCESS(ReadFromSocket( + accepted_sockets[i], &byte, sizeof(byte))); + const size_t index = byte - 'a'; + ASSERT_GE(index, 0u); + ASSERT_LT(index, kBacklog); + ASSERT_FALSE(byte_received[index]); + byte_received[index] = true; + } + + for (size_t i = 0; i < kBacklog; ++i) { + ASSERT_TRUE(byte_received[i]); + + delete client_sockets[i]; + delete connect_callbacks[i]; + delete accepted_sockets[i]; + } + + PASS(); +} + +std::string TestTCPSocket::ReadFirstLineFromSocket(pp::TCPSocket* socket, + std::string* s) { char buffer[1000]; s->clear(); // Make sure we don't just hang if |Read()| spews. while (s->size() < 10000) { TestCompletionCallback cb(instance_->pp_instance(), callback_type()); - int32_t rv = socket->Read(buffer, sizeof(buffer), cb.GetCallback()); - if (callback_type() == PP_REQUIRED && rv != PP_OK_COMPLETIONPENDING) - return PP_ERROR_FAILED; - cb.WaitForResult(rv); - if (cb.result() < 0) - return cb.result(); - if (cb.result() == 0) - return PP_ERROR_FAILED; // Didn't get a \n-terminated line. + cb.WaitForResult(socket->Read(buffer, sizeof(buffer), cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_GT(cb.result(), 0); s->reserve(s->size() + cb.result()); - for (int32_t i = 0; i < cb.result(); i++) { + for (int32_t i = 0; i < cb.result(); ++i) { s->push_back(buffer[i]); if (buffer[i] == '\n') - return PP_OK; + PASS(); } } - return PP_ERROR_FAILED; + PASS(); +} + +std::string TestTCPSocket::ReadFromSocket(pp::TCPSocket* socket, + char* buffer, + size_t num_bytes) { + while (num_bytes > 0) { + TestCompletionCallback callback(instance_->pp_instance(), callback_type()); + callback.WaitForResult( + socket->Read(buffer, num_bytes, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_GT(callback.result(), 0); + buffer += callback.result(); + num_bytes -= callback.result(); + } + ASSERT_EQ(0u, num_bytes); + PASS(); } -int32_t TestTCPSocket::WriteStringToSocket(pp::TCPSocket* socket, - const std::string& s) { +std::string TestTCPSocket::WriteToSocket(pp::TCPSocket* socket, + const std::string& s) { const char* buffer = s.data(); size_t written = 0; while (written < s.size()) { TestCompletionCallback cb(instance_->pp_instance(), callback_type()); - int32_t rv = socket->Write(buffer + written, s.size() - written, - cb.GetCallback()); - if (callback_type() == PP_REQUIRED && rv != PP_OK_COMPLETIONPENDING) - return PP_ERROR_FAILED; - cb.WaitForResult(rv); - if (cb.result() < 0) - return cb.result(); - if (cb.result() == 0) - return PP_ERROR_FAILED; + cb.WaitForResult( + socket->Write(buffer + written, s.size() - written, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_GT(cb.result(), 0); written += cb.result(); } - if (written != s.size()) - return PP_ERROR_FAILED; - return PP_OK; + ASSERT_EQ(written, s.size()); + PASS(); } + +std::string TestTCPSocket::GetAddressToBind(pp::NetAddress* address) { + pp::TCPSocket socket(instance_); + TestCompletionCallback callback(instance_->pp_instance(), callback_type()); + callback.WaitForResult(socket.Connect(addr_, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + ASSERT_TRUE(ReplacePort(instance_->pp_instance(), socket.GetLocalAddress(), 0, + address)); + ASSERT_FALSE(address->is_null()); + PASS(); +} + +std::string TestTCPSocket::StartListen(pp::TCPSocket* socket, int32_t backlog) { + pp::NetAddress any_port_address; + ASSERT_SUBTEST_SUCCESS(GetAddressToBind(&any_port_address)); + + TestCompletionCallback callback(instance_->pp_instance(), callback_type()); + callback.WaitForResult( + socket->Bind(any_port_address, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + callback.WaitForResult( + socket->Listen(backlog, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + PASS(); +} + diff --git a/ppapi/tests/test_tcp_socket.h b/ppapi/tests/test_tcp_socket.h index 44c7321..136dc46 100644 --- a/ppapi/tests/test_tcp_socket.h +++ b/ppapi/tests/test_tcp_socket.h @@ -27,9 +27,16 @@ class TestTCPSocket: public TestCase { std::string TestConnect(); std::string TestReadWrite(); std::string TestSetOption(); - - int32_t ReadFirstLineFromSocket(pp::TCPSocket* socket, std::string* s); - int32_t WriteStringToSocket(pp::TCPSocket* socket, const std::string& s); + std::string TestListen(); + std::string TestBacklog(); + + std::string ReadFirstLineFromSocket(pp::TCPSocket* socket, std::string* s); + std::string ReadFromSocket(pp::TCPSocket* socket, + char* buffer, + size_t num_bytes); + std::string WriteToSocket(pp::TCPSocket* socket, const std::string& s); + std::string GetAddressToBind(pp::NetAddress* address); + std::string StartListen(pp::TCPSocket* socket, int32_t backlog); pp::NetAddress addr_; }; diff --git a/ppapi/tests/test_utils.cc b/ppapi/tests/test_utils.cc index f9fd7b6..eecbe7a 100644 --- a/ppapi/tests/test_utils.cc +++ b/ppapi/tests/test_utils.cc @@ -179,6 +179,53 @@ bool ResolveHost(PP_Instance instance, } } +bool ReplacePort(PP_Instance instance, + const pp::NetAddress& input_addr, + uint16_t port, + pp::NetAddress* output_addr) { + switch (input_addr.GetFamily()) { + case PP_NETADDRESS_FAMILY_IPV4: { + PP_NetAddress_IPv4 ipv4_addr; + if (!input_addr.DescribeAsIPv4Address(&ipv4_addr)) + return false; + ipv4_addr.port = ConvertToNetEndian16(port); + *output_addr = pp::NetAddress(pp::InstanceHandle(instance), ipv4_addr); + return true; + } + case PP_NETADDRESS_FAMILY_IPV6: { + PP_NetAddress_IPv6 ipv6_addr; + if (!input_addr.DescribeAsIPv6Address(&ipv6_addr)) + return false; + ipv6_addr.port = ConvertToNetEndian16(port); + *output_addr = pp::NetAddress(pp::InstanceHandle(instance), ipv6_addr); + return true; + } + default: { + return false; + } + } +} + +uint16_t GetPort(const pp::NetAddress& addr) { + switch (addr.GetFamily()) { + case PP_NETADDRESS_FAMILY_IPV4: { + PP_NetAddress_IPv4 ipv4_addr; + if (!addr.DescribeAsIPv4Address(&ipv4_addr)) + return 0; + return ConvertFromNetEndian16(ipv4_addr.port); + } + case PP_NETADDRESS_FAMILY_IPV6: { + PP_NetAddress_IPv6 ipv6_addr; + if (!addr.DescribeAsIPv6Address(&ipv6_addr)) + return 0; + return ConvertFromNetEndian16(ipv6_addr.port); + } + default: { + return 0; + } + } +} + void NestedEvent::Wait() { PP_DCHECK(pp::Module::Get()->core()->IsMainThread()); // Don't allow nesting more than once; it doesn't work with the code as-is, diff --git a/ppapi/tests/test_utils.h b/ppapi/tests/test_utils.h index 357a1c6..4621c355 100644 --- a/ppapi/tests/test_utils.h +++ b/ppapi/tests/test_utils.h @@ -34,6 +34,11 @@ bool ResolveHost(PP_Instance instance, const std::string& host, uint16_t port, pp::NetAddress* addr); +bool ReplacePort(PP_Instance instance, + const pp::NetAddress& input_addr, + uint16_t port, + pp::NetAddress* output_addr); +uint16_t GetPort(const pp::NetAddress& addr); // NestedEvent allows you to run a nested MessageLoop and wait for a particular // event to complete. For example, you can use it to wait for a callback on a diff --git a/ppapi/thunk/interfaces_ppb_public_stable.h b/ppapi/thunk/interfaces_ppb_public_stable.h index 7747bfc..7327cde 100644 --- a/ppapi/thunk/interfaces_ppb_public_stable.h +++ b/ppapi/thunk/interfaces_ppb_public_stable.h @@ -78,6 +78,7 @@ PROXIED_IFACE(PPB_Instance, PPB_MOUSELOCK_INTERFACE_1_0, PPB_MouseLock_1_0) PROXIED_IFACE(NoAPIName, PPB_NETADDRESS_INTERFACE_1_0, PPB_NetAddress_1_0) PROXIED_IFACE(NoAPIName, PPB_NETWORKPROXY_INTERFACE_1_0, PPB_NetworkProxy_1_0) PROXIED_IFACE(NoAPIName, PPB_TCPSOCKET_INTERFACE_1_0, PPB_TCPSocket_1_0) +PROXIED_IFACE(NoAPIName, PPB_TCPSOCKET_INTERFACE_1_1, PPB_TCPSocket_1_1) PROXIED_IFACE(NoAPIName, PPB_TEXTINPUTCONTROLLER_INTERFACE_1_0, PPB_TextInputController_1_0) PROXIED_IFACE(NoAPIName, PPB_UDPSOCKET_INTERFACE_1_0, PPB_UDPSocket_1_0) diff --git a/ppapi/thunk/ppb_tcp_socket_api.h b/ppapi/thunk/ppb_tcp_socket_api.h index d33685f..beed0c6 100644 --- a/ppapi/thunk/ppb_tcp_socket_api.h +++ b/ppapi/thunk/ppb_tcp_socket_api.h @@ -19,6 +19,8 @@ class PPAPI_THUNK_EXPORT PPB_TCPSocket_API { public: virtual ~PPB_TCPSocket_API() {} + virtual int32_t Bind(PP_Resource addr, + scoped_refptr<TrackedCallback> callback) = 0; virtual int32_t Connect(PP_Resource addr, scoped_refptr<TrackedCallback> callback) = 0; virtual PP_Resource GetLocalAddress() = 0; @@ -29,6 +31,10 @@ class PPAPI_THUNK_EXPORT PPB_TCPSocket_API { virtual int32_t Write(const char* buffer, int32_t bytes_to_write, scoped_refptr<TrackedCallback> callback) = 0; + virtual int32_t Listen(int32_t backlog, + scoped_refptr<TrackedCallback> callback) = 0; + virtual int32_t Accept(PP_Resource* accepted_tcp_socket, + scoped_refptr<TrackedCallback> callback) = 0; virtual void Close() = 0; virtual int32_t SetOption(PP_TCPSocket_Option name, const PP_Var& value, diff --git a/ppapi/thunk/ppb_tcp_socket_thunk.cc b/ppapi/thunk/ppb_tcp_socket_thunk.cc index ff612fb..42ae702 100644 --- a/ppapi/thunk/ppb_tcp_socket_thunk.cc +++ b/ppapi/thunk/ppb_tcp_socket_thunk.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// From ppb_tcp_socket.idl modified Thu Jun 20 16:36:53 2013. +// From ppb_tcp_socket.idl modified Sun Sep 15 16:14:21 2013. #include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_errors.h" @@ -19,6 +19,14 @@ namespace thunk { namespace { +PP_Resource Create_1_0(PP_Instance instance) { + VLOG(4) << "PPB_TCPSocket::Create_1_0()"; + EnterResourceCreation enter(instance); + if (enter.failed()) + return 0; + return enter.functions()->CreateTCPSocket1_0(instance); +} + PP_Resource Create(PP_Instance instance) { VLOG(4) << "PPB_TCPSocket::Create()"; EnterResourceCreation enter(instance); @@ -33,6 +41,16 @@ PP_Bool IsTCPSocket(PP_Resource resource) { return PP_FromBool(enter.succeeded()); } +int32_t Bind(PP_Resource tcp_socket, + PP_Resource addr, + struct PP_CompletionCallback callback) { + VLOG(4) << "PPB_TCPSocket::Bind()"; + EnterResource<PPB_TCPSocket_API> enter(tcp_socket, callback, true); + if (enter.failed()) + return enter.retval(); + return enter.SetResult(enter.object()->Bind(addr, enter.callback())); +} + int32_t Connect(PP_Resource tcp_socket, PP_Resource addr, struct PP_CompletionCallback callback) { @@ -85,6 +103,27 @@ int32_t Write(PP_Resource tcp_socket, enter.callback())); } +int32_t Listen(PP_Resource tcp_socket, + int32_t backlog, + struct PP_CompletionCallback callback) { + VLOG(4) << "PPB_TCPSocket::Listen()"; + EnterResource<PPB_TCPSocket_API> enter(tcp_socket, callback, true); + if (enter.failed()) + return enter.retval(); + return enter.SetResult(enter.object()->Listen(backlog, enter.callback())); +} + +int32_t Accept(PP_Resource tcp_socket, + PP_Resource* accepted_tcp_socket, + struct PP_CompletionCallback callback) { + VLOG(4) << "PPB_TCPSocket::Accept()"; + EnterResource<PPB_TCPSocket_API> enter(tcp_socket, callback, true); + if (enter.failed()) + return enter.retval(); + return enter.SetResult(enter.object()->Accept(accepted_tcp_socket, + enter.callback())); +} + void Close(PP_Resource tcp_socket) { VLOG(4) << "PPB_TCPSocket::Close()"; EnterResource<PPB_TCPSocket_API> enter(tcp_socket, true); @@ -107,13 +146,28 @@ int32_t SetOption(PP_Resource tcp_socket, } const PPB_TCPSocket_1_0 g_ppb_tcpsocket_thunk_1_0 = { + &Create_1_0, + &IsTCPSocket, + &Connect, + &GetLocalAddress, + &GetRemoteAddress, + &Read, + &Write, + &Close, + &SetOption +}; + +const PPB_TCPSocket_1_1 g_ppb_tcpsocket_thunk_1_1 = { &Create, &IsTCPSocket, + &Bind, &Connect, &GetLocalAddress, &GetRemoteAddress, &Read, &Write, + &Listen, + &Accept, &Close, &SetOption }; @@ -124,5 +178,9 @@ const PPB_TCPSocket_1_0* GetPPB_TCPSocket_1_0_Thunk() { return &g_ppb_tcpsocket_thunk_1_0; } +const PPB_TCPSocket_1_1* GetPPB_TCPSocket_1_1_Thunk() { + return &g_ppb_tcpsocket_thunk_1_1; +} + } // namespace thunk } // namespace ppapi diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h index ae080bb..16f3d6b 100644 --- a/ppapi/thunk/resource_creation_api.h +++ b/ppapi/thunk/resource_creation_api.h @@ -148,7 +148,8 @@ class ResourceCreationAPI { virtual PP_Resource CreateNetworkMonitorPrivate(PP_Instance instance) = 0; virtual PP_Resource CreatePrinting(PP_Instance instance) = 0; virtual PP_Resource CreateTCPServerSocketPrivate(PP_Instance instance) = 0; - virtual PP_Resource CreateTCPSocket(PP_Instance instace) = 0; + virtual PP_Resource CreateTCPSocket1_0(PP_Instance instace) = 0; + virtual PP_Resource CreateTCPSocket(PP_Instance instance) = 0; virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instace) = 0; virtual PP_Resource CreateUDPSocket(PP_Instance instace) = 0; virtual PP_Resource CreateUDPSocketPrivate(PP_Instance instace) = 0; |