1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "jingle/glue/chrome_async_socket.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "jingle/glue/resolving_client_socket_factory.h"
17 #include "net/base/address_list.h"
18 #include "net/base/host_port_pair.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_util.h"
21 #include "net/socket/client_socket_handle.h"
22 #include "net/socket/ssl_client_socket.h"
23 #include "net/socket/tcp_client_socket.h"
24 #include "net/ssl/ssl_config_service.h"
25 #include "third_party/webrtc/base/socketaddress.h"
27 namespace jingle_glue
{
29 ChromeAsyncSocket::ChromeAsyncSocket(
30 ResolvingClientSocketFactory
* resolving_client_socket_factory
,
32 size_t write_buf_size
)
33 : resolving_client_socket_factory_(resolving_client_socket_factory
),
38 read_buf_(new net::IOBufferWithSize(read_buf_size
)),
42 write_buf_(new net::IOBufferWithSize(write_buf_size
)),
44 weak_ptr_factory_(this) {
45 DCHECK(resolving_client_socket_factory_
.get());
46 DCHECK_GT(read_buf_size
, 0U);
47 DCHECK_GT(write_buf_size
, 0U);
50 ChromeAsyncSocket::~ChromeAsyncSocket() {}
52 ChromeAsyncSocket::State
ChromeAsyncSocket::state() {
56 ChromeAsyncSocket::Error
ChromeAsyncSocket::error() {
60 int ChromeAsyncSocket::GetError() {
64 bool ChromeAsyncSocket::IsOpen() const {
65 return (state_
== STATE_OPEN
) || (state_
== STATE_TLS_OPEN
);
68 void ChromeAsyncSocket::DoNonNetError(Error error
) {
69 DCHECK_NE(error
, ERROR_NONE
);
70 DCHECK_NE(error
, ERROR_WINSOCK
);
75 void ChromeAsyncSocket::DoNetError(net::Error net_error
) {
76 error_
= ERROR_WINSOCK
;
77 net_error_
= net_error
;
80 void ChromeAsyncSocket::DoNetErrorFromStatus(int status
) {
81 DCHECK_LT(status
, net::OK
);
82 DoNetError(static_cast<net::Error
>(status
));
85 // STATE_CLOSED -> STATE_CONNECTING
87 bool ChromeAsyncSocket::Connect(const rtc::SocketAddress
& address
) {
88 if (state_
!= STATE_CLOSED
) {
89 LOG(DFATAL
) << "Connect() called on non-closed socket";
90 DoNonNetError(ERROR_WRONGSTATE
);
93 if (address
.hostname().empty() || address
.port() == 0) {
94 DoNonNetError(ERROR_DNS
);
98 DCHECK_EQ(state_
, buzz::AsyncSocket::STATE_CLOSED
);
99 DCHECK_EQ(read_state_
, IDLE
);
100 DCHECK_EQ(write_state_
, IDLE
);
102 state_
= STATE_CONNECTING
;
104 DCHECK(!weak_ptr_factory_
.HasWeakPtrs());
105 weak_ptr_factory_
.InvalidateWeakPtrs();
107 net::HostPortPair
dest_host_port_pair(address
.hostname(), address
.port());
110 resolving_client_socket_factory_
->CreateTransportClientSocket(
111 dest_host_port_pair
);
112 int status
= transport_socket_
->Connect(
113 base::Bind(&ChromeAsyncSocket::ProcessConnectDone
,
114 weak_ptr_factory_
.GetWeakPtr()));
115 if (status
!= net::ERR_IO_PENDING
) {
116 // We defer execution of ProcessConnectDone instead of calling it
117 // directly here as the caller may not expect an error/close to
118 // happen here. This is okay, as from the caller's point of view,
119 // the connect always happens asynchronously.
120 base::MessageLoop
* message_loop
= base::MessageLoop::current();
122 message_loop
->PostTask(
124 base::Bind(&ChromeAsyncSocket::ProcessConnectDone
,
125 weak_ptr_factory_
.GetWeakPtr(), status
));
130 // STATE_CONNECTING -> STATE_OPEN
131 // read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead())
133 void ChromeAsyncSocket::ProcessConnectDone(int status
) {
134 DCHECK_NE(status
, net::ERR_IO_PENDING
);
135 DCHECK_EQ(read_state_
, IDLE
);
136 DCHECK_EQ(write_state_
, IDLE
);
137 DCHECK_EQ(state_
, STATE_CONNECTING
);
138 if (status
!= net::OK
) {
139 DoNetErrorFromStatus(status
);
145 // Write buffer should be empty.
146 DCHECK_EQ(write_end_
, 0U);
150 // read_state_ == IDLE -> read_state_ == POSTED
152 void ChromeAsyncSocket::PostDoRead() {
154 DCHECK_EQ(read_state_
, IDLE
);
155 DCHECK_EQ(read_start_
, 0U);
156 DCHECK_EQ(read_end_
, 0U);
157 base::MessageLoop
* message_loop
= base::MessageLoop::current();
159 message_loop
->PostTask(
161 base::Bind(&ChromeAsyncSocket::DoRead
,
162 weak_ptr_factory_
.GetWeakPtr()));
163 read_state_
= POSTED
;
166 // read_state_ == POSTED -> read_state_ == PENDING
168 void ChromeAsyncSocket::DoRead() {
170 DCHECK_EQ(read_state_
, POSTED
);
171 DCHECK_EQ(read_start_
, 0U);
172 DCHECK_EQ(read_end_
, 0U);
173 // Once we call Read(), we cannot call StartTls() until the read
174 // finishes. This is okay, as StartTls() is called only from a read
175 // handler (i.e., after a read finishes and before another read is
178 transport_socket_
->Read(
179 read_buf_
.get(), read_buf_
->size(),
180 base::Bind(&ChromeAsyncSocket::ProcessReadDone
,
181 weak_ptr_factory_
.GetWeakPtr()));
182 read_state_
= PENDING
;
183 if (status
!= net::ERR_IO_PENDING
) {
184 ProcessReadDone(status
);
188 // read_state_ == PENDING -> read_state_ == IDLE
190 void ChromeAsyncSocket::ProcessReadDone(int status
) {
191 DCHECK_NE(status
, net::ERR_IO_PENDING
);
193 DCHECK_EQ(read_state_
, PENDING
);
194 DCHECK_EQ(read_start_
, 0U);
195 DCHECK_EQ(read_end_
, 0U);
198 read_end_
= static_cast<size_t>(status
);
200 } else if (status
== 0) {
201 // Other side closed the connection.
203 net_error_
= net::OK
;
205 } else { // status < 0
206 DoNetErrorFromStatus(status
);
211 // (maybe) read_state_ == IDLE -> read_state_ == POSTED (via
214 bool ChromeAsyncSocket::Read(char* data
, size_t len
, size_t* len_read
) {
215 if (!IsOpen() && (state_
!= STATE_TLS_CONNECTING
)) {
216 // Read() may be called on a closed socket if a previous read
217 // causes a socket close (e.g., client sends wrong password and
218 // server terminates connection).
220 // TODO(akalin): Fix handling of this on the libjingle side.
221 if (state_
!= STATE_CLOSED
) {
222 LOG(DFATAL
) << "Read() called on non-open non-tls-connecting socket";
224 DoNonNetError(ERROR_WRONGSTATE
);
227 DCHECK_LE(read_start_
, read_end_
);
228 if ((state_
== STATE_TLS_CONNECTING
) || read_end_
== 0U) {
229 if (state_
== STATE_TLS_CONNECTING
) {
230 DCHECK_EQ(read_state_
, IDLE
);
231 DCHECK_EQ(read_end_
, 0U);
233 DCHECK_NE(read_state_
, IDLE
);
238 DCHECK_EQ(read_state_
, IDLE
);
239 *len_read
= std::min(len
, read_end_
- read_start_
);
240 DCHECK_GT(*len_read
, 0U);
241 std::memcpy(data
, read_buf_
->data() + read_start_
, *len_read
);
242 read_start_
+= *len_read
;
243 if (read_start_
== read_end_
) {
246 // We defer execution of DoRead() here for similar reasons as
247 // ProcessConnectDone().
253 // (maybe) write_state_ == IDLE -> write_state_ == POSTED (via
256 bool ChromeAsyncSocket::Write(const char* data
, size_t len
) {
257 if (!IsOpen() && (state_
!= STATE_TLS_CONNECTING
)) {
258 LOG(DFATAL
) << "Write() called on non-open non-tls-connecting socket";
259 DoNonNetError(ERROR_WRONGSTATE
);
262 // TODO(akalin): Avoid this check by modifying the interface to have
263 // a "ready for writing" signal.
264 if ((static_cast<size_t>(write_buf_
->size()) - write_end_
) < len
) {
265 LOG(DFATAL
) << "queueing " << len
<< " bytes would exceed the "
266 << "max write buffer size = " << write_buf_
->size()
267 << " by " << (len
- write_buf_
->size()) << " bytes";
268 DoNetError(net::ERR_INSUFFICIENT_RESOURCES
);
271 std::memcpy(write_buf_
->data() + write_end_
, data
, len
);
273 // If we're TLS-connecting, the write buffer will get flushed once
274 // the TLS-connect finishes. Otherwise, start writing if we're not
275 // already writing and we have something to write.
276 if ((state_
!= STATE_TLS_CONNECTING
) &&
277 (write_state_
== IDLE
) && (write_end_
> 0U)) {
278 // We defer execution of DoWrite() here for similar reasons as
279 // ProcessConnectDone().
285 // write_state_ == IDLE -> write_state_ == POSTED
287 void ChromeAsyncSocket::PostDoWrite() {
289 DCHECK_EQ(write_state_
, IDLE
);
290 DCHECK_GT(write_end_
, 0U);
291 base::MessageLoop
* message_loop
= base::MessageLoop::current();
293 message_loop
->PostTask(
295 base::Bind(&ChromeAsyncSocket::DoWrite
,
296 weak_ptr_factory_
.GetWeakPtr()));
297 write_state_
= POSTED
;
300 // write_state_ == POSTED -> write_state_ == PENDING
302 void ChromeAsyncSocket::DoWrite() {
304 DCHECK_EQ(write_state_
, POSTED
);
305 DCHECK_GT(write_end_
, 0U);
306 // Once we call Write(), we cannot call StartTls() until the write
307 // finishes. This is okay, as StartTls() is called only after we
308 // have received a reply to a message we sent to the server and
309 // before we send the next message.
311 transport_socket_
->Write(
312 write_buf_
.get(), write_end_
,
313 base::Bind(&ChromeAsyncSocket::ProcessWriteDone
,
314 weak_ptr_factory_
.GetWeakPtr()));
315 write_state_
= PENDING
;
316 if (status
!= net::ERR_IO_PENDING
) {
317 ProcessWriteDone(status
);
321 // write_state_ == PENDING -> write_state_ == IDLE or POSTED (the
322 // latter via PostDoWrite())
324 void ChromeAsyncSocket::ProcessWriteDone(int status
) {
325 DCHECK_NE(status
, net::ERR_IO_PENDING
);
327 DCHECK_EQ(write_state_
, PENDING
);
328 DCHECK_GT(write_end_
, 0U);
330 if (status
< net::OK
) {
331 DoNetErrorFromStatus(status
);
335 size_t written
= static_cast<size_t>(status
);
336 if (written
> write_end_
) {
337 LOG(DFATAL
) << "bytes written = " << written
338 << " exceeds bytes requested = " << write_end_
;
339 DoNetError(net::ERR_UNEXPECTED
);
343 // TODO(akalin): Figure out a better way to do this; perhaps a queue
344 // of DrainableIOBuffers. This'll also allow us to not have an
345 // artificial buffer size limit.
346 std::memmove(write_buf_
->data(),
347 write_buf_
->data() + written
,
348 write_end_
- written
);
349 write_end_
-= written
;
350 if (write_end_
> 0U) {
357 bool ChromeAsyncSocket::Close() {
362 // (not STATE_CLOSED) -> STATE_CLOSED
364 void ChromeAsyncSocket::DoClose() {
365 weak_ptr_factory_
.InvalidateWeakPtrs();
366 if (transport_socket_
.get()) {
367 transport_socket_
->Disconnect();
369 transport_socket_
.reset();
375 if (state_
!= STATE_CLOSED
) {
376 state_
= STATE_CLOSED
;
379 // Reset error variables after SignalClosed() so slots connected
380 // to it can read it.
382 net_error_
= net::OK
;
385 // STATE_OPEN -> STATE_TLS_CONNECTING
387 bool ChromeAsyncSocket::StartTls(const std::string
& domain_name
) {
388 if ((state_
!= STATE_OPEN
) || (read_state_
== PENDING
) ||
389 (write_state_
!= IDLE
)) {
390 LOG(DFATAL
) << "StartTls() called in wrong state";
391 DoNonNetError(ERROR_WRONGSTATE
);
395 state_
= STATE_TLS_CONNECTING
;
399 DCHECK_EQ(write_end_
, 0U);
401 // Clear out any posted DoRead() tasks.
402 weak_ptr_factory_
.InvalidateWeakPtrs();
404 DCHECK(transport_socket_
.get());
405 scoped_ptr
<net::ClientSocketHandle
> socket_handle(
406 new net::ClientSocketHandle());
407 socket_handle
->SetSocket(transport_socket_
.Pass());
409 resolving_client_socket_factory_
->CreateSSLClientSocket(
410 socket_handle
.Pass(), net::HostPortPair(domain_name
, 443));
411 int status
= transport_socket_
->Connect(
412 base::Bind(&ChromeAsyncSocket::ProcessSSLConnectDone
,
413 weak_ptr_factory_
.GetWeakPtr()));
414 if (status
!= net::ERR_IO_PENDING
) {
415 base::MessageLoop
* message_loop
= base::MessageLoop::current();
417 message_loop
->PostTask(
419 base::Bind(&ChromeAsyncSocket::ProcessSSLConnectDone
,
420 weak_ptr_factory_
.GetWeakPtr(), status
));
425 // STATE_TLS_CONNECTING -> STATE_TLS_OPEN
426 // read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead())
427 // (maybe) write_state_ == IDLE -> write_state_ == POSTED (via
430 void ChromeAsyncSocket::ProcessSSLConnectDone(int status
) {
431 DCHECK_NE(status
, net::ERR_IO_PENDING
);
432 DCHECK_EQ(state_
, STATE_TLS_CONNECTING
);
433 DCHECK_EQ(read_state_
, IDLE
);
434 DCHECK_EQ(read_start_
, 0U);
435 DCHECK_EQ(read_end_
, 0U);
436 DCHECK_EQ(write_state_
, IDLE
);
437 if (status
!= net::OK
) {
438 DoNetErrorFromStatus(status
);
442 state_
= STATE_TLS_OPEN
;
444 if (write_end_
> 0U) {
447 SignalSSLConnected();
450 } // namespace jingle_glue