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

Side by Side Diff: remoting/jingle_glue/xmpp_socket_adapter.cc

Issue 10808094: Always use chromium threads for IO in remoting host (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 4 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) 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.
4
5 #include "remoting/jingle_glue/xmpp_socket_adapter.h"
6
7 #include <iomanip>
8 #include <string>
9
10 #include "base/logging.h"
11 #include "remoting/jingle_glue/ssl_adapter.h"
12 #include "third_party/libjingle/source/talk/base/byteorder.h"
13 #include "third_party/libjingle/source/talk/base/common.h"
14 #include "third_party/libjingle/source/talk/base/firewallsocketserver.h"
15 #include "third_party/libjingle/source/talk/base/socketadapters.h"
16 #include "third_party/libjingle/source/talk/base/ssladapter.h"
17 #include "third_party/libjingle/source/talk/base/thread.h"
18 #include "third_party/libjingle/source/talk/xmpp/xmppengine.h"
19
20 namespace remoting {
21
22 XmppSocketAdapter::XmppSocketAdapter(const buzz::XmppClientSettings& xcs,
23 bool allow_unverified_certs)
24 : state_(STATE_CLOSED),
25 error_(ERROR_NONE),
26 wsa_error_(0),
27 socket_(NULL),
28 protocol_(xcs.protocol()),
29 firewall_(false),
30 write_buffer_(NULL),
31 write_buffer_length_(0),
32 write_buffer_capacity_(0),
33 allow_unverified_certs_(allow_unverified_certs) {
34 proxy_.type = xcs.proxy();
35 proxy_.address.SetIP(xcs.proxy_host());
36 proxy_.address.SetPort(xcs.proxy_port());
37 proxy_.username = xcs.proxy_user();
38 proxy_.password = xcs.proxy_pass();
39 }
40
41 XmppSocketAdapter::~XmppSocketAdapter() {
42 FreeState();
43
44 // Clean up any previous socket - cannot delete socket on close because close
45 // happens during the child socket's stack callback.
46 if (socket_) {
47 delete socket_;
48 socket_ = NULL;
49 }
50 }
51
52 buzz::AsyncSocket::State XmppSocketAdapter::state() {
53 return state_;
54 }
55
56 buzz::AsyncSocket::Error XmppSocketAdapter::error() {
57 return error_;
58 }
59
60 int XmppSocketAdapter::GetError() {
61 return wsa_error_;
62 }
63
64 bool XmppSocketAdapter::FreeState() {
65 int code = 0;
66
67 // Clean up the socket.
68 if (socket_ && !(state_ == STATE_CLOSED || state_ == STATE_CLOSING)) {
69 code = socket_->Close();
70 }
71
72 delete[] write_buffer_;
73 write_buffer_ = NULL;
74 write_buffer_length_ = 0;
75 write_buffer_capacity_ = 0;
76
77 if (code) {
78 SetWSAError(code);
79 return false;
80 }
81 return true;
82 }
83
84 bool XmppSocketAdapter::Connect(const talk_base::SocketAddress& addr) {
85 if (state_ != STATE_CLOSED) {
86 SetError(ERROR_WRONGSTATE);
87 return false;
88 }
89
90 VLOG(1) << "XmppSocketAdapter::Connect(" << addr.ToString() << ")";
91
92 // Clean up any previous socket - cannot delete socket on close because close
93 // happens during the child socket's stack callback.
94 if (socket_) {
95 delete socket_;
96 socket_ = NULL;
97 }
98
99 talk_base::AsyncSocket* socket =
100 talk_base::Thread::Current()->socketserver()->CreateAsyncSocket(
101 SOCK_STREAM);
102 if (!socket) {
103 SetWSAError(WSA_NOT_ENOUGH_MEMORY);
104 return false;
105 }
106
107 if (firewall_) {
108 // TODO(sync): Change this to make WSAAsyncSockets support current thread
109 // socket server.
110 talk_base::FirewallSocketServer* fw =
111 static_cast<talk_base::FirewallSocketServer*>(
112 talk_base::Thread::Current()->socketserver());
113 socket = fw->WrapSocket(socket, SOCK_STREAM);
114 }
115
116 if (proxy_.type) {
117 talk_base::AsyncSocket* proxy_socket = 0;
118 if (proxy_.type == talk_base::PROXY_SOCKS5) {
119 proxy_socket = new talk_base::AsyncSocksProxySocket(
120 socket, proxy_.address, proxy_.username, proxy_.password);
121 } else {
122 // Note: we are trying unknown proxies as HTTPS currently.
123 proxy_socket = new talk_base::AsyncHttpsProxySocket(socket,
124 "chromoting", proxy_.address, proxy_.username,
125 proxy_.password);
126 }
127 if (!proxy_socket) {
128 SetWSAError(WSA_NOT_ENOUGH_MEMORY);
129 delete socket;
130 return false;
131 }
132 socket = proxy_socket; // For our purposes the proxy is now the socket.
133 }
134
135 if (protocol_ == cricket::PROTO_SSLTCP) {
136 talk_base::AsyncSocket *fake_ssl_socket =
137 new talk_base::AsyncSSLSocket(socket);
138 if (!fake_ssl_socket) {
139 SetWSAError(WSA_NOT_ENOUGH_MEMORY);
140 delete socket;
141 return false;
142 }
143 socket = fake_ssl_socket; // For our purposes the SSL socket is the socket.
144 }
145
146 #if defined(FEATURE_ENABLE_SSL)
147 talk_base::SSLAdapter* ssl_adapter = remoting::CreateSSLAdapter(socket);
148 socket = ssl_adapter; // For our purposes the SSL adapter is the socket.
149 #endif
150
151 socket->SignalReadEvent.connect(this, &XmppSocketAdapter::OnReadEvent);
152 socket->SignalWriteEvent.connect(this, &XmppSocketAdapter::OnWriteEvent);
153 socket->SignalConnectEvent.connect(this, &XmppSocketAdapter::OnConnectEvent);
154 socket->SignalCloseEvent.connect(this, &XmppSocketAdapter::OnCloseEvent);
155
156 // The linux implementation of socket::Connect returns an error when the
157 // connect didn't complete yet. This can be distinguished from a failure
158 // because socket::IsBlocking is true. Perhaps, the linux implementation
159 // should be made to behave like the windows version which doesn't do this,
160 // but it seems to be a pattern with these methods that they return an error
161 // if the operation didn't complete in a sync fashion and one has to check
162 // IsBlocking to tell if was a "real" error.
163 if (socket->Connect(addr) == SOCKET_ERROR && !socket->IsBlocking()) {
164 SetWSAError(socket->GetError());
165 delete socket;
166 return false;
167 }
168
169 socket_ = socket;
170 state_ = STATE_CONNECTING;
171 return true;
172 }
173
174 bool XmppSocketAdapter::Read(char* data, size_t len, size_t* len_read) {
175 if (len_read)
176 *len_read = 0;
177
178 if (state_ <= STATE_CLOSING) {
179 SetError(ERROR_WRONGSTATE);
180 return false;
181 }
182
183 DCHECK(socket_);
184
185 if (IsOpen()) {
186 int result = socket_->Recv(data, len);
187 if (result < 0) {
188 if (!socket_->IsBlocking()) {
189 SetWSAError(socket_->GetError());
190 return false;
191 }
192
193 result = 0;
194 }
195
196 if (len_read)
197 *len_read = result;
198 }
199
200 return true;
201 }
202
203 bool XmppSocketAdapter::Write(const char* data, size_t len) {
204 if (state_ <= STATE_CLOSING) {
205 // There may be data in a buffer that gets lost. Too bad!
206 SetError(ERROR_WRONGSTATE);
207 return false;
208 }
209
210 DCHECK(socket_);
211
212 size_t sent = 0;
213
214 // Try an immediate write when there is no buffer and we aren't in SSL mode
215 // or opening the connection.
216 if (write_buffer_length_ == 0 && IsOpen()) {
217 int result = socket_->Send(data, len);
218 if (result < 0) {
219 if (!socket_->IsBlocking()) {
220 SetWSAError(socket_->GetError());
221 return false;
222 }
223 result = 0;
224 }
225
226 sent = static_cast<size_t>(result);
227 }
228
229 // Buffer what we didn't send.
230 if (sent < len) {
231 QueueWriteData(data + sent, len - sent);
232 }
233
234 // Service the socket right away to push the written data out in SSL mode.
235 return HandleWritable();
236 }
237
238 bool XmppSocketAdapter::Close() {
239 if (state_ == STATE_CLOSING) {
240 return false; // Avoid recursion, but not unexpected.
241 }
242 if (state_ == STATE_CLOSED) {
243 // In theory should not be trying to re-InternalClose.
244 SetError(ERROR_WRONGSTATE);
245 return false;
246 }
247
248 // TODO(sync): deal with flushing close (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 VLOG(1) << "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 VLOG(1) << "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 VLOG(1) << "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 VLOG(1) << "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(DFATAL) << "unexpected XmppSocketAdapter::OnConnectEvent state: "
302 << state_;
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 VLOG(1) << "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 DCHECK_EQ(write_buffer_length_, 0U);
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 DCHECK_LE(write_buffer_length_, 64000U);
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 DCHECK(error);
374 DCHECK(wsa_error);
375
376 size_t flushed = 0;
377 while (flushed < write_buffer_length_) {
378 int sent = socket_->Send(write_buffer_ + flushed,
379 static_cast<int>(write_buffer_length_ - flushed));
380 if (sent < 0) {
381 if (!socket_->IsBlocking()) {
382 *error = ERROR_WINSOCK;
383 *wsa_error = socket_->GetError();
384 }
385 break;
386 }
387 flushed += static_cast<size_t>(sent);
388 }
389
390 // Remove flushed memory.
391 write_buffer_length_ -= flushed;
392 memmove(write_buffer_, write_buffer_ + flushed, write_buffer_length_);
393
394 // When everything is flushed, deallocate the buffer if it's gotten big.
395 if (write_buffer_length_ == 0) {
396 if (write_buffer_capacity_ > 8192) {
397 delete[] write_buffer_;
398 write_buffer_ = NULL;
399 write_buffer_capacity_ = 0;
400 }
401 }
402 }
403
404 void XmppSocketAdapter::SetError(Error error) {
405 if (error_ == ERROR_NONE) {
406 error_ = error;
407 }
408 }
409
410 void XmppSocketAdapter::SetWSAError(int error) {
411 if (error_ == ERROR_NONE && error != 0) {
412 error_ = ERROR_WINSOCK;
413 wsa_error_ = error;
414 }
415 }
416
417 bool XmppSocketAdapter::HandleReadable() {
418 if (!IsOpen())
419 return false;
420
421 SignalRead();
422 return true;
423 }
424
425 bool XmppSocketAdapter::HandleWritable() {
426 if (!IsOpen())
427 return false;
428
429 Error error = ERROR_NONE;
430 int wsa_error = 0;
431 FlushWriteQueue(&error, &wsa_error);
432 if (error != ERROR_NONE) {
433 Close();
434 return false;
435 }
436 return true;
437 }
438
439 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/jingle_glue/xmpp_socket_adapter.h ('k') | remoting/protocol/jingle_session_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698