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/iq_sender.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/time/time.h"
15 #include "remoting/signaling/signal_strategy.h"
16 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
17 #include "third_party/webrtc/libjingle/xmpp/constants.h"
22 scoped_ptr
<buzz::XmlElement
> IqSender::MakeIqStanza(
23 const std::string
& type
,
24 const std::string
& addressee
,
25 scoped_ptr
<buzz::XmlElement
> iq_body
) {
26 scoped_ptr
<buzz::XmlElement
> stanza(new buzz::XmlElement(buzz::QN_IQ
));
27 stanza
->AddAttr(buzz::QN_TYPE
, type
);
28 if (!addressee
.empty())
29 stanza
->AddAttr(buzz::QN_TO
, addressee
);
30 stanza
->AddElement(iq_body
.release());
34 IqSender::IqSender(SignalStrategy
* signal_strategy
)
35 : signal_strategy_(signal_strategy
) {
36 signal_strategy_
->AddListener(this);
39 IqSender::~IqSender() {
40 signal_strategy_
->RemoveListener(this);
43 scoped_ptr
<IqRequest
> IqSender::SendIq(scoped_ptr
<buzz::XmlElement
> stanza
,
44 const ReplyCallback
& callback
) {
45 std::string addressee
= stanza
->Attr(buzz::QN_TO
);
46 std::string id
= signal_strategy_
->GetNextId();
47 stanza
->AddAttr(buzz::QN_ID
, id
);
48 if (!signal_strategy_
->SendStanza(stanza
.Pass())) {
51 DCHECK(requests_
.find(id
) == requests_
.end());
52 scoped_ptr
<IqRequest
> request(new IqRequest(this, callback
, addressee
));
53 if (!callback
.is_null())
54 requests_
[id
] = request
.get();
55 return request
.Pass();
58 scoped_ptr
<IqRequest
> IqSender::SendIq(const std::string
& type
,
59 const std::string
& addressee
,
60 scoped_ptr
<buzz::XmlElement
> iq_body
,
61 const ReplyCallback
& callback
) {
62 return SendIq(MakeIqStanza(type
, addressee
, iq_body
.Pass()), callback
);
65 void IqSender::RemoveRequest(IqRequest
* request
) {
66 IqRequestMap::iterator it
= requests_
.begin();
67 while (it
!= requests_
.end()) {
68 IqRequestMap::iterator cur
= it
;
70 if (cur
->second
== request
) {
77 void IqSender::OnSignalStrategyStateChange(SignalStrategy::State state
) {
80 bool IqSender::OnSignalStrategyIncomingStanza(const buzz::XmlElement
* stanza
) {
81 if (stanza
->Name() != buzz::QN_IQ
) {
82 LOG(WARNING
) << "Received unexpected non-IQ packet " << stanza
->Str();
86 const std::string
& type
= stanza
->Attr(buzz::QN_TYPE
);
88 LOG(WARNING
) << "IQ packet missing type " << stanza
->Str();
92 if (type
!= "result" && type
!= "error") {
96 const std::string
& id
= stanza
->Attr(buzz::QN_ID
);
98 LOG(WARNING
) << "IQ packet missing id " << stanza
->Str();
102 std::string from
= stanza
->Attr(buzz::QN_FROM
);
104 IqRequestMap::iterator it
= requests_
.find(id
);
105 if (it
== requests_
.end()) {
109 IqRequest
* request
= it
->second
;
111 if (request
->addressee_
!= from
) {
112 LOG(ERROR
) << "Received IQ response from from a invalid JID. Ignoring it."
113 << " Message received from: " << from
114 << " Original JID: " << request
->addressee_
;
119 request
->OnResponse(stanza
);
124 IqRequest::IqRequest(IqSender
* sender
, const IqSender::ReplyCallback
& callback
,
125 const std::string
& addressee
)
128 addressee_(addressee
) {
131 IqRequest::~IqRequest() {
132 sender_
->RemoveRequest(this);
135 void IqRequest::SetTimeout(base::TimeDelta timeout
) {
136 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
137 FROM_HERE
, base::Bind(&IqRequest::OnTimeout
, AsWeakPtr()), timeout
);
140 void IqRequest::CallCallback(const buzz::XmlElement
* stanza
) {
141 if (!callback_
.is_null()) {
142 IqSender::ReplyCallback
callback(callback_
);
144 callback
.Run(this, stanza
);
148 void IqRequest::OnTimeout() {
149 CallCallback(nullptr);
152 void IqRequest::OnResponse(const buzz::XmlElement
* stanza
) {
153 // It's unsafe to delete signal strategy here, and the callback may
154 // want to do that, so we post task to invoke the callback later.
155 scoped_ptr
<buzz::XmlElement
> stanza_copy(new buzz::XmlElement(*stanza
));
156 base::ThreadTaskRunnerHandle::Get()->PostTask(
157 FROM_HERE
, base::Bind(&IqRequest::DeliverResponse
, AsWeakPtr(),
158 base::Passed(&stanza_copy
)));
161 void IqRequest::DeliverResponse(scoped_ptr
<buzz::XmlElement
> stanza
) {
162 CallCallback(stanza
.get());
165 } // namespace remoting