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

Side by Side Diff: chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.cc

Issue 194065: Initial commit of sync engine code to browser/sync.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Fixes to gtest include path, reverted syncapi. Created 11 years, 3 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 | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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/notifier/communicator/xmpp_socket_adapter.h"
6
7 #include <iomanip>
8 #include <string>
9
10 #include "chrome/browser/sync/notifier/communicator/product_info.h"
11 #include "talk/base/byteorder.h"
12 #include "talk/base/common.h"
13 #include "talk/base/firewallsocketserver.h"
14 #include "talk/base/logging.h"
15 #include "talk/base/socketadapters.h"
16 #include "talk/base/ssladapter.h"
17 #include "talk/xmpp/xmppengine.h"
18
19 namespace notifier {
20
21 XmppSocketAdapter::XmppSocketAdapter(const buzz::XmppClientSettings& xcs,
22 bool allow_unverified_certs)
23 : state_(STATE_CLOSED),
24 error_(ERROR_NONE),
25 wsa_error_(0),
26 socket_(NULL),
27 protocol_(xcs.protocol()),
28 firewall_(false),
29 write_buffer_(NULL),
30 write_buffer_length_(0),
31 write_buffer_capacity_(0),
32 allow_unverified_certs_(allow_unverified_certs) {
33 proxy_.type = xcs.proxy();
34 proxy_.address.SetIP(xcs.proxy_host(), false);
35 proxy_.address.SetPort(xcs.proxy_port());
36 proxy_.username = xcs.proxy_user();
37 proxy_.password = xcs.proxy_pass();
38 }
39
40 XmppSocketAdapter::~XmppSocketAdapter() {
41 FreeState();
42
43 // Clean up any previous socket - cannot delete socket on close because
44 // close happens during the child socket's stack callback.
45 if (socket_) {
46 delete socket_;
47 socket_ = NULL;
48 }
49 }
50
51 bool XmppSocketAdapter::FreeState() {
52 int code = 0;
53
54 // Clean up the socket
55 if (socket_ && !(state_ == STATE_CLOSED || state_ == STATE_CLOSING)) {
56 code = socket_->Close();
57 }
58
59 delete[] write_buffer_;
60 write_buffer_ = NULL;
61 write_buffer_length_ = 0;
62 write_buffer_capacity_ = 0;
63
64 if (code) {
65 SetWSAError(code);
66 return false;
67 }
68 return true;
69 }
70
71 bool XmppSocketAdapter::Connect(const talk_base::SocketAddress& addr) {
72 if (state_ != STATE_CLOSED) {
73 SetError(ERROR_WRONGSTATE);
74 return false;
75 }
76
77 LOG(LS_INFO) << "XmppSocketAdapter::Connect(" << addr.ToString() << ")";
78
79 // Clean up any previous socket - cannot delete socket on close because
80 // close happens during the child socket's stack callback.
81 if (socket_) {
82 delete socket_;
83 socket_ = NULL;
84 }
85
86 talk_base::AsyncSocket* socket =
87 talk_base::Thread::Current()->socketserver()
88 ->CreateAsyncSocket(SOCK_STREAM);
89 if (!socket) {
90 SetWSAError(WSA_NOT_ENOUGH_MEMORY);
91 return false;
92 }
93
94 if (firewall_) {
95 // TODO(sync): Change this to make WSAAsyncSockets support current thread
96 // socket server
97 talk_base::FirewallSocketServer* fw =
98 static_cast<talk_base::FirewallSocketServer*>(
99 talk_base::Thread::Current()->socketserver());
100 socket = fw->WrapSocket(socket, SOCK_STREAM);
101 }
102
103 if (proxy_.type) {
104 talk_base::AsyncSocket* proxy_socket = 0;
105 if (proxy_.type == talk_base::PROXY_SOCKS5) {
106 proxy_socket = new talk_base::AsyncSocksProxySocket(
107 socket, proxy_.address, proxy_.username, proxy_.password);
108 } else {
109 // Note: we are trying unknown proxies as HTTPS currently
110 proxy_socket = new talk_base::AsyncHttpsProxySocket(socket,
111 GetUserAgentString(), proxy_.address,
112 proxy_.username, proxy_.password);
113 }
114 if (!proxy_socket) {
115 SetWSAError(WSA_NOT_ENOUGH_MEMORY);
116 delete socket;
117 return false;
118 }
119 socket = proxy_socket; // for our purposes the proxy is now the socket
120 }
121
122 // #if defined(PRODUCTION)
123 if (protocol_ == cricket::PROTO_SSLTCP) {
124 talk_base::AsyncSocket *fake_ssl_socket =
125 new talk_base::AsyncSSLSocket(socket);
126 if (!fake_ssl_socket) {
127 SetWSAError(WSA_NOT_ENOUGH_MEMORY);
128 delete socket;
129 return false;
130 }
131 socket = fake_ssl_socket; // for our purposes the SSL socket is the socket
132 }
133 // #endif // PRODUCTION
134
135 #if defined(FEATURE_ENABLE_SSL)
136 talk_base::SSLAdapter* ssl = talk_base::SSLAdapter::Create(socket);
137 socket = ssl;
138 #endif
139
140 // #if !defined(PRODUCTION)
141 // if (protocol_ == cricket::PROTO_SSLTCP) {
142 // ssl->set_ignore_bad_cert(true);
143 // ssl->StartSSL(addr.hostname().c_str(), true);
144 // }
145 // #endif // PRODUCTION
146
147 socket->SignalReadEvent.connect(this, &XmppSocketAdapter::OnReadEvent);
148 socket->SignalWriteEvent.connect(this, &XmppSocketAdapter::OnWriteEvent);
149 socket->SignalConnectEvent.connect(this, &XmppSocketAdapter::OnConnectEvent);
150 socket->SignalCloseEvent.connect(this, &XmppSocketAdapter::OnCloseEvent);
151
152 // The linux implementation of socket::Connect
153 // returns an error when the connect didn't complete
154 // yet. This can be distinguished from a failure
155 // because socket::IsBlocking is true. Perhaps,
156 // the linux implementation should be made to
157 // behave like the windows version which doesn't do this,
158 // but it seems to be a pattern with these methods
159 // that they return an error if the operation didn't
160 // complete in a sync fashion and one has to check IsBlocking
161 // to tell if was a "real" error.
162 if (socket->Connect(addr) == SOCKET_ERROR && !socket->IsBlocking()) {
163 SetWSAError(socket->GetError());
164 delete socket;
165 return false;
166 }
167
168 socket_ = socket;
169 state_ = STATE_CONNECTING;
170 return true;
171 }
172
173 bool XmppSocketAdapter::Read(char* data, size_t len, size_t* len_read) {
174 if (len_read)
175 *len_read = 0;
176
177 if (state_ <= STATE_CLOSING) {
178 SetError(ERROR_WRONGSTATE);
179 return false;
180 }
181
182 ASSERT(socket_ != NULL);
183
184 if (IsOpen()) {
185 int result = socket_->Recv(data, len);
186 if (result < 0) {
187 if (!socket_->IsBlocking()) {
188 SetWSAError(socket_->GetError());
189 return false;
190 }
191
192 result = 0;
193 }
194
195 if (len_read)
196 *len_read = result;
197 }
198
199 return true;
200 }
201
202 bool XmppSocketAdapter::Write(const char* data, size_t len) {
203 if (state_ <= STATE_CLOSING) {
204 // There may be data in a buffer that gets lost. Too bad!
205 SetError(ERROR_WRONGSTATE);
206 return false;
207 }
208
209 ASSERT(socket_ != NULL);
210
211 size_t sent = 0;
212
213 // try an immediate write when there is no buffer
214 // and we aren't in SSL mode or opening the connection
215 if (write_buffer_length_ == 0 && IsOpen()) {
216 int result = socket_->Send(data, len);
217 if (result < 0) {
218 if (!socket_->IsBlocking()) {
219 SetWSAError(socket_->GetError());
220 return false;
221 }
222 result = 0;
223 }
224
225 sent = static_cast<size_t>(result);
226 }
227
228 // Buffer what we didn't send
229 if (sent < len) {
230 QueueWriteData(data + sent, len - sent);
231 }
232
233 // Service the socket right away to push the written data out in SSL mode
234 return HandleWritable();
235 }
236
237 bool XmppSocketAdapter::Close() {
238 if (state_ == STATE_CLOSING) {
239 return false; // avoid recursion, but not unexpected
240 }
241 if (state_ == STATE_CLOSED) {
242 // in theory should not be trying to re-InternalClose.
243 SetError(ERROR_WRONGSTATE);
244 return false;
245 }
246
247 // todo: deal with flushing close
248 // (flush, don't do reads, clean ssl)
249
250 // If we've gotten to the point where we really do have a socket underneath
251 // then close it. It should call us back to tell us it is closed, and
252 // NotifyClose will be called. We indicate "closing" state so that we
253 // do not recusively try to keep closing the socket.
254 if (socket_) {
255 state_ = STATE_CLOSING;
256 socket_->Close();
257 }
258
259 // If we didn't get the callback, then we better make sure we signal
260 // closed.
261 if (state_ != STATE_CLOSED) {
262 // The socket was closed manually, not directly due to error.
263 if (error_ != ERROR_NONE) {
264 LOG(LS_INFO) << "XmppSocketAdapter::Close - previous Error: " << error_
265 << " WSAError: " << wsa_error_;
266 error_ = ERROR_NONE;
267 wsa_error_ = 0;
268 }
269 NotifyClose();
270 }
271 return true;
272 }
273
274 void XmppSocketAdapter::NotifyClose() {
275 if (state_ == STATE_CLOSED) {
276 SetError(ERROR_WRONGSTATE);
277 } else {
278 LOG(LS_INFO) << "XmppSocketAdapter::NotifyClose - Error: " << error_
279 << " WSAError: " << wsa_error_;
280 state_ = STATE_CLOSED;
281 SignalClosed();
282 FreeState();
283 }
284 }
285
286 void XmppSocketAdapter::OnConnectEvent(talk_base::AsyncSocket *socket) {
287 if (state_ == STATE_CONNECTING) {
288 state_ = STATE_OPEN;
289 LOG(LS_INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_OPEN";
290 SignalConnected();
291 #if defined(FEATURE_ENABLE_SSL)
292 } else if (state_ == STATE_TLS_CONNECTING) {
293 state_ = STATE_TLS_OPEN;
294 LOG(LS_INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_TLS_OPEN";
295 SignalSSLConnected();
296 if (write_buffer_length_ > 0) {
297 HandleWritable();
298 }
299 #endif // defined(FEATURE_ENABLE_SSL)
300 } else {
301 LOG(LS_INFO) << "XmppSocketAdapter::OnConnectEvent - state is " << state_;
302 ASSERT(false);
303 }
304 }
305
306 void XmppSocketAdapter::OnReadEvent(talk_base::AsyncSocket *socket) {
307 HandleReadable();
308 }
309
310 void XmppSocketAdapter::OnWriteEvent(talk_base::AsyncSocket *socket) {
311 HandleWritable();
312 }
313
314 void XmppSocketAdapter::OnCloseEvent(talk_base::AsyncSocket *socket,
315 int error) {
316 LOG(LS_INFO) << "XmppSocketAdapter::OnCloseEvent(" << error << ")";
317 SetWSAError(error);
318 if (error == SOCKET_EACCES) {
319 SignalAuthenticationError(); // proxy needs authentication
320 }
321 NotifyClose();
322 }
323
324 #if defined(FEATURE_ENABLE_SSL)
325 bool XmppSocketAdapter::StartTls(const std::string& verify_host_name) {
326 if (state_ != STATE_OPEN) {
327 SetError(ERROR_WRONGSTATE);
328 return false;
329 }
330
331 state_ = STATE_TLS_CONNECTING;
332
333 ASSERT(write_buffer_length_ == 0);
334
335 talk_base::SSLAdapter* ssl_adapter =
336 static_cast<talk_base::SSLAdapter*>(socket_);
337
338 if (allow_unverified_certs_) {
339 ssl_adapter->set_ignore_bad_cert(true);
340 }
341
342 if (ssl_adapter->StartSSL(verify_host_name.c_str(), false) != 0) {
343 state_ = STATE_OPEN;
344 SetError(ERROR_SSL);
345 return false;
346 }
347
348 return true;
349 }
350 #endif // defined(FEATURE_ENABLE_SSL)
351
352 void XmppSocketAdapter::QueueWriteData(const char* data, size_t len) {
353 // expand buffer if needed
354 if (write_buffer_length_ + len > write_buffer_capacity_) {
355 size_t new_capacity = 1024;
356 while (new_capacity < write_buffer_length_ + len) {
357 new_capacity = new_capacity * 2;
358 }
359 char* new_buffer = new char[new_capacity];
360 ASSERT(write_buffer_length_ <= 64000);
361 memcpy(new_buffer, write_buffer_, write_buffer_length_);
362 delete[] write_buffer_;
363 write_buffer_ = new_buffer;
364 write_buffer_capacity_ = new_capacity;
365 }
366
367 // copy data into the end of buffer
368 memcpy(write_buffer_ + write_buffer_length_, data, len);
369 write_buffer_length_ += len;
370 }
371
372 void XmppSocketAdapter::FlushWriteQueue(Error* error, int* wsa_error) {
373 ASSERT(error && wsa_error);
374
375 size_t flushed = 0;
376 while (flushed < write_buffer_length_) {
377 int sent = socket_->Send(write_buffer_ + flushed,
378 static_cast<int>(write_buffer_length_ - flushed));
379 if (sent < 0) {
380 if (!socket_->IsBlocking()) {
381 *error = ERROR_WINSOCK;
382 *wsa_error = socket_->GetError();
383 }
384 break;
385 }
386 flushed += static_cast<size_t>(sent);
387 }
388
389 // remove flushed memory
390 write_buffer_length_ -= flushed;
391 memmove(write_buffer_, write_buffer_ + flushed, write_buffer_length_);
392
393 // when everything is flushed, deallocate the buffer if it's gotten big
394 if (write_buffer_length_ == 0) {
395 if (write_buffer_capacity_ > 8192) {
396 delete[] write_buffer_;
397 write_buffer_ = NULL;
398 write_buffer_capacity_ = 0;
399 }
400 }
401 }
402
403 void XmppSocketAdapter::SetError(Error error) {
404 if (error_ == ERROR_NONE) {
405 error_ = error;
406 }
407 }
408
409 void XmppSocketAdapter::SetWSAError(int error) {
410 if (error_ == ERROR_NONE && error != 0) {
411 error_ = ERROR_WINSOCK;
412 wsa_error_ = error;
413 }
414 }
415
416 bool XmppSocketAdapter::HandleReadable() {
417 if (!IsOpen())
418 return false;
419
420 SignalRead();
421 return true;
422 }
423
424 bool XmppSocketAdapter::HandleWritable() {
425 if (!IsOpen())
426 return false;
427
428 Error error = ERROR_NONE;
429 int wsa_error = 0;
430 FlushWriteQueue(&error, &wsa_error);
431 if (error != ERROR_NONE) {
432 Close();
433 return false;
434 }
435 return true;
436 }
437 } // namespace notifier
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698