Webreference-Getting out of the Sandbox: Building an Applet Proxy Server | 4
A Multi-threaded Server
In the UNIX world, many multi-user network servers create additional copies of themselves to handle incoming requests from clients. If you look at the list of running programs on a Web server that runs Apache, you will see 10 copies of "httpd" running. Since Java does not have a way to create new processes, the multi-threading mechanism is used instead. Each thread handles a single client. When it is finished, it is returned to the thread pool to accept another. The default design can handle five "users" simultaneously, though it is really limited by the host server's memory and performance.
The proxy server is built on top of a generic multi-threaded server class. The name of the game, again, is creating code we can use elsewhere, and a networked server is a common enough design element that it doesn't hurt to do a little extra work. The multi-threaded server has a private Vector called threadPool that stores a collection of service threads, miniature runnable pieces of code that can be dispatched to handle clients. Since the class needs to work with any possible service thread and listen to any port, its primary constructor takes a thread class and a port as its arguments. This class must extend ServiceThread or the server will throw an exception when it starts.
You start the server by calling the init() method. This method creates threadPoolSize (the default number is 5) threads for handling clients and starts them running. Note, as mentioned above, the check to make sure the serviceThreadClass is an instance of ServiceThread.
Once each ServiceThread starts running , it loops continuously, attempting to accept() a connection from a client. When it does, it prints out a message on the console that it got a client, and then immediately passes the buck to the method service(). The HttpProxyServiceThread class simply defines service() to talk to the client according to the protocol we have already outlined. It loops, looking for "PROXY" commands and responds in kind. It cuts off the connection with the client by ending the service() call when it sees QUIT.
The meat of the proxy server is in bold below. It attempts to open the URL specified in the PROXY statement. If the connection is made properly, it copies the entire remote file into a buffer in memory. It tells the client the length of the buffer with Content-length: and then prints out the entire set of data. This is the middle tier that connects the applet to the outside world, and does the actual proxying.
public void service(InputStream in, OutputStream out) throws Exception { PrintStream os = new PrintStream(out); DataInputStream is = new DataInputStream(in); os.println("Applet HTTP Proxy Server 1.0, (C) WebConcepts, LLC, 1997"); while (true) { String commandLine = is.readLine(); if (commandLine.regionMatches(true, 0, "PROXY ", 0, 6)) { String proxyUrlString = commandLine.substring(6); System.out.println("proxying to " + proxyUrlString); // fetch the file specified in the URL and redirect it to the client URL proxyURL = new URL(proxyUrlString); InputStream urlStream = proxyURL.openStream(); // read the stream into buffer byte[] buffer = new byte[0]; byte[] chunk = new byte[4096]; int count; while ((count = urlStream.read(chunk)) >= 0) { byte [] t = new byte[buffer.length + count]; System.arraycopy(buffer, 0, t, 0, buffer.length); System.arraycopy(chunk, 0, t, buffer.length, count); buffer = t; } // write the buffer to the output stream os.println("+OK URL fetched; data to follow"); os.println("Content-length: " + buffer.length); os.write(buffer); } else if (commandLine.regionMatches(true, 0, "QUIT", 0, 4)) { os.println("+OK logging out of proxy server"); return; } else { os.println("+ERR unknown directive"); continue; } } }
Comments are welcome
Created: Oct. 27, 1997Revised: Oct. 30, 1997
URL: https://webreference.com/dev/proxy/multi.html