Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 37 additions & 24 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,31 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.notnoop.apns</groupId>
<artifactId>apns</artifactId>
<version>1.0.0.Beta7-SNAPSHOT</version>
<version>1.0.0.Beta7-EC-3.2.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Java Apple Push Notification Service Library</name>

<!-- Inherit the Sonatype OSS deployment configuration -->
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>9</version>
</parent>

<scm>
<connection>scm:git:git://github.com/notnoop/java-apns.git</connection>
<developerConnection>scm:git:[email protected]:notnoop/java-apns.git</developerConnection>
<url>http://github.com/notnoop/java-apns</url>
<tag>HEAD</tag>
<connection>scm:git:git://github.com/encapsecurity/java-apns.git</connection>
<developerConnection>scm:git:[email protected]:encapsecurity/java-apns.git</developerConnection>
<url>http://github.com/encapsecurity/java-apns</url>
<tag>apns-1.0.0.Beta7-EC-3.2.0</tag>
</scm>

<!-- DISTRIBUTION AND DEPLOYMENT -->
<distributionManagement>
<repository>
<id>encap-release-repo</id>
<url>https://test.encap.no/repository/content/repositories/releases</url>
<uniqueVersion>true</uniqueVersion>
</repository>
<snapshotRepository>
<id>encap-snapshot-repo</id>
<url>https://test.encap.no/repository/content/repositories/snapshots</url>
<uniqueVersion>true</uniqueVersion>
</snapshotRepository>
</distributionManagement>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Expand Down Expand Up @@ -66,7 +73,7 @@
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
Expand All @@ -78,14 +85,14 @@
</execution>
</executions>
</plugin>
<plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<!--
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
-->
<!--
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
-->
<executions>
<execution>
<id>generate-javadoc</id>
Expand Down Expand Up @@ -125,9 +132,9 @@
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goals>
<goal>manifest</goal>
</goals>
</goals>
</execution>
</executions>
<configuration>
Expand Down Expand Up @@ -260,9 +267,9 @@
-->
<dependencies>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -307,6 +314,12 @@
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.littleshoot</groupId>
<artifactId>littleproxy</artifactId>
<version>1.1.0-beta1</version>
<scope>test</scope>
</dependency>
</dependencies>

<!-- Site Generation -->
Expand Down
77 changes: 27 additions & 50 deletions src/main/java/com/notnoop/apns/internal/TlsTunnelBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,19 @@
import java.net.ProtocolException;
import java.net.Proxy;
import java.net.Socket;

import javax.net.ssl.SSLSocketFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.httpclient.ConnectMethod;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.ProxyClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;

import org.apache.http.HttpHost;
import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.ProxyClient;
import org.apache.http.impl.execchain.TunnelRefusedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/**
* Establishes a TLS connection using an HTTP proxy. See <a
* href="http://www.ietf.org/rfc/rfc2817.txt">RFC 2817 5.2</a>. This class does
Expand Down Expand Up @@ -87,27 +90,26 @@ Socket makeTunnel(String host, int port, String proxyUsername,
logger.debug("Creating socket for Proxy : " + proxyAddress.getAddress() + ":" + proxyAddress.getPort());
Socket socket;
try {
ProxyClient client = new ProxyClient();
client.getParams().setParameter("http.useragent", "java-apns");
client.getHostConfiguration().setHost(host, port);
String proxyHost = proxyAddress.getAddress().toString().substring(0, proxyAddress.getAddress().toString().indexOf("/"));
client.getHostConfiguration().setProxy(proxyHost, proxyAddress.getPort());

HttpHost proxyHttpHost = new HttpHost(proxyHost, proxyAddress.getPort());
HttpHost destHttpHost = new HttpHost(host, port);
ProxyClient proxyClient = new ProxyClient();


ProxyClient.ConnectResponse response = client.connect();
socket = response.getSocket();
if (socket == null) {
ConnectMethod method = response.getConnectMethod();
// Read the proxy's HTTP response.
if(method.getStatusLine().getStatusCode() == 407) {
// Proxy server returned 407. We will now try to connect with auth Header
if(proxyUsername != null && proxyPassword != null) {
socket = AuthenticateProxy(method, client,proxyHost, proxyAddress.getPort(),
proxyUsername, proxyPassword);
} else {
throw new ProtocolException("Socket not created: " + method.getStatusLine());
}
}
if (proxyUsername == null || proxyPassword == null) {
// Empty credentials created to fulfill api contract in case of proxy without credentials
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("", "");
socket = proxyClient.tunnel(proxyHttpHost, destHttpHost, credentials);
} else {
try {
UsernamePasswordCredentials cred = new UsernamePasswordCredentials(proxyUsername, proxyPassword);
socket = proxyClient.tunnel(proxyHttpHost, destHttpHost, cred);
} catch (TunnelRefusedException e) {
// To support NT credentials we try to connect once more
// Would be nicer to move the decision of which credentials to use
NTCredentials credentials = new NTCredentials(proxyUsername, proxyPassword, null, null);
socket = proxyClient.tunnel(proxyHttpHost, destHttpHost, credentials);
}
}

} catch (Exception e) {
Expand All @@ -119,29 +121,4 @@ Socket makeTunnel(String host, int port, String proxyUsername,
return socket;
}

private Socket AuthenticateProxy(ConnectMethod method, ProxyClient client,
String proxyHost, int proxyPort,
String proxyUsername, String proxyPassword) throws IOException {
if("ntlm".equalsIgnoreCase(method.getProxyAuthState().getAuthScheme().getSchemeName())) {
// If Auth scheme is NTLM, set NT credentials with blank host and domain name
client.getState().setProxyCredentials(new AuthScope(proxyHost, proxyPort),
new NTCredentials(proxyUsername, proxyPassword,"",""));
} else {
// If Auth scheme is Basic/Digest, set regular Credentials
client.getState().setProxyCredentials(new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUsername, proxyPassword));
}

ProxyClient.ConnectResponse response = client.connect();
Socket socket = response.getSocket();

if (socket == null) {
method = response.getConnectMethod();
throw new ProtocolException("Proxy Authentication failed. Socket not created: "
+ method.getStatusLine());
}
return socket;
}

}

122 changes: 92 additions & 30 deletions src/test/java/com/notnoop/apns/internal/TlsTunnelBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,48 +30,110 @@
*/
package com.notnoop.apns.internal;

