blob: edb16301c8d67884245f2a0f4ba75130f1621c6a [file] [log] [blame]
Jason Monkda205a72013-08-21 15:57:30 -04001/**
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 */
Jason Monk602b2322013-07-03 17:04:33 -040016package com.android.proxyhandler;
17
Jason Monk6f8a68f2013-08-23 19:21:25 -040018import android.os.RemoteException;
Jason Monk602b2322013-07-03 17:04:33 -040019import android.util.Log;
20
Jason Monk6f8a68f2013-08-23 19:21:25 -040021import com.android.net.IProxyPortListener;
Jason Monk602b2322013-07-03 17:04:33 -040022import com.google.android.collect.Lists;
23
24import java.io.IOException;
25import java.io.InputStream;
26import java.io.OutputStream;
27import java.net.InetSocketAddress;
28import java.net.Proxy;
29import java.net.ProxySelector;
30import java.net.ServerSocket;
31import java.net.Socket;
32import java.net.SocketException;
33import java.net.URI;
34import java.net.URISyntaxException;
35import java.util.List;
36import java.util.concurrent.ExecutorService;
37import java.util.concurrent.Executors;
38
39/**
40 * @hide
41 */
42public class ProxyServer extends Thread {
43
44 private static final String CONNECT = "CONNECT";
45 private static final String HTTP_OK = "HTTP/1.1 200 OK\n";
46
47 private static final String TAG = "ProxyServer";
48
49 private ExecutorService threadExecutor;
50
51 public boolean mIsRunning = false;
52
53 private ServerSocket serverSocket;
Jason Monk6f8a68f2013-08-23 19:21:25 -040054 private int mPort;
55 private IProxyPortListener mCallback;
Jason Monk602b2322013-07-03 17:04:33 -040056
57 private class ProxyConnection implements Runnable {
58 private Socket connection;
59
60 private ProxyConnection(Socket connection) {
61 this.connection = connection;
62 }
63
64 @Override
65 public void run() {
66 try {
Jason Monk602b2322013-07-03 17:04:33 -040067 String requestLine = getLine(connection.getInputStream());
68 if (requestLine == null) {
69 connection.close();
70 return;
71 }
72 String[] splitLine = requestLine.split(" ");
73 if (splitLine.length < 3) {
74 connection.close();
75 return;
76 }
77 String requestType = splitLine[0];
78 String urlString = splitLine[1];
79
80 String host = "";
81 int port = 80;
82
83 if (requestType.equals(CONNECT)) {
84 String[] hostPortSplit = urlString.split(":");
85 host = hostPortSplit[0];
86 try {
87 port = Integer.parseInt(hostPortSplit[1]);
88 } catch (NumberFormatException nfe) {
89 port = 443;
90 }
91 urlString = "Https://" + host + ":" + port;
92 } else {
93 try {
94 URI url = new URI(urlString);
95 host = url.getHost();
96 port = url.getPort();
97 if (port < 0) {
98 port = 80;
99 }
100 } catch (URISyntaxException e) {
101 connection.close();
102 return;
103 }
104 }
105
106 List<Proxy> list = Lists.newArrayList();
107 try {
108 list = ProxySelector.getDefault().select(new URI(urlString));
109 } catch (URISyntaxException e) {
110 e.printStackTrace();
111 }
112 Socket server = null;
113 for (Proxy proxy : list) {
114 try {
115 if (!proxy.equals(Proxy.NO_PROXY)) {
116 // Only Inets created by PacProxySelector.
117 InetSocketAddress inetSocketAddress =
Jason Monk179d6e82013-10-30 12:37:28 -0400118 (InetSocketAddress)proxy.address();
119 server = new Socket(inetSocketAddress.getHostName(),
Jason Monk602b2322013-07-03 17:04:33 -0400120 inetSocketAddress.getPort());
121 sendLine(server, requestLine);
122 } else {
123 server = new Socket(host, port);
124 if (requestType.equals(CONNECT)) {
125 while (getLine(connection.getInputStream()).length() != 0);
126 // No proxy to respond so we must.
127 sendLine(connection, HTTP_OK);
128 } else {
129 sendLine(server, requestLine);
130 }
131 }
132 } catch (IOException ioe) {
133
134 }
135 if (server != null) {
136 break;
137 }
138 }
139 if (server == null) {
140 server = new Socket(host, port);
141 if (requestType.equals(CONNECT)) {
142 while (getLine(connection.getInputStream()).length() != 0);
143 // No proxy to respond so we must.
144 sendLine(connection, HTTP_OK);
145 } else {
146 sendLine(server, requestLine);
147 }
148 }
149 // Pass data back and forth until complete.
150 SocketConnect.connect(connection, server);
151 } catch (IOException e) {
152 Log.d(TAG, "Problem Proxying", e);
153 }
154 try {
155 connection.close();
156 } catch (IOException ioe) {
157
158 }
159 }
160
161 private String getLine(InputStream inputStream) throws IOException {
162 StringBuffer buffer = new StringBuffer();
163 int byteBuffer = inputStream.read();
164 if (byteBuffer < 0) return "";
165 do {
166 if (byteBuffer != '\r') {
167 buffer.append((char)byteBuffer);
168 }
169 byteBuffer = inputStream.read();
170 } while ((byteBuffer != '\n') && (byteBuffer >= 0));
171
172 return buffer.toString();
173 }
174
175 private void sendLine(Socket socket, String line) throws IOException {
176 OutputStream os = socket.getOutputStream();
177 os.write(line.getBytes());
178 os.write('\r');
179 os.write('\n');
180 os.flush();
181 }
182 }
183
184 public ProxyServer() {
185 threadExecutor = Executors.newCachedThreadPool();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400186 mPort = -1;
187 mCallback = null;
Jason Monk602b2322013-07-03 17:04:33 -0400188 }
189
190 @Override
191 public void run() {
192 try {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400193 serverSocket = new ServerSocket(0);
Jason Monk602b2322013-07-03 17:04:33 -0400194
Jason Monk6f8a68f2013-08-23 19:21:25 -0400195 if (serverSocket != null) {
196 setPort(serverSocket.getLocalPort());
Jason Monk602b2322013-07-03 17:04:33 -0400197
Jason Monk6f8a68f2013-08-23 19:21:25 -0400198 while (mIsRunning) {
199 try {
Jason Monkfa4518f2013-08-14 16:58:25 -0400200 Socket socket = serverSocket.accept();
201 // Only receive local connections.
202 if (socket.getInetAddress().isLoopbackAddress()) {
203 ProxyConnection parser = new ProxyConnection(socket);
Jason Monk602b2322013-07-03 17:04:33 -0400204
Jason Monkfa4518f2013-08-14 16:58:25 -0400205 threadExecutor.execute(parser);
206 } else {
207 socket.close();
208 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400209 } catch (IOException e) {
210 e.printStackTrace();
211 }
Jason Monk602b2322013-07-03 17:04:33 -0400212 }
213 }
214 } catch (SocketException e) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400215 Log.e(TAG, "Failed to start proxy server", e);
216 } catch (IOException e1) {
217 Log.e(TAG, "Failed to start proxy server", e1);
Jason Monk602b2322013-07-03 17:04:33 -0400218 }
219
220 mIsRunning = false;
221 }
222
Jason Monk6f8a68f2013-08-23 19:21:25 -0400223 public synchronized void setPort(int port) {
224 if (mCallback != null) {
225 try {
226 mCallback.setProxyPort(port);
227 } catch (RemoteException e) {
228 Log.w(TAG, "Proxy failed to report port to PacManager", e);
229 }
230 }
231 mPort = port;
232 }
233
234 public synchronized void setCallback(IProxyPortListener callback) {
235 if (mPort != -1) {
236 try {
237 callback.setProxyPort(mPort);
238 } catch (RemoteException e) {
239 Log.w(TAG, "Proxy failed to report port to PacManager", e);
240 }
241 }
242 mCallback = callback;
243 }
244
Jason Monk602b2322013-07-03 17:04:33 -0400245 public synchronized void startServer() {
246 mIsRunning = true;
247 start();
248 }
249
250 public synchronized void stopServer() {
251 mIsRunning = false;
252 if (serverSocket != null) {
253 try {
254 serverSocket.close();
255 serverSocket = null;
256 } catch (IOException e) {
257 e.printStackTrace();
258 }
259 }
260 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400261
262 public boolean isBound() {
263 return (mPort != -1);
264 }
265
266 public int getPort() {
267 return mPort;
268 }
Jason Monk602b2322013-07-03 17:04:33 -0400269}