diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ByteArray.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ByteArray.java index ab1dfff01..3e86d959e 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ByteArray.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ByteArray.java @@ -3,11 +3,6 @@ package com.jpexs.proxy; import java.io.IOException; import java.io.OutputStream; -/** - * Class used to represent an array of bytes as an Object. - * - * @author Mark Boyns - */ public class ByteArray { public byte bytes[]; public int offset = 0; diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Cleanable.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Cleanable.java new file mode 100644 index 000000000..5837b0301 --- /dev/null +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Cleanable.java @@ -0,0 +1,6 @@ +package com.jpexs.proxy; + +public interface Cleanable +{ + public void clean(); +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Client.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Client.java index de59d30c8..6fe6a0e1c 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Client.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Client.java @@ -1,25 +1,19 @@ package com.jpexs.proxy; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.IOException; +import java.io.*; import java.net.Socket; -/** - * Client which reads a Request and writes a Reply. - * - * @author Mark Boyns - */ -public class Client extends Connection { +public class Client extends Connection +{ /** * Create a Client from a Socket. */ - Client(Socket s) throws IOException { - super(s); - in = new BufferedInputStream(in); - //out = new DebugOutputStream(new BufferedOutputStream(out)); - out = new BufferedOutputStream(out); + Client(Socket s) throws IOException + { + super(s); + in = new BufferedInputStream(in); + //out = new DebugOutputStream(new BufferedOutputStream(out)); + out = new BufferedOutputStream(out); } @@ -27,17 +21,22 @@ public class Client extends Connection { * Read a Request. * * @returns a Request. + * @see Request */ - Request read() throws IOException { - Request request = new Request(this); - request.read(getInputStream()); - return request; + Request read() throws IOException + { + Request request = new Request(this); + request.read(getInputStream()); + return request; } /** * Write a Reply + * + * @see Reply */ - void write(Reply reply) throws IOException { - reply.write(getOutputStream()); + void write(Reply reply) throws IOException + { + reply.write(getOutputStream()); } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Connection.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Connection.java index 064c21892..8cfff8eee 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Connection.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Connection.java @@ -6,28 +6,25 @@ import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; +import java.security.KeyStore; +import java.security.KeyStoreException; -/** - * Create a TCP connection from a Socket or hostname/port - * with buffered IO. - * - * @author Mark Boyns - * @see java.net.Socket - */ -class Connection { +class Connection +{ Socket socket = null; InputStream in = null; OutputStream out = null; - + /** * Create a Connection from a Socket. * * @param socket a socket */ - Connection(Socket socket) throws IOException { - this.socket = socket; - in = socket.getInputStream(); - out = socket.getOutputStream(); + Connection(Socket socket) throws IOException + { + this.socket = socket; + in = socket.getInputStream(); + out = socket.getOutputStream(); } /** @@ -36,67 +33,82 @@ class Connection { * @param host remote hostname * @param port remote port */ - Connection(String host, int port) throws IOException { - this(new Socket(host, port)); + Connection(String host, int port) throws IOException + { + this(new Socket(InetAddress.getByName(host), port)); } - Connection() { + Connection() + { } /** * Return the input stream. */ - InputStream getInputStream() { - return in; + InputStream getInputStream() + { + return in; } /** * Return the output stream. */ - OutputStream getOutputStream() { - return out; + OutputStream getOutputStream() + { + return out; } - void setInputStream(InputStream in) { - this.in = in; + void setInputStream(InputStream in) + { + this.in = in; } - - void setOutputStream(OutputStream out) { - this.out = out; + + void setOutputStream(OutputStream out) + { + this.out = out; } /** * Close the connection. */ - void close() { - if (socket != null) { - try { - socket.close(); - } - catch (IOException e) { - System.out.println("Connection: " + e); - } - } + void close() + { + if (socket != null) + { + try + { + socket.close(); + } + catch (IOException e) + { + System.out.println("Connection: " + e); + } + } } - public Socket getSocket() { - return socket; + public Socket getSocket() + { + return socket; } - public InetAddress getInetAddress() { - return socket.getInetAddress(); + public InetAddress getInetAddress() + { + return socket.getInetAddress(); + } + + public int getPort() + { + return socket.getPort(); } - public int getPort() { - return socket.getPort(); - } - - public String toString() { - return getInetAddress().getHostAddress() + ":" + getPort(); + public String toString() + { + return getInetAddress().getHostAddress() + ":" + getPort(); } public void setTimeout(int timeout) - throws SocketException { - socket.setSoTimeout(timeout); + throws SocketException + { + socket.setSoTimeout(timeout); } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Copy.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Copy.java index 53169577e..709ef50db 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Copy.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Copy.java @@ -1,35 +1,39 @@ package com.jpexs.proxy; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.IOException; +import java.net.SocketException; -class Copy implements Runnable { +class Copy implements Runnable +{ InputStream in = null; OutputStream out = null; - - Copy(InputStream in, OutputStream out) { - this.in = in; - this.out = out; + + Copy(InputStream in, OutputStream out) + { + this.in = in; + this.out = out; } - public void run() { - int n; - byte buffer[] = new byte[8192]; + public void run() + { + int n; + byte buffer[] = new byte[8192]; - try { - while ((n = in.read(buffer, 0, buffer.length)) > 0) { - out.write(buffer, 0, n); - out.flush(); - } - out.flush(); - } - catch (IOException e) { - String s = e.toString(); - // ignore socket closed exceptions - if (s.toLowerCase().indexOf("socket closed") == -1) { - e.printStackTrace(); - } - } + try + { + while ((n = in.read(buffer, 0, buffer.length)) > 0) + { + out.write(buffer, 0, n); + out.flush(); + } + out.flush(); + }catch(SocketException e){ + } + catch (IOException e) + { + //Ignore errors + } } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Handler.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Handler.java index 87ae5e66f..8dddb0abd 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Handler.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Handler.java @@ -1,25 +1,21 @@ package com.jpexs.proxy; - import java.io.*; +import java.util.zip.*; import java.net.Socket; +import java.net.SocketTimeoutException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.List; import java.util.Locale; import java.util.TimeZone; -import java.util.zip.GZIPInputStream; -/** - * HTTP transaction handler. A handler is created by Server for - * each HTTP transaction. Given a socket, the handler will deal with - * the request, reply, and invoke request, reply, and content filters. - * - * @author Mark Boyns - */ -class Handler implements Runnable { +class Handler implements Runnable +{ static final boolean DEBUG = false; + + Client client = null; Socket socket = null; Request request = null; Reply reply = null; @@ -28,9 +24,7 @@ class Handler implements Runnable { int contentLength = -1; long idle = 0; double bytesPerSecond = 0; - Client client; - int MAXTIMEOUT = 1000 * 60 * 5; List replacements; CatchedListener catchedListener; List catchedContentTypes; @@ -39,7 +33,8 @@ class Handler implements Runnable { /** * Create a Handler. */ - Handler(Socket socket, List replacements, List catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) { + Handler(Socket socket,List replacements, List catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) + { this.socket = socket; this.replacements = replacements; this.catchedListener = catchedListener; @@ -50,268 +45,368 @@ class Handler implements Runnable { /** * Close all connections associated with this handler. */ - synchronized void close() { - if (client != null) { - client.close(); - client = null; - } - if (http != null) { - http.close(); - http = null; - } + synchronized void close() + { + if (client != null) + { + client.close(); + client = null; + } + if (http != null) + { + http.close(); + http = null; + } } /** * Flush all data to the client. */ - void flush() { - if (client != null) { - try { - client.getOutputStream().flush(); - } - catch (IOException e) { - e.printStackTrace(); - } - } + void flush() + { + if (client != null) + { + try + { + client.getOutputStream().flush(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } } - public void run() { - boolean keepAlive = false; - Exception reason = null; + public void run() + { + boolean keepAlive = false; + Exception reason = null; - Thread.currentThread().setName("Handler(" - + socket.getInetAddress().getHostAddress() - + ")"); + Thread.currentThread().setName("Handler(" + + socket.getInetAddress().getHostAddress() + + ")"); - try { - client = new Client(socket); - } catch (IOException e) { + try + { + client = new Client(socket); + client.setTimeout(ProxyConfig.readTimeout); + } + catch (IOException e) + { + e.printStackTrace(); + return; + } - } + try + { - do { - request = null; - reply = null; + do + { + request = null; + reply = null; + idle = System.currentTimeMillis(); - idle = System.currentTimeMillis(); + try + { + request = client.read(); + } + catch (IOException e) + { + e.printStackTrace(); + break; + } + idle = 0; - try { - request = client.read(); - if (Main.DEBUG_MODE) { - System.out.println("REQUEST: " + request.getURL()); - } - } - catch (IOException e) { - e.printStackTrace(); - break; - } + try + { + keepAlive = processRequest(); + } + catch (IOException ioe) + { + reason = ioe; + keepAlive = false; + } - idle = 0; + if (request != null && reply != null) + { + // XXX insert the number of bytes read into the + // reply content-length for logging. + if (reply != null && currentLength > 0) + { + reply.setHeaderField("Content-length", currentLength); + } + + } + } + while (keepAlive); + } + finally + { + + } - try { - keepAlive = processRequest(); - } - catch (IOException ioe) { - reason = ioe; - keepAlive = false; - } + if (reason != null && reason.getMessage().indexOf("Broken pipe") == -1) + { + if (client != null && request != null) + { + error(client.getOutputStream(), reason, request); + } - if (request != null && reply != null) { - // XXX insert the number of bytes read into the - // reply content-length for logging. - if (reply != null && currentLength > 0) { - reply.setHeaderField("Content-length", currentLength); - } - } - } - while (keepAlive); - - - if (reason != null && reason.getMessage().indexOf("Broken pipe") == -1) { - } - - close(); + //reason.printStackTrace(); + } + + close(); } - boolean processRequest() throws IOException { - boolean keepAlive = false; - - while (reply == null) { - boolean secure = false; - boolean uncompress = false; + boolean processRequest() throws IOException + { + boolean keepAlive = false; + + while (reply == null) + { + boolean secure = false; + boolean uncompress = false; - if (request.getCommand().equals("CONNECT")) { - secure = true; - } else if (request.getURL().startsWith("https://")) { - System.out.println("Netscape keep-alive bug: " + request.getURL()); - return false; - } else if (!request.getURL().startsWith("http://")) { - System.out.println("Unknown URL: " + request.getURL()); - return false; - } + if (request.getCommand().equals("CONNECT")) + { + secure = true; + } + else if (request.getURL().startsWith("/")) + { + + } + else if (request.getURL().startsWith("https://")) + { + System.out.println("Netscape keep-alive bug: " + request.getURL()); + return false; + } + else if (! request.getURL().startsWith("http://")) + { + System.out.println("Unknown URL: " + request.getURL()); + return false; + } - keepAlive = (request.containsHeaderField("Proxy-Connection") - && request.getHeaderField("Proxy-Connection").equals("Keep-Alive")); + /* Client wants Keep-Alive */ + if (ProxyConfig.proxyKeepAlive) + { + keepAlive = (request.containsHeaderField("Proxy-Connection") + && request.getHeaderField("Proxy-Connection").equals("Keep-Alive")); + } + + /* Filter the request. */ + //deleted + + /* None found. Use http or https relay. */ + if (secure) + { + http = createHttpsRelay(); + } + else + { + http = createHttpRelay(); + } + try + { + http.sendRequest(request); + if (http instanceof Http) + { + ((Http)http).setTimeout(ProxyConfig.readTimeout); + } + reply = http.recvReply(request); + } + catch (RetryRequestException e) + { + http.close(); + http = null; + continue; /* XXX */ + } + + /* Guess content-type if there aren't any headers. + Probably an upgraded HTTP/0.9 reply. */ + if (reply.headerCount() == 0) + { + String url = request.getURL(); + if (url.endsWith("/") + || url.endsWith(".html") || url.endsWith(".htm")) + { + reply.setHeaderField("Content-type", "text/html"); + } + } - /* First look for any HttpFilters */ + + /* Filter the reply. */ + if (false) + { + /* uncompress gzip encoded html so it can be filtered */ + if (!ProxyConfig.dontUncompress + && "text/html".equals(reply.getHeaderField("Content-type"))) + { + String encoding = reply.getHeaderField("Content-Encoding"); + if (encoding != null && encoding.indexOf("gzip") != -1) + { + reply.removeHeaderField("Content-Encoding"); + reply.removeHeaderField("Content-length"); + uncompress = true; + } + } + + //filter(reply); + } + + reply.removeHeaderField("Proxy-Connection"); + if (keepAlive && reply.containsHeaderField("Content-length")) + { + reply.setHeaderField("Proxy-Connection", "Keep-Alive"); + } + else + { + keepAlive = false; + } + + currentLength = -1; + contentLength = -1; + try + { + contentLength = Integer.parseInt(reply.getHeaderField("Content-length")); + } + catch (NumberFormatException e) + { + } - /* None found. Use http or https relay. */ - if (secure) { - http = createHttpsRelay(); - } else { - http = createHttpRelay(); - } + + if (secure) + { + Https https = (Https) http; + int timeout = ProxyConfig.readTimeout; + + client.write(reply); - try { - http.sendRequest(request); - if (http instanceof Http) { - ((Http) http).setTimeout(MAXTIMEOUT); - } - reply = http.recvReply(request); - } - catch (RetryRequestException e) { - http.close(); - http = null; - continue; /* XXX */ - } + try + { + client.setTimeout(timeout); + https.setTimeout(timeout); + + Copy cp = new Copy(client.getInputStream(), https.getOutputStream()); + ReusableThread thread = Server.getThread(); + thread.setRunnable(cp); + flushCopy(https.getInputStream(), client.getOutputStream(), -1, true); + client.close(); + } + catch (InterruptedIOException iioe) + { + // ignore socket timeout exceptions + } + } + else if (reply.hasContent()) + { + try + { + processContent(uncompress); + } + catch (IOException e) + { + if (http instanceof Http) + { + ((Http)http).reallyClose(); + } + else + { + http.close(); + } + http = null; - /* Guess content-type if there aren't any headers. - Probably an upgraded HTTP/0.9 reply. */ - if (reply.headerCount() == 0) { - String url = request.getURL(); - if (url.endsWith("/") - || url.endsWith(".html") || url.endsWith(".htm")) { - reply.setHeaderField("Content-type", "text/html"); - } - } + client.close(); + client = null; + throw e; + //return false; /* XXX */ + } + /* Document contains no data. */ + if (contentLength == 0) + { + client.close(); + } + } + else + { + client.write(reply); + } - String encoding = reply.getHeaderField("Content-Encoding"); - if (encoding != null && encoding.indexOf("gzip") != -1) { - reply.removeHeaderField("Content-Encoding"); - reply.removeHeaderField("Content-length"); - uncompress = true; - } - /* update reply */ + http.close(); + } - - reply.removeHeaderField("Proxy-Connection"); - if (keepAlive && reply.containsHeaderField("Content-length")) { - reply.setHeaderField("Proxy-Connection", "Keep-Alive"); - } else { - keepAlive = false; - } - - currentLength = -1; - contentLength = -1; - try { - contentLength = Integer.parseInt(reply.getHeaderField("Content-length")); - } - catch (NumberFormatException e) { - } - - /* update content-length reply */ - - - if (secure) { - Https https = (Https) http; - int timeout = MAXTIMEOUT; - - client.write(reply); - - try { - https.setTimeout(timeout); - - Copy cp = new Copy(client.getInputStream(), https.getOutputStream()); - WorkerThread.assignThread(cp, ""); - flushCopy(https.getInputStream(), client.getOutputStream(), -1, true); - client.close(); - } - catch (InterruptedIOException iioe) { - // ignore socket timeout exceptions - } - } else if (reply.hasContent()) { - try { - processContent(uncompress); - } - catch (IOException e) { - if (http instanceof Http) { - ((Http) http).reallyClose(); - } else { - http.close(); - } - http = null; - client.close(); - client = null; - throw e; - //return false; /* XXX */ - } - - /* Document contains no data. */ - if (contentLength == 0) { - client.close(); - } - } else { - client.write(reply); - } - - http.close(); - } - - return keepAlive; + return keepAlive; } - HttpRelay createHttpsRelay() throws IOException { - HttpRelay http; + HttpRelay createHttpsRelay() throws IOException + { + HttpRelay http; - http = new Https(request.getHost(), request.getPort()); + if (ProxyConfig.useHTTPSProxy) + { + http = new Https(ProxyConfig.httpsProxyHost, + ProxyConfig.httpsProxyPort, + true); + } + else + { + http = new Https(request.getHost(), request.getPort()); + } - return http; + return http; + } + + HttpRelay createHttpRelay() throws IOException + { + HttpRelay http; + + if (ProxyConfig.useHTTPProxy) + { + http = Http.open(ProxyConfig.httpProxyHost, + ProxyConfig.httpProxyPort, + true); + } + else + { + http = Http.open(request.getHost(), request.getPort()); + } + + return http; } - HttpRelay createHttpRelay() throws IOException { - HttpRelay http; + InputStream readChunkedTransfer(InputStream in) throws IOException + { + ByteArrayOutputStream chunks = new ByteArrayOutputStream(8192); + int size = 0; + contentLength = 0; + while ((size = reply.getChunkSize(in)) > 0) + { + contentLength += size; + copy(in, chunks, size, true); + reply.readLine(in); + } + reply.getChunkedFooter(in); - http = Http.open(request.getHost(), request.getPort()); + reply.removeHeaderField("Transfer-Encoding"); + reply.setHeaderField("Content-length", contentLength); - - return http; + return new ByteArrayInputStream(chunks.toByteArray()); } - - InputStream readChunkedTransfer(InputStream in) throws IOException { - ByteArrayOutputStream chunks = new ByteArrayOutputStream(8192); - int size = 0; - - contentLength = 0; - while ((size = reply.getChunkSize(in)) > 0) { - contentLength += size; - copy(in, chunks, size, true); - reply.readLine(in); - } - reply.getChunkedFooter(in); - - reply.removeHeaderField("Transfer-Encoding"); - reply.setHeaderField("Content-length", contentLength); - - return new ByteArrayInputStream(chunks.toByteArray()); - } - - - private static DateFormat httpDateFormat() { +private static DateFormat httpDateFormat() { DateFormat httpDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); httpDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); return httpDateFormat; } - void disableReplyCaching() { +void disableReplyCaching() { reply.removeHeaderField("Cache-Control"); reply.removeHeaderField("Last-Modified"); reply.removeHeaderField("Expires"); @@ -326,27 +421,33 @@ class Handler implements Runnable { reply.setHeaderField("Last-Modified", "Sat, 26 Jul 1997 05:00:00 GMT"); } +void processContent(boolean uncompress) throws IOException + { + InputStream in; + boolean chunked = false; - void processContent(boolean uncompress) throws IOException { - InputStream in; - boolean chunked = false; + if (reply.containsHeaderField("Transfer-Encoding") + && reply.getTransferEncoding().equals("chunked")) + { + in = readChunkedTransfer(reply.getContent()); + chunked = true; + } + else + { + in = reply.getContent(); + } - if (reply.containsHeaderField("Transfer-Encoding") - && reply.getTransferEncoding().equals("chunked")) { - in = readChunkedTransfer(reply.getContent()); - chunked = true; - } else { - in = reply.getContent(); - } + if (in == null) + { + System.out.println("No inputstream"); + return; + } + else if (uncompress) + { + in = new GZIPInputStream(in); + } - if (in == null) { - System.out.println("No inputstream"); - return; - } else if (uncompress) { - in = new GZIPInputStream(in); - } - - String url = request.getURL(); + String url = request.getURL(); boolean replaced = false; for (Replacement r : replacements) { if (r.matches(url)) { @@ -362,9 +463,7 @@ class Handler implements Runnable { fis.close(); buffer.close(); } catch (IOException ex) { - if (Main.DEBUG_MODE) { - System.err.println("ERROR:Cannot read file: " + r.targetFile); - } + } byte bytes[] = buffer.toByteArray(); contentLength = bytes.length; @@ -410,166 +509,217 @@ class Handler implements Runnable { } + /** * Return the content length. */ - int getTotalBytes() { - return contentLength > 0 ? contentLength : 0; + int getTotalBytes() + { + return contentLength > 0 ? contentLength : 0; } /** * Return the number of bytes read so far. */ - int getCurrentBytes() { - return currentLength > 0 ? currentLength : 0; + int getCurrentBytes() + { + return currentLength > 0 ? currentLength : 0; } + /** + * Send a error message to the client. + * + * @param out client + * @param e exception that occurred + * @param r request + */ + void error(OutputStream out, Exception e, Request r) + { + StringBuffer buf = new StringBuffer(); + buf.append("While trying to retrieve the URL: "+r.getURL()+"\r\n"); + buf.append("

\r\nThe following error was encountered:\r\n

\r\n"); + buf.append("

  • " + e.toString() + "
\r\n"); + String s = new HttpError(400, buf.toString()).toString(); + try + { + out.write(s.getBytes(), 0, s.length()); + out.flush(); + } + catch (Exception ex) + { + } + } /** * Copy in to out. * - * @param in InputStream - * @param out OutputStream + * @param in InputStream + * @param out OutputStream * @param monitored Update the Monitor */ void copy(InputStream in, OutputStream out, int length, boolean monitored) - throws IOException { - if (length == 0) { - return; - } + throws IOException + { + if (length == 0) + { + return; + } - int n; - byte buffer[] = new byte[8192]; - long start = System.currentTimeMillis(); - long now = 0, then = start; + int n; + byte buffer[] = new byte[8192]; + long start = System.currentTimeMillis(); + long now = 0, then = start; + + bytesPerSecond = 0; - bytesPerSecond = 0; + if (monitored) + { + currentLength = 0; + } + + for (;;) + { + n = (length > 0) ? Math.min(length, buffer.length) : buffer.length; + n = in.read(buffer, 0, n); + if (n < 0) + { + break; + } + + out.write(buffer, 0, n); - if (monitored) { - currentLength = 0; - } + if (monitored) + { + currentLength += n; - for (; ;) { - n = (length > 0) ? Math.min(length, buffer.length) : buffer.length; - n = in.read(buffer, 0, n); - if (n < 0) { - break; - } + } - out.write(buffer, 0, n); + now = System.currentTimeMillis(); + bytesPerSecond = currentLength / ((now - start) / 1000.0); - if (monitored) { - currentLength += n; + // flush after 1 second + if (now - then > 1000) + { + out.flush(); + } - } + if (length != -1) + { + length -= n; + if (length == 0) + { + break; + } + } - now = System.currentTimeMillis(); - bytesPerSecond = currentLength / ((now - start) / 1000.0); + then = now; + } - // flush after 1 second - if (now - then > 1000) { - out.flush(); - } + out.flush(); - if (length != -1) { - length -= n; - if (length == 0) { - break; - } - } - - then = now; - } - - out.flush(); - - if (DEBUG) { - System.out.println(currentLength + " bytes processed in " - + ((System.currentTimeMillis() - start) - / 1000.0) + " seconds " - + ((int) bytesPerSecond / 1024) + " kB/s"); - } + if (DEBUG) + { + System.out.println(currentLength + " bytes processed in " + + ((System.currentTimeMillis() - start) + / 1000.0) + " seconds " + + ((int)bytesPerSecond / 1024) + " kB/s"); + } } /** * Copy in to out. * - * @param in InputStream - * @param out OutputStream + * @param in InputStream + * @param out OutputStream * @param monitored Update the Monitor */ void flushCopy(InputStream in, OutputStream out, int length, boolean monitored) - throws IOException { - if (length == 0) { - return; - } + throws IOException + { + if (length == 0) + { + return; + } - int n; - byte buffer[] = new byte[8192]; - long start = System.currentTimeMillis(); - bytesPerSecond = 0; + int n; + byte buffer[] = new byte[8192]; + long start = System.currentTimeMillis(); + bytesPerSecond = 0; + + if (monitored) + { + currentLength = 0; + } + + for (;;) + { + n = (length > 0) ? Math.min(length, buffer.length) : buffer.length; + n = in.read(buffer, 0, n); + if (n < 0) + { + break; + } - if (monitored) { - currentLength = 0; - } + out.write(buffer, 0, n); + out.flush(); + if (monitored) + { + currentLength += n; - for (; ;) { - n = (length > 0) ? Math.min(length, buffer.length) : buffer.length; - n = in.read(buffer, 0, n); - if (n < 0) { - break; - } + } + bytesPerSecond = currentLength / ((System.currentTimeMillis() - start) / 1000.0); + if (length != -1) + { + length -= n; + if (length == 0) + { + break; + } + } + } + out.flush(); - out.write(buffer, 0, n); - out.flush(); - if (monitored) { - currentLength += n; - - } - bytesPerSecond = currentLength / ((System.currentTimeMillis() - start) / 1000.0); - if (length != -1) { - length -= n; - if (length == 0) { - break; - } - } - } - out.flush(); - - if (DEBUG) { - System.out.println(currentLength + " bytes processed in " - + ((System.currentTimeMillis() - start) / 1000.0) + " seconds " - + ((int) bytesPerSecond / 1024) + " kB/s"); - } + if (DEBUG) + { + System.out.println(currentLength + " bytes processed in " + + ((System.currentTimeMillis() - start) / 1000.0) + " seconds " + + ((int)bytesPerSecond / 1024) + " kB/s"); + } } /** * Return a string represenation of the hander's state. */ - public String toString() { - StringBuffer str = new StringBuffer(); - str.append("CLIENT "); - str.append(socket.getInetAddress().getHostAddress()); - str.append(":"); - str.append(socket.getPort()); - str.append(" - "); - if (request == null) { - str.append("idle " + ((System.currentTimeMillis() - idle) / 1000.0) + " sec"); - } else { - if (reply != null && currentLength > 0) { - str.append("("); - str.append(currentLength); - if (contentLength > 0) { - str.append("/"); - str.append(contentLength); - } - str.append(" "); - str.append(((int) bytesPerSecond / 1024) + " kB/s"); - str.append(") "); - } - str.append(request.getURL()); - } - return str.toString(); + public String toString() + { + StringBuffer str = new StringBuffer(); + str.append("CLIENT "); + str.append(socket.getInetAddress().getHostAddress()); + str.append(":"); + str.append(socket.getPort()); + str.append(" - "); + if (request == null) + { + str.append("idle " + ((System.currentTimeMillis() - idle) / 1000.0) + " sec"); + } + else + { + if (reply != null && currentLength > 0) + { + str.append("("); + str.append(currentLength); + if (contentLength > 0) + { + str.append("/"); + str.append(contentLength); + } + str.append(" "); + str.append(((int)bytesPerSecond / 1024) + " kB/s"); + str.append(") "); + } + str.append(request.getURL()); + } + return str.toString(); } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Http.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Http.java index 23e62f3af..9f26fe4df 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Http.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Http.java @@ -1,23 +1,21 @@ package com.jpexs.proxy; - import java.io.IOException; +import java.net.Socket; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; -/** - * @author Mark Boyns - */ -class Http extends HttpConnection { +class Http extends HttpConnection +{ static final boolean DEBUG = false; /* enable lots of debug output */ /* XXX - more than 1 should work now. */ static final int MAX_PENDING_REQUESTS = 1; - + static Hashtable cache = new Hashtable(33); private static Object httpLock = new Object(); - + String host; int port; @@ -27,270 +25,342 @@ class Http extends HttpConnection { long idle = 0; Vector queue = new Vector(); - Http(String host, int port) throws IOException { - this(host, port, false); + Http(String host, int port) throws IOException + { + this(host, port, false); + } + + Http(String host, int port, boolean isProxy) throws IOException + { + super(host, port); + this.host = host; + this.port = port; + this.proxy = isProxy; } - Http(String host, int port, boolean isProxy) throws IOException { - super(host, port); - this.host = host; - this.port = port; - this.proxy = isProxy; + Http(String host, int port, boolean isProxy,Socket sock) throws IOException + { + super(sock); + this.host = host; + this.port = port; + this.proxy = isProxy; } public synchronized void sendRequest(Request request) - throws IOException, RetryRequestException { - queue.addElement(request); - - try { - send(request); - } - catch (IOException e) { - if (persistent) { - persistent = false; - if (DEBUG) System.out.println("RETRY SEND " + request.getURL()); - throw new RetryRequestException(); - } - throw e; - } + throws IOException, RetryRequestException + { + queue.addElement(request); + + try + { + send(request); + } + catch (IOException e) + { + if (persistent) + { + persistent = false; + if (DEBUG) System.out.println("RETRY SEND " + request.getURL()); + throw new RetryRequestException(); + } + throw e; + } } public synchronized Reply recvReply(Request request) - throws IOException, RetryRequestException { - while (queue.firstElement() != request) { - try { - wait(); - } - catch (InterruptedException e) { - } - } + throws IOException, RetryRequestException + { + while (queue.firstElement() != request) + { + try + { + wait(); + } + catch (InterruptedException e) + { + } + } - if (closed) { - if (DEBUG) System.out.println("RETRY CLOSED " + request.getURL()); - throw new RetryRequestException(); - } + if (closed) + { + if (DEBUG) System.out.println("RETRY CLOSED " + request.getURL()); + throw new RetryRequestException(); + } - try { - return recv(); - } - catch (IOException e) { - if (persistent) { - persistent = false; - if (DEBUG) System.out.println("RETRY RECV " + request.getURL()); - throw new RetryRequestException(); - } - throw e; - } + try + { + return recv(); + } + catch (IOException e) + { + if (persistent) + { + persistent = false; + if (DEBUG) System.out.println("RETRY RECV " + request.getURL()); + throw new RetryRequestException(); + } + throw e; + } } - public void reallyClose() { - persistent = false; - if (DEBUG) - System.out.println("REALLY CLOSE " + this); - close(); + public void reallyClose() + { + persistent = false; + if (DEBUG) + System.out.println("REALLY CLOSE " + this); + close(); } - public synchronized void close() { - if (persistent) { - idle = System.currentTimeMillis(); - } else { - cacheRemove(host, port, this); - super.close(); - closed = true; - } + public synchronized void close() + { + if (persistent) + { + idle = System.currentTimeMillis(); + } + else + { + cacheRemove(host, port, this); + super.close(); + closed = true; + } - if (queue.size() > 0) { - queue.removeElementAt(0); - if (DEBUG) { - if (persistent) - System.out.println("DONE " + this); - else - System.out.println("CLOSE " + this); - } - notify(); - } + if (queue.size() > 0) + { + queue.removeElementAt(0); + if (DEBUG) + { + if (persistent) + System.out.println("DONE " + this); + else + System.out.println("CLOSE " + this); + } + notify(); + } } - private void send(Request request) throws IOException { - if (DEBUG) System.out.println("SEND " + request.getURL()); + private void send(Request request) throws IOException + { + if (DEBUG) System.out.println("SEND " + request.getURL()); - /* Prepare HTTP/1.1 request */ - request.removeHeaderField("Proxy-Connection"); - request.setHeaderField("Connection", "open"); - if (!request.containsHeaderField("Host")) { - request.setHeaderField("Host", request.getHost()); - } + /* Prepare HTTP/1.1 request */ + request.removeHeaderField("Proxy-Connection"); + request.setHeaderField("Connection", "open"); + if (!request.containsHeaderField("Host")) + { + request.setHeaderField("Host", request.getHost()); + } - if (proxy) { - request.write(getOutputStream()); - } else { - String oldStatusLine = request.statusLine; - StringBuffer head = new StringBuffer(); - head.append(request.getCommand()); - head.append(" "); - head.append(request.getPath()); - head.append(" "); - head.append("HTTP/1.0"); - request.statusLine = head.toString(); + if (proxy) + { + request.write(getOutputStream()); + } + else + { + String oldStatusLine = request.statusLine; + StringBuffer head = new StringBuffer(); + head.append(request.getCommand()); + head.append(" "); + head.append(request.getPath()); + head.append(" "); + head.append("HTTP/1.0"); + request.statusLine = head.toString(); - request.write(getOutputStream()); + request.write(getOutputStream()); - /* flush? */ - - request.statusLine = oldStatusLine; - } + /* flush? */ + + request.statusLine = oldStatusLine; + } } - private Reply recv() throws IOException { - Reply reply = new Reply(getInputStream()); - reply.read(); + private Reply recv() throws IOException + { + Reply reply = new Reply(getInputStream()); + reply.read(); - String conn = reply.getHeaderField("Connection"); + String conn = reply.getHeaderField("Connection"); - if (DEBUG) System.out.println("RECV " + reply.statusLine); + if (DEBUG) System.out.println("RECV " + reply.statusLine); - if (reply.containsHeaderField("Connection") - && reply.getHeaderField("Connection").equals("close")) { - persistent = false; - } else if (reply.getProtocol().equals("HTTP/1.1")) { - persistent = true; - } else { - persistent = false; - } + if (reply.containsHeaderField("Connection") + && reply.getHeaderField("Connection").equals("close")) + { + persistent = false; + } + else if (reply.getProtocol().equals("HTTP/1.1")) + { + persistent = true; + } + else + { + persistent = false; + } - /* Received HTTP/1.1 "Continue". Read another Reply. */ - if (reply.getStatusCode() == 100) { - reply = recv(); - } + /* Received HTTP/1.1 "Continue". Read another Reply. */ + if (reply.getStatusCode() == 100) + { + reply = recv(); + } - return reply; + return reply; } - private boolean isBusy() { - return queue.size() >= MAX_PENDING_REQUESTS; + private boolean isBusy() + { + return queue.size() >= MAX_PENDING_REQUESTS; } - private boolean isPersistent() { - return persistent; + private boolean isPersistent() + { + return persistent; } - private static String cacheKey(String host, int port) { - return host.toLowerCase() + ":" + port; + private static String cacheKey(String host, int port) + { + return host.toLowerCase() + ":" + port; } - private static Vector cacheLookup(String host, int port) { - Vector v = (Vector) cache.get(cacheKey(host, port)); - return v; + private static Vector cacheLookup(String host, int port) + { + Vector v = (Vector) cache.get(cacheKey(host, port)); + return v; } - private static boolean cacheContains(Http http) { - Vector v = (Vector) cache.get(cacheKey(http.host, http.port)); - return v != null ? v.contains(http) : false; + private static boolean cacheContains(Http http) + { + Vector v = (Vector) cache.get(cacheKey(http.host, http.port)); + return v != null ? v.contains(http) : false; } - private static void cacheInsert(String host, int port, Http http) { - String key = cacheKey(host, port); - Vector v = (Vector) cache.get(key); - if (v == null) { - v = new Vector(); - } - v.addElement(http); - cache.put(key, v); + private static void cacheInsert(String host, int port, Http http) + { + String key = cacheKey(host, port); + Vector v = (Vector) cache.get(key); + if (v == null) + { + v = new Vector(); + } + v.addElement(http); + cache.put(key, v); } - private static void cacheRemove(String host, int port, Http http) { - Vector v = (Vector) cache.get(cacheKey(host, port)); - if (v != null) { - v.removeElement(http); - if (v.isEmpty()) { - cache.remove(cacheKey(host, port)); - } - } + private static void cacheRemove(String host, int port, Http http) + { + Vector v = (Vector) cache.get(cacheKey(host, port)); + if (v != null) + { + v.removeElement(http); + if (v.isEmpty()) + { + cache.remove(cacheKey(host, port)); + } + } } - private static void cacheClean() { - long now = System.currentTimeMillis(); - Enumeration e = cache.keys(); - while (e.hasMoreElements()) { - Vector v = (Vector) cache.get(e.nextElement()); - for (int i = 0; i < v.size(); i++) { - Http http = (Http) v.elementAt(i); - if (http.idle > 0 && now - http.idle > 30000) /* 30 seconds */ { - if (DEBUG) System.out.println("IDLE " + http); - http.persistent = false; - http.close(); - } - } - } + private static void cacheClean() + { + long now = System.currentTimeMillis(); + Enumeration e = cache.keys(); + while (e.hasMoreElements()) + { + Vector v = (Vector) cache.get(e.nextElement()); + for (int i = 0; i < v.size(); i++) + { + Http http = (Http) v.elementAt(i); + if (http.idle > 0 && now - http.idle > 30000) /* 30 seconds */ + { + if (DEBUG) System.out.println("IDLE " + http); + http.persistent = false; + http.close(); + } + } + } } static Http open(String host, int port, boolean isProxy) - throws IOException { - Http http = null; + throws IOException + { + Http http = null; - synchronized (httpLock) { - Vector v = cacheLookup(host, port); - if (v != null) { - for (int i = 0; i < v.size(); i++) { - Http pick = (Http) v.elementAt(i); + synchronized (httpLock) + { + Vector v = cacheLookup(host, port); + if (v != null) + { + for (int i = 0; i < v.size(); i++) + { + Http pick = (Http) v.elementAt(i); - /* find an http connection that isn't busy */ - if (pick.isPersistent() && !pick.isBusy()) { - http = pick; - break; - } - } + /* find an http connection that isn't busy */ + if (pick.isPersistent() && !pick.isBusy()) + { + http = pick; + break; + } + } - if (http != null) { - http.idle = 0; - if (DEBUG) System.out.println("REUSE " + http); - } - } - } - - if (http == null) { - if (DEBUG) System.out.println("OPENING " + host + ":" + port); - http = new Http(host, port, isProxy); - if (DEBUG) System.out.println("OPENED " + http); - cacheInsert(host, port, http); - } - - return http; + if (http != null) + { + http.idle = 0; + if (DEBUG) System.out.println("REUSE " + http); + } + } + } + + if (http == null) + { + if (DEBUG) System.out.println("OPENING " + host + ":" + port); + http = new Http(host, port, isProxy); + if (DEBUG) System.out.println("OPENED " + http); + cacheInsert(host, port, http); + } + + return http; } - static Http open(String host, int port) throws IOException { - return open(host, port, false); + static Http open(String host, int port) throws IOException + { + return open(host, port, false); } - static Enumeration enumerate() { - Vector list = new Vector(); - Enumeration e = cache.keys(); - while (e.hasMoreElements()) { - Vector v = (Vector) cache.get(e.nextElement()); - for (int i = 0; i < v.size(); i++) { - list.addElement(v.elementAt(i)); - } - } - return list.elements(); + static Enumeration enumerate() + { + Vector list = new Vector(); + Enumeration e = cache.keys(); + while (e.hasMoreElements()) + { + Vector v = (Vector) cache.get(e.nextElement()); + for (int i = 0; i < v.size(); i++) + { + list.addElement(v.elementAt(i)); + } + } + return list.elements(); } - static synchronized void clean() { - cacheClean(); + static synchronized void clean() + { + cacheClean(); } - public String toString() { - StringBuffer buf = new StringBuffer(); - buf.append("SERVER "); - buf.append(super.toString()); - if (isPersistent()) { - buf.append(" - "); - if (queue.size() > 0) { - buf.append(queue.size()); - buf.append(" pending"); - } else { - buf.append("idle " + ((System.currentTimeMillis() - idle) / 1000.0) + " sec"); - } - } - return buf.toString(); + public String toString() + { + StringBuffer buf = new StringBuffer(); + buf.append("SERVER "); + buf.append(super.toString()); + if (isPersistent()) + { + buf.append(" - "); + if (queue.size() > 0) + { + buf.append(queue.size()); + buf.append(" pending"); + } + else + { + buf.append("idle " + ((System.currentTimeMillis() - idle) / 1000.0) + " sec"); + } + } + return buf.toString(); } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpConnection.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpConnection.java index 65064070e..685c68dfb 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpConnection.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpConnection.java @@ -1,48 +1,58 @@ package com.jpexs.proxy; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.IOException; import java.net.Socket; -abstract class HttpConnection extends Connection implements HttpRelay { - HttpConnection(String host, int port) throws IOException { - super(host, port); +abstract class HttpConnection extends Connection implements HttpRelay +{ + HttpConnection(String host, int port) throws IOException + { + super(host, port); } - HttpConnection(Socket s) throws IOException { - super(s); + HttpConnection(Socket s) throws IOException + { + super(s); } public void sendRequest(Request request) - throws IOException, RetryRequestException { - request.write(getOutputStream()); + throws IOException, RetryRequestException + { + request.write(getOutputStream()); } - + public Reply recvReply(Request request) - throws IOException, RetryRequestException { - Reply reply = new Reply(getInputStream()); - reply.read(); - return reply; + throws IOException, RetryRequestException + { + Reply reply = new Reply(getInputStream()); + reply.read(); + return reply; + } + + public void setInputStream(InputStream in) + { + super.setInputStream(in); + } + + public void setOutputStream(OutputStream out) + { + super.setOutputStream(out); } - public void setInputStream(InputStream in) { - super.setInputStream(in); + public InputStream getInputStream() + { + return super.getInputStream(); + } + + public OutputStream getOutputStream() + { + return super.getOutputStream(); } - public void setOutputStream(OutputStream out) { - super.setOutputStream(out); + public void close() + { + super.close(); } - - public InputStream getInputStream() { - return super.getInputStream(); - } - - public OutputStream getOutputStream() { - return super.getOutputStream(); - } - - public void close() { - super.close(); - } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpError.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpError.java new file mode 100644 index 000000000..ec0dc8c64 --- /dev/null +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpError.java @@ -0,0 +1,70 @@ +package com.jpexs.proxy; + +class HttpError +{ + StringBuffer content = null; + Reply reply = null; + + HttpError(int code, String message) + { + String error; + switch (code) + { + case 400: + error = "Bad Request"; + break; + + case 403: + error = "Forbidden"; + break; + + case 404: + error = "Not found"; + break; + + case 503: + error = "Service Unavailable"; + break; + + default: + error = "Error"; + break; + } + + reply = new Reply(); + reply.statusLine = "HTTP/1.0 " + code + " " + error; + reply.setHeaderField("Content-type", "text/html"); + reply.setHeaderField("Server", ProxyConfig.appName+"/" +ProxyConfig.appVersion); + + content = new StringBuffer(); + content.append(message); + } + + Reply getReply() + { + return reply; + } + + String getContent() + { + if (content == null) + { + return null; + } + return content.toString(); + } + + public String toString() + { + StringBuffer buf = new StringBuffer(); + if (reply != null) + { + buf.append(reply.toString()); + } + if (content != null) + { + buf.append(content.toString()); + } + return buf.toString(); + } +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpRelay.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpRelay.java index bb7ca27b6..f2808e42c 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpRelay.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/HttpRelay.java @@ -1,12 +1,12 @@ package com.jpexs.proxy; - +import java.io.InputStream; +import java.io.OutputStream; import java.io.IOException; -public interface HttpRelay { +public interface HttpRelay +{ void sendRequest(Request request) throws IOException, RetryRequestException; - Reply recvReply(Request request) throws IOException, RetryRequestException; - void close(); -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Https.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Https.java index 25be8218b..3d130a4ab 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Https.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Https.java @@ -2,39 +2,38 @@ package com.jpexs.proxy; import java.io.IOException; -/** - * @author Mark Boyns - */ class Https extends HttpConnection { - boolean proxy = false; - Https(String host, int port) throws IOException { - super(host, port); - } + boolean proxy = false; - Https(String host, int port, boolean isProxy) throws IOException { - this(host, port); - proxy = isProxy; - } + Https(String host, int port) throws IOException { + super(host, port); + } - public void sendRequest(Request request) - throws IOException, RetryRequestException { - if (proxy) { - super.sendRequest(request); - } else { - /* nothing */ - } - } + Https(String host, int port, boolean isProxy) throws IOException { + this(host, port); + proxy = isProxy; + } - public Reply recvReply(Request request) - throws IOException, RetryRequestException { - Reply reply = new Reply(getInputStream()); - if (proxy) { - reply.read(); - } else { - reply.statusLine = "HTTP/1.0 200 Connection established"; - reply.setHeaderField("Proxy-agent", "ASDec"); - } - return reply; - } -} \ No newline at end of file + public void sendRequest(Request request) + throws IOException, RetryRequestException { + if (proxy) { + super.sendRequest(request); + } else { + /* nothing */ + } + } + + public Reply recvReply(Request request) + throws java.io.IOException, RetryRequestException { + Reply reply = new Reply(getInputStream()); + if (proxy) { + reply.read(); + } else { + reply.statusLine = "HTTP/1.0 200 Connection established"; + reply.setHeaderField("Proxy-agent", ProxyConfig.appName); + } + + return reply; + } +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Janitor.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Janitor.java new file mode 100644 index 000000000..7d33cc83b --- /dev/null +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Janitor.java @@ -0,0 +1,41 @@ +package com.jpexs.proxy; + +import java.util.Enumeration; +import java.util.Vector; + +class Janitor implements Runnable +{ + private Vector cleanable = new Vector(); + + public void add(Cleanable c) + { + cleanable.addElement(c); + } + + public void run() + { + Thread.currentThread().setName("Janitor"); + + for (;;) + { + try + { + Thread.sleep(30 * 1000); /* 30 seconds */ + } + catch (Exception e) + { + e.printStackTrace(); + } + + for (Enumeration e = cleanable.elements(); + e.hasMoreElements(); ) + { + ((Cleanable)e.nextElement()).clean(); + } + + Http.clean(); + + + } + } +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Key.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Key.java index f10eda895..79c552550 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Key.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Key.java @@ -1,12 +1,5 @@ package com.jpexs.proxy; -/** - * A wrapper around java.lang.String to have case-insensitive - * hashCode and equals methods. - * - * @author Mark Boyns - * @see String - */ class Key { private String name = null; diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/LogFile.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/LogFile.java new file mode 100644 index 000000000..d4557172f --- /dev/null +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/LogFile.java @@ -0,0 +1,181 @@ +package com.jpexs.proxy; + +import java.io.*; +import java.text.*; +import java.util.*; +import java.util.zip.*; + +public class LogFile +{ + private static SimpleDateFormat format; + private static String zoneString; + private String filename = null; + private long maxLogFileSize; + private int maxLogFileHistory; + + static + { + format = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.US); + format.setTimeZone(TimeZone.getDefault()); + zoneString = generateZoneString(); + } + + public LogFile(String filename) + { + setLogFile(filename); + + maxLogFileSize = ProxyConfig.maxLogFileSize; + maxLogFileHistory = ProxyConfig.maxLogFileHistory; + } + + public void setLogFile(String filename) + { + this.filename = filename; + } + + /** + * calculate the local timezone offset + * for PST this is -0800 + */ + private static String generateZoneString() + { + Calendar calendar = Calendar.getInstance(); + int offset = calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET); + StringBuffer buf = new StringBuffer(); + if (offset < 0) + { + buf.append("-"); + offset = -offset; + } + int hours = offset/(60 * 60 * 1000); + int mins = (offset%(60 * 60 * 1000)) / (60 * 1000); + if (hours < 10) + buf.append("0"); + buf.append(hours); + if (mins < 10) + buf.append("0"); + buf.append(mins); + return buf.toString(); + } + + private void gzip(File src) + { + File dst =new File(src.getAbsolutePath() + ".gz"); + FileInputStream in; + GZIPOutputStream out; + byte[] buf = new byte[8192]; + int n; + + try + { + in = new FileInputStream(src); + out = new GZIPOutputStream(new FileOutputStream(dst), buf.length); + + while ((n = in.read(buf)) >= 0) + { + out.write(buf, 0, n); + } + + in.close(); + out.close(); + } + catch (IOException e) + { + System.out.println(e); + } + } + + private void rotate(int max) + { + File f; + + for (int i = max - 2; i >= 1; i--) + { + f = new File(filename + "." + i + ".gz"); + if (f.exists()) + { + f.renameTo(new File(filename + "." + (i+1) + ".gz")); + } + } + + f = new File(filename + ".0"); + if (f.exists()) + { + gzip(f); + f = new File(filename + ".0.gz"); + f.renameTo(new File(filename + ".1.gz")); + } + + f = new File(filename); + f.renameTo(new File(filename + ".0")); + } + + public synchronized void log(Request request, Reply reply) + { + int offset; + Date date; + + date = new Date(); + + StringBuffer buf = new StringBuffer(); + buf.append(request.getClient().getInetAddress().getHostAddress()); + buf.append(" - - "); // ident authuser + buf.append("["); + buf.append(format.format(date)); + buf.append(" "); + buf.append(zoneString); + buf.append("] \""); + buf.append(request.getRequest()); + buf.append("\" "); + buf.append(reply.getStatusCode()); + buf.append(" "); + int length; + try + { + length = Integer.parseInt(reply.getHeaderField("Content-length")); + } + catch (NumberFormatException e) + { + length = 0; + } + buf.append(length); + buf.append("\n"); + + if (!ProxyConfig.dontLogFilters) + { + for (Enumeration h = request.getLogHeaders(); + h != null && h.hasMoreElements(); ) + { + String header = (String)h.nextElement(); + buf.append("["); + buf.append(header); + buf.append("]\n"); + for (Enumeration e = request.getLogEntries(header); + e.hasMoreElements(); ) + { + buf.append("* "); + buf.append(e.nextElement().toString()); + buf.append("\n"); + } + } + } + + try + { + File file = new File(filename); + if (file.length() > maxLogFileSize) + { + rotate(maxLogFileHistory); + } + + FileOutputStream out = new FileOutputStream(filename, true); + out.write(buf.toString().getBytes()); + out.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Main.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Main.java index ef69c5f8b..0282b5c4e 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Main.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Main.java @@ -1,90 +1,66 @@ -package com.jpexs.proxy; - -import java.io.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; - -public class Main { - public static int port = 55555; - public static final String REPLACEMENTSFILE = "." + File.separator + "config" + File.separator + "replacements.ini"; - public static boolean DEBUG_MODE = false; - - - public static void enterToContinue() { - Scanner keyIn = new Scanner(System.in); - System.out.print("Press the enter key to continue"); - keyIn.nextLine(); - } - - public static void main(String args[]) { - for (int i = 0; i < args.length; i++) { - if (args[i].toLowerCase().equals("--help")) { - System.out.println("JPEXS Replacement Proxy"); - System.out.println("-----------------------"); - System.out.println("Replacements list is read from \"" + REPLACEMENTSFILE + "\" file."); - System.out.println("Optional commandline parameters:"); - System.out.println(" -d"); - System.out.println(" Print debug info to console "); - System.out.println(" -p"); - System.out.println(" Set proxy port to . Default is 55555."); - System.exit(0); - } - if (args[i].toLowerCase().equals("-d")) { - System.out.println("DEBUG mode ON"); - DEBUG_MODE = true; - } - if (args[i].toLowerCase().startsWith("-p")) { - try { - port = Integer.parseInt(args[i].substring(2)); - System.out.println("PORT set to " + port); - } catch (NumberFormatException ex) { - System.out.println("Invalit port, reset to " + port); - } - - } - } - List replacements = new ArrayList(); - if ((new File(REPLACEMENTSFILE)).exists()) { - try { - BufferedReader br = new BufferedReader(new FileReader(REPLACEMENTSFILE)); - String s = ""; - while ((s = br.readLine()) != null) { - String fileName = br.readLine(); - if (fileName == null) break; - fileName = fileName.replaceAll("[\\\\/]", File.separator); - Replacement r = new Replacement(s, fileName); - if (DEBUG_MODE) { - System.out.println("Added Replacement: " + r.urlPattern + " => " + r.targetFile); - } - replacements.add(r); - } - br.close(); - } catch (IOException e) { - - } - } else { - if (DEBUG_MODE) { - System.out.println("WARNING:REPLACEMENTS FILE NOT FOUND."); - } - } - Server.startServer(port, replacements, new ArrayList(), new CatchedListener() { - /** - * Method called when specified contentType is received - * - * @param contentType Content type - * @param url URL of the method - * @param data Data stream - */ - public void catched(String contentType, String url, InputStream data) { - - } - }, new ReplacedListener() { - public void replaced(Replacement replacement, String url, String contentType) { - if (DEBUG_MODE) { - System.out.println("REPLACED:" + url + " (Content-type:" + contentType + ") WITH FILE " + replacement.targetFile); - } - } - }); - } -} +package com.jpexs.proxy; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class Main +{ + + public static final String REPLACEMENTSFILE = "." + File.separator + "config" + File.separator + "replacements.ini"; + public static boolean DEBUG_MODE = false; + + public static void main(String argv[]) + { + + + List replacements = new ArrayList(); + if ((new File(REPLACEMENTSFILE)).exists()) { + try { + BufferedReader br = new BufferedReader(new FileReader(REPLACEMENTSFILE)); + String s = ""; + while ((s = br.readLine()) != null) { + String fileName = br.readLine(); + if (fileName == null) break; + fileName = fileName.replaceAll("[\\\\/]", File.separator); + Replacement r = new Replacement(s, fileName); + if (DEBUG_MODE) { + System.out.println("Added Replacement: " + r.urlPattern + " => " + r.targetFile); + } + replacements.add(r); + } + br.close(); + } catch (IOException e) { + + } + } else { + if (DEBUG_MODE) { + System.out.println("WARNING:REPLACEMENTS FILE NOT FOUND."); + } + } + Server.startServer(ProxyConfig.port, replacements, new ArrayList(), new CatchedListener() { + + /** + * Method called when specified contentType is received + * + * @param contentType Content type + * @param url URL of the method + * @param data Data stream + */ + public void catched(String contentType, String url, InputStream data) { + } + }, new ReplacedListener() { + + public void replaced(Replacement replacement, String url, String contentType) { + if (DEBUG_MODE) { + System.out.println("REPLACED:" + url + " (Content-type:" + contentType + ") WITH FILE " + replacement.targetFile); + } + } + }); + + } +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Message.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Message.java index a125a9d94..99d963f41 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Message.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Message.java @@ -1,20 +1,12 @@ package com.jpexs.proxy; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PushbackInputStream; -import java.util.Enumeration; import java.util.Hashtable; +import java.util.Enumeration; import java.util.Vector; +import java.io.*; -/** - * Abstract class to represent message headers. - * - * @author Mark Boyns - */ -public abstract class Message { +public abstract class Message +{ /** * Hashtable used to store message headers. */ @@ -25,158 +17,199 @@ public abstract class Message { */ String statusLine = null; - public String readLine(InputStream in) throws IOException { - char buf[] = new char[128]; - int offset = 0; - int ch; + public String readLine(InputStream in) throws IOException + { + char buf[] = new char[128]; + int offset = 0; + int ch; - for (; ;) { - ch = in.read(); - if (ch == -1 || ch == '\n') { - break; - } else if (ch == '\r') { - int tmpch = in.read(); - if (tmpch != '\n') { - if (!(in instanceof PushbackInputStream)) { - in = new PushbackInputStream(in); - } - ((PushbackInputStream) in).unread(tmpch); - } - break; - } else { - if (offset == buf.length) { - char tmpbuf[] = buf; - buf = new char[tmpbuf.length * 2]; - System.arraycopy(tmpbuf, 0, buf, 0, offset); - } - buf[offset++] = (char) ch; - } - } - return String.copyValueOf(buf, 0, offset); + for (;;) + { + ch = in.read(); + if (ch == -1 || ch == '\n') + { + break; + } + else if (ch == '\r') + { + int tmpch = in.read(); + if (tmpch != '\n') + { + if (! (in instanceof PushbackInputStream)) + { + in = new PushbackInputStream(in); + } + ((PushbackInputStream) in).unread(tmpch); + } + break; + } + else + { + if (offset == buf.length) + { + char tmpbuf[] = buf; + buf = new char[tmpbuf.length * 2]; + System.arraycopy(tmpbuf, 0, buf, 0, offset); + } + buf[offset++] = (char) ch; + } + } + return String.copyValueOf(buf, 0, offset); } /** * Read headers and store them in the hashtable. */ - void readHeaders(InputStream in) throws IOException { - int i; - Key key = null; - - for (; ;) { - String s = readLine(in); - if (s == null) { - break; - } - i = s.indexOf(':'); - if (i == -1) { - /* end of header */ - if (s.length() == 0) { - break; - } - /* multi-line headers */ - else if (key != null - && (s.startsWith(" ") || s.startsWith("\t"))) { - int index = getHeaderValueCount(key.toString()); - index--; - Vector v = (Vector) headers.get(key); - v.setElementAt(v.elementAt(index) + "\n" + s, index); - } - } else { - key = new Key(s.substring(0, i)); - Vector v; - if (headers.containsKey(key)) { - v = (Vector) headers.get(key); - } else { - v = new Vector(); - } - v.addElement(s.substring(i + 1).trim()); - headers.put(key, v); - } - } + void readHeaders(InputStream in) throws IOException + { + int i; + Key key = null; + + for (;;) + { + String s = readLine(in); + if (s == null) + { + break; + } + i = s.indexOf(':'); + if (i == -1) + { + /* end of header */ + if (s.length() == 0) + { + break; + } + /* multi-line headers */ + else if (key != null + && (s.startsWith(" ") || s.startsWith("\t"))) + { + int index = getHeaderValueCount(key.toString()); + index--; + Vector v = (Vector) headers.get(key); + v.setElementAt(v.elementAt(index) + "\n" + s, index); + } + } + else + { + key = new Key(s.substring(0, i)); + Vector v; + if (headers.containsKey(key)) + { + v = (Vector) headers.get(key); + } + else + { + v = new Vector(); + } + v.addElement(s.substring(i+1).trim()); + headers.put(key, v); + } + } } - public int headerCount() { - return headers.size(); + public int headerCount() + { + return headers.size(); } /** * Set the Status line. */ - public void setStatusLine(String l) { + public void setStatusLine(String l) + { statusLine = l; } - public int getHeaderValueCount(String name) { - Vector v = (Vector) headers.get(new Key(name)); - return v.size(); + public int getHeaderValueCount(String name) + { + Vector v = (Vector) headers.get(new Key(name)); + return v.size(); + } + + public String getHeaderField(String name) + { + return getHeaderField(name, 0); + } + + public String getHeaderField(String name, int index) + { + Vector v = (Vector) headers.get(new Key(name)); + if (v == null) + { + return null; + } + return (String) v.elementAt(index); } - public String getHeaderField(String name) { - return getHeaderField(name, 0); + public void setHeaderField(String name, String value) + { + setHeaderField(name, value, 0); } - public String getHeaderField(String name, int index) { - Vector v = (Vector) headers.get(new Key(name)); - if (v == null) { - return null; - } - return (String) v.elementAt(index); + public void setHeaderField(String name, String value, int index) + { + Vector v; + Key key = new Key(name); + + if (headers.containsKey(key)) + { + v = (Vector) headers.get(key); + } + else + { + v = new Vector(); + if (index == 0) + { + v.addElement(""); + } + headers.put(key, v); + } + v.setElementAt(value, index); + } + + public void setHeaderField(String name, int value) + { + setHeaderField(name, value, 0); } - public void setHeaderField(String name, String value) { - setHeaderField(name, value, 0); - } - - public void setHeaderField(String name, String value, int index) { - Vector v; - Key key = new Key(name); - - if (headers.containsKey(key)) { - v = (Vector) headers.get(key); - } else { - v = new Vector(); - if (index == 0) { - v.addElement(""); - } - headers.put(key, v); - } - v.setElementAt(value, index); - } - - public void setHeaderField(String name, int value) { - setHeaderField(name, value, 0); - } - - public void setHeaderField(String name, int value, int index) { - setHeaderField(name, new Integer(value).toString(), index); + public void setHeaderField(String name, int value, int index) + { + setHeaderField(name, new Integer(value).toString(), index); } /** * Set all header fields with the give name to the * specified value. */ - public void setHeaderFields(String name, String value) { - Vector v; - Key key = new Key(name); + public void setHeaderFields(String name, String value) + { + Vector v; + Key key = new Key(name); - v = (Vector) headers.get(key); - if (v != null) { - for (int i = 0; i < v.size(); i++) { - v.setElementAt(value, i); - } - } + v = (Vector) headers.get(key); + if (v != null) + { + for (int i = 0; i < v.size(); i++) + { + v.setElementAt(value, i); + } + } } - public void appendHeaderField(String name, String value) { - appendHeaderField(name, value, 0); + public void appendHeaderField(String name, String value) + { + appendHeaderField(name, value, 0); } - - public void appendHeaderField(String name, String value, int index) { - setHeaderField(name, getHeaderField(name, index) + value, index); + + public void appendHeaderField(String name, String value, int index) + { + setHeaderField(name, getHeaderField(name, index) + value, index); } - - public void removeHeaderField(String name) { - headers.remove(new Key(name)); + + public void removeHeaderField(String name) + { + headers.remove(new Key(name)); } /** @@ -184,70 +217,81 @@ public abstract class Message { * * @param name header name */ - public boolean containsHeaderField(String name) { - return headers.containsKey(new Key(name)); + public boolean containsHeaderField(String name) + { + return headers.containsKey(new Key(name)); } /** * @return an Enumeration of Strings */ - public Enumeration getHeaders() { - Vector v = new Vector(); + public Enumeration getHeaders() + { + Vector v = new Vector(); - for (Enumeration e = headers.keys(); e.hasMoreElements();) { - v.addElement(e.nextElement().toString()); - } + for (Enumeration e = headers.keys(); e.hasMoreElements(); ) + { + v.addElement(e.nextElement().toString()); + } - return v.elements(); + return v.elements(); } private final static byte[] COLON_SPACE = ": ".getBytes(); private final static byte[] CRLF = "\r\n".getBytes(); + + private ByteArray toByteArray(byte[] sep) + { + ByteArray buf = new ByteArray(); + Key key; + String value; + Vector v; + int i = 0; + + buf.append(statusLine); + buf.append(sep); + + for (Enumeration e = headers.keys(); e.hasMoreElements(); ) + { + key = (Key) e.nextElement(); + v = (Vector) headers.get(key); + for (i = 0; i < v.size(); i++) + { + buf.append(key.toString()); + buf.append(COLON_SPACE); + buf.append(v.elementAt(i).toString()); + buf.append(sep); + } + } + buf.append(sep); - private ByteArray toByteArray(byte[] sep) { - ByteArray buf = new ByteArray(); - Key key; - String value; - Vector v; - int i = 0; - - buf.append(statusLine); - buf.append(sep); - - for (Enumeration e = headers.keys(); e.hasMoreElements();) { - key = (Key) e.nextElement(); - v = (Vector) headers.get(key); - for (i = 0; i < v.size(); i++) { - buf.append(key.toString()); - buf.append(COLON_SPACE); - buf.append(v.elementAt(i).toString()); - buf.append(sep); - } - } - buf.append(sep); - - return buf; + return buf; } - private ByteArray toByteArray() { - return toByteArray(CRLF); + private ByteArray toByteArray() + { + return toByteArray(CRLF); } - private ByteArray toByteArray(String sep) { - return toByteArray(sep.getBytes()); + private ByteArray toByteArray(String sep) + { + return toByteArray(sep.getBytes()); } - - public String toString() { - return toByteArray().toString(); + + public String toString() + { + return toByteArray().toString(); } - - public String toString(String sep) { - return toByteArray(sep).toString(); + + public String toString(String sep) + { + return toByteArray(sep).toString(); } public void write(OutputStream out) - throws IOException { - toByteArray().writeTo(out); - out.flush(); + throws IOException + { + toByteArray().writeTo(out); + out.flush(); } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ProxyConfig.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ProxyConfig.java new file mode 100644 index 000000000..9f68ef338 --- /dev/null +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ProxyConfig.java @@ -0,0 +1,30 @@ +package com.jpexs.proxy; + +/** + * + * @author JPEXS + */ +public class ProxyConfig { + + public static boolean dontLogFilters=false; + public static long maxLogFileSize=1024*5; + public static int maxLogFileHistory=500; + + public static String appVersion="1.1"; + public static String appName="JPProxy"; + public static int port=55555; + public static String bindAddress; + + public static int readTimeout=50000; + public static boolean proxyKeepAlive=false; + public static boolean dontUncompress=false; + + public static boolean useHTTPSProxy=false; + public static String httpsProxyHost=""; + public static int httpsProxyPort=0; + + public static boolean useHTTPProxy=false; + public static String httpProxyHost=""; + public static int httpProxyPort=0; + +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Reply.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Reply.java index dfa259e4c..6581d8a73 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Reply.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Reply.java @@ -1,203 +1,245 @@ package com.jpexs.proxy; - -import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.StringBufferInputStream; import java.io.SequenceInputStream; import java.util.Hashtable; import java.util.StringTokenizer; -/** - * @author Mark Boyns - */ -public class Reply extends Message { +public class Reply extends Message +{ InputStream in = null; int statusCode = -1; - public Reply() { + public Reply() + { } - public Reply(InputStream in) { - setContent(in); + public Reply(InputStream in) + { + setContent(in); } - public void setContent(InputStream in) { - this.in = in; + public void setContent(InputStream in) + { + this.in = in; } - public InputStream getContent() { - return in; + public InputStream getContent() + { + return in; + } + + void read() throws IOException + { + if (in != null) + { + read(in); + } + } + + void read(InputStream in) throws IOException + { + statusLine = readLine(in); + if (statusLine == null || statusLine.length() == 0) + { + throw new IOException("Missing HTTP status line"); + } + + /* Look for HTTP/0.9 */ + if (!statusLine.startsWith("HTTP")) + { + /* Put back the line */ + if (this.in != null) + { + String putback = new String(statusLine + "\n"); + this.in = new SequenceInputStream(new StringBufferInputStream(putback), in); + } + /* Fake a status line and upgrade to HTTP/1.0 */ + statusLine = "HTTP/1.0 200 OK"; + return; + } + + readHeaders(in); + int code = getStatusCode(); + + /* RFC 2068: 204 and 304 MUST NOT contain a message body. */ + switch (code) + { + case 204: /* No Content */ + case 304: /* Not Modified */ + /* Ignore the message body if it exists */ + if (containsHeaderField("Content-length")) + { + int contentLength = 0; + try + { + contentLength = Integer.parseInt(getHeaderField("Content-length")); + } + catch (NumberFormatException e) + { + } + int n; + byte buffer[] = new byte[8192]; + while ((n = in.read(buffer, 0, buffer.length)) > 0) + { + /* ignore */ + } + removeHeaderField("Content-length"); + } + break; + } } - void read() throws IOException { - if (in != null) { - read(in); - } + public boolean hasContent() + { + switch (getStatusCode()) + { + case 204: + case 304: + return false; + + default: + return true; + } } - void read(InputStream in) throws IOException { - statusLine = readLine(in); - if (statusLine == null || statusLine.length() == 0) { - throw new IOException("Missing HTTP status line"); - } - - /* Look for HTTP/0.9 */ - if (!statusLine.startsWith("HTTP")) { - /* Put back the line */ - if (this.in != null) { - String putback = new String(statusLine + "\n"); - this.in = new SequenceInputStream(new ByteArrayInputStream(putback.getBytes("utf8")), in); - } - /* Fake a status line and upgrade to HTTP/1.0 */ - statusLine = "HTTP/1.0 200 OK"; - return; - } - - readHeaders(in); - int code = getStatusCode(); - - /* RFC 2068: 204 and 304 MUST NOT contain a message body. */ - switch (code) { - case 204: /* No Content */ - case 304: /* Not Modified */ - /* Ignore the message body if it exists */ - if (containsHeaderField("Content-length")) { - int contentLength = 0; - try { - contentLength = Integer.parseInt(getHeaderField("Content-length")); - } - catch (NumberFormatException e) { - } - int n; - byte buffer[] = new byte[8192]; - while ((n = in.read(buffer, 0, buffer.length)) > 0) { - /* ignore */ - } - removeHeaderField("Content-length"); - } - break; - } + public String getProtocol() + { + StringTokenizer st = new StringTokenizer(statusLine); + String protocol = (String) st.nextToken(); + return protocol; } - public boolean hasContent() { - switch (getStatusCode()) { - case 204: - case 304: - return false; + public int getStatusCode() + { + if (statusCode == -1) + { + StringTokenizer st = new StringTokenizer(statusLine); + String protocol = (String) st.nextToken(); + String status = (String) st.nextToken(); - default: - return true; - } + try + { + statusCode = Integer.parseInt(status); + } + catch (NumberFormatException e) + { + System.out.println("Malformed or missing status code"); + statusCode = 0; + } + } + + return statusCode; } - public String getProtocol() { - StringTokenizer st = new StringTokenizer(statusLine); - String protocol = (String) st.nextToken(); - return protocol; + private Hashtable headerParser(String header) + { + Hashtable table = new Hashtable(); + String type = getHeaderField(header); + if (type == null) + { + return table; + } + + StringTokenizer st = new StringTokenizer(type, ";"); + int count = 0; + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + token = token.trim(); + String name; + String value; + int i = token.indexOf('='); + if (i != -1) + { + name = token.substring(0, i); + value = token.substring(i+1); + } + else + { + name = token; + value = ""; + } + + if (count == 0) + { + table.put(header, name); + } + else + { + table.put(name, value); + } + + count++; + } + + return table; } - public int getStatusCode() { - if (statusCode == -1) { - StringTokenizer st = new StringTokenizer(statusLine); - String protocol = (String) st.nextToken(); - String status = (String) st.nextToken(); - - try { - statusCode = Integer.parseInt(status); - } - catch (NumberFormatException e) { - statusCode = 0; - } - } - - return statusCode; + public String getContentType() + { + Hashtable table = headerParser("Content-type"); + return(String) table.get("Content-type"); } - private Hashtable headerParser(String header) { - Hashtable table = new Hashtable(); - String type = getHeaderField(header); - if (type == null) { - return table; - } - - StringTokenizer st = new StringTokenizer(type, ";"); - int count = 0; - while (st.hasMoreTokens()) { - String token = st.nextToken(); - token = token.trim(); - String name; - String value; - int i = token.indexOf('='); - if (i != -1) { - name = token.substring(0, i); - value = token.substring(i + 1); - } else { - name = token; - value = ""; - } - - if (count == 0) { - table.put(header, name); - } else { - table.put(name, value); - } - - count++; - } - - return table; + public String getBoundary() + { + Hashtable table = headerParser("Content-type"); + return(String) table.get("boundary"); } - public String getContentType() { - Hashtable table = headerParser("Content-type"); - return (String) table.get("Content-type"); + public String getTransferEncoding() + { + Hashtable table = headerParser("Transfer-Encoding"); + return(String) table.get("Transfer-Encoding"); } - public String getBoundary() { - Hashtable table = headerParser("Content-type"); - return (String) table.get("boundary"); + public int getChunkSize(InputStream in) throws IOException + { + String line = readLine(in); + line = line.trim(); /* apache can have trailing spaces */ + int size = -1; + try + { + size = Integer.valueOf(line, 16).intValue(); + } + catch (NumberFormatException e) + { + System.out.println(e); + } + return size; } - public String getTransferEncoding() { - Hashtable table = headerParser("Transfer-Encoding"); - return (String) table.get("Transfer-Encoding"); + public void getChunkedFooter(InputStream in) throws IOException + { + for (;;) + { + String line = readLine(in); + if (line == null) + { + break; + } + int i = line.indexOf(':'); + if (i == -1) + { + break; + } + } } - public int getChunkSize(InputStream in) throws IOException { - String line = readLine(in); - line = line.trim(); /* apache can have trailing spaces */ - int size = -1; - try { - size = Integer.valueOf(line, 16).intValue(); - } - catch (NumberFormatException e) { - System.out.println(e); - } - return size; + public void setStatusLine(String line) + { + this.statusLine = line; } - public void getChunkedFooter(InputStream in) throws IOException { - for (; ;) { - String line = readLine(in); - if (line == null) { - break; - } - int i = line.indexOf(':'); - if (i == -1) { - break; - } - } + public static Reply createRedirect(String url) + { + Reply r = new Reply(); + r.setStatusLine("HTTP/1.0 302 Moved Temporarily"); + r.setHeaderField("Location", url); + return r; } - - public void setStatusLine(String line) { - this.statusLine = line; - } - - public static Reply createRedirect(String url) { - Reply r = new Reply(); - r.setStatusLine("HTTP/1.0 302 Moved Temporarily"); - r.setHeaderField("Location", url); - return r; - } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Request.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Request.java index d789d6b2c..7ecd0597e 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Request.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Request.java @@ -1,235 +1,265 @@ package com.jpexs.proxy; - -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.IOException; +import java.util.StringTokenizer; import java.util.Enumeration; import java.util.Hashtable; -import java.util.StringTokenizer; import java.util.Vector; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -/** - * @author Mark Boyns - */ -public class Request extends Message { - private static Pattern httpRegex; +public class Request extends Message +{ + private String command = null; private String url = null; private String protocol = null; private byte[] data = null; + private Client client = null; private Hashtable log; private Vector logHeaders; - private Client client; - static { - httpRegex = Pattern.compile("^(http|https):", Pattern.CASE_INSENSITIVE); + + Request(Client c) + { + client = c; } - public Request(Client client) { - this.client = client; - } + void read(InputStream in) throws IOException + { + statusLine = readLine(in); + if (statusLine == null || statusLine.length() == 0) + { + throw new IOException("Empty request"); + } - void read(InputStream in) throws IOException { - statusLine = readLine(in); - if (statusLine == null || statusLine.length() == 0) { - throw new IOException("Empty request"); - } + StringTokenizer st = new StringTokenizer(statusLine); + command = (String) st.nextToken(); + url = (String) st.nextToken(); + protocol = (String) st.nextToken(); - StringTokenizer st = new StringTokenizer(statusLine); - command = (String) st.nextToken(); - url = (String) st.nextToken(); - protocol = (String) st.nextToken(); + if (!url.startsWith("http")) + { + //TODO do something here + } - if (!url.startsWith("http")) { - Matcher match = httpRegex.matcher(url); - if (match.matches()) { - url = url.substring(match.start(), - match.end()).toLowerCase() - + url.substring(match.end()); - } - } + readHeaders(in); - readHeaders(in); - - if ("POST".equals(command) || "PUT".equals(command)) { - try { - int n = Integer.parseInt(getHeaderField("Content-length")); - data = new byte[n]; - int offset = 0; - while (offset < data.length) { - n = in.read(data, offset, data.length - offset); - if (n < 0) { - throw new IOException("Not enough " + command + " data"); - } - offset += n; - } - } - catch (NumberFormatException e) { - System.out.println("Malformed or missing " + command + " Content-length"); - } - } + if ("POST".equals(command) || "PUT".equals(command)) + { + try + { + int n = Integer.parseInt(getHeaderField("Content-length")); + data = new byte[n]; + int offset = 0; + while (offset < data.length) + { + n = in.read(data, offset, data.length - offset); + if (n < 0) + { + throw new IOException("Not enough " + command + " data"); + } + offset += n; + } + } + catch (NumberFormatException e) + { + System.out.println("Malformed or missing " + command + " Content-length"); + } + } } public void write(OutputStream out) - throws IOException { - super.write(out); - if (data != null) { - out.write(data); - out.flush(); - } + throws IOException + { + super.write(out); + if (data != null) + { + out.write(data); + out.flush(); + } } - public String getRequest() { - return statusLine; + public String getRequest() + { + return statusLine; } - public String getCommand() { - return command; + public String getCommand() + { + return command; + } + + public void setCommand(String command) + { + this.command = command; } - public void setCommand(String command) { - this.command = command; + public String getURL() + { + return url; } - public String getURL() { - return url; + public void setURL(String url) + { + this.url = url; } - public void setURL(String url) { - this.url = url; + public String getProtocol() + { + return protocol; } - public String getProtocol() { - return protocol; + public void setProtocol(String protocol) + { + this.protocol = protocol; } - public void setProtocol(String protocol) { - this.protocol = protocol; + public String getHost() + { + String url = getURL(); + String s; + + if (url.startsWith("http://")) + { + s = url.substring(7, url.indexOf('/', 7)); + } + else + { + s = url; + } + + int at = s.indexOf('@'); + if (at != -1 ) + { + s = s.substring(at+1); + } + + if (s.indexOf(':') != -1) + { + return s.substring(0, s.indexOf(':')); + } + + return s; } - public String getHost() { - String url = getURL(); - String s; + public int getPort() + { + int port = 80; + String url = getURL(); + String s; - if (url.startsWith("http://")) { - s = url.substring(7, url.indexOf('/', 7)); - } else { - s = url; - } + if (url.startsWith("http://")) + { + s = url.substring(7, url.indexOf('/', 7)); + } + else + { + s = url; + } - int at = s.indexOf('@'); - if (at != -1) { - s = s.substring(at + 1); - } + int at = s.indexOf('@'); + if (at != -1 ) + { + s = s.substring(at+1); + } - if (s.indexOf(':') != -1) { - return s.substring(0, s.indexOf(':')); - } - - return s; + if (s.indexOf(':') != -1) + { + try + { + port = Integer.parseInt(s.substring(s.indexOf(':') + 1)); + } + catch (NumberFormatException e) + { + System.out.println("Invalid port in " + url); + } + } + return port; } - public int getPort() { - int port = 80; - String url = getURL(); - String s; - - if (url.startsWith("http://")) { - s = url.substring(7, url.indexOf('/', 7)); - } else { - s = url; - } - - int at = s.indexOf('@'); - if (at != -1) { - s = s.substring(at + 1); - } - - if (s.indexOf(':') != -1) { - try { - port = Integer.parseInt(s.substring(s.indexOf(':') + 1)); - } - catch (NumberFormatException e) { - System.out.println("Invalid port in " + url); - } - } - return port; + public String getData() + { + if (data == null) + { + return null; + } + return new String(data); } - public String getData() { - if (data == null) { - return null; - } - return new String(data); + public String getPath() + { + String str = getURL(); + int pos = 0; + for (int i = 0; i < 3; i++) + { + pos = str.indexOf('/', pos); + pos++; + } + pos--; + return str.substring(pos); + } + + public String getDocument() + { + String path = getPath(); + int n = path.lastIndexOf('/'); + if (n == path.length() - 1) + { + n = path.lastIndexOf('/', n - 1); + } + if (n < 0) + { + return "/"; + } + else + { + return path.substring(n + 1); + } } - public byte[] getDataBytes() { - return data; + public Client getClient() + { + return client; } - public void setData(byte[] data) { - this.data = data; - } - - - public String getPath() { - String str = getURL(); - int pos = 0; - for (int i = 0; i < 3; i++) { - pos = str.indexOf('/', pos); - pos++; - } - pos--; - return str.substring(pos); - } - - public String getDocument() { - String path = getPath(); - int n = path.lastIndexOf('/'); - if (n == path.length() - 1) { - n = path.lastIndexOf('/', n - 1); - } - if (n < 0) { - return "/"; - } else { - return path.substring(n + 1); - } - } - - - public String getQueryString() { - String path = getPath(); - int n = path.indexOf('?'); - if (n < 0) { - return null; - } - return path.substring(n + 1); + public String getQueryString() + { + String path = getPath(); + int n = path.indexOf('?'); + if (n < 0) + { + return null; + } + return path.substring(n + 1); } public synchronized void addLogEntry(String header, - String message) { - if (log == null) { - log = new Hashtable(); - logHeaders = new Vector(); - } + String message) + { + if (log == null) + { + log = new Hashtable(); + logHeaders = new Vector(); + } - Vector v = (Vector) log.get(header); - if (log.get(header) == null) { - v = new Vector(); - log.put(header, v); - logHeaders.addElement(header); - } - v.addElement(message); + Vector v = (Vector)log.get(header); + if (log.get(header) == null) + { + v = new Vector(); + log.put(header, v); + logHeaders.addElement(header); + } + v.addElement(message); } - public Enumeration getLogHeaders() { - return logHeaders != null ? logHeaders.elements() : null; + public Enumeration getLogHeaders() + { + return logHeaders != null ? logHeaders.elements() : null; } - public Enumeration getLogEntries(String header) { - return log != null ? ((Vector) log.get(header)).elements() : null; + public Enumeration getLogEntries(String header) + { + return log != null ? ((Vector)log.get(header)).elements() : null; } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/RetryRequestException.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/RetryRequestException.java index dbc558497..9d2d20235 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/RetryRequestException.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/RetryRequestException.java @@ -1,11 +1,14 @@ package com.jpexs.proxy; -class RetryRequestException extends Exception { - RetryRequestException() { - super(); +class RetryRequestException extends Exception +{ + RetryRequestException() + { + super(); } - - RetryRequestException(String message) { - super(message); + + RetryRequestException(String message) + { + super(message); } -} \ No newline at end of file +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ReusableThread.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ReusableThread.java new file mode 100644 index 000000000..c2621772c --- /dev/null +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ReusableThread.java @@ -0,0 +1,70 @@ +package com.jpexs.proxy; + +public class ReusableThread extends Thread +{ + private ThreadPool pool = null; + private Runnable runnable = null; + private boolean alive = true; + private long lastrun = 0; + private int used = 0; + + public ReusableThread(ThreadPool pool) + { + this.pool = pool; + } + + public synchronized void setRunnable(Runnable runnable) + { + this.runnable = runnable; + notify(); + } + + public synchronized void terminate() + { + alive = false; + notify(); + } + + public long getLastRunTime() + { + return lastrun; + } + + public int useCount() + { + return used; + } + + public void run() + { + while (alive) + { + setName("ReusableThread: idle"); + + while (runnable == null && alive) + { + synchronized (this) + { + try + { + wait(); + } + catch (InterruptedException ie) + { + } + } + } + + if (alive) + { + setName("ReusableThread: busy"); + setPriority(Thread.NORM_PRIORITY); + lastrun = System.currentTimeMillis(); + used++; + runnable.run(); + runnable = null; + pool.put(this); + } + } + } +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Server.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Server.java index 259caca2a..70d9f69e8 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Server.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/Server.java @@ -1,88 +1,161 @@ -package com.jpexs.proxy; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.List; - -/** - * Proxy server - * - * @author JPEXS - */ -public class Server extends Thread { - private ServerSocket ssocket = null; - private boolean stoppped = false; - private List replacements; - private static Server server; - private List catchedContentTypes; - private CatchedListener catchedListener; - private int port; - private ReplacedListener replacedListener; - - private Server(int port, List replacements, List catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) { - this.replacements = replacements; - this.catchedContentTypes = catchedContentTypes; - this.catchedListener = catchedListener; - this.replacedListener = replacedListener; - this.port = port; - } - - private void stopRun() { - stoppped = true; - if (ssocket != null) { - try { - ssocket.close(); - } catch (IOException e) { - - } - } - } - - /** - * Starts proxy server - * - * @param port Listening port - * @param replacements List of replacements - * @param catchedContentTypes Content types to sniff - * @param catchedListener Catched listener - */ - public static void startServer(int port, List replacements, List catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) { - stopServer(); - server = new Server(port, replacements, catchedContentTypes, catchedListener, replacedListener); - //WorkerThread.assignThread(server, "Proxy server"); - server.start(); - } - - /** - * Stops proxy server - */ - public static void stopServer() { - if (server != null) server.stopRun(); - server = null; - } - - /** - * Runs the server - */ - public void run() { - - try { - ssocket = new ServerSocket(port); - } catch (IOException e) { - System.err.println("Cannot bind to port"); - return; - } - for (; ;) { - try { - Socket sock = ssocket.accept(); - Handler handler = new Handler(sock, replacements, catchedContentTypes, catchedListener, replacedListener); - WorkerThread.assignThread(handler, "Proxy handler"); - } catch (IOException e) { - - } - if (stoppped) - break; - } - } -} +package com.jpexs.proxy; + +import java.net.InetAddress; +import java.net.Socket; +import java.net.ServerSocket; +import java.io.IOException; +import java.io.DataOutputStream; +import java.util.List; + +public class Server implements Runnable +{ + ServerSocket server = null; + boolean running = false; + + private List catchedContentTypes; + private CatchedListener catchedListener; + private ReplacedListener replacedListener; + private List replacements; + + static ThreadPool pool; + + static Server myServer; + static boolean serverRunning=false; + + static boolean stopping=false; + + public static ReusableThread getThread() + { + return pool.get(); + } + + /** + * Starts proxy server + * + * @param port Listening port + * @param replacements List of replacements + * @param catchedContentTypes Content types to sniff + * @param catchedListener Catched listener + */ + public static boolean startServer(int port, List replacements, List catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) { + stopServer(); + try { + myServer = new Server(port, replacements, catchedContentTypes, catchedListener, replacedListener); + } catch (IOException ex) { + return false; + } + pool = new ThreadPool(ProxyConfig.appName+" Threads"); + /* Startup the Janitor */ + Janitor j = new Janitor(); + j.add(pool); + getThread().setRunnable(j); + serverRunning=true; + getThread().setRunnable(myServer); + return true; + } + + + public static void stopServer() + { + if(serverRunning){ + serverRunning=false; + try { + myServer.server.close(); + } catch (IOException ex) { + + } + pool.clean(); + } + } + Server(int port,List replacements, List catchedContentTypes, CatchedListener catchedListener, ReplacedListener replacedListener) throws IOException + { + + this.replacements = replacements; + this.catchedContentTypes = catchedContentTypes; + this.catchedListener = catchedListener; + this.replacedListener = replacedListener; + + try + { + String bindaddr = ProxyConfig.bindAddress; + if (bindaddr != null && bindaddr.length() > 0) + { + server = new ServerSocket(port, 512, + InetAddress.getByName(bindaddr)); + } + else + { + server = new ServerSocket(port, 512); + } + } + catch (IOException e) + { + throw e; + } + + /* Initialize internal Httpd */ + } + + synchronized void suspend() + { + running = false; + } + + synchronized void resume() + { + running = true; + } + + + public void run() + { + Thread.currentThread().setName(ProxyConfig.appName+" Server"); + running = true; + for (;;) + { + Socket socket; + + try + { + socket = server.accept(); + } + catch (IOException e) + { + if(stopping){ + break; + } + continue; + } + + if(stopping){ + break; + } + + if (running) + { + Handler h = new Handler(socket,replacements,catchedContentTypes,catchedListener,replacedListener); + ReusableThread rt = getThread(); + rt.setRunnable(h); + } + else + { + error(socket, 503, ProxyConfig.appName+" proxy service is suspended."); + } + } + } + + void error(Socket socket, int code, String message) + { + try + { + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + out.writeBytes((new HttpError(code, message)).toString()); + out.close(); + socket.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ThreadPool.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ThreadPool.java new file mode 100644 index 000000000..1568f7000 --- /dev/null +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/ThreadPool.java @@ -0,0 +1,53 @@ +package com.jpexs.proxy; + +import java.util.*; + +public class ThreadPool implements Cleanable +{ + private String name; + private Vector pool = new Vector(); + + public ThreadPool(String name) + { + this.name = name; + } + + public synchronized ReusableThread get() + { + ReusableThread rt = null; + + if (pool.size() > 0) + { + rt = (ReusableThread)pool.firstElement(); + pool.removeElement(rt); + } + + if (rt == null) + { + rt = new ReusableThread(this); + rt.start(); + } + + return rt; + } + + public synchronized void put(ReusableThread rt) + { + pool.addElement(rt); + } + + public synchronized void clean() + { + long now = System.currentTimeMillis(); + + for (Enumeration e = pool.elements(); e.hasMoreElements(); ) + { + ReusableThread rt = (ReusableThread) e.nextElement(); + if (now - rt.getLastRunTime() >= 30000) + { + rt.terminate(); + pool.removeElement(rt); + } + } + } +} diff --git a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/WorkerThread.java b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/WorkerThread.java index 7545afdb6..991dfb885 100644 --- a/trunk/libsrc/jpproxy/src/com/jpexs/proxy/WorkerThread.java +++ b/trunk/libsrc/jpproxy/src/com/jpexs/proxy/WorkerThread.java @@ -1,142 +1,133 @@ -// Copyright (c) 1999 Brian Wellington (bwelling@xbill.org) -// Portions Copyright (c) 1999 Network Associates, Inc. - package com.jpexs.proxy; -import java.util.Vector; +import java.util.*; -/** - * An extension of a Thread that uses threads from a pool, rather than - * allocating a new thread for each assigned task. - * - * @author Brian Wellington - */ public class WorkerThread extends Thread { - private Runnable task; - private String name; +private Runnable task; +private String name; - private static int nactive = 0; - private static Vector list = new Vector(); - private static int max = 10; - private static long lifetime = 900 * 1000; /* 15 minute default */ +private static int nactive = 0; +private static Vector list = new Vector(); +private static int max = 10; +private static long lifetime = 900 * 1000; /* 15 minute default */ - private WorkerThread() { - setDaemon(true); - } +private +WorkerThread() { + setDaemon(true); +} - /** - * Sets the lifetime of an idle WorkerThread (in ms). A WorkerThread - * will remain on the idle list for this much time before exiting. This - * does not affect WorkerThreads currently idling. - */ - synchronized static void - setLifetime(long time) { - lifetime = time; - } +/** + * Sets the lifetime of an idle WorkerThread (in ms). A WorkerThread + * will remain on the idle list for this much time before exiting. This + * does not affect WorkerThreads currently idling. + */ +synchronized static void +setLifetime(long time) { + lifetime = time; +} - /** - * Sets the maximum number of WorkerThreads that can exist at any given - * time. If this value is decreased below the current number of - * WorkerThreads, this will not take effect immediately. - */ - synchronized static void - setMaxThreads(int maxThreads) { - max = maxThreads; - } +/** + * Sets the maximum number of WorkerThreads that can exist at any given + * time. If this value is decreased below the current number of + * WorkerThreads, this will not take effect immediately. + */ +synchronized static void +setMaxThreads(int maxThreads) { + max = maxThreads; +} - /** - * Obtains a WorkerThread to which a task can be assigned. If an idle - * WorkerThread is present, it is removed from the idle list and returned. - * If not, and the maximum number of WorkerThreads has not been reached, - * a new WorkerThread is created. If the maximum number has been reached, - * this blocks until a WorkerThread is free. - */ - static WorkerThread - getThread() { - WorkerThread t; - synchronized (list) { - if (list.size() > 0) { - t = (WorkerThread) list.firstElement(); - list.removeElement(t); - } else if (nactive >= max) { - while (true) { - try { - list.wait(); - } - catch (InterruptedException e) { - } - if (list.size() == 0) - continue; - t = (WorkerThread) list.firstElement(); - list.removeElement(t); - break; - } - } else - t = new WorkerThread(); - nactive++; - } - return t; - } +/** + * Obtains a WorkerThread to which a task can be assigned. If an idle + * WorkerThread is present, it is removed from the idle list and returned. + * If not, and the maximum number of WorkerThreads has not been reached, + * a new WorkerThread is created. If the maximum number has been reached, + * this blocks until a WorkerThread is free. + */ +static WorkerThread +getThread() { + WorkerThread t; + synchronized (list) { + if (list.size() > 0) { + t = (WorkerThread) list.firstElement(); + list.removeElement(t); + } + else if (nactive >= max) { + while (true) { + try { + list.wait(); + } + catch (InterruptedException e) { + } + if (list.size() == 0) + continue; + t = (WorkerThread) list.firstElement(); + list.removeElement(t); + break; + } + } + else + t = new WorkerThread(); + nactive++; + } + return t; +} - /** - * Assigns a task to a WorkerThread - * - * @param task The task to be run - * @param name The name of the task - */ - public static void - assignThread(Runnable task, String name) { - while (true) { - try { - WorkerThread t = getThread(); - synchronized (t) { - t.task = task; - t.name = name; - if (!t.isAlive()) - t.start(); - else - t.notify(); - } - return; - } - catch (IllegalThreadStateException e) { - } - } - } +/** + * Assigns a task to a WorkerThread + * @param task The task to be run + * @param name The name of the task + */ +public static void +assignThread(Runnable task, String name) { + while (true) { + try { + WorkerThread t = getThread(); + synchronized (t) { + t.task = task; + t.name = name; + if (!t.isAlive()) + t.start(); + else + t.notify(); + } + return; + } + catch (IllegalThreadStateException e) { + } + } +} - /** - * Performs the task - */ - synchronized public void - run() { - while (true) { - setName(name); - try { - task.run(); - } - catch (Throwable t) { - System.err.println(t); - } - setName("idle thread"); - synchronized (list) { - list.addElement(this); - if (nactive >= max) - list.notify(); - nactive--; - } - task = null; - try { - wait(lifetime); - } - catch (InterruptedException e) { - } - if (task == null) { - list.removeElement(this); - return; - } - } - } +/** Performs the task */ +synchronized public void +run() { + while (true) { + setName(name); + try { + task.run(); + } + catch (Throwable t) { + System.err.println(t); + } + setName("idle thread"); + synchronized (list) { + list.addElement(this); + if (nactive >= max) + list.notify(); + nactive--; + } + task = null; + try { + wait(lifetime); + } + catch (InterruptedException e) { + } + if (task == null) { + list.removeElement(this); + return; + } + } +} -} \ No newline at end of file +}