import java.io.ByteArrayInputStream;
import static org.junit.Assert.assertNotNull;

import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.InputStream;
//import java.net.InetSocketAddress;
//import java.net.Proxy;
//import java.net.Socket;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.UnknownHostException;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.fail;
import org.littleshoot.proxy.HttpProxyServer;
import org.littleshoot.proxy.ProxyAuthenticator;
import org.littleshoot.proxy.impl.DefaultHttpProxyServer;

public class TlsTunnelBuilderTest {

private static final String PROXY_USERNAME = "proxy-username";
private static final String PROXY_PASSWORD = "proxy-password";
private TestHttpService testHttpService;
private String remoteHost;
private int remoteport = 9999;
private int localport = 8888;
private HttpProxyServer server;

@Before
public void setUp() throws UnknownHostException {

InetSocketAddress address = new InetSocketAddress(remoteport);
testHttpService = new TestHttpService(address);
remoteHost = address.getHostName();

server = DefaultHttpProxyServer.bootstrap()
.withProxyAuthenticator(new ProxyAuthenticator() {
@Override
public boolean authenticate(String userName, String password) {
return PROXY_USERNAME.equals(userName) && PROXY_PASSWORD.equals(password);
}
})
.withPort(localport)
.start();
}

@After
public void tearDown() {
testHttpService.stop();
server.stop();
}

@Test
public void makeTunnelSuccess() throws IOException {
/* Uncomment this test to verify with your proxy settings */
/*try {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.mydomain.com", 8080));

InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
Socket proxySocket = new Socket(proxyAddress.getAddress(), proxyAddress.getPort());
InetSocketAddress destAddress = new InetSocketAddress("myhost.com", 2195);

new TlsTunnelBuilder().makeTunnel(destAddress.getAddress().toString(),
destAddress.getPort(),
"proxy-username", "proxy-password",
proxyAddress);
} catch (IOException ex){
fail();
}*/
/* Change host in this test to verify with your proxy settings */

Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", localport));

InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
Socket proxySocket = new Socket(proxyAddress.getAddress(), proxyAddress.getPort());
assertNotNull(proxySocket);
proxySocket.close();

InetSocketAddress destAddress = new InetSocketAddress(remoteHost, remoteport);

Socket remoteAddress = new Socket(destAddress.getAddress(), destAddress.getPort());
assertNotNull(remoteAddress);
remoteAddress.close();

Socket socket = new TlsTunnelBuilder()
.makeTunnel(
remoteHost,
remoteport,
PROXY_USERNAME,
PROXY_PASSWORD,
proxyAddress);

assertNotNull(socket);
}

@Test
@Test(expected = IOException.class)
public void invalidProxyParams() throws IOException {
try {
new TlsTunnelBuilder().makeTunnel("origin.example.com", 9876, null, null, null);
fail();
} catch (IOException expected) {
// No operation
}
new TlsTunnelBuilder().makeTunnel("origin.example.com", 9876, null, null, null);
}

private InputStream inputStream(String content) throws IOException {
return new ByteArrayInputStream(content.getBytes("UTF-8"));

public class TestHttpService {

private HttpServer server;

public TestHttpService(InetSocketAddress inetSocketAddress) {
try {
server = HttpServer.create(inetSocketAddress, 0);
server.createContext("/");
server.setExecutor(null);
server.start();
} catch (IOException e) {
throw new IllegalStateException("Could not get ip to localhost", e);
}
}

public void stop() {
server.stop(0);
}

}

}