204 lines
6.4 KiB
Java
204 lines
6.4 KiB
Java
/*
|
|
This file is part of ServerProxy.
|
|
SocketProxy is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
Subsonic is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
You should have received a copy of the GNU General Public License
|
|
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
|
Copyright 2014 (C) Scott Jackson
|
|
*/
|
|
|
|
package net.nullsum.audinaut.util;
|
|
|
|
import android.util.Log;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.ServerSocket;
|
|
import java.net.Socket;
|
|
import java.net.SocketTimeoutException;
|
|
import java.net.URLDecoder;
|
|
import java.net.URLEncoder;
|
|
import java.net.UnknownHostException;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.StringTokenizer;
|
|
|
|
public abstract class ServerProxy implements Runnable {
|
|
private static final String TAG = ServerProxy.class.getSimpleName();
|
|
boolean isRunning;
|
|
private Thread thread;
|
|
private ServerSocket socket;
|
|
private int port;
|
|
|
|
ServerProxy() {
|
|
// Create listening socket
|
|
try {
|
|
socket = new ServerSocket(0);
|
|
socket.setSoTimeout(5000);
|
|
port = socket.getLocalPort();
|
|
} catch (UnknownHostException e) { // impossible
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "IOException initializing server", e);
|
|
}
|
|
}
|
|
|
|
public void start() {
|
|
if (socket.isBound()) {
|
|
thread = new Thread(this, "Socket Proxy");
|
|
thread.start();
|
|
} else {
|
|
Log.e(TAG, "Attempting to start a non-initialized proxy");
|
|
}
|
|
}
|
|
|
|
public void stop() {
|
|
isRunning = false;
|
|
if (thread != null) {
|
|
thread.interrupt();
|
|
}
|
|
}
|
|
|
|
public String getPrivateAddress(String request) {
|
|
return getAddress(request);
|
|
}
|
|
|
|
private String getAddress(String request) {
|
|
try {
|
|
return String.format("http://%s:%d/%s", "127.0.0.1", port, URLEncoder.encode(request, "UTF-8"));
|
|
} catch (UnsupportedEncodingException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
isRunning = true;
|
|
while (isRunning) {
|
|
try {
|
|
Socket client = socket.accept();
|
|
if (client == null) {
|
|
continue;
|
|
}
|
|
Log.i(TAG, "client connected");
|
|
|
|
ProxyTask task = getTask(client);
|
|
if (task.processRequest()) {
|
|
new Thread(task, "ProxyTask").start();
|
|
}
|
|
|
|
} catch (SocketTimeoutException e) {
|
|
// Do nothing
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "Error connecting to client", e);
|
|
}
|
|
}
|
|
Log.i(TAG, "Proxy interrupted. Shutting down.");
|
|
}
|
|
|
|
abstract ProxyTask getTask(Socket client);
|
|
|
|
protected abstract class ProxyTask implements Runnable {
|
|
final Socket client;
|
|
final Map<String, String> requestHeaders = new HashMap<>();
|
|
String path;
|
|
int cbSkip = 0;
|
|
|
|
public ProxyTask(Socket client) {
|
|
this.client = client;
|
|
}
|
|
|
|
boolean readRequest() {
|
|
InputStream is;
|
|
String firstLine;
|
|
BufferedReader reader;
|
|
try {
|
|
is = client.getInputStream();
|
|
reader = new BufferedReader(new InputStreamReader(is), 8192);
|
|
firstLine = reader.readLine();
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "Error parsing request", e);
|
|
return false;
|
|
}
|
|
|
|
if (firstLine == null) {
|
|
Log.i(TAG, "Proxy client closed connection without a request.");
|
|
return false;
|
|
}
|
|
|
|
StringTokenizer st = new StringTokenizer(firstLine);
|
|
if (!st.hasMoreTokens()) {
|
|
Log.w(TAG, "Unknown request with no tokens");
|
|
return false;
|
|
} else if (st.countTokens() < 2) {
|
|
Log.w(TAG, "Unknown request with no uri: \"" + firstLine + '"');
|
|
return false;
|
|
}
|
|
String uri = st.nextToken();
|
|
String realUri = uri.substring(1);
|
|
|
|
// Process path
|
|
try {
|
|
path = URLDecoder.decode(realUri, "UTF-8");
|
|
} catch (UnsupportedEncodingException e) {
|
|
Log.e(TAG, "Unsupported encoding", e);
|
|
return false;
|
|
}
|
|
|
|
// Get all of the headers
|
|
try {
|
|
String line;
|
|
while ((line = reader.readLine()) != null && !"".equals(line)) {
|
|
int index = line.indexOf(':');
|
|
// Ignore headers without ':' or where ':' is the last thing in the string
|
|
if (index != -1 && (index + 2) < line.length()) {
|
|
String headerName = line.substring(0, index);
|
|
String headerValue = line.substring(index + 2);
|
|
|
|
requestHeaders.put(headerName, headerValue);
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
// Don't really care once past first line
|
|
} catch (Exception e) {
|
|
Log.w(TAG, "Exception reading request", e);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean processRequest() {
|
|
if (!readRequest()) {
|
|
return false;
|
|
}
|
|
Log.i(TAG, "Processing request for " + path);
|
|
|
|
// Try to get range requested
|
|
String range = requestHeaders.get("Range");
|
|
if (range != null) {
|
|
int index = range.indexOf("=");
|
|
if (index >= 0) {
|
|
range = range.substring(index + 1);
|
|
|
|
index = range.indexOf("-");
|
|
if (index > 0) {
|
|
range = range.substring(0, index);
|
|
}
|
|
|
|
cbSkip = Integer.parseInt(range);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|