Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / remoting / signaling / xmpp_signal_strategy.cc
blob92c0db5b3b33874b11287f07a1a9c7b51d19ad6b
1 // Copyright 2014 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 "remoting/signaling/xmpp_signal_strategy.h"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/observer_list.h"
13 #include "base/rand_util.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/time/time.h"
19 #include "base/timer/timer.h"
20 #include "jingle/glue/proxy_resolving_client_socket.h"
21 #include "net/cert/cert_verifier.h"
22 #include "net/http/transport_security_state.h"
23 #include "net/socket/client_socket_factory.h"
24 #include "net/socket/client_socket_handle.h"
25 #include "net/socket/ssl_client_socket.h"
26 #include "net/url_request/url_request_context_getter.h"
27 #include "remoting/base/buffered_socket_writer.h"
28 #include "remoting/signaling/xmpp_login_handler.h"
29 #include "remoting/signaling/xmpp_stream_parser.h"
30 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
32 // Use 50 seconds keep-alive interval, in case routers terminate
33 // connections that are idle for more than a minute.
34 const int kKeepAliveIntervalSeconds = 50;
36 const int kReadBufferSize = 4096;
38 const int kDefaultXmppPort = 5222;
39 const int kDefaultHttpsPort = 443;
41 namespace remoting {
43 XmppSignalStrategy::XmppServerConfig::XmppServerConfig()
44 : port(kDefaultXmppPort), use_tls(true) {
47 XmppSignalStrategy::XmppServerConfig::~XmppServerConfig() {
50 class XmppSignalStrategy::Core : public XmppLoginHandler::Delegate {
51 public:
52 Core(
53 net::ClientSocketFactory* socket_factory,
54 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
55 const XmppServerConfig& xmpp_server_config);
56 ~Core() override;
58 void Connect();
59 void Disconnect();
60 State GetState() const;
61 Error GetError() const;
62 std::string GetLocalJid() const;
63 void AddListener(Listener* listener);
64 void RemoveListener(Listener* listener);
65 bool SendStanza(scoped_ptr<buzz::XmlElement> stanza);
67 void SetAuthInfo(const std::string& username,
68 const std::string& auth_token);
70 void VerifyNoListeners();
72 private:
73 void OnSocketConnected(int result);
74 void OnTlsConnected(int result);
76 void ReadSocket();
77 void OnReadResult(int result);
78 void HandleReadResult(int result);
80 // XmppLoginHandler::Delegate interface.
81 void SendMessage(const std::string& message) override;
82 void StartTls() override;
83 void OnHandshakeDone(const std::string& jid,
84 scoped_ptr<XmppStreamParser> parser) override;
85 void OnLoginHandlerError(SignalStrategy::Error error) override;
87 // Event handlers for XmppStreamParser.
88 void OnStanza(const scoped_ptr<buzz::XmlElement> stanza);
89 void OnParserError();
91 void OnNetworkError(int error);
93 void SendKeepAlive();
95 net::ClientSocketFactory* socket_factory_;
96 scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
97 XmppServerConfig xmpp_server_config_;
99 // Used by the |socket_|.
100 scoped_ptr<net::CertVerifier> cert_verifier_;
101 scoped_ptr<net::TransportSecurityState> transport_security_state_;
103 scoped_ptr<net::StreamSocket> socket_;
104 scoped_ptr<BufferedSocketWriter> writer_;
105 scoped_refptr<net::IOBuffer> read_buffer_;
106 bool read_pending_;
107 bool tls_pending_;
109 scoped_ptr<XmppLoginHandler> login_handler_;
110 scoped_ptr<XmppStreamParser> stream_parser_;
111 std::string jid_;
113 Error error_;
115 ObserverList<Listener, true> listeners_;
117 base::Timer keep_alive_timer_;
119 base::ThreadChecker thread_checker_;
121 DISALLOW_COPY_AND_ASSIGN(Core);
124 XmppSignalStrategy::Core::Core(
125 net::ClientSocketFactory* socket_factory,
126 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
127 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config)
128 : socket_factory_(socket_factory),
129 request_context_getter_(request_context_getter),
130 xmpp_server_config_(xmpp_server_config),
131 read_pending_(false),
132 tls_pending_(false),
133 error_(OK),
134 keep_alive_timer_(
135 FROM_HERE,
136 base::TimeDelta::FromSeconds(kKeepAliveIntervalSeconds),
137 base::Bind(&Core::SendKeepAlive, base::Unretained(this)),
138 true) {
139 #if defined(NDEBUG)
140 // Non-secure connections are allowed only for debugging.
141 CHECK(xmpp_server_config_.use_tls);
142 #endif
144 // TODO(sergeyu): Support for direct connections without TLS is not
145 // implemented yet.
146 if (!xmpp_server_config_.use_tls)
147 NOTIMPLEMENTED();
149 // Port 5222 may be blocked by firewall. talk.google.com allows connections on
150 // port 443 which can be used instead of 5222. The webapp still requests to
151 // use port 5222 for backwards compatibility with older versions of the host
152 // that do not pass correct |use_fake_ssl_client_socket| value to
153 // XmppClientSocketFactory (and so cannot connect to port 443). In case we are
154 // connecting to talk.google.com try to use port 443 anyway.
156 // TODO(sergeyu): Once all hosts support connections on port 443
157 // the webapp needs to be updated to request port 443 and these 2 lines can be
158 // removed. crbug.com/443384
159 if (xmpp_server_config_.host == "talk.google.com" &&
160 xmpp_server_config_.port == kDefaultXmppPort) {
161 xmpp_server_config_.port = kDefaultHttpsPort;
165 XmppSignalStrategy::Core::~Core() {
166 Disconnect();
169 void XmppSignalStrategy::Core::Connect() {
170 DCHECK(thread_checker_.CalledOnValidThread());
172 // Disconnect first if we are currently connected.
173 Disconnect();
175 error_ = OK;
177 FOR_EACH_OBSERVER(Listener, listeners_,
178 OnSignalStrategyStateChange(CONNECTING));
180 socket_.reset(new jingle_glue::ProxyResolvingClientSocket(
181 socket_factory_, request_context_getter_, net::SSLConfig(),
182 net::HostPortPair(xmpp_server_config_.host, xmpp_server_config_.port)));
184 int result = socket_->Connect(base::Bind(
185 &Core::OnSocketConnected, base::Unretained(this)));
186 if (result != net::ERR_IO_PENDING)
187 OnSocketConnected(result);
190 void XmppSignalStrategy::Core::Disconnect() {
191 DCHECK(thread_checker_.CalledOnValidThread());
193 if (socket_) {
194 login_handler_.reset();
195 stream_parser_.reset();
196 writer_.reset();
197 socket_.reset();
199 FOR_EACH_OBSERVER(Listener, listeners_,
200 OnSignalStrategyStateChange(DISCONNECTED));
204 SignalStrategy::State XmppSignalStrategy::Core::GetState() const {
205 DCHECK(thread_checker_.CalledOnValidThread());
207 if (stream_parser_) {
208 DCHECK(socket_);
209 return CONNECTED;
210 } else if (socket_) {
211 return CONNECTING;
212 } else {
213 return DISCONNECTED;
217 SignalStrategy::Error XmppSignalStrategy::Core::GetError() const {
218 DCHECK(thread_checker_.CalledOnValidThread());
219 return error_;
222 std::string XmppSignalStrategy::Core::GetLocalJid() const {
223 DCHECK(thread_checker_.CalledOnValidThread());
224 return jid_;
227 void XmppSignalStrategy::Core::AddListener(Listener* listener) {
228 DCHECK(thread_checker_.CalledOnValidThread());
229 listeners_.AddObserver(listener);
232 void XmppSignalStrategy::Core::RemoveListener(Listener* listener) {
233 DCHECK(thread_checker_.CalledOnValidThread());
234 listeners_.RemoveObserver(listener);
237 bool XmppSignalStrategy::Core::SendStanza(scoped_ptr<buzz::XmlElement> stanza) {
238 DCHECK(thread_checker_.CalledOnValidThread());
240 if (!stream_parser_) {
241 VLOG(0) << "Dropping signalling message because XMPP "
242 "connection has been terminated.";
243 return false;
246 SendMessage(stanza->Str());
247 return true;
250 void XmppSignalStrategy::Core::SetAuthInfo(const std::string& username,
251 const std::string& auth_token) {
252 DCHECK(thread_checker_.CalledOnValidThread());
253 xmpp_server_config_.username = username;
254 xmpp_server_config_.auth_token = auth_token;
257 void XmppSignalStrategy::Core::SendMessage(const std::string& message) {
258 DCHECK(thread_checker_.CalledOnValidThread());
259 scoped_refptr<net::IOBufferWithSize> buffer =
260 new net::IOBufferWithSize(message.size());
261 memcpy(buffer->data(), message.data(), message.size());
262 writer_->Write(buffer, base::Closure());
265 void XmppSignalStrategy::Core::StartTls() {
266 DCHECK(thread_checker_.CalledOnValidThread());
267 DCHECK(login_handler_);
269 // Reset the writer so we don't try to write to the raw socket anymore.
270 DCHECK_EQ(writer_->GetBufferSize(), 0);
271 writer_.reset();
273 DCHECK(!read_pending_);
275 scoped_ptr<net::ClientSocketHandle> socket_handle(
276 new net::ClientSocketHandle());
277 socket_handle->SetSocket(socket_.Pass());
279 cert_verifier_.reset(net::CertVerifier::CreateDefault());
280 transport_security_state_.reset(new net::TransportSecurityState());
281 net::SSLClientSocketContext context;
282 context.cert_verifier = cert_verifier_.get();
283 context.transport_security_state = transport_security_state_.get();
285 socket_ = socket_factory_->CreateSSLClientSocket(
286 socket_handle.Pass(),
287 net::HostPortPair(xmpp_server_config_.host, kDefaultHttpsPort),
288 net::SSLConfig(), context);
290 tls_pending_ = true;
291 int result = socket_->Connect(
292 base::Bind(&Core::OnTlsConnected, base::Unretained(this)));
293 if (result != net::ERR_IO_PENDING)
294 OnTlsConnected(result);
297 void XmppSignalStrategy::Core::OnHandshakeDone(
298 const std::string& jid,
299 scoped_ptr<XmppStreamParser> parser) {
300 DCHECK(thread_checker_.CalledOnValidThread());
302 jid_ = jid;
303 stream_parser_ = parser.Pass();
304 stream_parser_->SetCallbacks(
305 base::Bind(&Core::OnStanza, base::Unretained(this)),
306 base::Bind(&Core::OnParserError, base::Unretained(this)));
308 // Don't need |login_handler_| anymore.
309 login_handler_.reset();
311 FOR_EACH_OBSERVER(Listener, listeners_,
312 OnSignalStrategyStateChange(CONNECTED));
315 void XmppSignalStrategy::Core::OnLoginHandlerError(
316 SignalStrategy::Error error) {
317 DCHECK(thread_checker_.CalledOnValidThread());
319 error_ = error;
320 Disconnect();
323 void XmppSignalStrategy::Core::OnStanza(
324 const scoped_ptr<buzz::XmlElement> stanza) {
325 DCHECK(thread_checker_.CalledOnValidThread());
327 ObserverListBase<Listener>::Iterator it(&listeners_);
328 for (Listener* listener = it.GetNext(); listener; listener = it.GetNext()) {
329 if (listener->OnSignalStrategyIncomingStanza(stanza.get()))
330 return;
334 void XmppSignalStrategy::Core::OnParserError() {
335 DCHECK(thread_checker_.CalledOnValidThread());
337 error_ = NETWORK_ERROR;
338 Disconnect();
341 void XmppSignalStrategy::Core::OnSocketConnected(int result) {
342 DCHECK(thread_checker_.CalledOnValidThread());
344 if (result != net::OK) {
345 OnNetworkError(result);
346 return;
349 writer_.reset(new BufferedSocketWriter());
350 writer_->Init(socket_.get(), base::Bind(&Core::OnNetworkError,
351 base::Unretained(this)));
353 XmppLoginHandler::TlsMode tls_mode;
354 if (xmpp_server_config_.use_tls) {
355 tls_mode = (xmpp_server_config_.port == kDefaultXmppPort)
356 ? XmppLoginHandler::TlsMode::WITH_HANDSHAKE
357 : XmppLoginHandler::TlsMode::WITHOUT_HANDSHAKE;
358 } else {
359 tls_mode = XmppLoginHandler::TlsMode::NO_TLS;
362 // The server name is passed as to attribute in the <stream>. When connecting
363 // to talk.google.com it affects the certificate the server will use for TLS:
364 // talk.google.com uses gmail certificate when specified server is gmail.com
365 // or googlemail.com and google.com cert otherwise. In the same time it
366 // doesn't accept talk.google.com as target server. Here we use google.com
367 // server name when authenticating to talk.google.com. This ensures that the
368 // server will use google.com cert which will be accepted by the TLS
369 // implementation in Chrome (TLS API doesn't allow specifying domain other
370 // than the one that was passed to connect()).
371 std::string server = xmpp_server_config_.host;
372 if (server == "talk.google.com")
373 server = "google.com";
375 login_handler_.reset(
376 new XmppLoginHandler(server, xmpp_server_config_.username,
377 xmpp_server_config_.auth_token, tls_mode, this));
378 login_handler_->Start();
380 ReadSocket();
383 void XmppSignalStrategy::Core::OnTlsConnected(int result) {
384 DCHECK(thread_checker_.CalledOnValidThread());
385 DCHECK(tls_pending_);
386 tls_pending_ = false;
388 if (result != net::OK) {
389 OnNetworkError(result);
390 return;
393 writer_.reset(new BufferedSocketWriter());
394 writer_->Init(socket_.get(), base::Bind(&Core::OnNetworkError,
395 base::Unretained(this)));
397 login_handler_->OnTlsStarted();
399 ReadSocket();
402 void XmppSignalStrategy::Core::ReadSocket() {
403 DCHECK(thread_checker_.CalledOnValidThread());
405 while (socket_ && !read_pending_ && !tls_pending_) {
406 read_buffer_ = new net::IOBuffer(kReadBufferSize);
407 int result = socket_->Read(
408 read_buffer_.get(), kReadBufferSize,
409 base::Bind(&Core::OnReadResult, base::Unretained(this)));
410 HandleReadResult(result);
414 void XmppSignalStrategy::Core::OnReadResult(int result) {
415 DCHECK(thread_checker_.CalledOnValidThread());
416 DCHECK(read_pending_);
417 read_pending_ = false;
418 HandleReadResult(result);
419 ReadSocket();
422 void XmppSignalStrategy::Core::HandleReadResult(int result) {
423 DCHECK(thread_checker_.CalledOnValidThread());
425 if (result == net::ERR_IO_PENDING) {
426 read_pending_ = true;
427 return;
430 if (result < 0) {
431 OnNetworkError(result);
432 return;
435 if (result == 0) {
436 // Connection was closed by the server.
437 error_ = OK;
438 Disconnect();
439 return;
442 if (stream_parser_) {
443 stream_parser_->AppendData(std::string(read_buffer_->data(), result));
444 } else {
445 login_handler_->OnDataReceived(std::string(read_buffer_->data(), result));
449 void XmppSignalStrategy::Core::OnNetworkError(int error) {
450 DCHECK(thread_checker_.CalledOnValidThread());
452 LOG(ERROR) << "XMPP socket error " << error;
453 error_ = NETWORK_ERROR;
454 Disconnect();
457 void XmppSignalStrategy::Core::SendKeepAlive() {
458 DCHECK(thread_checker_.CalledOnValidThread());
460 if (GetState() == CONNECTED)
461 SendMessage(" ");
464 XmppSignalStrategy::XmppSignalStrategy(
465 net::ClientSocketFactory* socket_factory,
466 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
467 const XmppServerConfig& xmpp_server_config)
468 : core_(new Core(socket_factory,
469 request_context_getter,
470 xmpp_server_config)) {
473 XmppSignalStrategy::~XmppSignalStrategy() {
474 // All listeners should be removed at this point, so it's safe to detach
475 // |core_|.
476 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, core_.release());
479 void XmppSignalStrategy::Connect() {
480 core_->Connect();
483 void XmppSignalStrategy::Disconnect() {
484 core_->Disconnect();
487 SignalStrategy::State XmppSignalStrategy::GetState() const {
488 return core_->GetState();
491 SignalStrategy::Error XmppSignalStrategy::GetError() const {
492 return core_->GetError();
495 std::string XmppSignalStrategy::GetLocalJid() const {
496 return core_->GetLocalJid();
499 void XmppSignalStrategy::AddListener(Listener* listener) {
500 core_->AddListener(listener);
503 void XmppSignalStrategy::RemoveListener(Listener* listener) {
504 core_->RemoveListener(listener);
506 bool XmppSignalStrategy::SendStanza(scoped_ptr<buzz::XmlElement> stanza) {
507 return core_->SendStanza(stanza.Pass());
510 std::string XmppSignalStrategy::GetNextId() {
511 return base::Uint64ToString(base::RandUint64());
514 void XmppSignalStrategy::SetAuthInfo(const std::string& username,
515 const std::string& auth_token) {
516 core_->SetAuthInfo(username, auth_token);
519 } // namespace remoting