Home | History | Annotate | Download | only in proxyhandler
      1 /**
      2  * Copyright (c) 2013, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.proxyhandler;
     17 
     18 import android.net.ProxyProperties;
     19 import android.os.RemoteException;
     20 import android.util.Log;
     21 
     22 import com.android.net.IProxyPortListener;
     23 import com.google.android.collect.Lists;
     24 
     25 import java.io.IOException;
     26 import java.io.InputStream;
     27 import java.io.OutputStream;
     28 import java.net.InetAddress;
     29 import java.net.InetSocketAddress;
     30 import java.net.Proxy;
     31 import java.net.ProxySelector;
     32 import java.net.ServerSocket;
     33 import java.net.Socket;
     34 import java.net.SocketException;
     35 import java.net.URI;
     36 import java.net.URISyntaxException;
     37 import java.util.List;
     38 import java.util.concurrent.ExecutorService;
     39 import java.util.concurrent.Executors;
     40 
     41 /**
     42  * @hide
     43  */
     44 public class ProxyServer extends Thread {
     45 
     46     private static final String CONNECT = "CONNECT";
     47     private static final String HTTP_OK = "HTTP/1.1 200 OK\n";
     48 
     49     private static final String TAG = "ProxyServer";
     50 
     51     private ExecutorService threadExecutor;
     52 
     53     public boolean mIsRunning = false;
     54 
     55     private ServerSocket serverSocket;
     56     private int mPort;
     57     private IProxyPortListener mCallback;
     58 
     59     private class ProxyConnection implements Runnable {
     60         private Socket connection;
     61 
     62         private ProxyConnection(Socket connection) {
     63             this.connection = connection;
     64         }
     65 
     66         @Override
     67         public void run() {
     68             try {
     69                 String requestLine = getLine(connection.getInputStream());
     70                 if (requestLine == null) {
     71                     connection.close();
     72                     return;
     73                 }
     74                 String[] splitLine = requestLine.split(" ");
     75                 if (splitLine.length < 3) {
     76                     connection.close();
     77                     return;
     78                 }
     79                 String requestType = splitLine[0];
     80                 String urlString = splitLine[1];
     81 
     82                 String host = "";
     83                 int port = 80;
     84 
     85                 if (requestType.equals(CONNECT)) {
     86                     String[] hostPortSplit = urlString.split(":");
     87                     host = hostPortSplit[0];
     88                     try {
     89                         port = Integer.parseInt(hostPortSplit[1]);
     90                     } catch (NumberFormatException nfe) {
     91                         port = 443;
     92                     }
     93                     urlString = "Https://" + host + ":" + port;
     94                 } else {
     95                     try {
     96                         URI url = new URI(urlString);
     97                         host = url.getHost();
     98                         port = url.getPort();
     99                         if (port < 0) {
    100                             port = 80;
    101                         }
    102                     } catch (URISyntaxException e) {
    103                         connection.close();
    104                         return;
    105                     }
    106                 }
    107 
    108                 List<Proxy> list = Lists.newArrayList();
    109                 try {
    110                     list = ProxySelector.getDefault().select(new URI(urlString));
    111                 } catch (URISyntaxException e) {
    112                     e.printStackTrace();
    113                 }
    114                 Socket server = null;
    115                 for (Proxy proxy : list) {
    116                     try {
    117                         if (!proxy.equals(Proxy.NO_PROXY)) {
    118                             // Only Inets created by PacProxySelector.
    119                             InetSocketAddress inetSocketAddress =
    120                                     (InetSocketAddress)proxy.address();
    121                             server = new Socket(inetSocketAddress.getHostName(),
    122                                     inetSocketAddress.getPort());
    123                             sendLine(server, requestLine);
    124                         } else {
    125                             server = new Socket(host, port);
    126                             if (requestType.equals(CONNECT)) {
    127                                 while (getLine(connection.getInputStream()).length() != 0);
    128                                 // No proxy to respond so we must.
    129                                 sendLine(connection, HTTP_OK);
    130                             } else {
    131                                 sendLine(server, requestLine);
    132                             }
    133                         }
    134                     } catch (IOException ioe) {
    135 
    136                     }
    137                     if (server != null) {
    138                         break;
    139                     }
    140                 }
    141                 if (server == null) {
    142                     server = new Socket(host, port);
    143                     if (requestType.equals(CONNECT)) {
    144                         while (getLine(connection.getInputStream()).length() != 0);
    145                         // No proxy to respond so we must.
    146                         sendLine(connection, HTTP_OK);
    147                     } else {
    148                         sendLine(server, requestLine);
    149                     }
    150                 }
    151                 // Pass data back and forth until complete.
    152                 SocketConnect.connect(connection, server);
    153             } catch (IOException e) {
    154                 Log.d(TAG, "Problem Proxying", e);
    155             }
    156             try {
    157                 connection.close();
    158             } catch (IOException ioe) {
    159 
    160             }
    161         }
    162 
    163         private String getLine(InputStream inputStream) throws IOException {
    164             StringBuffer buffer = new StringBuffer();
    165             int byteBuffer = inputStream.read();
    166             if (byteBuffer < 0) return "";
    167             do {
    168                 if (byteBuffer != '\r') {
    169                     buffer.append((char)byteBuffer);
    170                 }
    171                 byteBuffer = inputStream.read();
    172             } while ((byteBuffer != '\n') && (byteBuffer >= 0));
    173 
    174             return buffer.toString();
    175         }
    176 
    177         private void sendLine(Socket socket, String line) throws IOException {
    178             OutputStream os = socket.getOutputStream();
    179             os.write(line.getBytes());
    180             os.write('\r');
    181             os.write('\n');
    182             os.flush();
    183         }
    184     }
    185 
    186     public ProxyServer() {
    187         threadExecutor = Executors.newCachedThreadPool();
    188         mPort = -1;
    189         mCallback = null;
    190     }
    191 
    192     @Override
    193     public void run() {
    194         try {
    195             serverSocket = new ServerSocket(0);
    196 
    197             if (serverSocket != null) {
    198                 setPort(serverSocket.getLocalPort());
    199 
    200                 while (mIsRunning) {
    201                     try {
    202                         Socket socket = serverSocket.accept();
    203                         // Only receive local connections.
    204                         if (socket.getInetAddress().isLoopbackAddress()) {
    205                             ProxyConnection parser = new ProxyConnection(socket);
    206 
    207                             threadExecutor.execute(parser);
    208                         } else {
    209                             socket.close();
    210                         }
    211                     } catch (IOException e) {
    212                         e.printStackTrace();
    213                     }
    214                 }
    215             }
    216         } catch (SocketException e) {
    217             Log.e(TAG, "Failed to start proxy server", e);
    218         } catch (IOException e1) {
    219             Log.e(TAG, "Failed to start proxy server", e1);
    220         }
    221 
    222         mIsRunning = false;
    223     }
    224 
    225     public synchronized void setPort(int port) {
    226         if (mCallback != null) {
    227             try {
    228                 mCallback.setProxyPort(port);
    229             } catch (RemoteException e) {
    230                 Log.w(TAG, "Proxy failed to report port to PacManager", e);
    231             }
    232         }
    233         mPort = port;
    234     }
    235 
    236     public synchronized void setCallback(IProxyPortListener callback) {
    237         if (mPort != -1) {
    238             try {
    239                 callback.setProxyPort(mPort);
    240             } catch (RemoteException e) {
    241                 Log.w(TAG, "Proxy failed to report port to PacManager", e);
    242             }
    243         }
    244         mCallback = callback;
    245     }
    246 
    247     public synchronized void startServer() {
    248         mIsRunning = true;
    249         start();
    250     }
    251 
    252     public synchronized void stopServer() {
    253         mIsRunning = false;
    254         if (serverSocket != null) {
    255             try {
    256                 serverSocket.close();
    257                 serverSocket = null;
    258             } catch (IOException e) {
    259                 e.printStackTrace();
    260             }
    261         }
    262     }
    263 
    264     public boolean isBound() {
    265         return (mPort != -1);
    266     }
    267 
    268     public int getPort() {
    269         return mPort;
    270     }
    271 }
    272