OLD | NEW |
| (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_signal_strategy.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/location.h" | |
9 #include "base/logging.h" | |
10 #include "base/single_thread_task_runner.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/thread_task_runner_handle.h" | |
13 #include "jingle/glue/chrome_async_socket.h" | |
14 #include "jingle/glue/task_pump.h" | |
15 #include "jingle/glue/xmpp_client_socket_factory.h" | |
16 #include "jingle/notifier/base/gaia_constants.h" | |
17 #include "jingle/notifier/base/gaia_token_pre_xmpp_auth.h" | |
18 #include "net/socket/client_socket_factory.h" | |
19 #include "net/url_request/url_request_context_getter.h" | |
20 #include "third_party/libjingle/source/talk/base/thread.h" | |
21 #include "third_party/libjingle/source/talk/xmpp/prexmppauth.h" | |
22 #include "third_party/libjingle/source/talk/xmpp/saslcookiemechanism.h" | |
23 | |
24 const char kDefaultResourceName[] = "chromoting"; | |
25 | |
26 // Use 58 seconds keep-alive interval, in case routers terminate | |
27 // connections that are idle for more than a minute. | |
28 const int kKeepAliveIntervalSeconds = 50; | |
29 | |
30 // Read buffer size used by ChromeAsyncSocket for read and write buffers. Most | |
31 // of XMPP messages are smaller than 4kB. | |
32 const size_t kReadBufferSize = 4096; | |
33 const size_t kWriteBufferSize = 4096; | |
34 | |
35 namespace remoting { | |
36 | |
37 XmppSignalStrategy::XmppServerConfig::XmppServerConfig() {} | |
38 XmppSignalStrategy::XmppServerConfig::~XmppServerConfig() {} | |
39 | |
40 XmppSignalStrategy::XmppSignalStrategy( | |
41 net::ClientSocketFactory* socket_factory, | |
42 scoped_refptr<net::URLRequestContextGetter> request_context_getter, | |
43 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config) | |
44 : socket_factory_(socket_factory), | |
45 request_context_getter_(request_context_getter), | |
46 resource_name_(kDefaultResourceName), | |
47 xmpp_client_(NULL), | |
48 xmpp_server_config_(xmpp_server_config), | |
49 state_(DISCONNECTED), | |
50 error_(OK) { | |
51 #if defined(NDEBUG) | |
52 CHECK(xmpp_server_config_.use_tls); | |
53 #endif | |
54 } | |
55 | |
56 XmppSignalStrategy::~XmppSignalStrategy() { | |
57 Disconnect(); | |
58 | |
59 // Destroying task runner will destroy XmppClient, but XmppClient may be on | |
60 // the stack and it doesn't handle this case properly, so we need to delay | |
61 // destruction. | |
62 base::ThreadTaskRunnerHandle::Get()->DeleteSoon( | |
63 FROM_HERE, task_runner_.release()); | |
64 } | |
65 | |
66 void XmppSignalStrategy::Connect() { | |
67 DCHECK(CalledOnValidThread()); | |
68 | |
69 // Disconnect first if we are currently connected. | |
70 Disconnect(); | |
71 | |
72 buzz::XmppClientSettings settings; | |
73 buzz::Jid login_jid(xmpp_server_config_.username); | |
74 settings.set_user(login_jid.node()); | |
75 settings.set_host(login_jid.domain()); | |
76 settings.set_resource(resource_name_); | |
77 settings.set_token_service(xmpp_server_config_.auth_service); | |
78 settings.set_auth_token(buzz::AUTH_MECHANISM_GOOGLE_TOKEN, | |
79 xmpp_server_config_.auth_token); | |
80 settings.set_server(talk_base::SocketAddress( | |
81 xmpp_server_config_.host, xmpp_server_config_.port)); | |
82 settings.set_use_tls( | |
83 xmpp_server_config_.use_tls ? buzz::TLS_ENABLED : buzz::TLS_DISABLED); | |
84 | |
85 scoped_ptr<jingle_glue::XmppClientSocketFactory> xmpp_socket_factory( | |
86 new jingle_glue::XmppClientSocketFactory( | |
87 socket_factory_, net::SSLConfig(), request_context_getter_, false)); | |
88 buzz::AsyncSocket* socket = new jingle_glue::ChromeAsyncSocket( | |
89 xmpp_socket_factory.release(), kReadBufferSize, kWriteBufferSize); | |
90 | |
91 task_runner_.reset(new jingle_glue::TaskPump()); | |
92 xmpp_client_ = new buzz::XmppClient(task_runner_.get()); | |
93 xmpp_client_->Connect( | |
94 settings, std::string(), socket, CreatePreXmppAuth(settings)); | |
95 xmpp_client_->SignalStateChange | |
96 .connect(this, &XmppSignalStrategy::OnConnectionStateChanged); | |
97 xmpp_client_->engine()->AddStanzaHandler(this, buzz::XmppEngine::HL_TYPE); | |
98 xmpp_client_->Start(); | |
99 | |
100 SetState(CONNECTING); | |
101 } | |
102 | |
103 void XmppSignalStrategy::Disconnect() { | |
104 DCHECK(CalledOnValidThread()); | |
105 | |
106 if (xmpp_client_) { | |
107 xmpp_client_->engine()->RemoveStanzaHandler(this); | |
108 | |
109 xmpp_client_->Disconnect(); | |
110 | |
111 // |xmpp_client_| should be set to NULL in OnConnectionStateChanged() | |
112 // in response to Disconnect() call above. | |
113 DCHECK(xmpp_client_ == NULL); | |
114 } | |
115 } | |
116 | |
117 SignalStrategy::State XmppSignalStrategy::GetState() const { | |
118 DCHECK(CalledOnValidThread()); | |
119 return state_; | |
120 } | |
121 | |
122 SignalStrategy::Error XmppSignalStrategy::GetError() const { | |
123 DCHECK(CalledOnValidThread()); | |
124 return error_; | |
125 } | |
126 | |
127 std::string XmppSignalStrategy::GetLocalJid() const { | |
128 DCHECK(CalledOnValidThread()); | |
129 return xmpp_client_->jid().Str(); | |
130 } | |
131 | |
132 void XmppSignalStrategy::AddListener(Listener* listener) { | |
133 DCHECK(CalledOnValidThread()); | |
134 listeners_.AddObserver(listener); | |
135 } | |
136 | |
137 void XmppSignalStrategy::RemoveListener(Listener* listener) { | |
138 DCHECK(CalledOnValidThread()); | |
139 listeners_.RemoveObserver(listener); | |
140 } | |
141 | |
142 bool XmppSignalStrategy::SendStanza(scoped_ptr<buzz::XmlElement> stanza) { | |
143 DCHECK(CalledOnValidThread()); | |
144 if (!xmpp_client_) { | |
145 VLOG(0) << "Dropping signalling message because XMPP " | |
146 "connection has been terminated."; | |
147 return false; | |
148 } | |
149 | |
150 buzz::XmppReturnStatus status = xmpp_client_->SendStanza(stanza.release()); | |
151 return status == buzz::XMPP_RETURN_OK || status == buzz::XMPP_RETURN_PENDING; | |
152 } | |
153 | |
154 std::string XmppSignalStrategy::GetNextId() { | |
155 DCHECK(CalledOnValidThread()); | |
156 if (!xmpp_client_) { | |
157 // If the connection has been terminated then it doesn't matter | |
158 // what Id we return. | |
159 return std::string(); | |
160 } | |
161 return xmpp_client_->NextId(); | |
162 } | |
163 | |
164 bool XmppSignalStrategy::HandleStanza(const buzz::XmlElement* stanza) { | |
165 DCHECK(CalledOnValidThread()); | |
166 ObserverListBase<Listener>::Iterator it(listeners_); | |
167 Listener* listener; | |
168 while ((listener = it.GetNext()) != NULL) { | |
169 if (listener->OnSignalStrategyIncomingStanza(stanza)) | |
170 return true; | |
171 } | |
172 return false; | |
173 } | |
174 | |
175 void XmppSignalStrategy::SetAuthInfo(const std::string& username, | |
176 const std::string& auth_token, | |
177 const std::string& auth_service) { | |
178 DCHECK(CalledOnValidThread()); | |
179 xmpp_server_config_.username = username; | |
180 xmpp_server_config_.auth_token = auth_token; | |
181 xmpp_server_config_.auth_service = auth_service; | |
182 } | |
183 | |
184 void XmppSignalStrategy::SetResourceName(const std::string &resource_name) { | |
185 DCHECK(CalledOnValidThread()); | |
186 resource_name_ = resource_name; | |
187 } | |
188 | |
189 void XmppSignalStrategy::OnConnectionStateChanged( | |
190 buzz::XmppEngine::State state) { | |
191 DCHECK(CalledOnValidThread()); | |
192 | |
193 if (state == buzz::XmppEngine::STATE_OPEN) { | |
194 keep_alive_timer_.Start( | |
195 FROM_HERE, base::TimeDelta::FromSeconds(kKeepAliveIntervalSeconds), | |
196 this, &XmppSignalStrategy::SendKeepAlive); | |
197 SetState(CONNECTED); | |
198 } else if (state == buzz::XmppEngine::STATE_CLOSED) { | |
199 // Make sure we dump errors to the log. | |
200 int subcode; | |
201 buzz::XmppEngine::Error error = xmpp_client_->GetError(&subcode); | |
202 VLOG(0) << "XMPP connection was closed: error=" << error | |
203 << ", subcode=" << subcode; | |
204 | |
205 keep_alive_timer_.Stop(); | |
206 | |
207 // Client is destroyed by the TaskRunner after the client is | |
208 // closed. Reset the pointer so we don't try to use it later. | |
209 xmpp_client_ = NULL; | |
210 | |
211 switch (error) { | |
212 case buzz::XmppEngine::ERROR_UNAUTHORIZED: | |
213 case buzz::XmppEngine::ERROR_AUTH: | |
214 case buzz::XmppEngine::ERROR_MISSING_USERNAME: | |
215 error_ = AUTHENTICATION_FAILED; | |
216 break; | |
217 | |
218 default: | |
219 error_ = NETWORK_ERROR; | |
220 } | |
221 | |
222 SetState(DISCONNECTED); | |
223 } | |
224 } | |
225 | |
226 void XmppSignalStrategy::SetState(State new_state) { | |
227 if (state_ != new_state) { | |
228 state_ = new_state; | |
229 FOR_EACH_OBSERVER(Listener, listeners_, | |
230 OnSignalStrategyStateChange(new_state)); | |
231 } | |
232 } | |
233 | |
234 void XmppSignalStrategy::SendKeepAlive() { | |
235 xmpp_client_->SendRaw(" "); | |
236 } | |
237 | |
238 // static | |
239 buzz::PreXmppAuth* XmppSignalStrategy::CreatePreXmppAuth( | |
240 const buzz::XmppClientSettings& settings) { | |
241 buzz::Jid jid(settings.user(), settings.host(), buzz::STR_EMPTY); | |
242 std::string mechanism = notifier::kDefaultGaiaAuthMechanism; | |
243 if (settings.token_service() == "oauth2") { | |
244 mechanism = "X-OAUTH2"; | |
245 } | |
246 | |
247 return new notifier::GaiaTokenPreXmppAuth( | |
248 jid.Str(), settings.auth_token(), settings.token_service(), mechanism); | |
249 } | |
250 | |
251 } // namespace remoting | |
OLD | NEW |