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

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

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

Powered by Google App Engine
This is Rietveld 408576698