/**
* Copyright (c) 2009 - 2012 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org>
*
* This file is part of org.appwork.utils.net.httpconnection
*
* This software is licensed under the Artistic License 2.0,
* see the LICENSE file or http://www.opensource.org/licenses/artistic-license-2.0.php
* for details
*/
package org.appwork.utils.net.httpconnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
/**
* @author daniel
*
*/
public abstract class SocksHTTPconnection extends HTTPConnectionImpl {
protected static enum AUTH {
PLAIN,
NONE
}
protected Socket sockssocket = null;
protected InputStream socksinputstream = null;
protected OutputStream socksoutputstream = null;
protected int httpPort;
protected String httpHost;
protected StringBuilder proxyRequest = null;
protected InetSocketAddress proxyInetSocketAddress = null;
public SocksHTTPconnection(final URL url, final HTTPProxy proxy) {
super(url, proxy);
}
abstract protected void authenticateProxyPlain() throws IOException;
@Override
public void connect() throws IOException {
if (this.isConnected()) { return;/* oder fehler */
}
try {
this.validateProxy();
final InetAddress hosts[] = this.resolvHostIP(this.proxy.getHost());
IOException ee = null;
long startTime = System.currentTimeMillis();
for (final InetAddress host : hosts) {
this.sockssocket = new Socket();
this.sockssocket.setSoTimeout(this.readTimeout);
try {
/* create and connect to socks5 proxy */
startTime = System.currentTimeMillis();
this.sockssocket.connect(this.proxyInetSocketAddress = new InetSocketAddress(host, this.proxy.getPort()), this.connectTimeout);
/* connection is okay */
ee = null;
break;
} catch (final IOException e) {
/* connection failed, try next available ip */
this.proxyInetSocketAddress = null;
try {
this.sockssocket.close();
} catch (final Throwable e2) {
}
ee = e;
}
}
if (ee != null) { throw new ProxyConnectException(ee, this.proxy); }
this.socksinputstream = this.sockssocket.getInputStream();
this.socksoutputstream = this.sockssocket.getOutputStream();
/* establish connection to socks */
this.proxyRequest = new StringBuilder();
final AUTH auth = this.sayHello();
switch (auth) {
case PLAIN:
this.proxyRequest.append("<-PLAIN AUTH\r\n");
/* username/password authentication */
this.authenticateProxyPlain();
break;
case NONE:
this.proxyRequest.append("<-NONE AUTH\r\n");
break;
}
/* establish to destination through socks */
this.httpPort = this.httpURL.getPort();
this.httpHost = this.httpURL.getHost();
if (this.httpPort == -1) {
this.httpPort = this.httpURL.getDefaultPort();
}
final Socket establishedConnection = this.establishConnection();
if (this.httpURL.getProtocol().startsWith("https")) {
/* we need to lay ssl over normal socks5 connection */
SSLSocket sslSocket = null;
try {
final SSLSocketFactory socketFactory = TrustALLSSLFactory.getSSLFactoryTrustALL();
sslSocket = (SSLSocket) socketFactory.createSocket(establishedConnection, this.httpHost, this.httpPort, true);
sslSocket.startHandshake();
} catch (final SSLHandshakeException e) {
try {
this.sockssocket.close();
} catch (final Throwable e2) {
}
throw new ProxyConnectException(e, this.proxy);
}
this.httpSocket = sslSocket;
} else {
/* we can continue to use the socks connection */
this.httpSocket = establishedConnection;
}
this.httpResponseCode = -1;
this.requestTime = System.currentTimeMillis() - startTime;
this.httpPath = new org.appwork.utils.Regex(this.httpURL.toString(), "https?://.*?(/.+)").getMatch(0);
if (this.httpPath == null) {
this.httpPath = "/";
}
/* now send Request */
this.sendRequest();
} catch (final IOException e) {
try {
this.disconnect();
} catch (final Throwable e2) {
}
if (e instanceof HTTPProxyException) { throw e; }
throw new ProxyConnectException(e, this.proxy);
}
}
@Override
public void disconnect() {
super.disconnect();
try {
this.sockssocket.close();
} catch (final Throwable e) {
}
}
abstract protected Socket establishConnection() throws IOException;
@Override
protected String getRequestInfo() {
if (this.proxyRequest != null) {
final StringBuilder sb = new StringBuilder();
final String type = this.proxy.getType().name();
sb.append("-->" + type + ":").append(this.proxy.getHost() + ":" + this.proxy.getPort()).append("\r\n");
if (this.proxyInetSocketAddress != null && this.proxyInetSocketAddress.getAddress() != null) {
sb.append("-->" + type + "IP:").append(this.proxyInetSocketAddress.getAddress().getHostAddress()).append("\r\n");
}
sb.append("----------------CONNECTRequest(" + type + ")----------\r\n");
sb.append(this.proxyRequest.toString());
sb.append("------------------------------------------------\r\n");
sb.append(super.getRequestInfo());
return sb.toString();
}
return super.getRequestInfo();
}
/* reads response with expLength bytes */
protected byte[] readResponse(final int expLength) throws IOException {
final byte[] response = new byte[expLength];
int index = 0;
int read = 0;
while (index < expLength && (read = this.socksinputstream.read()) != -1) {
response[index] = (byte) read;
index++;
}
if (index < expLength) { throw new IOException("SocksHTTPConnection: not enough data read"); }
return response;
}
abstract protected AUTH sayHello() throws IOException;
abstract protected void validateProxy() throws IOException;
}