Audinaut-subsonic-app-android/app/src/main/java/net/nullsum/audinaut/util/ServerProxy.java

204 lines
6.4 KiB
Java
Raw Normal View History

2017-02-27 05:36:43 +01:00
/*
2018-03-24 20:25:12 +01:00
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
2017-02-27 05:36:43 +01:00
*/
package net.nullsum.audinaut.util;
2018-03-25 03:28:28 +02:00
import android.util.Log;
2017-02-27 05:36:43 +01:00
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 {
2018-03-24 20:25:12 +01:00
private static final String TAG = ServerProxy.class.getSimpleName();
2018-03-25 03:28:28 +02:00
boolean isRunning;
2018-03-24 20:25:12 +01:00
private Thread thread;
private ServerSocket socket;
private int port;
2018-03-25 03:28:28 +02:00
ServerProxy() {
2018-03-24 20:25:12 +01:00
// 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() {
2018-03-25 03:28:28 +02:00
if (socket.isBound()) {
2018-03-24 20:25:12 +01:00
thread = new Thread(this, "Socket Proxy");
thread.start();
} else {
Log.e(TAG, "Attempting to start a non-initialized proxy");
}
}
public void stop() {
isRunning = false;
2018-03-25 03:28:28 +02:00
if (thread != null) {
2018-03-24 20:25:12 +01:00
thread.interrupt();
}
}
public String getPrivateAddress(String request) {
2018-03-25 03:28:28 +02:00
return getAddress(request);
2018-03-24 20:25:12 +01:00
}
2018-03-25 03:28:28 +02:00
private String getAddress(String request) {
2018-03-24 20:25:12 +01:00
try {
2018-03-25 03:28:28 +02:00
return String.format("http://%s:%d/%s", "127.0.0.1", port, URLEncoder.encode(request, "UTF-8"));
2018-03-24 20:25:12 +01:00
} 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 {
2018-03-25 03:28:28 +02:00
final Socket client;
final Map<String, String> requestHeaders = new HashMap<>();
String path;
int cbSkip = 0;
2018-03-24 20:25:12 +01:00
public ProxyTask(Socket client) {
this.client = client;
}
2018-03-25 03:28:28 +02:00
boolean readRequest() {
2018-03-24 20:25:12 +01:00
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);
2018-03-25 03:28:28 +02:00
if (!st.hasMoreTokens()) {
2018-03-24 20:25:12 +01:00
Log.w(TAG, "Unknown request with no tokens");
return false;
2018-03-25 03:28:28 +02:00
} else if (st.countTokens() < 2) {
2018-03-24 20:25:12 +01:00
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;
2018-03-25 03:28:28 +02:00
while ((line = reader.readLine()) != null && !"".equals(line)) {
2018-03-24 20:25:12 +01:00
int index = line.indexOf(':');
// Ignore headers without ':' or where ':' is the last thing in the string
2018-03-25 03:28:28 +02:00
if (index != -1 && (index + 2) < line.length()) {
2018-03-24 20:25:12 +01:00
String headerName = line.substring(0, index);
String headerValue = line.substring(index + 2);
requestHeaders.put(headerName, headerValue);
}
}
2018-03-25 03:28:28 +02:00
} catch (IOException e) {
2018-03-24 20:25:12 +01:00
// Don't really care once past first line
2018-03-25 03:28:28 +02:00
} catch (Exception e) {
2018-03-24 20:25:12 +01:00
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");
2018-03-25 03:28:28 +02:00
if (range != null) {
2018-03-24 20:25:12 +01:00
int index = range.indexOf("=");
2018-03-25 03:28:28 +02:00
if (index >= 0) {
2018-03-24 20:25:12 +01:00
range = range.substring(index + 1);
index = range.indexOf("-");
2018-03-25 03:28:28 +02:00
if (index > 0) {
2018-03-24 20:25:12 +01:00
range = range.substring(0, index);
}
cbSkip = Integer.parseInt(range);
}
}
return true;
}
}
2017-02-27 05:36:43 +01:00
}