mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-12-22 12:37:58 +01:00
Added support for HOXT (XEP-0332)
This is initial impementation of XEP-0332 (SMACK-552) - HTTP over XMPP transport. Created extensions, providers and unit tests. Two features are missing: jingle and sipub.
This commit is contained in:
parent
fcc8414a92
commit
236ea71cee
20 changed files with 2052 additions and 1 deletions
|
@ -12,6 +12,7 @@
|
|||
<className>org.jivesoftware.smackx.ExtensionsProviderInitializer</className>
|
||||
<className>org.jivesoftware.smackx.ExtensionsStartupClasses</className>
|
||||
<className>org.jivesoftware.smackx.ExperimentalProviderInitializer</className>
|
||||
<className>org.jivesoftware.smackx.ExperimentalStartupClasses</className>
|
||||
<className>org.jivesoftware.smackx.WorkgroupProviderInitializer</className>
|
||||
<className>org.jivesoftware.smackx.LegacyProviderInitializer</className>
|
||||
</optionalStartupClasses>
|
||||
|
|
156
documentation/extensions/hoxt.html
Normal file
156
documentation/extensions/hoxt.html
Normal file
|
@ -0,0 +1,156 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>HTTP over XMPP transport</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="header">HTTP over XMPP transport</div><p>
|
||||
|
||||
Allows to transport HTTP communication over XMPP peer-to-peer networks.<p>
|
||||
|
||||
<ul>
|
||||
<li><a href="#disco">Discover HOXT support</a></li>
|
||||
<li><a href="#iqexchange">IQ exchange</a></li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="subheader"><a name="disco">Discover HOXT support</a></div><p>
|
||||
|
||||
<b>Description</b><p>
|
||||
|
||||
Before using this extension you must ensure that your counterpart supports it also.</p>
|
||||
|
||||
<b>Usage</b><p>
|
||||
|
||||
<p>Once you have your <i><b>ServiceDiscoveryManager</b></i> you will be able to discover information associated with
|
||||
an XMPP entity. To discover the information of a given XMPP entity send <b>discoverInfo(entityID)</b>
|
||||
to your <i><b>ServiceDiscoveryManager</b></i> where entityID is the ID of the entity. The message
|
||||
<b>discoverInfo(entityID)</b> will answer an instance of <i><b>DiscoverInfo</b></i> that contains
|
||||
the discovered information.</p>
|
||||
|
||||
<b>Examples</b><p>
|
||||
|
||||
In this example we can see how to check if the counterpart supports HOXT: <br>
|
||||
<blockquote>
|
||||
<pre> <font color="#3f7f5f">// Obtain the ServiceDiscoveryManager associated with my XMPPConnection</font>
|
||||
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||
|
||||
<font color="#3f7f5f">// Get the information of a given XMPP entity</font>
|
||||
DiscoverInfo discoInfo = discoManager.discoverInfo("juliet@capulet.com");
|
||||
|
||||
<font color="#3f7f5f">// Check if room is HOXT is supported</font>
|
||||
discoInfo.containsFeature("urn:xmpp:http");
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="subheader"><a name="iqexchange">IQ exchange</a></div><p>
|
||||
|
||||
<b>Description</b><p>
|
||||
|
||||
You can use IQ's to perform HTTP requests and responses.
|
||||
This is applicable to relatively short requests and responses (due to limitation of XMPP message size).</p>
|
||||
|
||||
<b>Usage</b><p>
|
||||
|
||||
<p>First you need to register a <i><b>PacketListener</b></i> to be able to handle intended IQs.<p>
|
||||
For the HTTP client you:
|
||||
<ul>
|
||||
<li>You create and send <i><b>HttpOverXmppReq</b></i> request.</li>
|
||||
<li>Then you handle the <i><b>HttpOverXmppResp</b></i> response in your <i><b>PacketListener</b></i>.</li>
|
||||
</ul>
|
||||
|
||||
For the HTTP server you:
|
||||
<ul>
|
||||
<li>You handle the <i><b>HttpOverXmppReq</b></i> requests in your <i><b>PacketListener</b></i>.</li>
|
||||
<li>And create and send <i><b>HttpOverXmppResp</b></i> responses.</li>
|
||||
</ul>
|
||||
|
||||
</p>
|
||||
|
||||
<b>Examples</b><p>
|
||||
|
||||
In this example we are HTTP client, so we send request (POST) and handle the response: <br>
|
||||
<blockquote>
|
||||
<pre> <font color="#3f7f5f">// register listener for IQ packets</font>
|
||||
connection.addPacketListener(new IqPacketListener(), new PacketTypeFilter(IQ.class));
|
||||
|
||||
<font color="#3f7f5f">// create a request body</font>
|
||||
String urlEncodedMessage = "I_love_you";
|
||||
|
||||
<font color="#3f7f5f">// create request</font>
|
||||
HttpOverXmppReq.Req req = new HttpOverXmppReq.Req(HttpMethod.POST, "/mailbox");
|
||||
req.setVersion("1.1");
|
||||
|
||||
<font color="#3f7f5f">// prepare headers</font>
|
||||
Set<Header> set = new HashSet<Header>();
|
||||
set.add(new Header("Host", "juliet.capulet.com"));
|
||||
set.add(new Header("Content-Type", "application/x-www-form-urlencoded"));
|
||||
set.add(new Header("Content-Length", Integer.toString(urlEncodedMessage.length())));
|
||||
req.setHeaders(new HeadersExtension(set));
|
||||
|
||||
<font color="#3f7f5f">// provide body or request (not mandatory, - empty body is used for GET)</font>
|
||||
AbstractHttpOverXmpp.Text child = new AbstractHttpOverXmpp.Text(urlEncodedMessage);
|
||||
AbstractHttpOverXmpp.Data data = new AbstractHttpOverXmpp.Data(child);
|
||||
req.setData(data);
|
||||
|
||||
<font color="#3f7f5f">// create IQ packet</font>
|
||||
HttpOverXmppReq packet = new HttpOverXmppReq();
|
||||
packet.setReq(req);
|
||||
packet.setTo("juliet@capulet.com/balcony");
|
||||
packet.setType(IQ.Type.SET);
|
||||
packet.setPacketID("42");
|
||||
|
||||
<font color="#3f7f5f">// send it</font>
|
||||
connection.sendPacket(packet);
|
||||
|
||||
|
||||
<font color="#3f7f5f">// then in your PacketListener</font>
|
||||
private class IqPacketListener implements PacketListener {
|
||||
|
||||
@Override
|
||||
public void processPacket(Packet packet) {
|
||||
IQ iq = (IQ) packet;
|
||||
|
||||
<font color="#3f7f5f">// verify from and packed ID</font>
|
||||
if (iq.getFrom().equals("juliet@capulet.com/balcony") && (iq.getPacketID().equals("42"))) {
|
||||
|
||||
<font color="#3f7f5f">// ensure it's not ERROR</font>
|
||||
if (iq.getType().equals(IQ.Type.RESULT)) {
|
||||
|
||||
<font color="#3f7f5f">// check if correct IQ implementation arrived</font>
|
||||
if (iq instanceof HttpOverXmppResp) {
|
||||
HttpOverXmppResp resp = (HttpOverXmppResp) iq;
|
||||
|
||||
<font color="#3f7f5f">// check HTTP response code</font>
|
||||
if (resp.getResp().getStatusCode() == 200) {
|
||||
|
||||
<font color="#3f7f5f">// get content of the response</font>
|
||||
AbstractHttpOverXmpp.DataChild child = resp.getResp().getData().getChild();
|
||||
|
||||
<font color="#3f7f5f">// check which type of content of the response arrived</font>
|
||||
if (child instanceof AbstractHttpOverXmpp.Xml) {
|
||||
|
||||
<font color="#3f7f5f">// print the message and anxiously read if from console ;)</font>
|
||||
System.out.println(((AbstractHttpOverXmpp.Xml) child).getText());
|
||||
} else {
|
||||
<font color="#3f7f5f">// process other AbstractHttpOverXmpp.DataChild subtypes</font>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -85,6 +85,11 @@
|
|||
<td><a href="http://www.xmpp.org/extensions/xep-0016.html">XEP-0016</a></td>
|
||||
<td>Enabling or disabling communication with other entities.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="hoxt.html">HTTP over XMPP transport</a></td>
|
||||
<td><a href="http://www.xmpp.org/extensions/xep-0332.html">XEP-0332</a></td>
|
||||
<td>Allows to transport HTTP communication over XMPP peer-to-peer networks.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import org.jivesoftware.smack.SmackConfiguration;
|
||||
import org.jivesoftware.smack.initializer.SmackInitializer;
|
||||
import org.jivesoftware.smack.util.FileUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link SmackInitializer} implementation for experimental module.
|
||||
*/
|
||||
public class ExperimentalStartupClasses implements SmackInitializer {
|
||||
|
||||
private static final String EXTENSIONS_XML = "classpath:org.jivesoftware.smackx/extensions.xml";
|
||||
|
||||
private List<Exception> exceptions = new LinkedList<Exception>();
|
||||
// TODO log
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
InputStream is;
|
||||
try {
|
||||
is = FileUtils.getStreamForUrl(EXTENSIONS_XML, null);
|
||||
SmackConfiguration.processConfigFile(is, exceptions);;
|
||||
}
|
||||
catch (Exception e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Exception> getExceptions() {
|
||||
return Collections.unmodifiableList(exceptions);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||
|
||||
/**
|
||||
* Manager for HTTP ove XMPP transport (XEP-0332) extension.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public class HOXTManager {
|
||||
|
||||
/**
|
||||
* Namespace for this extension.
|
||||
*/
|
||||
public static final String NAMESPACE = "urn:xmpp:http";
|
||||
|
||||
static {
|
||||
XMPPConnection.addConnectionCreationListener(new ConnectionCreationListener() {
|
||||
@Override
|
||||
public void connectionCreated(XMPPConnection connection) {
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(NAMESPACE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given entity understands the HTTP ove XMPP transport format and allows the exchange of such.
|
||||
*
|
||||
* @param jid jid
|
||||
* @param connection connection
|
||||
* @return true if the given entity understands the HTTP ove XMPP transport format and exchange.
|
||||
* @throws XMPPErrorException
|
||||
* @throws NoResponseException
|
||||
* @throws NotConnectedException
|
||||
*/
|
||||
public static boolean isSupported(String jid, XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||
return ServiceDiscoveryManager.getInstanceFor(connection).supportsFeature(jid, NAMESPACE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,361 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
|
||||
|
||||
/**
|
||||
* Abstract parent for Req and Resp IQ packets.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public abstract class AbstractHttpOverXmpp extends IQ {
|
||||
|
||||
/**
|
||||
* Abstract representation of parent of Req and Resp elements.
|
||||
*/
|
||||
public static abstract class AbstractBody {
|
||||
|
||||
private HeadersExtension headers;
|
||||
private Data data;
|
||||
|
||||
protected String version;
|
||||
|
||||
/**
|
||||
* Returns string containing xml representation of this object.
|
||||
*
|
||||
* @return xml representation of this object
|
||||
*/
|
||||
public String toXML() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getStartTag());
|
||||
builder.append(headers.toXML());
|
||||
builder.append(data.toXML());
|
||||
builder.append(getEndTag());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns start tag.
|
||||
*
|
||||
* @return start tag
|
||||
*/
|
||||
protected abstract String getStartTag();
|
||||
|
||||
/**
|
||||
* Returns end tag.
|
||||
*
|
||||
* @return end tag
|
||||
*/
|
||||
protected abstract String getEndTag();
|
||||
|
||||
/**
|
||||
* Returns version attribute.
|
||||
*
|
||||
* @return version attribute
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets version attribute.
|
||||
*
|
||||
* @param version version attribute
|
||||
*/
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Headers element.
|
||||
*
|
||||
* @return Headers element
|
||||
*/
|
||||
public HeadersExtension getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Headers element.
|
||||
*
|
||||
* @param headers Headers element
|
||||
*/
|
||||
public void setHeaders(HeadersExtension headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Data element.
|
||||
*
|
||||
* @return Data element
|
||||
*/
|
||||
public Data getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Data element.
|
||||
*
|
||||
* @param data Headers element
|
||||
*/
|
||||
public void setData(Data data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of Data element.<p>
|
||||
* This class is immutable.
|
||||
*/
|
||||
public static class Data {
|
||||
|
||||
private final DataChild child;
|
||||
|
||||
/**
|
||||
* Creates Data element.
|
||||
*
|
||||
* @param child element nested by Data
|
||||
*/
|
||||
public Data(DataChild child) {
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string containing xml representation of this object.
|
||||
*
|
||||
* @return xml representation of this object
|
||||
*/
|
||||
public String toXML() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<data>");
|
||||
builder.append(child.toXML());
|
||||
builder.append("</data>");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element nested by Data.
|
||||
*
|
||||
* @return element nested by Data
|
||||
*/
|
||||
public DataChild getChild() {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for child elements of Data element.
|
||||
*/
|
||||
public static interface DataChild {
|
||||
|
||||
/**
|
||||
* Returns string containing xml representation of this object.
|
||||
*
|
||||
* @return xml representation of this object
|
||||
*/
|
||||
public String toXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of Text element.<p>
|
||||
* This class is immutable.
|
||||
*/
|
||||
public static class Text implements DataChild {
|
||||
|
||||
private final String text;
|
||||
|
||||
/**
|
||||
* Creates this element.
|
||||
*
|
||||
* @param text value of text
|
||||
*/
|
||||
public Text(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<text>");
|
||||
if (text != null) {
|
||||
builder.append(text);
|
||||
}
|
||||
builder.append("</text>");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text of this element.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of Base64 element.<p>
|
||||
* This class is immutable.
|
||||
*/
|
||||
public static class Base64 implements DataChild {
|
||||
|
||||
private final String text;
|
||||
|
||||
/**
|
||||
* Creates this element.
|
||||
*
|
||||
* @param text value of text
|
||||
*/
|
||||
public Base64(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<base64>");
|
||||
if (text != null) {
|
||||
builder.append(text);
|
||||
}
|
||||
builder.append("</base64>");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text of this element.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of Xml element.<p>
|
||||
* This class is immutable.
|
||||
*/
|
||||
public static class Xml implements DataChild {
|
||||
|
||||
private final String text;
|
||||
|
||||
/**
|
||||
* Creates this element.
|
||||
*
|
||||
* @param text value of text
|
||||
*/
|
||||
public Xml(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<xml>");
|
||||
if (text != null) {
|
||||
builder.append(text);
|
||||
}
|
||||
builder.append("</xml>");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text of this element.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of ChunkedBase64 element.<p>
|
||||
* This class is immutable.
|
||||
*/
|
||||
public static class ChunkedBase64 implements DataChild {
|
||||
|
||||
private final String streamId;
|
||||
|
||||
/**
|
||||
* Creates ChunkedBase86 element.
|
||||
*
|
||||
* @param streamId streamId attribute
|
||||
*/
|
||||
public ChunkedBase64(String streamId) {
|
||||
this.streamId = streamId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<chunkedBase64 streamId='");
|
||||
builder.append(streamId);
|
||||
builder.append("'/>");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns streamId attribute.
|
||||
*
|
||||
* @return streamId attribute
|
||||
*/
|
||||
public String getStreamId() {
|
||||
return streamId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of Ibb element.<p>
|
||||
* This class is immutable.
|
||||
*/
|
||||
public static class Ibb implements DataChild {
|
||||
|
||||
private final String sid;
|
||||
|
||||
/**
|
||||
* Creates Ibb element.
|
||||
*
|
||||
* @param sid sid attribute
|
||||
*/
|
||||
public Ibb(String sid) {
|
||||
this.sid = sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<ibb sid='");
|
||||
builder.append(sid);
|
||||
builder.append("'/>");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sid attribute.
|
||||
*
|
||||
* @return sid attribute
|
||||
*/
|
||||
public String getSid() {
|
||||
return sid;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smackx.hoxt.HOXTManager;
|
||||
|
||||
/**
|
||||
* Packet extension for base64 binary chunks.<p>
|
||||
* This class is immutable.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public class Base64BinaryChunk implements PacketExtension {
|
||||
|
||||
public static final String ELEMENT_CHUNK = "chunk";
|
||||
public static final String ATTRIBUTE_STREAM_ID = "streamId";
|
||||
public static final String ATTRIBUTE_LAST = "last";
|
||||
|
||||
private final String streamId;
|
||||
private final boolean last;
|
||||
private final String text;
|
||||
|
||||
/**
|
||||
* Creates the extension.
|
||||
*
|
||||
* @param text value of text attribute
|
||||
* @param streamId value of streamId attribute
|
||||
* @param last value of last attribute
|
||||
*/
|
||||
public Base64BinaryChunk(String text, String streamId, boolean last) {
|
||||
this.text = text;
|
||||
this.streamId = streamId;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the extension. Last attribute will be initialized with default value (false).
|
||||
*
|
||||
* @param text value of text attribute
|
||||
* @param streamId value of streamId attribute
|
||||
*/
|
||||
public Base64BinaryChunk(String text, String streamId) {
|
||||
this(text, streamId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns streamId attribute.
|
||||
*
|
||||
* @return streamId attribute
|
||||
*/
|
||||
public String getStreamId() {
|
||||
return streamId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last attribute.
|
||||
*
|
||||
* @return last attribute
|
||||
*/
|
||||
public boolean isLast() {
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text attribute.
|
||||
*
|
||||
* @return text attribute
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT_CHUNK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return HOXTManager.NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<chunk xmlns='urn:xmpp:http' streamId='");
|
||||
builder.append(streamId);
|
||||
builder.append("' last='");
|
||||
builder.append(Boolean.toString(last));
|
||||
builder.append("'>");
|
||||
builder.append(text);
|
||||
builder.append("</chunk>");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.packet;
|
||||
|
||||
/**
|
||||
* Enum containing HTTP methods.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public enum HttpMethod {
|
||||
OPTIONS,
|
||||
GET,
|
||||
HEAD,
|
||||
POST,
|
||||
PUT,
|
||||
DELETE,
|
||||
TRACE,
|
||||
PATCH
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.hoxt.HOXTManager;
|
||||
|
||||
/**
|
||||
* Represents Req IQ packet.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public class HttpOverXmppReq extends AbstractHttpOverXmpp {
|
||||
|
||||
private Req req;
|
||||
|
||||
@Override
|
||||
public String getChildElementXML() {
|
||||
return req.toXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Req element.
|
||||
*
|
||||
* @return Req element
|
||||
*/
|
||||
public Req getReq() {
|
||||
return req;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Req element.
|
||||
*
|
||||
* @param req Req element
|
||||
*/
|
||||
public void setReq(Req req) {
|
||||
this.req = req;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents Req element.
|
||||
*/
|
||||
public static class Req extends AbstractBody {
|
||||
|
||||
private HttpMethod method;
|
||||
private String resource;
|
||||
|
||||
// TODO: validate: xs:minInclusive value='256' xs:maxInclusive value='65536'
|
||||
private int maxChunkSize = 0; // 0 means not set
|
||||
|
||||
private boolean sipub = true;
|
||||
|
||||
private boolean ibb = true;
|
||||
private boolean jingle = true;
|
||||
|
||||
/**
|
||||
* Creates this object.
|
||||
*
|
||||
* @param method method attribute
|
||||
* @param resource resource attribute
|
||||
*/
|
||||
public Req(HttpMethod method, String resource) {
|
||||
this.method = method;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getStartTag() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<req");
|
||||
builder.append(" ");
|
||||
builder.append("xmlns='").append(HOXTManager.NAMESPACE).append("'");
|
||||
builder.append(" ");
|
||||
builder.append("method='").append(method.toString()).append("'");
|
||||
builder.append(" ");
|
||||
builder.append("resource='").append(StringUtils.escapeForXML(resource)).append("'");
|
||||
builder.append(" ");
|
||||
builder.append("version='").append(StringUtils.escapeForXML(version)).append("'");
|
||||
if (maxChunkSize != 0) {
|
||||
builder.append(" ");
|
||||
builder.append("maxChunkSize='").append(Integer.toString(maxChunkSize)).append("'");
|
||||
}
|
||||
builder.append(" ");
|
||||
builder.append("sipub='").append(Boolean.toString(sipub)).append("'");
|
||||
builder.append(" ");
|
||||
builder.append("ibb='").append(Boolean.toString(ibb)).append("'");
|
||||
builder.append(" ");
|
||||
builder.append("jingle='").append(Boolean.toString(jingle)).append("'");
|
||||
builder.append(">");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getEndTag() {
|
||||
return "</req>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns method attribute.
|
||||
*
|
||||
* @return method attribute
|
||||
*/
|
||||
public HttpMethod getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns resource attribute.
|
||||
*
|
||||
* @return resource attribute
|
||||
*/
|
||||
public String getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns maxChunkSize attribute.
|
||||
*
|
||||
* @return maxChunkSize attribute
|
||||
*/
|
||||
public int getMaxChunkSize() {
|
||||
return maxChunkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets maxChunkSize attribute.
|
||||
*
|
||||
* @param maxChunkSize maxChunkSize attribute
|
||||
*/
|
||||
public void setMaxChunkSize(int maxChunkSize) {
|
||||
this.maxChunkSize = maxChunkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sipub attribute.
|
||||
*
|
||||
* @return sipub attribute
|
||||
*/
|
||||
public boolean isSipub() {
|
||||
return sipub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sipub attribute.
|
||||
*
|
||||
* @param sipub sipub attribute
|
||||
*/
|
||||
public void setSipub(boolean sipub) {
|
||||
this.sipub = sipub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ibb attribute.
|
||||
*
|
||||
* @return ibb attribute
|
||||
*/
|
||||
public boolean isIbb() {
|
||||
return ibb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ibb attribute.
|
||||
*
|
||||
* @param ibb ibb attribute
|
||||
*/
|
||||
public void setIbb(boolean ibb) {
|
||||
this.ibb = ibb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns jingle attribute.
|
||||
*
|
||||
* @return jingle attribute
|
||||
*/
|
||||
public boolean isJingle() {
|
||||
return jingle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets jingle attribute.
|
||||
*
|
||||
* @param jingle jingle attribute
|
||||
*/
|
||||
public void setJingle(boolean jingle) {
|
||||
this.jingle = jingle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.hoxt.HOXTManager;
|
||||
|
||||
/**
|
||||
* Represents Resp IQ packet.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public class HttpOverXmppResp extends AbstractHttpOverXmpp {
|
||||
|
||||
private Resp resp;
|
||||
|
||||
@Override
|
||||
public String getChildElementXML() {
|
||||
return resp.toXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Resp element.
|
||||
*
|
||||
* @return Resp element
|
||||
*/
|
||||
public Resp getResp() {
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Resp element.
|
||||
*
|
||||
* @param resp Resp element
|
||||
*/
|
||||
public void setResp(Resp resp) {
|
||||
this.resp = resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents Resp element.
|
||||
*/
|
||||
public static class Resp extends AbstractBody {
|
||||
|
||||
private int statusCode;
|
||||
private String statusMessage = null;
|
||||
|
||||
@Override
|
||||
protected String getStartTag() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<resp");
|
||||
builder.append(" ");
|
||||
builder.append("xmlns='").append(HOXTManager.NAMESPACE).append("'");
|
||||
builder.append(" ");
|
||||
builder.append("version='").append(StringUtils.escapeForXML(version)).append("'");
|
||||
builder.append(" ");
|
||||
builder.append("statusCode='").append(Integer.toString(statusCode)).append("'");
|
||||
if (statusMessage != null) {
|
||||
builder.append(" ");
|
||||
builder.append("statusMessage='").append(StringUtils.escapeForXML(statusMessage)).append("'");
|
||||
}
|
||||
builder.append(">");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getEndTag() {
|
||||
return "</resp>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns statusCode attribute.
|
||||
*
|
||||
* @return statusCode attribute
|
||||
*/
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets statusCode attribute.
|
||||
*
|
||||
* @param statusCode statusCode attribute
|
||||
*/
|
||||
public void setStatusCode(int statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns statusMessage attribute.
|
||||
*
|
||||
* @return statusMessage attribute
|
||||
*/
|
||||
public String getStatusMessage() {
|
||||
return statusMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets statusMessage attribute.
|
||||
*
|
||||
* @param statusMessage statusMessage attribute
|
||||
*/
|
||||
public void setStatusMessage(String statusMessage) {
|
||||
this.statusMessage = statusMessage;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.provider;
|
||||
|
||||
import org.jivesoftware.smack.provider.IQProvider;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.hoxt.packet.AbstractHttpOverXmpp;
|
||||
import org.jivesoftware.smackx.shim.packet.Header;
|
||||
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
|
||||
import org.jivesoftware.smackx.shim.provider.HeaderProvider;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Abstract parent for Req and Resp packet providers.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public abstract class AbstractHttpOverXmppProvider implements IQProvider {
|
||||
|
||||
private static final String ELEMENT_HEADERS = "headers";
|
||||
private static final String ELEMENT_HEADER = "header";
|
||||
private static final String ELEMENT_DATA = "data";
|
||||
private static final String ELEMENT_TEXT = "text";
|
||||
private static final String ELEMENT_BASE_64 = "base64";
|
||||
private static final String ELEMENT_CHUNKED_BASE_64 = "chunkedBase64";
|
||||
private static final String ELEMENT_XML = "xml";
|
||||
static final String ELEMENT_IBB = "ibb";
|
||||
static final String ELEMENT_SIPUB = "sipub";
|
||||
static final String ELEMENT_JINGLE = "jingle";
|
||||
|
||||
private static final String ATTRIBUTE_STREAM_ID = "streamId";
|
||||
private static final String ATTRIBUTE_SID = "sid";
|
||||
static final String ATTRIBUTE_VERSION = "version";
|
||||
|
||||
/**
|
||||
* Parses Headers and Data elements.
|
||||
*
|
||||
* @param parser parser
|
||||
* @param elementName name of concrete implementation of this element
|
||||
* @param body parent Body element
|
||||
* @throws Exception if anything goes wrong
|
||||
*/
|
||||
protected void parseHeadersAndData(XmlPullParser parser, String elementName, AbstractHttpOverXmpp.AbstractBody body) throws Exception {
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals(ELEMENT_HEADERS)) {
|
||||
HeadersExtension headersExtension = parseHeaders(parser);
|
||||
body.setHeaders(headersExtension);
|
||||
} else if (parser.getName().endsWith(ELEMENT_DATA)) {
|
||||
AbstractHttpOverXmpp.Data data = parseData(parser);
|
||||
body.setData(data);
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected tag:" + parser.getName() + "'");
|
||||
}
|
||||
} else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(elementName)) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HeadersExtension parseHeaders(XmlPullParser parser) throws Exception {
|
||||
HeaderProvider provider = new HeaderProvider();
|
||||
Set<Header> set = new HashSet<Header>();
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals(ELEMENT_HEADER)) {
|
||||
Header header = (Header) provider.parseExtension(parser);
|
||||
set.add(header);
|
||||
}
|
||||
} else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(ELEMENT_HEADERS)) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new HeadersExtension(set);
|
||||
}
|
||||
|
||||
private AbstractHttpOverXmpp.Data parseData(XmlPullParser parser) throws Exception {
|
||||
AbstractHttpOverXmpp.DataChild child = null;
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals(ELEMENT_TEXT)) {
|
||||
child = parseText(parser);
|
||||
} else if (parser.getName().equals(ELEMENT_BASE_64)) {
|
||||
child = parseBase64(parser);
|
||||
} else if (parser.getName().equals(ELEMENT_CHUNKED_BASE_64)) {
|
||||
child = parseChunkedBase64(parser);
|
||||
} else if (parser.getName().equals(ELEMENT_XML)) {
|
||||
child = parseXml(parser);
|
||||
} else if (parser.getName().equals(ELEMENT_IBB)) {
|
||||
child = parseIbb(parser);
|
||||
} else if (parser.getName().equals(ELEMENT_SIPUB)) {
|
||||
// TODO: sipub is allowed by xep-0332, but is not implemented yet
|
||||
throw new UnsupportedOperationException("sipub is not supported yet");
|
||||
} else if (parser.getName().equals(ELEMENT_JINGLE)) {
|
||||
// TODO: jingle is allowed by xep-0332, but is not implemented yet
|
||||
throw new UnsupportedOperationException("jingle is not supported yet");
|
||||
} else {
|
||||
// other elements are not allowed
|
||||
throw new IllegalArgumentException("unsupported child tag: " + parser.getName());
|
||||
}
|
||||
} else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(ELEMENT_DATA)) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AbstractHttpOverXmpp.Data data = new AbstractHttpOverXmpp.Data(child);
|
||||
return data;
|
||||
}
|
||||
|
||||
private AbstractHttpOverXmpp.Text parseText(XmlPullParser parser) throws Exception {
|
||||
String text = null;
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(ELEMENT_TEXT)) {
|
||||
done = true;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
|
||||
}
|
||||
} else if (eventType == XmlPullParser.TEXT) {
|
||||
text = parser.getText();
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
||||
}
|
||||
}
|
||||
|
||||
return new AbstractHttpOverXmpp.Text(text);
|
||||
}
|
||||
|
||||
private AbstractHttpOverXmpp.Xml parseXml(XmlPullParser parser) throws Exception {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean done = false;
|
||||
boolean startClosed = true;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if ((eventType == XmlPullParser.END_TAG) && parser.getName().equals(ELEMENT_XML)) {
|
||||
done = true;
|
||||
} else { // just write everything else as text
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
|
||||
if (!startClosed) {
|
||||
builder.append('>');
|
||||
}
|
||||
|
||||
builder.append('<');
|
||||
builder.append(parser.getName());
|
||||
appendXmlAttributes(parser, builder);
|
||||
startClosed = false;
|
||||
} else if (eventType == XmlPullParser.END_TAG) {
|
||||
|
||||
if (startClosed) {
|
||||
builder.append("</");
|
||||
builder.append(parser.getName());
|
||||
builder.append('>');
|
||||
} else {
|
||||
builder.append("/>");
|
||||
startClosed = true;
|
||||
}
|
||||
} else if (eventType == XmlPullParser.TEXT) {
|
||||
|
||||
if (!startClosed) {
|
||||
builder.append('>');
|
||||
startClosed = true;
|
||||
}
|
||||
builder.append(StringUtils.escapeForXML(parser.getText()));
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new AbstractHttpOverXmpp.Xml(builder.toString());
|
||||
}
|
||||
|
||||
private void appendXmlAttributes(XmlPullParser parser, StringBuilder builder) throws Exception {
|
||||
// NOTE: for now we ignore namespaces
|
||||
int count = parser.getAttributeCount();
|
||||
|
||||
if (count > 0) {
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
builder.append(' ');
|
||||
builder.append(parser.getAttributeName(i));
|
||||
builder.append("=\"");
|
||||
builder.append(StringUtils.escapeForXML(parser.getAttributeValue(i)));
|
||||
builder.append('"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractHttpOverXmpp.Base64 parseBase64(XmlPullParser parser) throws Exception {
|
||||
String text = null;
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.END_TAG) {
|
||||
|
||||
if (parser.getName().equals(ELEMENT_BASE_64)) {
|
||||
done = true;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
|
||||
}
|
||||
} else if (eventType == XmlPullParser.TEXT) {
|
||||
text = parser.getText();
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
||||
}
|
||||
}
|
||||
|
||||
return new AbstractHttpOverXmpp.Base64(text);
|
||||
}
|
||||
|
||||
private AbstractHttpOverXmpp.ChunkedBase64 parseChunkedBase64(XmlPullParser parser) throws Exception {
|
||||
String streamId = parser.getAttributeValue("", ATTRIBUTE_STREAM_ID);
|
||||
AbstractHttpOverXmpp.ChunkedBase64 child = new AbstractHttpOverXmpp.ChunkedBase64(streamId);
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(ELEMENT_CHUNKED_BASE_64)) {
|
||||
done = true;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected end tag: " + parser.getName());
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected event type: " + eventType);
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
private AbstractHttpOverXmpp.Ibb parseIbb(XmlPullParser parser) throws Exception {
|
||||
String sid = parser.getAttributeValue("", ATTRIBUTE_SID);
|
||||
AbstractHttpOverXmpp.Ibb child = new AbstractHttpOverXmpp.Ibb(sid);
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(ELEMENT_IBB)) {
|
||||
done = true;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected end tag: " + parser.getName());
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected event type: " + eventType);
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||
import org.jivesoftware.smackx.hoxt.packet.Base64BinaryChunk;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Packet provider for base64 binary chunks.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public class Base64BinaryChunkProvider implements PacketExtensionProvider {
|
||||
|
||||
/**
|
||||
* Required no-argument constructor.
|
||||
*/
|
||||
public Base64BinaryChunkProvider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
|
||||
String streamId = parser.getAttributeValue("", Base64BinaryChunk.ATTRIBUTE_STREAM_ID);
|
||||
String lastString = parser.getAttributeValue("", Base64BinaryChunk.ATTRIBUTE_LAST);
|
||||
boolean last = false;
|
||||
|
||||
if (lastString != null) {
|
||||
last = Boolean.parseBoolean(lastString);
|
||||
}
|
||||
|
||||
String text = null;
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(Base64BinaryChunk.ELEMENT_CHUNK)) {
|
||||
done = true;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
|
||||
}
|
||||
} else if (eventType == XmlPullParser.TEXT) {
|
||||
text = parser.getText();
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
||||
}
|
||||
}
|
||||
|
||||
return new Base64BinaryChunk(text, streamId, last);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smackx.hoxt.packet.HttpMethod;
|
||||
import org.jivesoftware.smackx.hoxt.packet.HttpOverXmppReq;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Req packet provider.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public class HttpOverXmppReqProvider extends AbstractHttpOverXmppProvider {
|
||||
|
||||
private static final String ELEMENT_REQ = "req";
|
||||
|
||||
private static final String ATTRIBUTE_METHOD = "method";
|
||||
private static final String ATTRIBUTE_RESOURCE = "resource";
|
||||
private static final String ATTRIBUTE_MAX_CHUNK_SIZE = "maxChunkSize";
|
||||
|
||||
/**
|
||||
* Mandatory no argument constructor.
|
||||
*/
|
||||
public HttpOverXmppReqProvider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IQ parseIQ(XmlPullParser parser) throws Exception {
|
||||
String method = parser.getAttributeValue("", ATTRIBUTE_METHOD);
|
||||
String resource = parser.getAttributeValue("", ATTRIBUTE_RESOURCE);
|
||||
String version = parser.getAttributeValue("", ATTRIBUTE_VERSION);
|
||||
String maxChunkSize = parser.getAttributeValue("", ATTRIBUTE_MAX_CHUNK_SIZE);
|
||||
|
||||
HttpMethod reqMethod = HttpMethod.valueOf(method);
|
||||
HttpOverXmppReq.Req req = new HttpOverXmppReq.Req(reqMethod, resource);
|
||||
req.setVersion(version);
|
||||
|
||||
Boolean sipub = true;
|
||||
Boolean jingle = true;
|
||||
Boolean ibb = true;
|
||||
|
||||
String sipubStr = parser.getAttributeValue("", AbstractHttpOverXmppProvider.ELEMENT_SIPUB);
|
||||
String ibbStr = parser.getAttributeValue("", AbstractHttpOverXmppProvider.ELEMENT_IBB);
|
||||
String jingleStr = parser.getAttributeValue("", AbstractHttpOverXmppProvider.ELEMENT_JINGLE);
|
||||
|
||||
if (sipubStr != null) {
|
||||
sipub = Boolean.valueOf(sipubStr);
|
||||
}
|
||||
if (ibbStr != null) {
|
||||
ibb = Boolean.valueOf(ibbStr);
|
||||
}
|
||||
if (jingleStr != null) {
|
||||
jingle = Boolean.valueOf(jingleStr);
|
||||
}
|
||||
|
||||
req.setIbb(ibb);
|
||||
req.setSipub(sipub);
|
||||
req.setJingle(jingle);
|
||||
|
||||
if (maxChunkSize != null) {
|
||||
int maxChunkSizeValue = Integer.parseInt(maxChunkSize);
|
||||
req.setMaxChunkSize(maxChunkSizeValue);
|
||||
}
|
||||
|
||||
parseHeadersAndData(parser, ELEMENT_REQ, req);
|
||||
HttpOverXmppReq packet = new HttpOverXmppReq();
|
||||
packet.setReq(req);
|
||||
return packet;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smackx.hoxt.packet.HttpOverXmppResp;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Resp packet provider.
|
||||
*
|
||||
* @author Andriy Tsykholyas
|
||||
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
||||
*/
|
||||
public class HttpOverXmppRespProvider extends AbstractHttpOverXmppProvider {
|
||||
|
||||
private static final String ELEMENT_RESP = "resp";
|
||||
|
||||
private static final String ATTRIBUTE_STATUS_MESSAGE = "statusMessage";
|
||||
private static final String ATTRIBUTE_STATUS_CODE = "statusCode";
|
||||
|
||||
/**
|
||||
* Mandatory no argument constructor.
|
||||
*/
|
||||
public HttpOverXmppRespProvider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IQ parseIQ(XmlPullParser parser) throws Exception {
|
||||
String version = parser.getAttributeValue("", ATTRIBUTE_VERSION);
|
||||
String statusMessage = parser.getAttributeValue("", ATTRIBUTE_STATUS_MESSAGE);
|
||||
String statusCodeString = parser.getAttributeValue("", ATTRIBUTE_STATUS_CODE);
|
||||
int statusCode = Integer.parseInt(statusCodeString);
|
||||
|
||||
HttpOverXmppResp.Resp resp = new HttpOverXmppResp.Resp();
|
||||
resp.setVersion(version);
|
||||
resp.setStatusMessage(statusMessage);
|
||||
resp.setStatusCode(statusCode);
|
||||
|
||||
parseHeadersAndData(parser, ELEMENT_RESP, resp);
|
||||
|
||||
HttpOverXmppResp packet = new HttpOverXmppResp();
|
||||
packet.setResp(resp);
|
||||
return packet;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,23 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Providers for workgroup extensions -->
|
||||
<smackProviders>
|
||||
<smackProviders>
|
||||
|
||||
<!-- XEP-0332 HTTP over XMPP transport -->
|
||||
<iqProvider>
|
||||
<elementName>req</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.HttpOverXmppReqProvider</className>
|
||||
</iqProvider>
|
||||
<iqProvider>
|
||||
<elementName>resp</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.HttpOverXmppRespProvider</className>
|
||||
</iqProvider>
|
||||
<extensionProvider>
|
||||
<elementName>chunk</elementName>
|
||||
<namespace>urn:xmpp:http</namespace>
|
||||
<className>org.jivesoftware.smackx.hoxt.provider.Base64BinaryChunkProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- XEP-0280 Message Carbons -->
|
||||
<extensionProvider>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<smack>
|
||||
<startupClasses>
|
||||
<className>org.jivesoftware.smackx.hoxt.HOXTManager</className>
|
||||
</startupClasses>
|
||||
</smack>
|
|
@ -0,0 +1,203 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.test.util.TestUtils;
|
||||
import org.jivesoftware.smackx.hoxt.packet.AbstractHttpOverXmpp;
|
||||
import org.jivesoftware.smackx.hoxt.packet.HttpOverXmppReq;
|
||||
import org.jivesoftware.smackx.hoxt.packet.HttpOverXmppResp;
|
||||
import org.jivesoftware.smackx.shim.packet.Header;
|
||||
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
|
||||
import org.junit.Test;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests correct headers and data parsing in 'req' and 'resp' elements.
|
||||
*/
|
||||
public class AbstractHttpOverXmppProviderTest {
|
||||
|
||||
@Test
|
||||
public void areRespHeadersParsedCorrectly() throws Exception {
|
||||
String string = "<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200' statusMessage='OK'>"
|
||||
+ "<headers xmlns='http://jabber.org/protocol/shim'>"
|
||||
+ "<header name='Date'>Fri, 03 May 2013 13:52:10 GMT-4</header>"
|
||||
+ "<header name='Allow'>OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE</header>"
|
||||
+ "<header name='Content-Length'>0</header>"
|
||||
+ "</headers>"
|
||||
+ "</resp>";
|
||||
Map<String, String> expectedHeaders = new HashMap<String, String>();
|
||||
expectedHeaders.put("Date", "Fri, 03 May 2013 13:52:10 GMT-4");
|
||||
expectedHeaders.put("Allow", "OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE");
|
||||
expectedHeaders.put("Content-Length", "0");
|
||||
|
||||
AbstractHttpOverXmppProvider provider = new HttpOverXmppRespProvider();
|
||||
XmlPullParser parser = TestUtils.getParser(string, "resp");
|
||||
|
||||
IQ iq = provider.parseIQ(parser);
|
||||
assertTrue(iq instanceof HttpOverXmppResp);
|
||||
AbstractHttpOverXmpp.AbstractBody body = ((HttpOverXmppResp) iq).getResp();
|
||||
|
||||
checkHeaders(body.getHeaders(), expectedHeaders);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void areReqHeadersParsedCorrectly() throws Exception {
|
||||
String string = "<req xmlns='urn:xmpp:http' method='GET' resource='/rdf/xep' version='1.1'>"
|
||||
+ "<headers xmlns='http://jabber.org/protocol/shim'>"
|
||||
+ "<header name='Host'>clayster.com</header>"
|
||||
+ "</headers>"
|
||||
+ "</req>";
|
||||
Map<String, String> expectedHeaders = new HashMap<String, String>();
|
||||
expectedHeaders.put("Host", "clayster.com");
|
||||
|
||||
AbstractHttpOverXmppProvider provider = new HttpOverXmppReqProvider();
|
||||
XmlPullParser parser = TestUtils.getParser(string, "req");
|
||||
|
||||
IQ iq = provider.parseIQ(parser);
|
||||
assertTrue(iq instanceof HttpOverXmppReq);
|
||||
AbstractHttpOverXmpp.AbstractBody body = ((HttpOverXmppReq) iq).getReq();
|
||||
|
||||
checkHeaders(body.getHeaders(), expectedHeaders);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isTextDataParsedCorrectly() throws Exception {
|
||||
String expectedText = "@prefix dc: <http://purl.org/dc/elements/1.1/>."
|
||||
+ "@base <http://clayster.com/>."
|
||||
+ "<xep> dc:title \"HTTP over XMPP\";"
|
||||
+ "dc:creator <PeterWaher>;"
|
||||
+ "dc:publisher <XSF>.";
|
||||
String encodedText = "@prefix dc: <http://purl.org/dc/elements/1.1/>."
|
||||
+ "@base <http://clayster.com/>."
|
||||
+ "<xep> dc:title \"HTTP over XMPP\";"
|
||||
+ "dc:creator <PeterWaher>;"
|
||||
+ "dc:publisher <XSF>.";
|
||||
String string = "<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200' statusMessage='OK'>"
|
||||
+ "<headers xmlns='http://jabber.org/protocol/shim'><header name='Server'>Clayster</header></headers>"
|
||||
+ "<data><text>"
|
||||
+ encodedText
|
||||
+ "</text></data></resp>";
|
||||
|
||||
AbstractHttpOverXmpp.Text text = (AbstractHttpOverXmpp.Text) parseAbstractBody(
|
||||
string, "resp").getData().getChild();
|
||||
assertEquals(expectedText, text.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isXmlDataParsedCorrectly() throws Exception {
|
||||
String expectedXml = "<sparql><head><variable name=\"title\"/><variable name=\"creator\"/>" // no xmlns here
|
||||
+ "</head><results><result>"
|
||||
+ "<binding name=\"title\">"
|
||||
+ "<literal>HTTP over XMPP</literal>"
|
||||
+ "</binding>"
|
||||
+ "<binding name=\"creator\">"
|
||||
+ "<uri>http://clayster.com/PeterWaher</uri>"
|
||||
+ "</binding>"
|
||||
+ "</result>"
|
||||
+ "</results>"
|
||||
+ "</sparql>";
|
||||
String encodedXml = "<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\"><head><variable name=\"title\"/><variable name=\"creator\"/>"
|
||||
+ "</head><results><result>"
|
||||
+ "<binding name=\"title\">"
|
||||
+ "<literal>HTTP over XMPP</literal>"
|
||||
+ "</binding>"
|
||||
+ "<binding name=\"creator\">"
|
||||
+ "<uri>http://clayster.com/PeterWaher</uri>"
|
||||
+ "</binding>"
|
||||
+ "</result>"
|
||||
+ "</results>"
|
||||
+ "</sparql>";
|
||||
String string = "<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200' statusMessage='OK'>"
|
||||
+ "<headers xmlns='http://jabber.org/protocol/shim'><header name='Server'>Clayster</header></headers>"
|
||||
+ "<data><xml>"
|
||||
+ encodedXml
|
||||
+ "</xml></data></resp>";
|
||||
AbstractHttpOverXmpp.Xml xmlProviderValue = (AbstractHttpOverXmpp.Xml) parseAbstractBody(
|
||||
string, "resp").getData().getChild();
|
||||
assertEquals(expectedXml, xmlProviderValue.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isBase64DataParsedCorrectly() throws Exception {
|
||||
String base64Data = "iVBORw0KGgoAAAANSUhEUgAAASwAAAGQCAYAAAAUdV17AAAAAXNSR0 ... tVWJd+e+y1AAAAABJRU5ErkJggg==";
|
||||
String string = "<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200' statusMessage='OK'>"
|
||||
+ "<headers xmlns='http://jabber.org/protocol/shim'><header name='Server'>Clayster</header></headers>"
|
||||
+ "<data><base64>"
|
||||
+ base64Data
|
||||
+ "</base64></data></resp>";
|
||||
AbstractHttpOverXmpp.Base64 base64ProviderValue = (AbstractHttpOverXmpp.Base64) parseAbstractBody(
|
||||
string, "resp").getData().getChild();
|
||||
assertEquals(base64Data, base64ProviderValue.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChunkedBase64DataParsedCorrectly() throws Exception {
|
||||
String streamId = "Stream0001";
|
||||
String chunkBase64Data = " <chunkedBase64 streamId='" + streamId + "'/>";
|
||||
String string = "<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200' statusMessage='OK'>"
|
||||
+ "<headers xmlns='http://jabber.org/protocol/shim'><header name='Server'>Clayster</header></headers>"
|
||||
+ "<data>"
|
||||
+ chunkBase64Data
|
||||
+ "</data></resp>";
|
||||
AbstractHttpOverXmpp.ChunkedBase64 chunkedBase64Value = (AbstractHttpOverXmpp.ChunkedBase64) parseAbstractBody(
|
||||
string, "resp").getData().getChild();
|
||||
assertEquals(streamId, chunkedBase64Value.getStreamId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isIbbDataParsedCorrectly() throws Exception {
|
||||
String sid = "Stream0002";
|
||||
String ibbData = " <ibb sid='" + sid + "'/>";
|
||||
String string = "<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200' statusMessage='OK'>"
|
||||
+ "<headers xmlns='http://jabber.org/protocol/shim'><header name='Server'>Clayster</header></headers>"
|
||||
+ "<data>"
|
||||
+ ibbData
|
||||
+ "</data></resp>";
|
||||
AbstractHttpOverXmpp.Ibb ibbValue = (AbstractHttpOverXmpp.Ibb) parseAbstractBody(
|
||||
string, "resp").getData().getChild();
|
||||
assertEquals(sid, ibbValue.getSid());
|
||||
}
|
||||
|
||||
private AbstractHttpOverXmpp.AbstractBody parseAbstractBody(String string, String tag) throws Exception {
|
||||
AbstractHttpOverXmppProvider provider = new HttpOverXmppRespProvider();
|
||||
XmlPullParser parser = TestUtils.getParser(string, tag);
|
||||
|
||||
IQ iq = provider.parseIQ(parser);
|
||||
assertTrue(iq instanceof HttpOverXmppResp);
|
||||
AbstractHttpOverXmpp.AbstractBody body = ((HttpOverXmppResp) iq).getResp();
|
||||
return body;
|
||||
}
|
||||
|
||||
private void checkHeaders(HeadersExtension headers, Map<String, String> expectedHeaders) {
|
||||
Collection<Header> collection = headers.getHeaders();
|
||||
|
||||
assertEquals(collection.size(), expectedHeaders.size());
|
||||
|
||||
for (Header header : collection) {
|
||||
assertTrue(expectedHeaders.containsKey(header.getName()));
|
||||
assertEquals(expectedHeaders.get(header.getName()), header.getValue());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.test.util.TestUtils;
|
||||
import org.jivesoftware.smackx.hoxt.packet.Base64BinaryChunk;
|
||||
import org.junit.Test;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests correct parsing of 'chunk' elements in Message stanza.
|
||||
*/
|
||||
public class Base64BinaryChunkProviderTest {
|
||||
|
||||
@Test
|
||||
public void isNonLatsChunkParsedCorrectly() throws Exception {
|
||||
String base64Text = "iVBORw0KGgoAAAANSUhEUgAAASwAAAGQCAYAA";
|
||||
String string = "<chunk xmlns='urn:xmpp:http' streamId='Stream0001'>" + base64Text + "</chunk>";
|
||||
|
||||
Base64BinaryChunkProvider provider = new Base64BinaryChunkProvider();
|
||||
XmlPullParser parser = TestUtils.getParser(string, "chunk");
|
||||
|
||||
PacketExtension extension = provider.parseExtension(parser);
|
||||
assertTrue(extension instanceof Base64BinaryChunk);
|
||||
|
||||
Base64BinaryChunk chunk = (Base64BinaryChunk) extension;
|
||||
assertEquals("Stream0001", chunk.getStreamId());
|
||||
assertFalse(chunk.isLast());
|
||||
assertEquals(base64Text, chunk.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLatsChunkParsedCorrectly() throws Exception {
|
||||
String base64Text = "2uPzi9u+tVWJd+e+y1AAAAABJRU5ErkJggg==";
|
||||
String string = "<chunk xmlns='urn:xmpp:http' streamId='Stream0001' last='true'>" + base64Text + "</chunk>";
|
||||
|
||||
Base64BinaryChunkProvider provider = new Base64BinaryChunkProvider();
|
||||
XmlPullParser parser = TestUtils.getParser(string, "chunk");
|
||||
|
||||
PacketExtension extension = provider.parseExtension(parser);
|
||||
assertTrue(extension instanceof Base64BinaryChunk);
|
||||
|
||||
Base64BinaryChunk chunk = (Base64BinaryChunk) extension;
|
||||
assertEquals("Stream0001", chunk.getStreamId());
|
||||
assertTrue(chunk.isLast());
|
||||
assertEquals(base64Text, chunk.getText());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.test.util.TestUtils;
|
||||
import org.jivesoftware.smackx.hoxt.packet.HttpMethod;
|
||||
import org.jivesoftware.smackx.hoxt.packet.HttpOverXmppReq;
|
||||
import org.junit.Test;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class HttpOverXmppReqProviderTest {
|
||||
|
||||
@Test
|
||||
public void areAllReqAttributesCorrectlyParsed() throws Exception {
|
||||
String string = "<req xmlns='urn:xmpp:http' method='OPTIONS' resource='*' version='1.1'/>";
|
||||
HttpOverXmppReq.Req req = parseReq(string);
|
||||
assertEquals(req.getVersion(), "1.1");
|
||||
assertEquals(req.getMethod(), HttpMethod.OPTIONS);
|
||||
assertEquals(req.getResource(), "*");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void areGetRequestAttributesCorrectlyParsed() throws Exception {
|
||||
String string = "<req xmlns='urn:xmpp:http' method='GET' resource='/rdf/xep' version='1.1'/>";
|
||||
HttpOverXmppReq.Req req = parseReq(string);
|
||||
assertEquals(req.getVersion(), "1.1");
|
||||
assertEquals(req.getMethod(), HttpMethod.GET);
|
||||
assertEquals(req.getResource(), "/rdf/xep");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReqOptionAttributesCorrectlyParsed() throws Exception {
|
||||
String string = "<req xmlns='urn:xmpp:http' method='OPTIONS' resource='*' version='1.1' maxChunkSize='256' sipub='false' ibb='true' jingle='false'/>";
|
||||
HttpOverXmppReq.Req req = parseReq(string);
|
||||
assertEquals(req.getMaxChunkSize(), 256);
|
||||
assertEquals(req.isSipub(), false);
|
||||
assertEquals(req.isIbb(), true);
|
||||
assertEquals(req.isJingle(), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReqOptionalAttributesDefaultValues() throws Exception {
|
||||
String string = "<req xmlns='urn:xmpp:http' method='OPTIONS' resource='*' version='1.1'/>";
|
||||
HttpOverXmppReq.Req req = parseReq(string);
|
||||
assertEquals(req.isSipub(), true);
|
||||
assertEquals(req.isIbb(), true);
|
||||
assertEquals(req.isJingle(), true);
|
||||
}
|
||||
|
||||
private HttpOverXmppReq.Req parseReq(String string) throws Exception {
|
||||
HttpOverXmppReqProvider provider = new HttpOverXmppReqProvider();
|
||||
XmlPullParser parser = TestUtils.getParser(string, "req");
|
||||
IQ iq = provider.parseIQ(parser);
|
||||
assertTrue(iq instanceof HttpOverXmppReq);
|
||||
HttpOverXmppReq castedIq = (HttpOverXmppReq) iq;
|
||||
return castedIq.getReq();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014 Andriy Tsykholyas
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.hoxt.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.test.util.TestUtils;
|
||||
import org.jivesoftware.smackx.hoxt.packet.HttpOverXmppResp;
|
||||
import org.junit.Test;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests correct attribute parsing in 'resp' element.
|
||||
*/
|
||||
public class HttpOverXmppRespProviderTest {
|
||||
|
||||
@Test
|
||||
public void areAllRespAttributesCorrectlyParsed() throws Exception {
|
||||
String string = "<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200' statusMessage='OK'/>";
|
||||
HttpOverXmppRespProvider provider = new HttpOverXmppRespProvider();
|
||||
XmlPullParser parser = TestUtils.getParser(string, "resp");
|
||||
|
||||
IQ iq = provider.parseIQ(parser);
|
||||
assertTrue(iq instanceof HttpOverXmppResp);
|
||||
HttpOverXmppResp castedIq = (HttpOverXmppResp) iq;
|
||||
HttpOverXmppResp.Resp resp = castedIq.getResp();
|
||||
|
||||
assertEquals(resp.getVersion(), "1.1");
|
||||
assertEquals(resp.getStatusCode(), 200);
|
||||
assertEquals(resp.getStatusMessage(), "OK");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void areRespAttributesWothoutMessageCorrectlyParsed() throws Exception {
|
||||
String string = "<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200'/>";
|
||||
HttpOverXmppRespProvider provider = new HttpOverXmppRespProvider();
|
||||
XmlPullParser parser = TestUtils.getParser(string, "resp");
|
||||
|
||||
IQ iq = provider.parseIQ(parser);
|
||||
assertTrue(iq instanceof HttpOverXmppResp);
|
||||
HttpOverXmppResp castedIq = (HttpOverXmppResp) iq;
|
||||
HttpOverXmppResp.Resp resp = castedIq.getResp();
|
||||
|
||||
assertEquals(resp.getVersion(), "1.1");
|
||||
assertEquals(resp.getStatusCode(), 200);
|
||||
assertNull(resp.getStatusMessage());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue