1 package org.feistymeow.networking;
7 import org.apache.commons.logging.Log;
8 import org.apache.commons.logging.LogFactory;
10 import org.feistymeow.dragdrop.ListTransferable;
13 * Provides a lightweight way for RNS structures to be accessible over http.
15 * @Author Chris Koeritz
19 * original example thanks to Matt Mahoney, at
20 * http://cs.fit.edu/~mmahoney/cse3103/java/Webserver.java
23 public class BasicWebServer {
24 static private Log logger = LogFactory.getLog(BasicWebServer.class);
27 private boolean leaving = false; // turns to true when should stop serving.
28 servingThread socketThread;
29 private ServerSocket realSocket;
31 BasicWebServer(int portIn) {
35 public void shutDown() {
37 if (realSocket != null) {
40 } catch (IOException e) {
43 if (socketThread != null) {
48 public class servingThread implements Runnable {
49 private Thread thread;
50 private ServerSocket serverSocket;
52 servingThread(ServerSocket socket) {
53 serverSocket = socket;
54 thread = new Thread(this);
62 logger.debug("about to accept on server socket.");
63 Socket s = serverSocket.accept(); // Wait for a client to
65 logger.debug("accepted client, spawning handler.");
66 new ClientHandler(s); // Handle the client in a separate
68 } catch (Throwable cause) {
70 "exception raised while handling accepted socket",
77 // enums for outcomes? really need better reporting.
78 public int startServing() {
79 if (socketThread != null)
80 return 1; // already running outcome.
82 realSocket = new ServerSocket(port);
83 } catch (Throwable cause) {
84 logger.error("failure to start server on port " + port, cause);
86 // socket failure outcome.
88 socketThread = new servingThread(realSocket);
92 public String predictMimeType(String filename) {
94 // kludge to try one type:
95 return "text/plain;charset=utf-8";
99 * if (filename.endsWith(".html") || filename.endsWith(".htm")) return
100 * "text/html"; if (filename.endsWith(".jpg") ||
101 * filename.endsWith(".jpeg")) return "image/jpeg"; if
102 * (filename.endsWith(".gif")) return "image/gif"; if
103 * (filename.endsWith(".class")) return "application/octet-stream";
104 * return "text/plain";
108 // A ClientHandler reads an HTTP request and responds
109 class ClientHandler extends Thread {
110 private Socket socket; // The accepted socket from the Webserver
112 // Start the thread in the constructor
113 public ClientHandler(Socket s) {
118 // Read the HTTP request, respond, and close the connection
121 logger.debug("into client run(): listening for gets.");
123 // Open connections to the socket
124 BufferedReader in = new BufferedReader(new InputStreamReader(
125 socket.getInputStream()));
126 PrintStream out = new PrintStream(new BufferedOutputStream(
127 socket.getOutputStream()));
129 // Read filename from first input line "GET /filename.html ..."
130 // or if not in this format, treat as a file not found.
131 String s = in.readLine();
132 logger.debug("request is: " + s); // Log the request
134 // Attempt to serve the file. Catch FileNotFoundException and
135 // return an HTTP error "404 Not Found". Treat invalid requests
137 String filename = "";
138 StringTokenizer st = new StringTokenizer(s);
141 boolean transferFile = true;
142 // get the command first.
143 String command = st.nextToken();
144 // Parse the filename from the command.
145 if (st.hasMoreElements() && command.equalsIgnoreCase("GET")
146 && st.hasMoreElements()) {
147 filename = st.nextToken();
148 } else if (st.hasMoreElements()
149 && command.equalsIgnoreCase("HEAD")
150 && st.hasMoreElements()) {
151 filename = st.nextToken();
152 transferFile = false; // don't need to do that, just the
155 logger.error("going to blow file not found exception now.");
156 throw new FileNotFoundException(); // Bad request
158 logger.info("filename to handle is now: " + filename);
160 // Append trailing "/" with "index.html"
161 // /hmmm: may want to make this assume directory.
162 if (filename.endsWith("/"))
163 logger.error("unhandled attempt to get item ending in slash");
164 // if (filename.endsWith("/"))
165 // filename += "index.html";
167 // Remove leading / from filename
168 // / while (filename.indexOf("/") == 0)
169 // / filename = filename.substring(1);
171 // Replace "/" with "\" in path for PC-based servers
172 filename = filename.replace('/', File.separator.charAt(0));
174 logger.info("asking for rns path of " + filename);
176 // Check for illegal characters to prevent access to
178 if (filename.indexOf("..") >= 0
179 || filename.indexOf(':') >= 0
180 || filename.indexOf('|') >= 0)
181 throw new FileNotFoundException();
183 logger.info("got past filename checks for: " + filename);
186 * this doesn't actually check that trailing slash is
187 * missing! // If a directory is requested and the trailing
188 * / is missing, // send the client an HTTP request to
189 * append it. (This is // necessary for relative links to
190 * work correctly in the client). if ((new
191 * GeniiPath(filename)).isDirectory()) {
192 * out.print("HTTP/1.0 301 Moved Permanently\r\n" +
193 * "Location: /" + filename + "/\r\n\r\n"); out.close();
197 // trying to get around worrying about mime types by saying
198 // "just get this there".
199 String mimeType = predictMimeType(filename);
200 // //"application/octet-stream";
202 File source = new File(filename);
203 if (!source.exists()) {
204 logger.error("source does not exist for serving: "
207 // hmmm: below could be abstracted to more general
209 out.println("HTTP/1.1 404 Not Found\r\n"
210 + "Content-type: text/html\r\n\r\n"
211 + "<html><head></head><body>" + filename
212 + " not found</body></html>\n");
216 out.print("HTTP/1.1 200 OK\r\n" + "Content-type: "
217 + mimeType + "\r\n" + "Connection: close" + "\r\n"
218 // // + "\r\nContent-Length: " + source.size? +
222 logger.debug("closing stream for finished HEAD request.");
226 logger.debug("moving to handle GET request.");
227 InputStream f = source.openInputStream();
228 logger.debug("opened stream on source");
229 // Send file contents to client, then close the connection.
230 byte[] a = new byte[4096];
232 while ((n = f.read(a)) > 0)
234 logger.debug("wrote file back for request, closing stream.");
236 } catch (FileNotFoundException x) {
237 logger.error("failed to find requested file: " + filename);
238 out.println("HTTP/1.1 404 Not Found\r\n"
239 + "Content-type: text/html\r\n\r\n"
240 + "<html><head></head><body>" + filename
241 + " not found</body></html>\n");
244 } catch (IOException x) {
245 logger.error("exception blew out in outer area of web server",
247 // / System.out.println(x);