Clover Coverage Report
Coverage timestamp: Fri May 24 2013 13:47:27 UTC
../../../../../img/srcFileCovDistChart0.png 96% of files have more coverage
192   584   72   10.11
66   345   0.38   9.5
19     3.79  
2    
 
  HttpProxy       Line # 59 56 18 0% 0.0
  ProxyThread       Line # 235 136 54 0% 0.0
 
No Tests
 
1    package org.qedeq.kernel.bo.test;
2   
3    /* <!-- in case someone opens this in a browser... --> <pre> */
4    /*
5    * This is a simple multi-threaded Java proxy server
6    * for HTTP requests (HTTPS doesn't seem to work, because
7    * the CONNECT requests aren't always handled properly).
8    * I implemented the class as a thread so you can call it
9    * from other programs and kill it, if necessary (by using
10    * the closeSocket() method).
11    *
12    * We'll call this the 1.1 version of this class. All I
13    * changed was to separate the HTTP header elements with
14    * \r\n instead of just \n, to comply with the official
15    * HTTP specification.
16    *
17    * This can be used either as a direct proxy to other
18    * servers, or as a forwarding proxy to another proxy
19    * server. This makes it useful if you want to monitor
20    * traffic going to and from a proxy server (for example,
21    * you can run this on your local machine and set the
22    * fwdServer and fwdPort to a real proxy server, and then
23    * tell your browser to use "localhost" as the proxy, and
24    * you can watch the browser traffic going in and out).
25    *
26    * One limitation of this implementation is that it doesn't
27    * close the ProxyThread socket if the client disconnects
28    * or the server never responds, so you could end up with
29    * a bunch of loose threads running amuck and waiting for
30    * connections. As a band-aid, you can set the server socket
31    * to timeout after a certain amount of time (use the
32    * setTimeout() method in the ProxyThread class), although
33    * this can cause false timeouts if a remote server is simply
34    * slow to respond.
35    *
36    * Another thing is that it doesn't limit the number of
37    * socket threads it will create, so if you use this on a
38    * really busy machine that processed a bunch of requests,
39    * you may have problems. You should use thread pools if
40    * you're going to try something like this in a "real"
41    * application.
42    *
43    * Note that if you're using the "main" method to run this
44    * by itself and you don't need the debug output, it will
45    * run a bit faster if you pipe the std output to 'nul'.
46    *
47    * You may use this code as you wish, just don't pretend
48    * that you wrote it yourself, and don't hold me liable for
49    * anything that it does or doesn't do. If you're feeling
50    * especially honest, please include a link to nsftools.com
51    * along with the code. Thanks, and good luck.
52    *
53    * Julian Robichaux -- http://www.nsftools.com
54    */
55    import java.io.*;
56    import java.net.*;
57    import java.lang.reflect.Array;
58   
 
