updated code for kona libs, not quite working yet.
[feisty_meow.git] / kona / src / org / feistymeow / networking / BasicWebServer.java
1 package org.feistymeow.networking;
2
3 import java.util.*;
4 import java.io.*;
5 import java.net.*;
6
7 import org.apache.commons.logging.Log;
8 import org.apache.commons.logging.LogFactory;
9
10 import org.feistymeow.dragdrop.ListTransferable;
11
12 /**
13  * Provides a lightweight way for RNS structures to be accessible over http.
14  * 
15  * @Author Chris Koeritz
16  */
17
18 /*
19  * original example thanks to Matt Mahoney, at
20  * http://cs.fit.edu/~mmahoney/cse3103/java/Webserver.java
21  */
22
23 public class BasicWebServer {
24         static private Log logger = LogFactory.getLog(BasicWebServer.class);
25
26         private int port;
27         private boolean leaving = false; // turns to true when should stop serving.
28         servingThread socketThread;
29         private ServerSocket realSocket;
30
31         BasicWebServer(int portIn) {
32                 port = portIn;
33         }
34
35         public void shutDown() {
36                 leaving = true;
37                 if (realSocket != null) {
38                         try {
39                                 realSocket.close();
40                         } catch (IOException e) {
41                         }
42                 }
43                 if (socketThread != null) {
44                         // stop it?
45                 }
46         }
47
48         public class servingThread implements Runnable {
49                 private Thread thread;
50                 private ServerSocket serverSocket;
51
52                 servingThread(ServerSocket socket) {
53                         serverSocket = socket;
54                         thread = new Thread(this);
55                         thread.start();
56                 }
57
58                 @Override
59                 public void run() {
60                         while (!leaving) {
61                                 try {
62                                         logger.debug("about to accept on server socket.");
63                                         Socket s = serverSocket.accept(); // Wait for a client to
64                                                                                                                 // connect
65                                         logger.debug("accepted client, spawning handler.");
66                                         new ClientHandler(s); // Handle the client in a separate
67                                                                                         // thread
68                                 } catch (Throwable cause) {
69                                         logger.error(
70                                                         "exception raised while handling accepted socket",
71                                                         cause);
72                                 }
73                         }
74                 }
75         }
76
77         // enums for outcomes? really need better reporting.
78         public int startServing() {
79                 if (socketThread != null)
80                         return 1; // already running outcome.
81                 try {
82                         realSocket = new ServerSocket(port);
83                 } catch (Throwable cause) {
84                         logger.error("failure to start server on port " + port, cause);
85                         return 1;
86                         // socket failure outcome.
87                 }
88                 socketThread = new servingThread(realSocket);
89                 return 0;
90         }
91
92         public String predictMimeType(String filename) {
93
94                 // kludge to try one type:
95                 return "text/plain;charset=utf-8";
96
97                 /*
98                  * 
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";
105                  */
106         }
107
108         // A ClientHandler reads an HTTP request and responds
109         class ClientHandler extends Thread {
110                 private Socket socket; // The accepted socket from the Webserver
111
112                 // Start the thread in the constructor
113                 public ClientHandler(Socket s) {
114                         socket = s;
115                         start();
116                 }
117
118                 // Read the HTTP request, respond, and close the connection
119                 public void run() {
120                         try {
121                                 logger.debug("into client run(): listening for gets.");
122
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()));
128
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
133
134                                 // Attempt to serve the file. Catch FileNotFoundException and
135                                 // return an HTTP error "404 Not Found". Treat invalid requests
136                                 // the same way.
137                                 String filename = "";
138                                 StringTokenizer st = new StringTokenizer(s);
139                                 try {
140
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
153                                                                                                 // header.
154                                         } else {
155                                                 logger.error("going to blow file not found exception now.");
156                                                 throw new FileNotFoundException(); // Bad request
157                                         }
158                                         logger.info("filename to handle is now: " + filename);
159
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";
166
167                                         // Remove leading / from filename
168                                         // / while (filename.indexOf("/") == 0)
169                                         // / filename = filename.substring(1);
170
171                                         // Replace "/" with "\" in path for PC-based servers
172                                         filename = filename.replace('/', File.separator.charAt(0));
173
174                                         logger.info("asking for rns path of " + filename);
175
176                                         // Check for illegal characters to prevent access to
177                                         // superdirectories
178                                         if (filename.indexOf("..") >= 0
179                                                         || filename.indexOf(':') >= 0
180                                                         || filename.indexOf('|') >= 0)
181                                                 throw new FileNotFoundException();
182
183                                         logger.info("got past filename checks for: " + filename);
184
185                                         /*
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();
194                                          * return; }
195                                          */
196
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";
201
202                                         File source = new File(filename);
203                                         if (!source.exists()) {
204                                                 logger.error("source does not exist for serving: "
205                                                                 + filename);
206                                                 // do something!
207                                                 // hmmm: below could be abstracted to more general
208                                                 // denial method.
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");
213                                                 out.close();
214
215                                         }
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? +
219                                                         // "\r\n"
220                                                         + "\r\n");
221                                         if (!transferFile) {
222                                                 logger.debug("closing stream for finished HEAD request.");
223                                                 out.close();
224                                                 return;
225                                         }
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];
231                                         int n;
232                                         while ((n = f.read(a)) > 0)
233                                                 out.write(a, 0, n);
234                                         logger.debug("wrote file back for request, closing stream.");
235                                         out.close();
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");
242                                         out.close();
243                                 }
244                         } catch (IOException x) {
245                                 logger.error("exception blew out in outer area of web server",
246                                                 x);
247                                 // / System.out.println(x);
248                         }
249                 }
250         }
251 }