1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-09 10:19:41 +02:00

Fix and improve the HTTP File Upload implementation

Fix a few resource leaks. Improve the API and add an integration
test. Also add compability layer for XEP-0363: HTTP File Upload 0.2.

SMACK-747
This commit is contained in:
Florian Schmaus 2017-03-09 21:35:29 +01:00
parent 72d4c8b611
commit 09b6608a3a
26 changed files with 691 additions and 177 deletions

View file

@ -16,10 +16,16 @@
*/
package org.igniterealtime.smack.inttest;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Random;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.net.ssl.HttpsURLConnection;
import org.jivesoftware.smack.StanzaCollector;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
@ -37,9 +43,12 @@ public abstract class AbstractSmackIntTest {
protected final long timeout;
protected AbstractSmackIntTest(String testRunId, long timeout) {
protected final Configuration sinttestConfiguration;
protected AbstractSmackIntTest(String testRunId, Configuration configuration) {
this.testRunId = testRunId;
this.timeout = timeout;
this.sinttestConfiguration = configuration;
this.timeout = configuration.replyTimeout;
}
protected void performActionAndWaitUntilStanzaReceived(Runnable action, XMPPConnection connection, StanzaFilter filter)
@ -71,4 +80,19 @@ public abstract class AbstractSmackIntTest {
protected interface Condition {
boolean evaluate() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
}
protected File createNewTempFile() throws IOException {
File file = File.createTempFile("smack-integration-test-" + testRunId + "-temp-file", null);
file.deleteOnExit();
return file;
}
protected HttpURLConnection getHttpUrlConnectionFor(URL url) throws IOException {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
if (sinttestConfiguration.tlsContext != null && urlConnection instanceof HttpsURLConnection) {
HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.tlsContext.getSocketFactory());
}
return urlConnection;
}
}

View file

@ -41,7 +41,7 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
protected final XMPPConnection connection;
public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment.testRunId, environment.configuration.replyTimeout);
super(environment.testRunId, environment.configuration);
this.connection = this.conOne = environment.conOne;
this.conTwo = environment.conTwo;
this.conThree = environment.conThree;

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2015-2016 Florian Schmaus
* Copyright 2015-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,14 +19,10 @@ package org.igniterealtime.smack.inttest;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.jid.DomainBareJid;
import eu.geekplace.javapinning.java7.Java7Pinning;
public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmackIntTest {
private final SmackIntegrationTestEnvironment environment;
@ -39,7 +35,7 @@ public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmack
protected final DomainBareJid service;
public AbstractSmackLowLevelIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment.testRunId, environment.configuration.replyTimeout);
super(environment.testRunId, environment.configuration);
this.environment = environment;
this.configuration = environment.configuration;
this.service = configuration.service;
@ -47,9 +43,8 @@ public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmack
public final XMPPTCPConnectionConfiguration.Builder getConnectionConfiguration() throws KeyManagementException, NoSuchAlgorithmException {
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
if (configuration.serviceTlsPin != null) {
SSLContext sc = Java7Pinning.forPin(configuration.serviceTlsPin);
builder.setCustomSSLContext(sc);
if (configuration.tlsContext != null) {
builder.setCustomSSLContext(configuration.tlsContext);
}
builder.setSecurityMode(configuration.securityMode);
builder.setXmppDomain(service);

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2015-2016 Florian Schmaus
* Copyright 2015-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,8 @@ package org.igniterealtime.smack.inttest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@ -26,6 +28,8 @@ import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import javax.net.ssl.SSLContext;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
@ -33,6 +37,8 @@ import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import eu.geekplace.javapinning.java7.Java7Pinning;
public final class Configuration {
public enum AccountRegistration {
@ -45,6 +51,8 @@ public final class Configuration {
public final String serviceTlsPin;
public final SSLContext tlsContext;
public final SecurityMode securityMode;
public final int replyTimeout;
@ -78,10 +86,16 @@ public final class Configuration {
private Configuration(DomainBareJid service, String serviceTlsPin, SecurityMode securityMode, int replyTimeout,
boolean debug, String accountOneUsername, String accountOnePassword, String accountTwoUsername,
String accountTwoPassword, String accountThreeUsername, String accountThreePassword, Set<String> enabledTests, Set<String> disabledTests,
Set<String> testPackages, String adminAccountUsername, String adminAccountPassword) {
Set<String> testPackages, String adminAccountUsername, String adminAccountPassword)
throws KeyManagementException, NoSuchAlgorithmException {
this.service = Objects.requireNonNull(service,
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
this.serviceTlsPin = serviceTlsPin;
if (serviceTlsPin != null) {
tlsContext = Java7Pinning.forPin(serviceTlsPin);
} else {
tlsContext = null;
}
this.securityMode = securityMode;
if (replyTimeout > 0) {
this.replyTimeout = replyTimeout;
@ -257,7 +271,7 @@ public final class Configuration {
return this;
}
public Configuration build() {
public Configuration build() throws KeyManagementException, NoSuchAlgorithmException {
return new Configuration(service, serviceTlsPin, securityMode, replyTimeout, debug, accountOneUsername,
accountOnePassword, accountTwoUsername, accountTwoPassword, accountThreeUsername, accountThreePassword, enabledTests, disabledTests,
testPackages, adminAccountUsername, adminAccountPassword);
@ -266,7 +280,8 @@ public final class Configuration {
private static final String SINTTEST = "sinttest.";
public static Configuration newConfiguration() throws IOException {
public static Configuration newConfiguration()
throws IOException, KeyManagementException, NoSuchAlgorithmException {
Properties properties = new Properties();
File propertiesFile = findPropertiesFile();

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2015-2016 Florian Schmaus
* Copyright 2015-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -43,8 +43,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import org.igniterealtime.smack.inttest.IntTestUtil.UsernameAndPassword;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.SmackConfiguration;
@ -64,8 +62,6 @@ import org.reflections.scanners.MethodParameterScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import eu.geekplace.javapinning.java7.Java7Pinning;
public class SmackIntegrationTestFramework {
private static final Logger LOGGER = Logger.getLogger(SmackIntegrationTestFramework.class.getName());
@ -553,9 +549,8 @@ public class SmackIntegrationTestFramework {
.setResource(middlefix + '-' + testRunResult.testRunId)
.setSecurityMode(config.securityMode);
// @formatter:on
if (StringUtils.isNotEmpty(config.serviceTlsPin)) {
SSLContext sc = Java7Pinning.forPin(config.serviceTlsPin);
builder.setCustomSSLContext(sc);
if (config.tlsContext != null) {
builder.setCustomSSLContext(config.tlsContext);
}
XMPPTCPConnection connection = new XMPPTCPConnection(builder.build());
connection.connect();
@ -581,9 +576,8 @@ public class SmackIntegrationTestFramework {
SmackException, IOException, XMPPException {
Configuration config = environment.configuration;
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
if (config.serviceTlsPin != null) {
SSLContext sc = Java7Pinning.forPin(config.serviceTlsPin);
builder.setCustomSSLContext(sc);
if (config.tlsContext != null) {
builder.setCustomSSLContext(config.tlsContext);
}
builder.setSecurityMode(config.securityMode);
builder.setXmppDomain(config.service);

View file

@ -0,0 +1,105 @@
/**
*
* Copyright 2017 Florian Schmaus
*
* 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.httpfileupload;
import static org.junit.Assert.assertArrayEquals;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.TestNotPossibleException;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest {
private static final int FILE_SIZE = 1024*128;
private final HttpFileUploadManager hfumOne;
public HttpFileUploadIntegrationTest(SmackIntegrationTestEnvironment environment) throws XMPPErrorException,
NotConnectedException, NoResponseException, InterruptedException, TestNotPossibleException {
super(environment);
hfumOne = HttpFileUploadManager.getInstanceFor(conOne);
if (!hfumOne.discoverUploadService()) {
throw new TestNotPossibleException(
"HttpFileUploadManager was unable to discover a HTTP File Upload service");
}
UploadService uploadService = hfumOne.getDefaultUploadService();
if (!uploadService.acceptsFileOfSize(FILE_SIZE)) {
throw new TestNotPossibleException("The upload service at " + uploadService.getAddress()
+ " does not accept files of size " + FILE_SIZE
+ ". It only accepts files with a maximum size of " + uploadService.getMaxFileSize());
}
hfumOne.setTlsContext(environment.configuration.tlsContext);
}
@SmackIntegrationTest
public void httpFileUploadTest() throws FileNotFoundException, IOException, XMPPErrorException, InterruptedException, SmackException {
final int fileSize = FILE_SIZE;
File file = createNewTempFile();
FileOutputStream fos = new FileOutputStream(file.getCanonicalPath());
byte[] upBytes;
try {
upBytes = new byte[fileSize];
INSECURE_RANDOM.nextBytes(upBytes);
fos.write(upBytes);
}
finally {
fos.close();
}
URL getUrl = hfumOne.uploadFile(file, new UploadProgressListener() {
@Override
public void onUploadProgress(long uploadedBytes, long totalBytes) {
double progress = uploadedBytes / totalBytes;
LOGGER.fine("HTTP File Upload progress " + progress + "% (" + uploadedBytes + '/' + totalBytes + ')');
}
});
HttpURLConnection urlConnection = getHttpUrlConnectionFor(getUrl);
ByteArrayOutputStream baos = new ByteArrayOutputStream(fileSize);
byte[] buffer = new byte[4096];
int n;
try {
InputStream is = new BufferedInputStream(urlConnection.getInputStream());
while ((n = is.read(buffer)) != -1) {
baos.write(buffer, 0, n);
}
}
finally {
urlConnection.disconnect();
}
byte[] downBytes = baos.toByteArray();
assertArrayEquals(upBytes, downBytes);
}
}

View file

@ -0,0 +1 @@
../../../../../../../../smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2015 Florian Schmaus
* Copyright 2015-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +16,16 @@
*/
package org.igniterealtime.smack.inttest;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import org.jxmpp.jid.JidTestUtil;
public class SmackIntegrationTestUnitTestUtil {
public static DummySmackIntegrationTestFramework getFrameworkForUnitTest(Class<? extends AbstractSmackIntTest> unitTest) {
public static DummySmackIntegrationTestFramework getFrameworkForUnitTest(
Class<? extends AbstractSmackIntTest> unitTest)
throws KeyManagementException, NoSuchAlgorithmException {
// @formatter:off
Configuration configuration = Configuration.builder()
.setService(JidTestUtil.DOMAIN_BARE_JID_1)