59    public class HttpProxy extends Thread
60    {
61    public static final int DEFAULT_PORT = 8080;
62   
63    private ServerSocket server = null;
64    private int thisPort = DEFAULT_PORT;
65    private String fwdServer = "";
66    private int fwdPort = 0;
67    private int ptTimeout = ProxyThread.DEFAULT_TIMEOUT;
68    private int debugLevel = 0;
69    private PrintStream debugOut = System.out;
70   
71   
72    /* here's a main method, in case you want to run this by itself */
 
73  0 toggle public static void main (String args[])
74    {
75  0 int port = 0;
76  0 String fwdProxyServer = "";
77  0 int fwdProxyPort = 0;
78   
79  0 if (args.length == 0)
80    {
81  0 System.err.println("USAGE: java jProxy <port number> [<fwd proxy> <fwd port>]");
82  0 System.err.println(" <port number> the port this service listens on");
83  0 System.err.println(" <fwd proxy> optional proxy server to forward requests to");
84  0 System.err.println(" <fwd port> the port that the optional proxy server is on");
85  0 System.err.println("\nHINT: if you don't want to see all the debug information flying by,");
86  0 System.err.println("you can pipe the output to a file or to 'nul' using \">\". For example:");
87  0 System.err.println(" to send output to the file prox.txt: java jProxy 8080 > prox.txt");
88  0 System.err.println(" to make the output go away: java jProxy 8080 > nul");
89  0 return;
90    }
91   
92    // get the command-line parameters
93  0 port = Integer.parseInt(args[0]);
94  0 if (args.length > 2)
95    {
96  0 fwdProxyServer = args[1];
97  0 fwdProxyPort = Integer.parseInt(args[2]);
98    }
99   
100    // create and start the jProxy thread, using a 20 second timeout
101    // value to keep the threads from piling up too much
102  0 System.err.println(" ** Starting jProxy on port " + port + ". Press CTRL-C to end. **\n");
103  0 HttpProxy jp = new HttpProxy(port, fwdProxyServer, fwdProxyPort, 20);
104  0 jp.setDebug(1, System.out); // or set the debug level to 2 for tons of output
105  0 jp.start();
106   
107    // run forever; if you were calling this class from another
108    // program and you wanted to stop the jProxy thread at some
109    // point, you could write a loop that waits for a certain
110    // condition and then calls jProxy.closeSocket() to kill
111    // the running jProxy thread
112  0 while (true)
113    {
114  0 try { Thread.sleep(3000); } catch (Exception e) {}
115    }
116   
117    // if we ever had a condition that stopped the loop above,
118    // we'd want to do this to kill the running thread
119    //jp.closeSocket();
120    //return;
121    }
122   
123   
124    /* the proxy server just listens for connections and creates
125    * a new thread for each connection attempt (the ProxyThread
126    * class really does all the work)
127    */
 
128  0 toggle public HttpProxy (int port)
129    {
130  0 thisPort = port;
131    }
132   
 
133  0 toggle public HttpProxy (int port, String proxyServer, int proxyPort)
134    {
135  0 thisPort = port;
136  0 fwdServer = proxyServer;
137  0 fwdPort = proxyPort;
138    }
139   
 
140  0 toggle public HttpProxy (int port, String proxyServer, int proxyPort, int timeout)
141    {
142  0 thisPort = port;
143  0 fwdServer = proxyServer;
144  0 fwdPort = proxyPort;
145  0 ptTimeout = timeout;
146    }
147   
148   
149    /* allow the user to decide whether or not to send debug
150    * output to the console or some other PrintStream
151    */
 
152  0 toggle public void setDebug (int level, PrintStream out)
153    {
154  0 debugLevel = level;
155  0 debugOut = out;
156    }
157   
158   
159    /* get the port that we're supposed to be listening on
160    */
 
161  0 toggle public int getPort ()
162    {
163  0 return thisPort;
164    }
165   
166   
167    /* return whether or not the socket is currently open
168    */
 
169  0 toggle public boolean isRunning ()
170    {
171  0 if (server == null)
172  0 return false;
173    else
174  0 return true;
175    }
176   
177   
178    /* closeSocket will close the open ServerSocket; use this
179    * to halt a running jProxy thread
180    */
 
181  0 toggle public void closeSocket ()
182    {
183  0 try {
184    // close the open server socket
185  0 server.close();
186    // send it a message to make it stop waiting immediately
187    // (not really necessary)
188    /*Socket s = new Socket("localhost", thisPort);
189    OutputStream os = s.getOutputStream();
190    os.write((byte)0);
191    os.close();
192    s.close();*/
193    } catch(Exception e) {
194  0 if (debugLevel > 0)
195  0 debugOut.println(e);
196    }
197   
198  0 server = null;
199    }
200   
201   
 
202  0 toggle public void run()
203    {
204  0 try {
205    // create a server socket, and loop forever listening for
206    // client connections
207  0 server = new ServerSocket(thisPort);
208  0 if (debugLevel > 0)
209  0 debugOut.println("Started jProxy on port " + thisPort);
210   
211  0 while (true)
212    {
213  0 Socket client = server.accept();
214  0 ProxyThread t = new ProxyThread(client, fwdServer, fwdPort);
215  0 t.setDebug(debugLevel, debugOut);
216  0 t.setTimeout(ptTimeout);
217  0 t.start();
218    }
219    } catch (Exception e) {
220  0 if (debugLevel > 0)
221  0 debugOut.println("jProxy Thread error: " + e);
222    }
223   
224  0 closeSocket();
225    }
226   
227    }
228   
229   
230    /*
231    * The ProxyThread will take an HTTP request from the client
232    * socket and send it to either the server that the client is
233    * trying to contact, or another proxy server
234    */
 
235    class ProxyThread extends Thread
236    {
237    private Socket pSocket;
238    private String fwdServer = "";
239    private int fwdPort = 0;
240    private int debugLevel = 0;
241    private PrintStream debugOut = System.out;
242   
243    // the socketTimeout is used to time out the connection to
244    // the remote server after a certain period of inactivity;
245    // the value is in milliseconds -- use zero if you don't want
246    // a timeout
247    public static final int DEFAULT_TIMEOUT = 20 * 1000;
248    private int socketTimeout = DEFAULT_TIMEOUT;
249   
250   
 
251  0 toggle public ProxyThread(Socket s)
252    {
253  0 pSocket = s;
254    }
255   
 
256  0 toggle public ProxyThread(Socket s, String proxy, int port)
257    {
258  0 pSocket = s;
259  0 fwdServer = proxy;
260  0 fwdPort = port;
261    }
262   
263   
 
264  0 toggle public void setTimeout (int timeout)
265    {
266    // assume that the user will pass the timeout value
267    // in seconds (because that's just more intuitive)
268  0 socketTimeout = timeout * 1000;
269    }
270   
271   
 
272  0 toggle public void setDebug (int level, PrintStream out)
273    {
274  0 debugLevel = level;
275  0 debugOut = out;
276    }
277   
278   
 
279  0 toggle public void run()
280    {
281  0 try
282    {
283  0 long startTime = System.currentTimeMillis();
284   
285    // client streams (make sure you're using streams that use
286    // byte arrays, so things like GIF and JPEG files and file
287    // downloads will transfer properly)
288  0 BufferedInputStream clientIn = new BufferedInputStream(pSocket.getInputStream());
289  0 BufferedOutputStream clientOut = new BufferedOutputStream(pSocket.getOutputStream());
290   
291    // the socket to the remote server
292  0 Socket server = null;
293   
294    // other variables
295  0 byte[] request = null;
296  0 byte[] response = null;
297  0 int requestLength = 0;
298  0 int responseLength = 0;
299  0 int pos = -1;
300  0 StringBuffer host = new StringBuffer("");
301  0 String hostName = "";
302  0 int hostPort = 80;
303   
304    // get the header info (the web browser won't disconnect after
305    // it's sent a request, so make sure the waitForDisconnect
306    // parameter is false)
307  0 request = getHTTPData(clientIn, host, false);
308  0 requestLength = Array.getLength(request);
309   
310    // separate the host name from the host port, if necessary
311    // (like if it's "servername:8000")
312  0 hostName = host.toString();
313  0 pos = hostName.indexOf(":");
314  0 if (pos > 0)
315    {
316  0 try { hostPort = Integer.parseInt(hostName.substring(pos + 1));
317    } catch (Exception e) { }
318  0 hostName = hostName.substring(0, pos);
319    }
320   
321    // either forward this request to another proxy server or
322    // send it straight to the Host
323  0 try
324    {
325  0 if ((fwdServer.length() > 0) && (fwdPort > 0))
326    {
327  0 server = new Socket(fwdServer, fwdPort);
328    } else {
329  0 server = new Socket(hostName, hostPort);
330    }
331    } catch (Exception e) {
332    // tell the client there was an error
333  0 String errMsg = "HTTP/1.0 500\nContent Type: text/plain\n\n" +
334    "Error connecting to the server:\n" + e + "\n";
335  0 clientOut.write(errMsg.getBytes(), 0, errMsg.length());
336    }
337   
338  0 if (server != null)
339    {
340  0 server.setSoTimeout(socketTimeout);
341  0 BufferedInputStream serverIn = new BufferedInputStream(server.getInputStream());
342  0 BufferedOutputStream serverOut = new BufferedOutputStream(server.getOutputStream());
343   
344    // send the request out
345  0 serverOut.write(request, 0, requestLength);
346  0 serverOut.flush();
347   
348    // and get the response; if we're not at a debug level that
349    // requires us to return the data in the response, just stream
350    // it back to the client to save ourselves from having to
351    // create and destroy an unnecessary byte array. Also, we
352    // should set the waitForDisconnect parameter to 'true',
353    // because some servers (like Google) don't always set the
354    // Content-Length header field, so we have to listen until
355    // they decide to disconnect (or the connection times out).
356  0 if (debugLevel > 1)
357    {
358  0 response = getHTTPData(serverIn, true);
359  0 responseLength = Array.getLength(response);
360    } else {
361  0 responseLength = streamHTTPData(serverIn, clientOut, true);
362    }
363   
364  0 serverIn.close();
365  0 serverOut.close();
366    }
367   
368    // send the response back to the client, if we haven't already
369  0 if (debugLevel > 1)
370  0 clientOut.write(response, 0, responseLength);
371   
372    // if the user wants debug info, send them debug info; however,
373    // keep in mind that because we're using threads, the output won't
374    // necessarily be synchronous
375  0 if (debugLevel > 0)
376    {
377  0 long endTime = System.currentTimeMillis();
378  0 debugOut.println("Request from " + pSocket.getInetAddress().getHostAddress() +
379    " on Port " + pSocket.getLocalPort() +
380    " to host " + hostName + ":" + hostPort +
381    "\n (" + requestLength + " bytes sent, " +
382    responseLength + " bytes returned, " +
383    Long.toString(endTime - startTime) + " ms elapsed)");
384  0 debugOut.flush();
385    }
386  0 if (debugLevel > 1)
387    {
388  0 debugOut.println("REQUEST:\n" + (new String(request)));
389  0 debugOut.println("RESPONSE:\n" + (new String(response)));
390  0 debugOut.flush();
391    }
392   
393    // close all the client streams so we can listen again
394  0 clientOut.close();
395  0 clientIn.close();
396  0 pSocket.close();
397    } catch (Exception e) {
398  0 if (debugLevel > 0)
399  0 debugOut.println("Error in ProxyThread: " + e);
400    //e.printStackTrace();
401    }
402   
403    }
404   
405   
 
406  0 toggle private byte[] getHTTPData (InputStream in, boolean waitForDisconnect)
407    {
408    // get the HTTP data from an InputStream, and return it as
409    // a byte array
410    // the waitForDisconnect parameter tells us what to do in case
411    // the HTTP header doesn't specify the Content-Length of the
412    // transmission
413  0 StringBuffer foo = new StringBuffer("");
414  0 return getHTTPData(in, foo, waitForDisconnect);
415    }
416   
417   
 
418  0 toggle private byte[] getHTTPData (InputStream in, StringBuffer host, boolean waitForDisconnect)
419    {
420    // get the HTTP data from an InputStream, and return it as
421    // a byte array, and also return the Host entry in the header,
422    // if it's specified -- note that we have to use a StringBuffer
423    // for the 'host' variable, because a String won't return any
424    // information when it's used as a parameter like that
425  0 ByteArrayOutputStream bs = new ByteArrayOutputStream();
426  0 streamHTTPData(in, bs, host, waitForDisconnect);
427  0 return bs.toByteArray();
428    }
429   
430   
 
431  0 toggle private int streamHTTPData (InputStream in, OutputStream out, boolean waitForDisconnect)
432    {
433  0 StringBuffer foo = new StringBuffer("");
434  0 return streamHTTPData(in, out, foo, waitForDisconnect);
435    }
436   
 
437  0 toggle private int streamHTTPData (InputStream in, OutputStream out,
438    StringBuffer host, boolean waitForDisconnect)
439    {
440    // get the HTTP data from an InputStream, and send it to
441    // the designated OutputStream
442  0 StringBuffer header = new StringBuffer("");
443  0 String data = "";
444  0 int responseCode = 200;
445  0 int contentLength = 0;
446  0 int pos = -1;
447  0 int byteCount = 0;
448   
449  0 try
450    {
451    // get the first line of the header, so we know the response code
452  0 data = readLine(in);
453  0 if (data != null)
454    {
455  0 header.append(data + "\r\n");
456  0 pos = data.indexOf(" ");
457  0 if ((data.toLowerCase().startsWith("http")) &&
458    (pos >= 0) && (data.indexOf(" ", pos+1) >= 0))
459    {
460  0 String rcString = data.substring(pos+1, data.indexOf(" ", pos+1));
461  0 try
462    {
463  0 responseCode = Integer.parseInt(rcString);
464    } catch (Exception e) {
465  0 if (debugLevel > 0)
466  0 debugOut.println("Error parsing response code " + rcString);
467    }
468    }
469    }
470   
471    // get the rest of the header info
472  0 while ((data = readLine(in)) != null)
473    {
474    // the header ends at the first blank line
475  0 if (data.length() == 0)
476  0 break;
477  0 header.append(data + "\r\n");
478   
479    // check for the Host header
480  0 pos = data.toLowerCase().indexOf("host:");
481  0 if (pos >= 0)
482    {
483  0 host.setLength(0);
484  0 host.append(data.substring(pos + 5).trim());
485    }
486   
487    // check for the Content-Length header
488  0 pos = data.toLowerCase().indexOf("content-length:");
489  0 if (pos >= 0)
490  0 contentLength = Integer.parseInt(data.substring(pos + 15).trim());
491    }
492   
493    // add a blank line to terminate the header info
494  0 header.append("\r\n");
495   
496    // convert the header to a byte array, and write it to our stream
497  0 out.write(header.toString().getBytes(), 0, header.length());
498   
499    // if the header indicated that this was not a 200 response,
500    // just return what we've got if there is no Content-Length,
501    // because we may not be getting anything else
502  0 if ((responseCode != 200) && (contentLength == 0))
503    {
504  0 out.flush();
505  0 return header.length();
506    }
507   
508    // get the body, if any; we try to use the Content-Length header to
509    // determine how much data we're supposed to be getting, because
510    // sometimes the client/server won't disconnect after sending us
511    // information...
512  0 if (contentLength > 0)
513  0 waitForDisconnect = false;
514   
515  0 if ((contentLength > 0) || (waitForDisconnect))
516    {
517  0 try {
518  0 byte[] buf = new byte[4096];
519  0 int bytesIn = 0;
520  0 while ( ((byteCount < contentLength) || (waitForDisconnect))
521    && ((bytesIn = in.read(buf)) >= 0) )
522    {
523  0 out.write(buf, 0, bytesIn);
524  0 byteCount += bytesIn;
525    }
526    } catch (Exception e) {
527  0 String errMsg = "Error getting HTTP body: " + e;
528  0 if (debugLevel > 0)
529  0 debugOut.println(errMsg);
530    //bs.write(errMsg.getBytes(), 0, errMsg.length());
531    }
532    }
533    } catch (Exception e) {
534  0 if (debugLevel > 0)
535  0 debugOut.println("Error getting HTTP data: " + e);
536    }
537   
538    //flush the OutputStream and return
539  0 try { out.flush(); } catch (Exception e) {}
540  0 return (header.length() + byteCount);
541    }
542   
543   
 
544  0 toggle private String readLine (InputStream in)
545    {
546    // reads a line of text from an InputStream
547  0 StringBuffer data = new StringBuffer("");
548  0 int c;
549   
550  0 try
551    {
552    // if we have nothing to read, just return null
553  0 in.mark(1);
554  0 if (in.read() == -1)
555  0 return null;
556    else
557  0 in.reset();
558   
559  0 while ((c = in.read()) >= 0)
560    {
561    // check for an end-of-line character
562  0 if ((c == 0) || (c == 10) || (c == 13))
563  0 break;
564    else
565  0 data.append((char)c);
566    }
567   
568    // deal with the case where the end-of-line terminator is \r\n
569  0 if (c == 13)
570    {
571  0 in.mark(1);
572  0 if (in.read() != 10)
573  0 in.reset();
574    }
575    } catch (Exception e) {
576  0 if (debugLevel > 0)
577  0 debugOut.println("Error getting header: " + e);
578    }
579   
580    // and return what we have
581  0 return data.toString();
582    }
583   
584    }