package org.appwork.utils.net.httpconnection;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import org.appwork.utils.Regex;
import org.appwork.utils.encoding.Base64;
public class HTTPProxyHTTPConnectionImpl extends HTTPConnectionImpl {
private int httpPort;
private String httpHost;
private StringBuilder proxyRequest;
private InetSocketAddress proxyInetSocketAddress = null;
private boolean preferConnectMethod = true;
public HTTPProxyHTTPConnectionImpl(final URL url, final HTTPProxy p) {
super(url, p);
this.preferConnectMethod = p.isConnectMethodPrefered();
}
/*
* SSL over HTTP Proxy, see
* http://muffin.doit.org/docs/rfc/tunneling_ssl.html
*/
@Override
public void connect() throws IOException {
if (this.isConnected()) { return;/* oder fehler */
}
try {
if (this.proxy == null || !this.proxy.getType().equals(HTTPProxy.TYPE.HTTP)) { throw new IOException("HTTPProxyHTTPConnection: invalid HTTP Proxy!"); }
if (this.proxy.getPass() != null && this.proxy.getPass().length() > 0 || this.proxy.getUser() != null && this.proxy.getUser().length() > 0) {
/* add proxy auth in case username/pw are set */
final String user = this.proxy.getUser() == null ? "" : this.proxy.getUser();
final String pass = this.proxy.getPass() == null ? "" : this.proxy.getPass();
this.requestProperties.put("Proxy-Authorization", "Basic " + new String(Base64.encodeToByte((user + ":" + pass).getBytes(), false)));
}
final InetAddress hosts[] = this.resolvHostIP(this.proxy.getHost());
IOException ee = null;
long startTime = System.currentTimeMillis();
for (final InetAddress host : hosts) {
this.httpSocket = new Socket();
this.httpSocket.setSoTimeout(this.readTimeout);
try {
/* create and connect to socks5 proxy */
startTime = System.currentTimeMillis();
this.httpSocket.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.httpSocket.close();
} catch (final Throwable e2) {
}
ee = e;
}
}
if (ee != null) { throw new ProxyConnectException(ee, this.proxy); }
this.requestTime = System.currentTimeMillis() - startTime;
if (this.httpURL.getProtocol().startsWith("https") || this.isConnectMethodPrefered()) {
/* ssl via CONNECT method or because we prefer CONNECT */
/* build CONNECT request */
this.proxyRequest = new StringBuilder();
this.proxyRequest.append("CONNECT ");
this.proxyRequest.append(this.httpURL.getHost() + ":" + (this.httpURL.getPort() != -1 ? this.httpURL.getPort() : this.httpURL.getDefaultPort()));
this.proxyRequest.append(" HTTP/1.1\r\n");
if (this.requestProperties.get("User-Agent") != null) {
this.proxyRequest.append("User-Agent: " + this.requestProperties.get("User-Agent") + "\r\n");
}
if (this.requestProperties.get("Host") != null) {
/* use existing host header */
this.proxyRequest.append("Host: " + this.requestProperties.get("Host") + "\r\n");
} else {
/* add host from url as fallback */
this.proxyRequest.append("Host: " + this.httpURL.getHost() + "\r\n");
}
if (this.requestProperties.get("Proxy-Authorization") != null) {
this.proxyRequest.append("Proxy-Authorization: " + this.requestProperties.get("Proxy-Authorization") + "\r\n");
}
this.proxyRequest.append("\r\n");
/* send CONNECT to proxy */
this.httpSocket.getOutputStream().write(this.proxyRequest.toString().getBytes("UTF-8"));
this.httpSocket.getOutputStream().flush();
/* parse CONNECT response */
ByteBuffer header = HTTPConnectionUtils.readheader(this.httpSocket.getInputStream(), true);
byte[] bytes = new byte[header.limit()];
header.get(bytes);
final String proxyResponseStatus = new String(bytes, "ISO-8859-1").trim();
this.proxyRequest.append(proxyResponseStatus + "\r\n");
String proxyCode = null;
if (proxyResponseStatus.startsWith("HTTP")) {
/* parse response code */
proxyCode = new Regex(proxyResponseStatus, "HTTP.*? (\\d+)").getMatch(0);
}
if (!"200".equals(proxyCode)) {
/* something went wrong */
try {
this.httpSocket.close();
} catch (final Throwable nothing) {
}
if ("407".equals(proxyCode)) {
/* auth invalid/missing */
throw new ProxyAuthException(this.proxy);
}
throw new ProxyConnectException(this.proxy);
}
/* read rest of CONNECT headers */
/*
* Again, the response follows the HTTP/1.0 protocol, so the
* response line starts with the protocol version specifier, and
* the response line is followed by zero or more response
* headers, followed by an empty line. The line separator is CR
* LF pair, or a single LF.
*/
while (true) {
/*
* read line by line until we reach the single empty line as
* seperator
*/
header = HTTPConnectionUtils.readheader(this.httpSocket.getInputStream(), true);
if (header.limit() <= 2) {
/* empty line, <=2, as it may contains \r and/or \n */
break;
}
bytes = new byte[header.limit()];
header.get(bytes);
final String temp = new String(bytes, "UTF-8").trim();
this.proxyRequest.append(temp + "\r\n");
}
this.httpPort = this.httpURL.getPort();
this.httpHost = this.httpURL.getHost();
if (this.httpPort == -1) {
this.httpPort = this.httpURL.getDefaultPort();
}
if (this.httpURL.getProtocol().startsWith("https")) {
SSLSocket sslSocket = null;
try {
final SSLSocketFactory socketFactory = TrustALLSSLFactory.getSSLFactoryTrustALL();
sslSocket = (SSLSocket) socketFactory.createSocket(this.httpSocket, this.httpHost, this.httpPort, true);
sslSocket.startHandshake();
} catch (final SSLHandshakeException e) {
try {
sslSocket.close();
} catch (final Throwable e3) {
}
try {
this.httpSocket.close();
} catch (final Throwable e2) {
}
throw new IOException("HTTPProxyHTTPConnection: " + e.toString());
}
this.httpSocket = sslSocket;
}
/* httpPath needs to be like normal http request, eg /index.html */
this.httpPath = new org.appwork.utils.Regex(this.httpURL.toString(), "https?://.*?(/.+)").getMatch(0);
if (this.httpPath == null) {
this.httpPath = "/";
}
} else {
/* direct connect via proxy */
/*
* httpPath needs to include complete path here, eg
* http://google.de/
*/
this.proxyRequest = new StringBuilder("DIRECT\r\n");
this.httpPath = this.httpURL.toString();
}
/* 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 InputStream getInputStream() throws IOException {
this.connect();
this.connectInputStream();
if (this.getResponseCode() == 407) {
/* auth invalid/missing */
throw new ProxyAuthException(this.proxy);
}
if (this.getResponseCode() == 504) { throw new ConnectException(this.getResponseCode() + " " + this.getResponseMessage()); }
return super.getInputStream();
}
@Override
protected String getRequestInfo() {
if (this.proxyRequest != null) {
final StringBuilder sb = new StringBuilder();
sb.append("-->HTTPProxy:").append(this.proxy.getHost() + ":" + this.proxy.getPort()).append("\r\n");
if (this.proxyInetSocketAddress != null && this.proxyInetSocketAddress.getAddress() != null) {
sb.append("-->HTTPProxyIP:").append(this.proxyInetSocketAddress.getAddress().getHostAddress()).append("\r\n");
}
sb.append("----------------CONNECTRequest(HTTP)------------\r\n");
sb.append(this.proxyRequest.toString());
sb.append("------------------------------------------------\r\n");
sb.append(super.getRequestInfo());
return sb.toString();
}
return super.getRequestInfo();
}
public boolean isConnectMethodPrefered() {
return this.preferConnectMethod;
}
public void setPreferConnectMethod(final boolean preferConnectMethod) {
this.preferConnectMethod = preferConnectMethod;
}
}