mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-26 16:22:06 +01:00
Added stream compression support. SMACK-112
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@3306 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
86ce253883
commit
15defec50f
3 changed files with 221 additions and 46 deletions
|
@ -320,10 +320,17 @@ class PacketReader {
|
|||
resetParser();
|
||||
}
|
||||
else if (parser.getName().equals("failure")) {
|
||||
if ("urn:ietf:params:xml:ns:xmpp-tls".equals(parser.getNamespace(null))) {
|
||||
String namespace = parser.getNamespace(null);
|
||||
if ("urn:ietf:params:xml:ns:xmpp-tls".equals(namespace)) {
|
||||
// TLS negotiation has failed. The server will close the connection
|
||||
throw new Exception("TLS negotiation has failed");
|
||||
}
|
||||
else if ("http://jabber.org/protocol/compress".equals(namespace)) {
|
||||
// Stream compression has been denied. This is a recoverable
|
||||
// situation. It is still possible to authenticate and
|
||||
// use the connection but using an uncompressed connection
|
||||
connection.streamCompressionDenied();
|
||||
}
|
||||
else {
|
||||
// SASL authentication has failed. The server may close the connection
|
||||
// depending on the number of retries
|
||||
|
@ -347,6 +354,14 @@ class PacketReader {
|
|||
// will be to bind the resource
|
||||
connection.getSASLAuthentication().authenticated();
|
||||
}
|
||||
else if (parser.getName().equals("compressed")) {
|
||||
// Server confirmed that it's possible to use stream compression. Start
|
||||
// stream compression
|
||||
connection.startStreamCompression();
|
||||
// Reset the state of the parser since a new stream element is going
|
||||
// to be sent by the server
|
||||
resetParser();
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("stream")) {
|
||||
|
@ -464,6 +479,10 @@ class PacketReader {
|
|||
// The server supports sessions
|
||||
connection.getSASLAuthentication().sessionsSupported();
|
||||
}
|
||||
else if (parser.getName().equals("compression")) {
|
||||
// The server supports stream compression
|
||||
connection.setAvailableCompressionMethods(parseCompressionMethods(parser));
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("features")) {
|
||||
|
@ -504,6 +523,28 @@ class PacketReader {
|
|||
return mechanisms;
|
||||
}
|
||||
|
||||
private Collection parseCompressionMethods(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
List methods = new ArrayList();
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String elementName = parser.getName();
|
||||
if (elementName.equals("method")) {
|
||||
methods.add(parser.nextText());
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("compression")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an IQ packet.
|
||||
*
|
||||
|
|
|
@ -36,6 +36,7 @@ import javax.net.ssl.SSLSocket;
|
|||
import java.io.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
|
@ -129,6 +130,15 @@ public class XMPPConnection {
|
|||
*/
|
||||
Map chats = Collections.synchronizedMap(new HashMap());
|
||||
|
||||
/**
|
||||
* Collection of available stream compression methods offered by the server.
|
||||
*/
|
||||
private Collection compressionMethods;
|
||||
/**
|
||||
* Flag that indicates if stream compression is actually in use.
|
||||
*/
|
||||
private boolean usingCompression;
|
||||
|
||||
/**
|
||||
* Creates a new connection to the specified XMPP server. A DNS SRV lookup will be
|
||||
* performed to try to determine the IP address and port corresponding to the
|
||||
|
@ -909,9 +919,38 @@ public class XMPPConnection {
|
|||
|
||||
private void initReaderAndWriter() throws XMPPException {
|
||||
try {
|
||||
if (!usingCompression) {
|
||||
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
||||
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Class zoClass = Class.forName("com.jcraft.jzlib.ZOutputStream");
|
||||
//ZOutputStream out = new ZOutputStream(socket.getOutputStream(), JZlib.Z_BEST_COMPRESSION);
|
||||
Constructor constructor =
|
||||
zoClass.getConstructor(new Class[]{OutputStream.class, Integer.TYPE});
|
||||
Object out = constructor.newInstance(new Object[] {socket.getOutputStream(), new Integer(9)});
|
||||
//out.setFlushMode(JZlib.Z_PARTIAL_FLUSH);
|
||||
Method method = zoClass.getMethod("setFlushMode", new Class[] {Integer.TYPE});
|
||||
method.invoke(out, new Object[] {new Integer(1)});
|
||||
writer = new BufferedWriter(new OutputStreamWriter((OutputStream) out, "UTF-8"));
|
||||
|
||||
Class ziClass = Class.forName("com.jcraft.jzlib.ZInputStream");
|
||||
//ZInputStream in = new ZInputStream(socket.getInputStream());
|
||||
constructor = ziClass.getConstructor(new Class[]{InputStream.class});
|
||||
Object in = constructor.newInstance(new Object[] {socket.getInputStream()});
|
||||
//in.setFlushMode(JZlib.Z_PARTIAL_FLUSH);
|
||||
method = ziClass.getMethod("setFlushMode", new Class[] {Integer.TYPE});
|
||||
method.invoke(in, new Object[] {new Integer(1)});
|
||||
reader = new BufferedReader(new InputStreamReader((InputStream) in, "UTF-8"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
||||
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new XMPPException(
|
||||
"XMPPError establishing connection with server.",
|
||||
|
@ -1061,4 +1100,128 @@ public class XMPPConnection {
|
|||
// Send a new opening stream to the server
|
||||
packetWriter.openStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the available stream compression methods offered by the server.
|
||||
*
|
||||
* @param methods compression methods offered by the server.
|
||||
*/
|
||||
void setAvailableCompressionMethods(Collection methods) {
|
||||
compressionMethods = methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified compression method was offered by the server.
|
||||
*
|
||||
* @param method the method to check.
|
||||
* @return true if the specified compression method was offered by the server.
|
||||
*/
|
||||
private boolean hasAvailableCompressionMethod(String method) {
|
||||
return compressionMethods != null && compressionMethods.contains(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the server offered stream compression to the client.
|
||||
*
|
||||
* @return true if the server offered stream compression to the client.
|
||||
*/
|
||||
public boolean getServerSupportsCompression() {
|
||||
return compressionMethods != null && !compressionMethods.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if network traffic is being compressed. When using stream compression network
|
||||
* traffic can be reduced up to 90%. Therefore, stream compression is ideal when using a slow
|
||||
* speed network connection. However, the server will need to use more CPU time in order to
|
||||
* un/compress network data so under high load the server performance might be affected.<p>
|
||||
*
|
||||
* Note: To use stream compression the smackx.jar file has to be present in the classpath.
|
||||
*
|
||||
* @return true if network traffic is being compressed.
|
||||
*/
|
||||
public boolean isUsingCompression() {
|
||||
return usingCompression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts using stream compression that will compress network traffic. Traffic can be
|
||||
* reduced up to 90%. Therefore, stream compression is ideal when using a slow speed network
|
||||
* connection. However, the server and the client will need to use more CPU time in order to
|
||||
* un/compress network data so under high load the server performance might be affected.<p>
|
||||
*
|
||||
* Stream compression has to have been previously offered by the server. Currently only the
|
||||
* zlib method is supported by the client. Stream compression negotiation has to be done
|
||||
* before authentication took place.<p>
|
||||
*
|
||||
* Note: To use stream compression the smackx.jar file has to be present in the classpath.
|
||||
*
|
||||
* @return true if stream compression negotiation was successful.
|
||||
*/
|
||||
public boolean useCompression() {
|
||||
// If stream compression was offered by the server and we want to use
|
||||
// compression then send compression request to the server
|
||||
if (authenticated) {
|
||||
throw new IllegalStateException("Compression should be negotiated before authentication.");
|
||||
}
|
||||
try {
|
||||
Class.forName("com.jcraft.jzlib.ZOutputStream");
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException("Cannot use compression. Add smackx.jar to the classpath");
|
||||
}
|
||||
if (hasAvailableCompressionMethod("zlib")) {
|
||||
requestStreamCompression();
|
||||
// Wait until compression is being used or a timeout happened
|
||||
synchronized (this) {
|
||||
try {
|
||||
this.wait(SmackConfiguration.getPacketReplyTimeout() * 5);
|
||||
}
|
||||
catch (InterruptedException e) {}
|
||||
}
|
||||
return usingCompression;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the server that we want to start using stream compression. When using TLS
|
||||
* then negotiation of stream compression can only happen after TLS was negotiated. If TLS
|
||||
* compression is being used the stream compression should not be used.
|
||||
*/
|
||||
private void requestStreamCompression() {
|
||||
try {
|
||||
writer.write("<compress xmlns='http://jabber.org/protocol/compress'>");
|
||||
writer.write("<method>zlib</method></compress>");
|
||||
writer.flush();
|
||||
}
|
||||
catch (IOException e) {
|
||||
packetReader.notifyConnectionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start using stream compression since the server has acknowledged stream compression.
|
||||
*/
|
||||
void startStreamCompression() throws Exception {
|
||||
// Secure the plain connection
|
||||
usingCompression = true;
|
||||
// Initialize the reader and writer with the new secured version
|
||||
initReaderAndWriter();
|
||||
|
||||
// Set the new writer to use
|
||||
packetWriter.setWriter(writer);
|
||||
// Send a new opening stream to the server
|
||||
packetWriter.openStream();
|
||||
// Notify that compression is being used
|
||||
synchronized (this) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
void streamCompressionDenied() {
|
||||
// Notify that compression has been denied
|
||||
synchronized (this) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,53 +1,21 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
* $Date: $
|
||||
*
|
||||
* Copyright (C) 2002-2003 Jive Software. All rights reserved.
|
||||
* ====================================================================
|
||||
* The Jive Software License (based on Apache Software License, Version 1.1)
|
||||
* Copyright 2003-2005 Jive Software.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* All rights reserved. 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
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution,
|
||||
* if any, must include the following acknowledgment:
|
||||
* "This product includes software developed by
|
||||
* Jive Software (http://www.jivesoftware.com)."
|
||||
* Alternately, this acknowledgment may appear in the software itself,
|
||||
* if and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "Smack" and "Jive Software" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please
|
||||
* contact webmaster@jivesoftware.com.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Smack",
|
||||
* nor may "Smack" appear in their name, without prior written
|
||||
* permission of Jive Software.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
* 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.smack.test;
|
||||
|
||||
|
@ -242,6 +210,9 @@ public abstract class SmackTestCase extends TestCase {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
if (Boolean.getBoolean("test.compressionEnabled")) {
|
||||
getConnection(i).useCompression();
|
||||
}
|
||||
// Login with the new test account
|
||||
getConnection(i).login("user" + i, "user" + i);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue