1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h"
7 #include "base/lazy_instance.h"
8 #include "extensions/browser/api/socket/tcp_socket.h"
9 #include "extensions/browser/event_router.h"
10 #include "extensions/browser/extension_system.h"
11 #include "extensions/browser/extensions_browser_client.h"
12 #include "net/base/net_errors.h"
15 int kDefaultBufferSize
= 4096;
18 namespace extensions
{
21 using content::BrowserThread
;
23 static base::LazyInstance
<
24 BrowserContextKeyedAPIFactory
<TCPSocketEventDispatcher
> > g_factory
=
25 LAZY_INSTANCE_INITIALIZER
;
28 BrowserContextKeyedAPIFactory
<TCPSocketEventDispatcher
>*
29 TCPSocketEventDispatcher::GetFactoryInstance() {
30 return g_factory
.Pointer();
34 TCPSocketEventDispatcher
* TCPSocketEventDispatcher::Get(
35 content::BrowserContext
* context
) {
36 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
38 return BrowserContextKeyedAPIFactory
<TCPSocketEventDispatcher
>::Get(context
);
41 TCPSocketEventDispatcher::TCPSocketEventDispatcher(
42 content::BrowserContext
* context
)
43 : thread_id_(Socket::kThreadId
), browser_context_(context
) {
44 ApiResourceManager
<ResumableTCPSocket
>* manager
=
45 ApiResourceManager
<ResumableTCPSocket
>::Get(browser_context_
);
47 << "There is no socket manager. "
48 "If this assertion is failing during a test, then it is likely that "
49 "TestExtensionSystem is failing to provide an instance of "
50 "ApiResourceManager<ResumableTCPSocket>.";
51 sockets_
= manager
->data_
;
54 TCPSocketEventDispatcher::~TCPSocketEventDispatcher() {}
56 TCPSocketEventDispatcher::ReadParams::ReadParams() {}
58 TCPSocketEventDispatcher::ReadParams::~ReadParams() {}
60 void TCPSocketEventDispatcher::OnSocketConnect(const std::string
& extension_id
,
62 DCHECK_CURRENTLY_ON(thread_id_
);
64 StartSocketRead(extension_id
, socket_id
);
67 void TCPSocketEventDispatcher::OnSocketResume(const std::string
& extension_id
,
69 DCHECK_CURRENTLY_ON(thread_id_
);
71 StartSocketRead(extension_id
, socket_id
);
74 void TCPSocketEventDispatcher::StartSocketRead(const std::string
& extension_id
,
76 DCHECK_CURRENTLY_ON(thread_id_
);
79 params
.thread_id
= thread_id_
;
80 params
.browser_context_id
= browser_context_
;
81 params
.extension_id
= extension_id
;
82 params
.sockets
= sockets_
;
83 params
.socket_id
= socket_id
;
89 void TCPSocketEventDispatcher::StartRead(const ReadParams
& params
) {
90 DCHECK_CURRENTLY_ON(params
.thread_id
);
92 ResumableTCPSocket
* socket
=
93 params
.sockets
->Get(params
.extension_id
, params
.socket_id
);
95 // This can happen if the socket is closed while our callback is active.
98 DCHECK(params
.extension_id
== socket
->owner_extension_id())
99 << "Socket has wrong owner.";
101 // Don't start another read if the socket has been paused.
102 if (socket
->paused())
105 int buffer_size
= socket
->buffer_size();
106 if (buffer_size
<= 0)
107 buffer_size
= kDefaultBufferSize
;
108 socket
->Read(buffer_size
,
109 base::Bind(&TCPSocketEventDispatcher::ReadCallback
, params
));
113 void TCPSocketEventDispatcher::ReadCallback(
114 const ReadParams
& params
,
116 scoped_refptr
<net::IOBuffer
> io_buffer
) {
117 DCHECK_CURRENTLY_ON(params
.thread_id
);
119 // If |bytes_read| == 0, the connection has been closed by the peer.
120 // If |bytes_read| < 0, there was a network error, and |bytes_read| is a value
123 if (bytes_read
== 0) {
124 bytes_read
= net::ERR_CONNECTION_CLOSED
;
127 if (bytes_read
> 0) {
128 // Dispatch "onReceive" event.
129 sockets_tcp::ReceiveInfo receive_info
;
130 receive_info
.socket_id
= params
.socket_id
;
131 receive_info
.data
.assign(io_buffer
->data(), io_buffer
->data() + bytes_read
);
132 scoped_ptr
<base::ListValue
> args
=
133 sockets_tcp::OnReceive::Create(receive_info
);
134 scoped_ptr
<Event
> event(new Event(events::SOCKETS_TCP_ON_RECEIVE
,
135 sockets_tcp::OnReceive::kEventName
,
137 PostEvent(params
, event
.Pass());
139 // Post a task to delay the read until the socket is available, as
140 // calling StartReceive at this point would error with ERR_IO_PENDING.
141 BrowserThread::PostTask(
144 base::Bind(&TCPSocketEventDispatcher::StartRead
, params
));
145 } else if (bytes_read
== net::ERR_IO_PENDING
) {
146 // This happens when resuming a socket which already had an
147 // active "read" callback.
149 // Dispatch "onReceiveError" event but don't start another read to avoid
150 // potential infinite reads if we have a persistent network error.
151 sockets_tcp::ReceiveErrorInfo receive_error_info
;
152 receive_error_info
.socket_id
= params
.socket_id
;
153 receive_error_info
.result_code
= bytes_read
;
154 scoped_ptr
<base::ListValue
> args
=
155 sockets_tcp::OnReceiveError::Create(receive_error_info
);
156 scoped_ptr
<Event
> event(new Event(events::SOCKETS_TCP_ON_RECEIVE_ERROR
,
157 sockets_tcp::OnReceiveError::kEventName
,
159 PostEvent(params
, event
.Pass());
161 // Since we got an error, the socket is now "paused" until the application
163 ResumableTCPSocket
* socket
=
164 params
.sockets
->Get(params
.extension_id
, params
.socket_id
);
166 socket
->set_paused(true);
172 void TCPSocketEventDispatcher::PostEvent(const ReadParams
& params
,
173 scoped_ptr
<Event
> event
) {
174 DCHECK_CURRENTLY_ON(params
.thread_id
);
176 BrowserThread::PostTask(BrowserThread::UI
,
178 base::Bind(&DispatchEvent
,
179 params
.browser_context_id
,
181 base::Passed(event
.Pass())));
185 void TCPSocketEventDispatcher::DispatchEvent(void* browser_context_id
,
186 const std::string
& extension_id
,
187 scoped_ptr
<Event
> event
) {
188 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
190 content::BrowserContext
* context
=
191 reinterpret_cast<content::BrowserContext
*>(browser_context_id
);
192 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context
))
195 EventRouter
* event_router
= EventRouter::Get(context
);
197 event_router
->DispatchEventToExtension(extension_id
, event
.Pass());
201 } // namespace extensions