/*
* PS3 Media Server, for streaming any medias to your PS3.
* Copyright (C) 2008 A.Brochard
*
* This program 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; version 2
* of the License only.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package net.pms.network;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Proxy extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(Proxy.class);
private Socket socket, socketToWeb;
private BufferedReader fromBrowser;
private OutputStream toBrowser;
private PrintWriter toWeb;
private boolean writeCache;
public Proxy(Socket s, boolean writeCache) throws IOException {
socket = s;
fromBrowser = new BufferedReader(new InputStreamReader(socket.getInputStream()));
toBrowser = socket.getOutputStream();
this.writeCache = writeCache;
LOGGER.trace("Got connection from " + socket);
start();
}
@Override
public void run() {
// mms://202.167.254.196/FOX
// http://www.cnn.com/video/live/cnnlive_1.asx [rtsp]
// http://atdhe.net/watchtv4.php?b=n [rtmp, like hulu]
try {
String getter = null;
String str, targetHost = "";
int targetPort = 80;
StringBuilder httpHeader = new StringBuilder();
while (true) {
str = fromBrowser.readLine();
if (str == null) {
break;
}
if (str.startsWith("GET") || str.startsWith("DESCRIBE") || str.startsWith("POST") || str.startsWith("HEAD")) {
getter = str;
}
if (str.startsWith("Accept-Encoding: gzip")) {
str = "Accept-Encoding: identity";
}
httpHeader.append(str).append("\r\n");
if (str.startsWith("Host: ")) {
targetHost = str.substring(6);
} else if (str.startsWith("DESCRIBE")) {
targetPort = 554;
targetHost = str.substring(str.indexOf("//") + 2);
targetHost = targetHost.substring(0, targetHost.indexOf('/'));
}
if (str.length() == 0) {
break;
}
}
String target = targetHost;
if (targetHost.indexOf(':') > -1) {
try {
targetPort = Integer.parseInt(targetHost.substring(targetHost.indexOf(':') + 1));
} catch (NumberFormatException nfe) {
LOGGER.debug("Could not parse port from \"" + targetHost.substring(targetHost.indexOf(':') + 1) + "\"");
}
target = targetHost.substring(0, targetHost.indexOf(':'));
}
LOGGER.trace("[PROXY] Connect to: " + target + " and port: " + targetPort);
socketToWeb = new Socket(InetAddress.getByName(target), targetPort);
InputStream sockWebInputStream = socketToWeb.getInputStream();
toWeb = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socketToWeb.getOutputStream())), true);
toWeb.println(httpHeader.toString());
toWeb.flush();
StringTokenizer st = new StringTokenizer(getter, " ");
st.nextToken();
String askedResource = st.nextToken();
askedResource = askedResource.substring(askedResource.indexOf(targetHost) + targetHost.length());
LOGGER.trace("[PROXY] Asked resource: " + askedResource);
String directoryResource = askedResource.substring(0, askedResource.lastIndexOf('/'));
directoryResource = getWritableFileName(directoryResource);
String fileResource = askedResource.substring(askedResource.lastIndexOf('/') + 1);
fileResource = getWritableFileName(fileResource);
fileResource += ".cached";
String fileN = "proxycache/" + target + "/" + directoryResource;
File directoryResourceFile = new File(fileN);
if (writeCache && !(directoryResourceFile.mkdirs())) {
LOGGER.debug("Could not create directory \"" + directoryResourceFile.getAbsolutePath() + "\"");
}
File cachedResource = new File(directoryResourceFile, fileResource);
// LOGGER.trace("Trying to find: " + cachedResource.getAbsolutePath());
byte[] buffer = new byte[8192];
boolean resourceExists = cachedResource.exists() || this.getClass().getResource("/" + fileN) != null;
boolean inMemory = writeCache && !resourceExists;
FileOutputStream fOUT = null;
if (resourceExists) {
LOGGER.trace("[PROXY] File is cached: " + cachedResource.getAbsolutePath());
sockWebInputStream.close();
if (cachedResource.exists()) {
sockWebInputStream = new FileInputStream(cachedResource);
} else {
sockWebInputStream = this.getClass().getResourceAsStream("/" + fileN);
}
} else if (writeCache) {
LOGGER.trace("[PROXY] File is not cached / Writing in it: " + cachedResource.getAbsolutePath());
fOUT = new FileOutputStream(cachedResource, false);
}
OutputStream baos;
if (inMemory) {
baos = new ByteArrayOutputStream();
} else {
baos = toBrowser;
}
long total_read = 0;
int bytes_read;
long CL = 10000000000L;
while (total_read < CL && (bytes_read = sockWebInputStream.read(buffer)) != -1) {
if (!resourceExists) {
if (10000000000L == CL) {
String s = new String(buffer, 0, bytes_read);
int clPos = s.indexOf("Content-Length: ");
if (clPos > -1) {
CL = Integer.parseInt(s.substring(clPos + 16, s.indexOf('\n', clPos)).trim());
LOGGER.trace("Found Content Length: " + CL);
}
}
if (bytes_read >= 7) {
byte end[] = new byte[7];
System.arraycopy(buffer, bytes_read - 7, end, 0, 7);
if (new String(end).equals("\r\n0\r\n\r\n")) {
LOGGER.trace("end of transfer chunked");
CL = -1;
}
}
if (writeCache) {
fOUT.write(buffer, 0, bytes_read);
}
}
baos.write(buffer, 0, bytes_read);
total_read += bytes_read;
}
sockWebInputStream.close();
if (inMemory) {
baos.close();
toBrowser.write(((ByteArrayOutputStream) baos).toByteArray());
}
if (writeCache && fOUT != null) {
fOUT.close();
}
socketToWeb.close();
toBrowser.close();
} catch (IOException e) {
LOGGER.debug("Caught exception", e);
} finally {
try {
if (toWeb != null) {
toWeb.close();
}
if (toBrowser != null) {
toBrowser.close();
}
socket.close();
} catch (IOException e) {
LOGGER.debug("Caught exception", e);
}
}
}
private String getWritableFileName(String resource) {
resource = resource.replace('?', '\u00b5');
resource = resource.replace('|', '\u00b5');
resource = resource.replace('/', '\u00b5');
resource = resource.replace('\\', '\u00b5');
resource = resource.replace('>', '\u00b5');
resource = resource.replace('<', '\u00b5');
resource = resource.replace('|', '\u00b5');
return resource;
}
}