Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(349)

Side by Side Diff: chrome/browser/sync/tools/chrome_async_socket.cc

Issue 2863030: Added implementation of buzz::AsyncSocket that uses Chrome sockets. (Closed)
Patch Set: Addressed willchan's last comments Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2010 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.
4
5 #include "chrome/browser/sync/tools/chrome_async_socket.h"
6
7 #if defined(OS_WIN)
8 #include <winsock2.h>
9 #elif defined(OS_POSIX)
10 #include <arpa/inet.h>
11 #endif
12
13 #include <algorithm>
14 #include <cstring>
15 #include <cstdlib>
16
17 #include "base/compiler_specific.h"
18 #include "base/logging.h"
19 #include "net/base/address_list.h"
20 #include "net/base/io_buffer.h"
21 #include "net/base/ssl_config_service.h"
22 #include "net/base/sys_addrinfo.h"
23 #include "net/socket/client_socket_factory.h"
24 #include "net/socket/ssl_client_socket.h"
25 #include "net/socket/tcp_client_socket.h"
26 #include "talk/base/socketaddress.h"
27
28 namespace sync_tools {
29
30 ChromeAsyncSocket::ChromeAsyncSocket(
31 net::ClientSocketFactory* client_socket_factory,
32 const net::SSLConfig& ssl_config,
33 size_t read_buf_size,
34 size_t write_buf_size,
35 net::NetLog* net_log)
36 : connect_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
37 &ChromeAsyncSocket::ProcessConnectDone),
38 read_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
39 &ChromeAsyncSocket::ProcessReadDone),
40 write_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
41 &ChromeAsyncSocket::ProcessWriteDone),
42 ssl_connect_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
43 &ChromeAsyncSocket::ProcessSSLConnectDone),
44 client_socket_factory_(client_socket_factory),
45 ssl_config_(ssl_config),
46 bound_net_log_(
47 net::BoundNetLog::Make(net_log, net::NetLog::SOURCE_SOCKET)),
48 state_(STATE_CLOSED),
49 error_(ERROR_NONE),
50 net_error_(net::OK),
51 scoped_runnable_method_factory_(
52 ALLOW_THIS_IN_INITIALIZER_LIST(this)),
53 read_state_(IDLE),
54 read_buf_(new net::IOBufferWithSize(read_buf_size)),
55 read_start_(0),
56 read_end_(0),
57 write_state_(IDLE),
58 write_buf_(new net::IOBufferWithSize(write_buf_size)),
59 write_end_(0) {
60 DCHECK(client_socket_factory_);
61 DCHECK_GT(read_buf_size, 0);
62 DCHECK_GT(write_buf_size, 0);
63 }
64
65 ChromeAsyncSocket::~ChromeAsyncSocket() {}
66
67 ChromeAsyncSocket::State ChromeAsyncSocket::state() {
68 return state_;
69 }
70
71 ChromeAsyncSocket::Error ChromeAsyncSocket::error() {
72 return error_;
73 }
74
75 int ChromeAsyncSocket::GetError() {
76 return net_error_;
77 }
78
79 bool ChromeAsyncSocket::IsOpen() const {
80 return (state_ == STATE_OPEN) || (state_ == STATE_TLS_OPEN);
81 }
82
83 void ChromeAsyncSocket::DoNonNetError(Error error) {
84 DCHECK_NE(error, ERROR_NONE);
85 DCHECK_NE(error, ERROR_WINSOCK);
86 error_ = error;
87 net_error_ = net::OK;
88 }
89
90 void ChromeAsyncSocket::DoNetError(net::Error net_error) {
91 error_ = ERROR_WINSOCK;
92 net_error_ = net_error;
93 }
94
95 void ChromeAsyncSocket::DoNetErrorFromStatus(int status) {
96 DCHECK_LT(status, net::OK);
97 DoNetError(static_cast<net::Error>(status));
98 }
99
100 namespace {
101
102 net::AddressList SocketAddressToAddressList(
103 const talk_base::SocketAddress& address) {
104 DCHECK_NE(address.ip(), 0);
105 // Use malloc() as net::AddressList uses free().
106 addrinfo* ai = static_cast<addrinfo*>(std::malloc(sizeof *ai));
107 memset(ai, 0, sizeof *ai);
108 ai->ai_family = AF_INET;
109 ai->ai_socktype = SOCK_STREAM;
110 ai->ai_addrlen = sizeof(sockaddr_in);
111
112 sockaddr_in* addr = static_cast<sockaddr_in*>(std::malloc(sizeof *addr));
113 memset(addr, 0, sizeof *addr);
114 addr->sin_family = AF_INET;
115 addr->sin_addr.s_addr = htonl(address.ip());
116 addr->sin_port = htons(address.port());
117 ai->ai_addr = reinterpret_cast<sockaddr*>(addr);
118
119 net::AddressList address_list;
120 address_list.Adopt(ai);
121 return address_list;
122 }
123
124 } // namespace
125
126 // STATE_CLOSED -> STATE_CONNECTING
127
128 bool ChromeAsyncSocket::Connect(const talk_base::SocketAddress& address) {
129 if (state_ != STATE_CLOSED) {
130 LOG(DFATAL) << "Connect() called on non-closed socket";
131 DoNonNetError(ERROR_WRONGSTATE);
132 return false;
133 }
134 if (address.ip() == 0) {
135 DoNonNetError(ERROR_DNS);
136 return false;
137 }
138
139 DCHECK_EQ(state_, buzz::AsyncSocket::STATE_CLOSED);
140 DCHECK_EQ(read_state_, IDLE);
141 DCHECK_EQ(write_state_, IDLE);
142
143 state_ = STATE_CONNECTING;
144
145 DCHECK(scoped_runnable_method_factory_.empty());
146 scoped_runnable_method_factory_.RevokeAll();
147
148 net::AddressList address_list = SocketAddressToAddressList(address);
149 transport_socket_.reset(
150 client_socket_factory_->
151 CreateTCPClientSocket(address_list, bound_net_log_.net_log()));
152 int status = transport_socket_->Connect(&connect_callback_);
153 if (status != net::ERR_IO_PENDING) {
154 // We defer execution of ProcessConnectDone instead of calling it
155 // directly here as the caller may not expect an error/close to
156 // happen here. This is okay, as from the caller's point of view,
157 // the connect always happens asynchronously.
158 MessageLoop* message_loop = MessageLoop::current();
159 CHECK(message_loop);
160 message_loop->PostTask(
161 FROM_HERE,
162 scoped_runnable_method_factory_.NewRunnableMethod(
163 &ChromeAsyncSocket::ProcessConnectDone, status));
164 }
165 return true;
166 }
167
168 // STATE_CONNECTING -> STATE_OPEN
169 // read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead())
170
171 void ChromeAsyncSocket::ProcessConnectDone(int status) {
172 DCHECK_NE(status, net::ERR_IO_PENDING);
173 DCHECK_EQ(read_state_, IDLE);
174 DCHECK_EQ(write_state_, IDLE);
175 DCHECK_EQ(state_, STATE_CONNECTING);
176 if (status != net::OK) {
177 DoNetErrorFromStatus(status);
178 DoClose();
179 return;
180 }
181 state_ = STATE_OPEN;
182 PostDoRead();
183 // Write buffer should be empty.
184 DCHECK_EQ(write_end_, 0);
185 SignalConnected();
186 }
187
188 // read_state_ == IDLE -> read_state_ == POSTED
189
190 void ChromeAsyncSocket::PostDoRead() {
191 DCHECK(IsOpen());
192 DCHECK_EQ(read_state_, IDLE);
193 DCHECK_EQ(read_start_, 0);
194 DCHECK_EQ(read_end_, 0);
195 MessageLoop* message_loop = MessageLoop::current();
196 CHECK(message_loop);
197 message_loop->PostTask(
198 FROM_HERE,
199 scoped_runnable_method_factory_.NewRunnableMethod(
200 &ChromeAsyncSocket::DoRead));
201 read_state_ = POSTED;
202 }
203
204 // read_state_ == POSTED -> read_state_ == PENDING
205
206 void ChromeAsyncSocket::DoRead() {
207 DCHECK(IsOpen());
208 DCHECK_EQ(read_state_, POSTED);
209 DCHECK_EQ(read_start_, 0);
210 DCHECK_EQ(read_end_, 0);
211 // Once we call Read(), we cannot call StartTls() until the read
212 // finishes. This is okay, as StartTls() is called only from a read
213 // handler (i.e., after a read finishes and before another read is
214 // done).
215 int status =
216 transport_socket_->Read(
217 read_buf_.get(), read_buf_->size(), &read_callback_);
218 read_state_ = PENDING;
219 if (status != net::ERR_IO_PENDING) {
220 ProcessReadDone(status);
221 }
222 }
223
224 // read_state_ == PENDING -> read_state_ == IDLE
225
226 void ChromeAsyncSocket::ProcessReadDone(int status) {
227 DCHECK_NE(status, net::ERR_IO_PENDING);
228 DCHECK(IsOpen());
229 DCHECK_EQ(read_state_, PENDING);
230 DCHECK_EQ(read_start_, 0);
231 DCHECK_EQ(read_end_, 0);
232 read_state_ = IDLE;
233 if (status > 0) {
234 read_end_ = status;
235 SignalRead();
236 } else if (status == 0) {
237 // Other side closed the connection.
238 error_ = ERROR_NONE;
239 net_error_ = net::OK;
240 DoClose();
241 } else { // status < 0
242 DoNetErrorFromStatus(status);
243 DoClose();
244 }
245 }
246
247 // (maybe) read_state_ == IDLE -> read_state_ == POSTED (via
248 // PostDoRead())
249
250 bool ChromeAsyncSocket::Read(char* data, size_t len, size_t* len_read) {
251 if (!IsOpen() && (state_ != STATE_TLS_CONNECTING)) {
252 LOG(DFATAL) << "Read() called on non-open non-tls-connecting socket";
253 DoNonNetError(ERROR_WRONGSTATE);
254 return false;
255 }
256 DCHECK_LE(read_start_, read_end_);
257 if ((state_ == STATE_TLS_CONNECTING) || read_end_ == 0) {
258 if (state_ == STATE_TLS_CONNECTING) {
259 DCHECK_EQ(read_state_, IDLE);
260 DCHECK_EQ(read_end_, 0);
261 } else {
262 DCHECK_NE(read_state_, IDLE);
263 }
264 *len_read = 0;
265 return true;
266 }
267 DCHECK_EQ(read_state_, IDLE);
268 *len_read = std::min(len, read_end_ - read_start_);
269 DCHECK_GT(*len_read, 0);
270 std::memcpy(data, read_buf_->data() + read_start_, *len_read);
271 read_start_ += *len_read;
272 if (read_start_ == read_end_) {
273 read_start_ = 0;
274 read_end_ = 0;
275 // We defer execution of DoRead() here for similar reasons as
276 // ProcessConnectDone().
277 PostDoRead();
278 }
279 return true;
280 }
281
282 // (maybe) write_state_ == IDLE -> write_state_ == POSTED (via
283 // PostDoWrite())
284
285 bool ChromeAsyncSocket::Write(const char* data, size_t len) {
286 if (!IsOpen() && (state_ != STATE_TLS_CONNECTING)) {
287 LOG(DFATAL) << "Write() called on non-open non-tls-connecting socket";
288 DoNonNetError(ERROR_WRONGSTATE);
289 return false;
290 }
291 // TODO(akalin): Avoid this check by modifying the interface to have
292 // a "ready for writing" signal.
293 if ((write_buf_->size() - write_end_) < len) {
294 LOG(DFATAL) << "queueing " << len << " bytes would exceed the "
295 << "max write buffer size = " << write_buf_->size()
296 << " by " << (len - write_buf_->size()) << " bytes";
297 DoNetError(net::ERR_INSUFFICIENT_RESOURCES);
298 return false;
299 }
300 std::memcpy(write_buf_->data() + write_end_, data, len);
301 write_end_ += len;
302 // If we're TLS-connecting, the write buffer will get flushed once
303 // the TLS-connect finishes. Otherwise, start writing if we're not
304 // already writing and we have something to write.
305 if ((state_ != STATE_TLS_CONNECTING) &&
306 (write_state_ == IDLE) && (write_end_ > 0)) {
307 // We defer execution of DoWrite() here for similar reasons as
308 // ProcessConnectDone().
309 PostDoWrite();
310 }
311 return true;
312 }
313
314 // write_state_ == IDLE -> write_state_ == POSTED
315
316 void ChromeAsyncSocket::PostDoWrite() {
317 DCHECK(IsOpen());
318 DCHECK_EQ(write_state_, IDLE);
319 DCHECK_GT(write_end_, 0);
320 MessageLoop* message_loop = MessageLoop::current();
321 CHECK(message_loop);
322 message_loop->PostTask(
323 FROM_HERE,
324 scoped_runnable_method_factory_.NewRunnableMethod(
325 &ChromeAsyncSocket::DoWrite));
326 write_state_ = POSTED;
327 }
328
329 // write_state_ == POSTED -> write_state_ == PENDING
330
331 void ChromeAsyncSocket::DoWrite() {
332 DCHECK(IsOpen());
333 DCHECK_EQ(write_state_, POSTED);
334 DCHECK_GT(write_end_, 0);
335 // Once we call Write(), we cannot call StartTls() until the write
336 // finishes. This is okay, as StartTls() is called only after we
337 // have received a reply to a message we sent to the server and
338 // before we send the next message.
339 int status =
340 transport_socket_->Write(
341 write_buf_.get(), write_end_, &write_callback_);
342 write_state_ = PENDING;
343 if (status != net::ERR_IO_PENDING) {
344 ProcessWriteDone(status);
345 }
346 }
347
348 // write_state_ == PENDING -> write_state_ == IDLE or POSTED (the
349 // latter via PostDoWrite())
350
351 void ChromeAsyncSocket::ProcessWriteDone(int status) {
352 DCHECK_NE(status, net::ERR_IO_PENDING);
353 DCHECK(IsOpen());
354 DCHECK_EQ(write_state_, PENDING);
355 DCHECK_GT(write_end_, 0);
356 write_state_ = IDLE;
357 if (status < net::OK) {
358 DoNetErrorFromStatus(status);
359 DoClose();
360 return;
361 }
362 if (status > write_end_) {
363 LOG(DFATAL) << "bytes read = " << status
364 << " exceeds bytes requested = " << write_end_;
365 DoNetError(net::ERR_UNEXPECTED);
366 DoClose();
367 return;
368 }
369 // TODO(akalin): Figure out a better way to do this; perhaps a queue
370 // of DrainableIOBuffers. This'll also allow us to not have an
371 // artificial buffer size limit.
372 std::memmove(write_buf_->data(),
373 write_buf_->data() + status,
374 write_end_ - status);
375 write_end_ -= status;
376 if (write_end_ > 0) {
377 PostDoWrite();
378 }
379 }
380
381 // * -> STATE_CLOSED
382
383 bool ChromeAsyncSocket::Close() {
384 DoClose();
385 return true;
386 }
387
388 // (not STATE_CLOSED) -> STATE_CLOSED
389
390 void ChromeAsyncSocket::DoClose() {
391 scoped_runnable_method_factory_.RevokeAll();
392 if (transport_socket_.get()) {
393 transport_socket_->Disconnect();
394 }
395 transport_socket_.reset();
396 read_state_ = IDLE;
397 read_start_ = 0;
398 read_end_ = 0;
399 write_state_ = IDLE;
400 write_end_ = 0;
401 if (state_ != STATE_CLOSED) {
402 state_ = STATE_CLOSED;
403 SignalClosed();
404 }
405 // Reset error variables after SignalClosed() so slots connected
406 // to it can read it.
407 error_ = ERROR_NONE;
408 net_error_ = net::OK;
409 }
410
411 // STATE_OPEN -> STATE_TLS_CONNECTING
412
413 bool ChromeAsyncSocket::StartTls(const std::string& domain_name) {
414 if ((state_ != STATE_OPEN) || (read_state_ == PENDING) ||
415 (write_state_ != IDLE)) {
416 LOG(DFATAL) << "StartTls() called in wrong state";
417 DoNonNetError(ERROR_WRONGSTATE);
418 return false;
419 }
420
421 state_ = STATE_TLS_CONNECTING;
422 read_state_ = IDLE;
423 read_start_ = 0;
424 read_end_ = 0;
425 DCHECK_EQ(write_end_, 0);
426
427 // Clear out any posted DoRead() tasks.
428 scoped_runnable_method_factory_.RevokeAll();
429
430 DCHECK(transport_socket_.get());
431 transport_socket_.reset(
432 client_socket_factory_->CreateSSLClientSocket(
433 transport_socket_.release(), domain_name, ssl_config_));
434 int status = transport_socket_->Connect(&ssl_connect_callback_);
435 if (status != net::ERR_IO_PENDING) {
436 MessageLoop* message_loop = MessageLoop::current();
437 CHECK(message_loop);
438 message_loop->PostTask(
439 FROM_HERE,
440 scoped_runnable_method_factory_.NewRunnableMethod(
441 &ChromeAsyncSocket::ProcessSSLConnectDone, status));
442 }
443 return true;
444 }
445
446 // STATE_TLS_CONNECTING -> STATE_TLS_OPEN
447 // read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead())
448 // (maybe) write_state_ == IDLE -> write_state_ == POSTED (via
449 // PostDoWrite())
450
451 void ChromeAsyncSocket::ProcessSSLConnectDone(int status) {
452 DCHECK_NE(status, net::ERR_IO_PENDING);
453 DCHECK_EQ(state_, STATE_TLS_CONNECTING);
454 DCHECK_EQ(read_state_, IDLE);
455 DCHECK_EQ(read_start_, 0);
456 DCHECK_EQ(read_end_, 0);
457 DCHECK_EQ(write_state_, IDLE);
458 if (status != net::OK) {
459 DoNetErrorFromStatus(status);
460 return;
461 }
462 state_ = STATE_TLS_OPEN;
463 PostDoRead();
464 if (write_end_ > 0) {
465 PostDoWrite();
466 }
467 SignalSSLConnected();
468 }
469
470 } // namespace sync_tools
OLDNEW
« no previous file with comments | « chrome/browser/sync/tools/chrome_async_socket.h ('k') | chrome/browser/sync/tools/chrome_async_socket_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698