| /** |
| * Copyright (c) 2013, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.proxyhandler; |
| |
| import android.os.RemoteException; |
| import android.util.Log; |
| |
| import com.android.net.IProxyPortListener; |
| import com.google.android.collect.Lists; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.InetSocketAddress; |
| import java.net.Proxy; |
| import java.net.ProxySelector; |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| import java.net.SocketException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.List; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| |
| /** |
| * @hide |
| */ |
| public class ProxyServer extends Thread { |
| |
| private static final String CONNECT = "CONNECT"; |
| private static final String HTTP_OK = "HTTP/1.1 200 OK\n"; |
| |
| private static final String TAG = "ProxyServer"; |
| |
| private ExecutorService threadExecutor; |
| |
| public boolean mIsRunning = false; |
| |
| private ServerSocket serverSocket; |
| private int mPort; |
| private IProxyPortListener mCallback; |
| |
| private class ProxyConnection implements Runnable { |
| private Socket connection; |
| |
| private ProxyConnection(Socket connection) { |
| this.connection = connection; |
| } |
| |
| @Override |
| public void run() { |
| try { |
| String requestLine = getLine(connection.getInputStream()); |
| if (requestLine == null) { |
| connection.close(); |
| return; |
| } |
| String[] splitLine = requestLine.split(" "); |
| if (splitLine.length < 3) { |
| connection.close(); |
| return; |
| } |
| String requestType = splitLine[0]; |
| String urlString = splitLine[1]; |
| |
| String host = ""; |
| int port = 80; |
| |
| if (requestType.equals(CONNECT)) { |
| String[] hostPortSplit = urlString.split(":"); |
| host = hostPortSplit[0]; |
| try { |
| port = Integer.parseInt(hostPortSplit[1]); |
| } catch (NumberFormatException nfe) { |
| port = 443; |
| } |
| urlString = "Https://" + host + ":" + port; |
| } else { |
| try { |
| URI url = new URI(urlString); |
| host = url.getHost(); |
| port = url.getPort(); |
| if (port < 0) { |
| port = 80; |
| } |
| } catch (URISyntaxException e) { |
| connection.close(); |
| return; |
| } |
| } |
| |
| List<Proxy> list = Lists.newArrayList(); |
| try { |
| list = ProxySelector.getDefault().select(new URI(urlString)); |
| } catch (URISyntaxException e) { |
| e.printStackTrace(); |
| } |
| Socket server = null; |
| for (Proxy proxy : list) { |
| try { |
| if (!proxy.equals(Proxy.NO_PROXY)) { |
| // Only Inets created by PacProxySelector. |
| InetSocketAddress inetSocketAddress = |
| (InetSocketAddress)proxy.address(); |
| server = new Socket(inetSocketAddress.getHostName(), |
| inetSocketAddress.getPort()); |
| sendLine(server, requestLine); |
| } else { |
| server = new Socket(host, port); |
| if (requestType.equals(CONNECT)) { |
| while (getLine(connection.getInputStream()).length() != 0); |
| // No proxy to respond so we must. |
| sendLine(connection, HTTP_OK); |
| } else { |
| sendLine(server, requestLine); |
| } |
| } |
| } catch (IOException ioe) { |
| |
| } |
| if (server != null) { |
| break; |
| } |
| } |
| if (server == null) { |
| server = new Socket(host, port); |
| if (requestType.equals(CONNECT)) { |
| while (getLine(connection.getInputStream()).length() != 0); |
| // No proxy to respond so we must. |
| sendLine(connection, HTTP_OK); |
| } else { |
| sendLine(server, requestLine); |
| } |
| } |
| // Pass data back and forth until complete. |
| SocketConnect.connect(connection, server); |
| } catch (IOException e) { |
| Log.d(TAG, "Problem Proxying", e); |
| } |
| try { |
| connection.close(); |
| } catch (IOException ioe) { |
| |
| } |
| } |
| |
| private String getLine(InputStream inputStream) throws IOException { |
| StringBuffer buffer = new StringBuffer(); |
| int byteBuffer = inputStream.read(); |
| if (byteBuffer < 0) return ""; |
| do { |
| if (byteBuffer != '\r') { |
| buffer.append((char)byteBuffer); |
| } |
| byteBuffer = inputStream.read(); |
| } while ((byteBuffer != '\n') && (byteBuffer >= 0)); |
| |
| return buffer.toString(); |
| } |
| |
| private void sendLine(Socket socket, String line) throws IOException { |
| OutputStream os = socket.getOutputStream(); |
| os.write(line.getBytes()); |
| os.write('\r'); |
| os.write('\n'); |
| os.flush(); |
| } |
| } |
| |
| public ProxyServer() { |
| threadExecutor = Executors.newCachedThreadPool(); |
| mPort = -1; |
| mCallback = null; |
| } |
| |
| @Override |
| public void run() { |
| try { |
| serverSocket = new ServerSocket(0); |
| |
| if (serverSocket != null) { |
| setPort(serverSocket.getLocalPort()); |
| |
| while (mIsRunning) { |
| try { |
| Socket socket = serverSocket.accept(); |
| // Only receive local connections. |
| if (socket.getInetAddress().isLoopbackAddress()) { |
| ProxyConnection parser = new ProxyConnection(socket); |
| |
| threadExecutor.execute(parser); |
| } else { |
| socket.close(); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } catch (SocketException e) { |
| Log.e(TAG, "Failed to start proxy server", e); |
| } catch (IOException e1) { |
| Log.e(TAG, "Failed to start proxy server", e1); |
| } |
| |
| mIsRunning = false; |
| } |
| |
| public synchronized void setPort(int port) { |
| if (mCallback != null) { |
| try { |
| mCallback.setProxyPort(port); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Proxy failed to report port to PacManager", e); |
| } |
| } |
| mPort = port; |
| } |
| |
| public synchronized void setCallback(IProxyPortListener callback) { |
| if (mPort != -1) { |
| try { |
| callback.setProxyPort(mPort); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Proxy failed to report port to PacManager", e); |
| } |
| } |
| mCallback = callback; |
| } |
| |
| public synchronized void startServer() { |
| mIsRunning = true; |
| start(); |
| } |
| |
| public synchronized void stopServer() { |
| mIsRunning = false; |
| if (serverSocket != null) { |
| try { |
| serverSocket.close(); |
| serverSocket = null; |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| public boolean isBound() { |
| return (mPort != -1); |
| } |
| |
| public int getPort() { |
| return mPort; |
| } |
| } |