blob: 10bcdad04030858421550fa37a6db97cb5d2d34b [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.net.ProxyProperties;
19import android.os.RemoteException;
Jason Monk602b2322013-07-03 17:04:33 -040020import android.util.Log;
21
Jason Monk6f8a68f2013-08-23 19:21:25 -040022import com.android.net.IProxyPortListener;
Jason Monk602b2322013-07-03 17:04:33 -040023import com.google.android.collect.Lists;
24
25import java.io.IOException;
26import java.io.InputStream;
27import java.io.OutputStream;
Jason Monkfa4518f2013-08-14 16:58:25 -040028import java.net.InetAddress;
Jason Monk602b2322013-07-03 17:04:33 -040029import java.net.InetSocketAddress;
30import java.net.Proxy;
31import java.net.ProxySelector;
32import java.net.ServerSocket;
33import java.net.Socket;
34import java.net.SocketException;
35import java.net.URI;
36import java.net.URISyntaxException;
37import java.util.List;
38import java.util.concurrent.ExecutorService;
39import java.util.concurrent.Executors;
40
41/**
42 * @hide
43 */
44public 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;
Jason Monk6f8a68f2013-08-23 19:21:25 -040056 private int mPort;
57 private IProxyPortListener mCallback;
Jason Monk602b2322013-07-03 17:04:33 -040058
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 {
Jason Monk602b2322013-07-03 17:04:33 -040069 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 =
Jason Monk179d6e82013-10-30 12:37:28 -0400120 (InetSocketAddress)proxy.address();
121 server = new Socket(inetSocketAddress.getHostName(),
Jason Monk602b2322013-07-03 17:04:33 -0400122 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();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400188 mPort = -1;
189 mCallback = null;
Jason Monk602b2322013-07-03 17:04:33 -0400190 }
191
192 @Override
193 public void run() {
194 try {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400195 serverSocket = new ServerSocket(0);
Jason Monk602b2322013-07-03 17:04:33 -0400196
Jason Monk6f8a68f2013-08-23 19:21:25 -0400197 if (serverSocket != null) {
198 setPort(serverSocket.getLocalPort());
Jason Monk602b2322013-07-03 17:04:33 -0400199
Jason Monk6f8a68f2013-08-23 19:21:25 -0400200 while (mIsRunning) {
201 try {
Jason Monkfa4518f2013-08-14 16:58:25 -0400202 Socket socket = serverSocket.accept();
203 // Only receive local connections.
204 if (socket.getInetAddress().isLoopbackAddress()) {
205 ProxyConnection parser = new ProxyConnection(socket);
Jason Monk602b2322013-07-03 17:04:33 -0400206
Jason Monkfa4518f2013-08-14 16:58:25 -0400207 threadExecutor.execute(parser);
208 } else {
209 socket.close();
210 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400211 } catch (IOException e) {
212 e.printStackTrace();
213 }
Jason Monk602b2322013-07-03 17:04:33 -0400214 }
215 }
216 } catch (SocketException e) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400217 Log.e(TAG, "Failed to start proxy server", e);
218 } catch (IOException e1) {
219 Log.e(TAG, "Failed to start proxy server", e1);
Jason Monk602b2322013-07-03 17:04:33 -0400220 }
221
222 mIsRunning = false;
223 }
224
Jason Monk6f8a68f2013-08-23 19:21:25 -0400225 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
Jason Monk602b2322013-07-03 17:04:33 -0400247 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 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400263
264 public boolean isBound() {
265 return (mPort != -1);
266 }
267
268 public int getPort() {
269 return mPort;
270 }
Jason Monk602b2322013-07-03 17:04:33 -0400271}