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; import org.appwork.utils.net.CountingOutputStream; public class HTTPProxyHTTPConnectionImpl extends HTTPConnectionImpl { private int httpPort; private String httpHost; private StringBuilder proxyRequest; public HTTPProxyHTTPConnectionImpl(final URL url, final HTTPProxy p) { super(url, p); } /* * SSL over HTTP Proxy, see * http://muffin.doit.org/docs/rfc/tunneling_ssl.html */ @Override public void connect() throws IOException { 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))); } if (this.isConnected()) { return; } this.httpSocket = new Socket(); this.httpSocket.setSoTimeout(this.readTimeout); this.httpResponseCode = -1; final InetAddress host = InetAddress.getByName(this.proxy.getHost()); final long startTime = System.currentTimeMillis(); try { this.httpSocket.connect(new InetSocketAddress(host, this.proxy.getPort()), this.connectTimeout); } catch (final IOException e) { this.proxy.setStatus(HTTPProxy.STATUS.OFFLINE); throw new ProxyConnectException(e.getMessage()); } this.requestTime = System.currentTimeMillis() - startTime; if (this.httpURL.getProtocol().startsWith("https")) { /* ssl via CONNECT method */ /* 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) { this.proxyRequest.append("Host: " + this.requestProperties.get("Host") + "\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 */ this.proxy.setStatus(HTTPProxy.STATUS.INVALIDAUTH); throw new ProxyAuthException(); } throw new ProxyConnectException("CONNECT seems not supported:" + proxyResponseStatus); } /* 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.position() == 0) { /* empty line */ break; } bytes = new byte[header.limit()]; header.get(bytes); final String temp = new String(bytes, "UTF-8"); this.proxyRequest.append(temp); } SSLSocket sslSocket = null; this.httpPort = this.httpURL.getPort(); this.httpHost = this.httpURL.getHost(); if (this.httpPort == -1) { this.httpPort = this.httpURL.getDefaultPort(); } try { final SSLSocketFactory socketFactory = TrustALLSSLFactory.getSSLFactoryTrustALL(); sslSocket = (SSLSocket) socketFactory.createSocket(this.httpSocket, this.httpHost, this.httpPort, true); sslSocket.startHandshake(); } catch (final SSLHandshakeException e) { try { this.httpSocket.close(); } catch (final Throwable e2) { } throw new IOException("HTTPProxyHTTPConnection: " + e, e); } 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.httpPath = this.httpURL.toString(); } /* now send Request */ final StringBuilder sb = new StringBuilder(); sb.append(this.httpMethod.name()).append(' ').append(this.httpPath).append(" HTTP/1.1\r\n"); for (final String key : this.requestProperties.keySet()) { if (this.requestProperties.get(key) == null) { continue; } sb.append(key).append(": ").append(this.requestProperties.get(key)).append("\r\n"); } sb.append("\r\n"); this.httpSocket.getOutputStream().write(sb.toString().getBytes("UTF-8")); this.httpSocket.getOutputStream().flush(); if (this.httpMethod != RequestMethod.POST) { this.outputStream = this.httpSocket.getOutputStream(); this.outputClosed = true; this.connectInputStream(); } else { this.outputStream = new CountingOutputStream(this.httpSocket.getOutputStream()); } } @Override public InputStream getInputStream() throws IOException { this.connect(); this.connectInputStream(); if (this.getResponseCode() == 407) { /* auth invalid/missing */ this.proxy.setStatus(HTTPProxy.STATUS.INVALIDAUTH); throw new ProxyAuthException(); } 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"); sb.append("----------------CONNECTRequest------------------\r\n"); sb.append(this.proxyRequest.toString()); sb.append(super.getRequestInfo()); return sb.toString(); } return super.getRequestInfo(); } }