1da205a749fadb3a87357d9bd607f094c7717764aJason Monk/** 2da205a749fadb3a87357d9bd607f094c7717764aJason Monk * Copyright (c) 2013, The Android Open Source Project 3da205a749fadb3a87357d9bd607f094c7717764aJason Monk * 4da205a749fadb3a87357d9bd607f094c7717764aJason Monk * Licensed under the Apache License, Version 2.0 (the "License"); 5da205a749fadb3a87357d9bd607f094c7717764aJason Monk * you may not use this file except in compliance with the License. 6da205a749fadb3a87357d9bd607f094c7717764aJason Monk * You may obtain a copy of the License at 7da205a749fadb3a87357d9bd607f094c7717764aJason Monk * 8da205a749fadb3a87357d9bd607f094c7717764aJason Monk * http://www.apache.org/licenses/LICENSE-2.0 9da205a749fadb3a87357d9bd607f094c7717764aJason Monk * 10da205a749fadb3a87357d9bd607f094c7717764aJason Monk * Unless required by applicable law or agreed to in writing, software 11da205a749fadb3a87357d9bd607f094c7717764aJason Monk * distributed under the License is distributed on an "AS IS" BASIS, 12da205a749fadb3a87357d9bd607f094c7717764aJason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13da205a749fadb3a87357d9bd607f094c7717764aJason Monk * See the License for the specific language governing permissions and 14da205a749fadb3a87357d9bd607f094c7717764aJason Monk * limitations under the License. 15da205a749fadb3a87357d9bd607f094c7717764aJason Monk */ 16602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkpackage com.android.proxyhandler; 17602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 186f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monkimport android.os.RemoteException; 19602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.util.Log; 20602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 216f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monkimport com.android.net.IProxyPortListener; 22602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport com.google.android.collect.Lists; 231e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikovimport com.google.android.collect.Sets; 24602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 25602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.io.IOException; 26602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.io.InputStream; 27602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.io.OutputStream; 28602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.InetSocketAddress; 29602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.Proxy; 30602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.ProxySelector; 31602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.ServerSocket; 32602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.Socket; 33602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.SocketException; 34602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.URI; 35602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.URISyntaxException; 36602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.util.List; 371e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikovimport java.util.Set; 38602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.util.concurrent.ExecutorService; 39602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.util.concurrent.Executors; 40602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 41602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk/** 42602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk * @hide 43602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk */ 44602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkpublic class ProxyServer extends Thread { 45602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 46602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private static final String CONNECT = "CONNECT"; 47602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private static final String HTTP_OK = "HTTP/1.1 200 OK\n"; 48602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 49602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private static final String TAG = "ProxyServer"; 50602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 511e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // HTTP Headers 521e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov private static final String HEADER_CONNECTION = "connection"; 531e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov private static final String HEADER_PROXY_CONNECTION = "proxy-connection"; 541e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 55602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private ExecutorService threadExecutor; 56602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 57602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk public boolean mIsRunning = false; 58602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 59602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private ServerSocket serverSocket; 606f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk private int mPort; 616f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk private IProxyPortListener mCallback; 62602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 63602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private class ProxyConnection implements Runnable { 64602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private Socket connection; 65602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 66602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private ProxyConnection(Socket connection) { 67602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk this.connection = connection; 68602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 69602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 70602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk @Override 71602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk public void run() { 72602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk try { 73602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk String requestLine = getLine(connection.getInputStream()); 74602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk String[] splitLine = requestLine.split(" "); 75602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (splitLine.length < 3) { 76602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk connection.close(); 77602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk return; 78602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 79602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk String requestType = splitLine[0]; 80602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk String urlString = splitLine[1]; 811e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String httpVersion = splitLine[2]; 82602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 831e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov URI url = null; 841e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String host; 851e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov int port; 86602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 87602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (requestType.equals(CONNECT)) { 88602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk String[] hostPortSplit = urlString.split(":"); 89602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk host = hostPortSplit[0]; 901e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // Use default SSL port if not specified. Parse it otherwise 911e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (hostPortSplit.length < 2) { 92602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk port = 443; 931e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } else { 941e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov try { 951e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov port = Integer.parseInt(hostPortSplit[1]); 961e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } catch (NumberFormatException nfe) { 971e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov connection.close(); 981e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov return; 991e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 100602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 101602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk urlString = "Https://" + host + ":" + port; 102602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } else { 103602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk try { 1041e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov url = new URI(urlString); 105602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk host = url.getHost(); 106602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk port = url.getPort(); 107602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (port < 0) { 108602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk port = 80; 109602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 110602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } catch (URISyntaxException e) { 111602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk connection.close(); 112602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk return; 113602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 114602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 115602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 116602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk List<Proxy> list = Lists.newArrayList(); 117602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk try { 118602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk list = ProxySelector.getDefault().select(new URI(urlString)); 119602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } catch (URISyntaxException e) { 120602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk e.printStackTrace(); 121602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 122602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk Socket server = null; 123602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk for (Proxy proxy : list) { 124602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk try { 125602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (!proxy.equals(Proxy.NO_PROXY)) { 126602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk // Only Inets created by PacProxySelector. 127602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk InetSocketAddress inetSocketAddress = 128179d6e8e2067c380d3c1f7a1a26877b81a9240cbJason Monk (InetSocketAddress)proxy.address(); 129179d6e8e2067c380d3c1f7a1a26877b81a9240cbJason Monk server = new Socket(inetSocketAddress.getHostName(), 130602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk inetSocketAddress.getPort()); 131602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk sendLine(server, requestLine); 132602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } else { 133602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk server = new Socket(host, port); 134602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (requestType.equals(CONNECT)) { 1351e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov skipToRequestBody(connection); 136602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk // No proxy to respond so we must. 137602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk sendLine(connection, HTTP_OK); 138602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } else { 1391e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // Proxying the request directly to the origin server. 1401e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov sendAugmentedRequestToHost(connection, server, 1411e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov requestType, url, httpVersion); 142602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 143602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 144602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } catch (IOException ioe) { 1451e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (Log.isLoggable(TAG, Log.VERBOSE)) { 1461e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov Log.v(TAG, "Unable to connect to proxy " + proxy, ioe); 1471e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 148602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 149602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (server != null) { 150602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk break; 151602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 152602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 1531e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (list.isEmpty()) { 154602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk server = new Socket(host, port); 155602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (requestType.equals(CONNECT)) { 1561e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov skipToRequestBody(connection); 157602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk // No proxy to respond so we must. 158602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk sendLine(connection, HTTP_OK); 159602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } else { 1601e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // Proxying the request directly to the origin server. 1611e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov sendAugmentedRequestToHost(connection, server, 1621e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov requestType, url, httpVersion); 163602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 164602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 165602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk // Pass data back and forth until complete. 1661e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (server != null) { 1671e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov SocketConnect.connect(connection, server); 1681e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 1691e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } catch (Exception e) { 170602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk Log.d(TAG, "Problem Proxying", e); 171602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 172602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk try { 173602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk connection.close(); 174602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } catch (IOException ioe) { 1751e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // Do nothing 1761e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 1771e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 1781e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 1791e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov /** 1801e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * Sends HTTP request-line (i.e. the first line in the request) 1811e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * that contains absolute path of a given absolute URI. 1821e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * 1831e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param server server to send the request to. 1841e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param requestType type of the request, a.k.a. HTTP method. 1851e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param absoluteUri absolute URI which absolute path should be extracted. 1861e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param httpVersion version of HTTP, e.g. HTTP/1.1. 1871e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @throws IOException if the request-line cannot be sent. 1881e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov */ 1891e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov private void sendRequestLineWithPath(Socket server, String requestType, 1901e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov URI absoluteUri, String httpVersion) throws IOException { 1911e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 1921e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String absolutePath = getAbsolutePathFromAbsoluteURI(absoluteUri); 1931e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String outgoingRequestLine = String.format("%s %s %s", 1941e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov requestType, absolutePath, httpVersion); 1951e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov sendLine(server, outgoingRequestLine); 1961e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 197602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 1981e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov /** 1991e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * Extracts absolute path form a given URI. E.g., passing 2001e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * <code>http://google.com:80/execute?query=cat#top</code> 2011e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * will result in <code>/execute?query=cat#top</code>. 2021e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * 2031e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param uri URI which absolute path has to be extracted, 2041e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @return the absolute path of the URI, 2051e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov */ 2061e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov private String getAbsolutePathFromAbsoluteURI(URI uri) { 2071e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String rawPath = uri.getRawPath(); 2081e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String rawQuery = uri.getRawQuery(); 2091e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String rawFragment = uri.getRawFragment(); 2101e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov StringBuilder absolutePath = new StringBuilder(); 2111e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 2121e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (rawPath != null) { 2131e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov absolutePath.append(rawPath); 2141e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } else { 2151e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov absolutePath.append("/"); 2161e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 2171e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (rawQuery != null) { 2181e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov absolutePath.append("?").append(rawQuery); 2191e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 2201e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (rawFragment != null) { 2211e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov absolutePath.append("#").append(rawFragment); 222602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 2231e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov return absolutePath.toString(); 224602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 225602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 226602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private String getLine(InputStream inputStream) throws IOException { 2271e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov StringBuilder buffer = new StringBuilder(); 228602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk int byteBuffer = inputStream.read(); 229602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (byteBuffer < 0) return ""; 230602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk do { 231602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (byteBuffer != '\r') { 232602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk buffer.append((char)byteBuffer); 233602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 234602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk byteBuffer = inputStream.read(); 235602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } while ((byteBuffer != '\n') && (byteBuffer >= 0)); 236602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 237602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk return buffer.toString(); 238602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 239602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 240602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk private void sendLine(Socket socket, String line) throws IOException { 241602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk OutputStream os = socket.getOutputStream(); 242602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk os.write(line.getBytes()); 243602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk os.write('\r'); 244602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk os.write('\n'); 245602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk os.flush(); 246602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 2471e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 2481e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov /** 2491e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * Reads from socket until an empty line is read which indicates the end of HTTP headers. 2501e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * 2511e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param socket socket to read from. 2521e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @throws IOException if an exception took place during the socket read. 2531e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov */ 2541e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov private void skipToRequestBody(Socket socket) throws IOException { 2551e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov while (getLine(socket.getInputStream()).length() != 0); 2561e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 2571e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 2581e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov /** 2591e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * Sends an augmented request to the final host (DIRECT connection). 2601e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * 2611e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param src socket to read HTTP headers from.The socket current position should point 2621e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * to the beginning of the HTTP header section. 2631e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param dst socket to write the augmented request to. 2641e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param httpMethod original request http method. 2651e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param uri original request absolute URI. 2661e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param httpVersion original request http version. 2671e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @throws IOException if an exception took place during socket reads or writes. 2681e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov */ 2691e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov private void sendAugmentedRequestToHost(Socket src, Socket dst, 2701e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String httpMethod, URI uri, String httpVersion) throws IOException { 2711e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 2721e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov sendRequestLineWithPath(dst, httpMethod, uri, httpVersion); 2731e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov filterAndForwardRequestHeaders(src, dst); 2741e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 2751e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // Currently the proxy does not support keep-alive connections; therefore, 2761e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // the proxy has to request the destination server to close the connection 2771e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // after the destination server sent the response. 2781e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov sendLine(dst, "Connection: close"); 2791e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 2801e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // Sends and empty line that indicates termination of the header section. 2811e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov sendLine(dst, ""); 2821e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 2831e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 2841e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov /** 2851e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * Forwards original request headers filtering out the ones that have to be removed. 2861e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * 2871e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param src source socket that contains original request headers. 2881e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param dst destination socket to send the filtered headers to. 2891e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @throws IOException if the data cannot be read from or written to the sockets. 2901e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov */ 2911e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov private void filterAndForwardRequestHeaders(Socket src, Socket dst) throws IOException { 2921e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String line; 2931e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov do { 2941e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov line = getLine(src.getInputStream()); 2951e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (line.length() > 0 && !shouldRemoveHeaderLine(line)) { 2961e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov sendLine(dst, line); 2971e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 2981e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } while (line.length() > 0); 2991e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 3001e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 3011e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov /** 3021e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * Returns true if a given header line has to be removed from the original request. 3031e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * 3041e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @param line header line that should be analysed. 3051e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov * @return true if the header line should be removed and not forwarded to the destination. 3061e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov */ 3071e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov private boolean shouldRemoveHeaderLine(String line) { 3081e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov int colIndex = line.indexOf(":"); 3091e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (colIndex != -1) { 3101e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov String headerName = line.substring(0, colIndex).trim(); 3111e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (headerName.regionMatches(true, 0, HEADER_CONNECTION, 0, 3121e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov HEADER_CONNECTION.length()) 3131e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov || headerName.regionMatches(true, 0, HEADER_PROXY_CONNECTION, 3141e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov 0, HEADER_PROXY_CONNECTION.length())) { 3151e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov return true; 3161e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 3171e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 3181e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov return false; 3191e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } 320602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 321602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 322602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk public ProxyServer() { 323602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk threadExecutor = Executors.newCachedThreadPool(); 3246f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk mPort = -1; 3256f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk mCallback = null; 326602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 327602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 328602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk @Override 329602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk public void run() { 330602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk try { 3316f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk serverSocket = new ServerSocket(0); 332602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 3331e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov setPort(serverSocket.getLocalPort()); 334602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 3351e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov while (mIsRunning) { 3361e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov try { 3371e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov Socket socket = serverSocket.accept(); 3381e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov // Only receive local connections. 3391e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov if (socket.getInetAddress().isLoopbackAddress()) { 3401e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov ProxyConnection parser = new ProxyConnection(socket); 341602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 3421e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov threadExecutor.execute(parser); 3431e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } else { 3441e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov socket.close(); 3456f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 3461e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov } catch (IOException e) { 3471e64ab3e56f6efafc25301df5da21d69bb75b470Andrei Kapishnikov e.printStackTrace(); 348602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 349602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 350602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } catch (SocketException e) { 3516f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk Log.e(TAG, "Failed to start proxy server", e); 3526f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } catch (IOException e1) { 3536f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk Log.e(TAG, "Failed to start proxy server", e1); 354602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 355602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 356602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk mIsRunning = false; 357602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 358602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 3596f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk public synchronized void setPort(int port) { 3606f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk if (mCallback != null) { 3616f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk try { 3626f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk mCallback.setProxyPort(port); 3636f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } catch (RemoteException e) { 3646f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk Log.w(TAG, "Proxy failed to report port to PacManager", e); 3656f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 3666f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 3676f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk mPort = port; 3686f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 3696f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk 3706f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk public synchronized void setCallback(IProxyPortListener callback) { 3716f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk if (mPort != -1) { 3726f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk try { 3736f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk callback.setProxyPort(mPort); 3746f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } catch (RemoteException e) { 3756f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk Log.w(TAG, "Proxy failed to report port to PacManager", e); 3766f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 3776f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 3786f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk mCallback = callback; 3796f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 3806f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk 381602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk public synchronized void startServer() { 382602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk mIsRunning = true; 383602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk start(); 384602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 385602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk 386602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk public synchronized void stopServer() { 387602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk mIsRunning = false; 388602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk if (serverSocket != null) { 389602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk try { 390602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk serverSocket.close(); 391602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk serverSocket = null; 392602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } catch (IOException e) { 393602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk e.printStackTrace(); 394602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 395602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 396602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk } 3976f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk 3986f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk public boolean isBound() { 3996f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk return (mPort != -1); 4006f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 4016f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk 4026f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk public int getPort() { 4036f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk return mPort; 4046f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk } 405602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk} 406