Sun Java Solaris Communities My SDN Account Join SDN
 
Article

WebServercode

 
 
1 /*
2 * An example of a very simple, 
  * multi-threaded HTTP server.
3 * Implementation notes are in 
  * WebServer.html, and also
4 * as comments in the source code.
5 */
6
7 import java.io.*;
8 import java.net.*;
9 import java.util.*;
10
11 class WebServer implements HttpConstants {
12
13  /* static class data/methods */
14
15  /* print to stdout */
16  protected static void p(String s) {
17       System.out.println(s);
18      }
19
20  /* print to the log file */ 
21  protected static void log(String s) {
22    synchronized (log) {
23        log.println(s);
24        log.flush();
25       }
26   }
27
28  static PrintStream log = null;
29 /* our server's configuration information 
    *                              is stored
30  * in these properties
31  */
32  protected static Properties props = 
                           new Properties();
33
34 /* Where worker threads stand idle */
35 static Vector threads = new Vector();
36
37 /* the web server's virtual root */
38 static File root;
39
40 /* timeout on client connections */
41 static int timeout = 0;
42
43 /* max # worker threads */
44 static int workers = 5;
45
46
47 /* load www-server.properties from java.home     
48  */    
49 static void loadProps() throws IOException 
                     {  File f = new File
    (System.getProperty(
             "java.home")+File.separator+
50  "lib"+File.separator+"www-server.properties");
51   if (f.exists()) {
52  InputStream is =new BufferedInputStream(new
53                     FileInputStream(f));
54    props.load(is);
55     is.close();
56  String r = props.getProperty("root");
57   if (r != null) {
58     root = new File(r);
59  if (!root.exists()) {
60  throw new Error(root + " doesn't exist as 
              server root");
61     }
62      }
63  r = props.getProperty("timeout");
64  if (r != null) {
65  timeout = Integer.parseInt(r);
66     }
67  r = props.getProperty("workers");
68  if (r != null) {
69   workers = Integer.parseInt(r);
70     }
71   r = props.getProperty("log");
72  if (r != null) {
73   p("opening log file: " + r);
74  log = new PrintStream(new BufferedOutputStream(
75          new FileOutputStream(r)));
76     }
77  }
78
79  /* if no properties were specified, 
     *  choose defaults */
80     if (root == null) {
81  root = new File(System.getProperty(
                           "user.dir"));
82   }
83  if (timeout <= 1000) {
84     timeout = 5000;
85    }
86   if (workers  25) {
87   workers = 5;
88   }
89   if (log == null) {
90   p("logging to stdout");
91        log = System.out;
92     }
93  }
94
95  static void printProps() {
96     p("root="+root);
97     p("timeout="+timeout);
98     p("workers="+workers);
99    }
100
101 public static void main(
         String[] a) throws Exception {
102          int port = 8080;
103          if (a.length > 0) {
104      port = Integer.parseInt(a[0]);
105          }
106     loadProps();
107     printProps();
108     /* start worker threads */
109   for (int i = 0; i < workers; ++i) {
110  Worker w = new Worker();
111   (new Thread(w, "worker #"+i)).start();
112   threads.addElement(w);
113          }
114
115  ServerSocket ss = new ServerSocket(port);
116    while (true) {
117
118   Socket s = ss.accept();
119
120  Worker w = null;  
     
121    synchronized (threads) {
122   if (threads.isEmpty()) {
123     Worker ws = new Worker();
124       ws.setSocket(s);
125      (new Thread(ws, "additional worker")).start();
126   } else {
127    w = (Worker) threads.elementAt(0);
128     threads.removeElementAt(0);
129       w.setSocket(s);
130       }
131    }
132  }
133      }
134  }
135
136    
137  class Worker extends WebServer
     implements HttpConstants, Runnable {
138   final static int BUF_SIZE = 2048;
139
140  static final byte[] EOL = 
               {(byte)'\r', (byte)'\n' };
141
142  /* buffer to use for requests */
143    byte[] buf;
144  /* Socket to client we're handling */
145  private Socket s;
146
147   Worker() {
148     buf = new byte[2048];
149    s = null;
150      }
151   
152  synchronized void setSocket(Socket s) {
153     this.s = s;
154     notify();
155      }
156
157   public synchronized void run() {
158    while(true) {
159      if (s == null) {
160    /* nothing to do */
161   try {        
          
162    wait();
163     } catch (InterruptedException e) {
164    /* should not happen */
165     continue;
166     }
167    }
168   try {
169     handleClient();
170     } catch (Exception e) {
171    e.printStackTrace();
172    }
173   /* go back in wait queue if there's fewer
174    * than numHandler connections.
175    */
176     s = null;
177   Vector pool = WebServer.threads;
178   synchronized (pool) {
179    if (pool.size() >= WebServer.workers) {
180     /* too many threads, exit this one */
181    return;
182    } else {
183   pool.addElement(this);
184       }
185      }
186    }
187 }
188   
189  void handleClient() throws IOException {
190  InputStream is = new BufferedInputStream(
                  s.getInputStream());
191 PrintStream ps = new PrintStream(
                   s.getOutputStream());
192  /* we will only block in read 
      *          for this many milliseconds
193   * before we fail with 
      *     java.io.InterruptedIOException,
194   * at which point we will 
      * abandon the connection.
195  */
196   s.setSoTimeout(WebServer.timeout);
197
198 /* zero out the buffer from last time */
199  for (int i = 0; i
200     buf[i] = 0;
201
202   try {
203  /* We only support HTTP GET/HEAD, and don't
204  * support any fancy HTTP options,
205  * so we're only interested really in
206   * the first line.
207               */
208   int nread = 0, r = 0;
209
210  outerloop:
211    while (nread < BUF_SIZE) {
212    r = is.read(buf, nread, BUF_SIZE - nread);
213   if (r == -1) {
214      /* EOF */
215       return;
216    }
217   int i = nread;
218    nread += r;
219    for (; i < nread; i++) {
220    if (buf[i] == (byte)'\n' || 
                   buf[i] == (byte)'\r') {
221      break outerloop;
222       }
223       }
224       }
225
226  /* are we doing a GET or just a HEAD */
227    boolean doingGet;
228    /* beginning of file name */
229    int index;
230   if (buf[0] == (byte)'G' &&
231     buf[1] == (byte)'E' &&
232  
233    buf[2] == (byte)'T' &&
234  
235    buf[3] == (byte)' ') {
236    doingGet = true;
237     index = 4;
238     } else if (buf[0] == (byte)'H' &&
239      buf[1] == (byte)'E' &&
240  
241  buf[2] == (byte)'A' &&
242  
243  buf[3] == (byte)'D'&&
244  
245   buf[4] == (byte)' ') {
246    doingGet = false;
247   index = 5; 
248    } else {
249   /* we don't support this method */
250  ps.print("HTTP/1.0 " + HTTP_BAD_METHOD +
251   " unsupported method type: ");
252   ps.write(buf, 0, 5);
253  ps.write(EOL);
254    ps.flush();
255    s.close();
256    return;
257     }
258
259 int i = 0;
260  for (i = index; i < nread; i++) {
261  if (buf[i] == (byte)' ') {
262    break;
263      }
264     }
265  String fname = (new String(buf, 0, index,
        i-index)).replace('/', File.separatorChar);
266   if (fname.startsWith(File.separator)) {
267    fname = fname.substring(1);
268    }
269  File targ = new File(WebServer.root, fname);
270   if (targ.isDirectory()) {
271    File ind = new File(targ, "index.html");
272   if (ind.exists()) {
273     targ = ind;
274     }
275      }
276    boolean OK = printHeaders(targ, ps);
277    if (doingGet) {
278   if (OK) {       
          
279   sendFile(targ, ps);
280   } else {
281     send404(targ, ps);
282        }
283    }
284    } finally {
285      s.close();
286          }
287      } 
288
289   boolean printHeaders(File targ, 
       PrintStream ps) throws IOException {
290      boolean ret = false;
291   int rCode = 0;
292    if (!targ.exists()) {
293    rCode = HTTP_NOT_FOUND;
294    ps.print("HTTP/1.0 " + HTTP_NOT_FOUND + " not found");
295    ps.write(EOL);
296   ret = false;
297    }  else {
298    rCode = HTTP_OK;
299   ps.print("HTTP/1.0 " + HTTP_OK+" OK");
300   ps.write(EOL);
301   ret = true;
302    }      
303  log("From " +s.getInetAddress().getHostAddress()+
304    ": GET " +targ.getAbsolutePath()+"-->"+rCode);
305   ps.print("Server: Simple java");
306    ps.write(EOL);
307    ps.print("Date: " + (new Date()));
308   ps.write(EOL);
309   if (ret) {
310    if (!targ.isDirectory()) {
311      ps.print("Content-length: "+targ.length());
312      ps.write(EOL);
313     ps.print("Last Modified: " + (new
         Date(targ.lastModified())));
314    ps.write(EOL);
315    String name = targ.getName();
316      int ind = name.lastIndexOf('.');
317   String ct = null;
318     if (ind > 0) {
319     ct = (String) map.get(name.substring(ind));
320    }
321    if (ct == null) {
322      ct = "unknown/unknown";
323      }
324     ps.print("Content-type: " + ct);
325     ps.write(EOL);
326    } else {
327      ps.print("Content-type: text/html");
328    ps.write(EOL);
329       }
330          }
331          return ret;
332      }
333
334   void send404(File targ, PrintStream ps)
                      throws IOException {
335    ps.write(EOL);
336    ps.write(EOL);
337    ps.println("Not Found\n\n"+
338    "The requested resource was not found.\n");
339      }
340
341 void sendFile(File targ, PrintStream ps)
             throws IOException {
342   InputStream is = null;
343   ps.write(EOL);
344    if (targ.isDirectory()) {
345    /* here, we take advantage of the fact
346    * that FileURLConnection will parse a directory
347    * listing into HTML for us.
348    */
349   File ind = new File(targ, "index.html");
350   if (ind.exists()) {
351     is = new FileInputStream(ind);
352      } else {
353     URL u = new URL("file", "", targ.getAbsolutePath());
354    is = u.openStream();
355     }
356    } else {
357    is = new FileInputStream(targ.getAbsolutePath());
358      }
359    
360   try {
361    int n;
362    while ((n = is.read(buf)) > 0) {
363    ps.write(buf, 0, n);
364      }
365     } finally {
366       is.close();
367          }
368      }
369
370  /* mapping of file extensions to 
      *content-types */
371  static java.util.Hashtable map = 
      new java.util.Hashtable();
372
373    static {
374          fillMap();
375    }
376  static void setSuffix(String k, String v) {
377          map.put(k, v);
378      }
379
380  static void fillMap() {
381  setSuffix("", "content/unknown");
382  setSuffix(".uu", "application/octet-stream");
383  setSuffix(".exe", "application/octet-stream");
384  setSuffix(".ps", "application/postscript");
385  setSuffix(".zip", "application/zip");
386  setSuffix(".sh", "application/x-shar");
387  setSuffix(".tar", "application/x-tar");
388  setSuffix(".snd", "audio/basic");
389  setSuffix(".au", "audio/basic");
390  setSuffix(".wav", "audio/x-wav");
391  setSuffix(".gif", "image/gif");
392  setSuffix(".jpg", "image/jpeg");
393  setSuffix(".jpeg", "image/jpeg");
394  setSuffix(".htm", "text/html");
395  setSuffix(".html", "text/html");
396  setSuffix(".text", "text/plain");
397  setSuffix(".c", "text/plain");
398  setSuffix(".cc", "text/plain");
399  setSuffix(".c++", "text/plain");
400  setSuffix(".h", "text/plain");
401  setSuffix(".pl", "text/plain");
402  setSuffix(".txt", "text/plain");
403  setSuffix(".java", "text/plain");
404      }
405
406  }
407
408  interface HttpConstants {
409  /** 2XX: generally "OK" */
410  public static final int HTTP_OK = 200;
411  public static final int HTTP_CREATED = 201;
412  public static final int HTTP_ACCEPTED = 202;
413  public static final int HTTP_NOT_AUTHORITATIVE = 203;
414  public static final int HTTP_NO_CONTENT = 204;
415  public static final int HTTP_RESET = 205;
416  public static final int HTTP_PARTIAL = 206;
417
418  /** 3XX: relocation/redirect */
419  public static final int HTTP_MULT_CHOICE = 300;
420  public static final int HTTP_MOVED_PERM = 301;
421  public static final int HTTP_MOVED_TEMP = 302;
422  public static final int HTTP_SEE_OTHER = 303;
423  public static final int HTTP_NOT_MODIFIED = 304;
424  public static final int HTTP_USE_PROXY = 305;
425
426 /** 4XX: client error */
427  public static final int HTTP_BAD_REQUEST = 400;
428  public static final int HTTP_UNAUTHORIZED = 401;
429  public static final int HTTP_PAYMENT_REQUIRED = 402;
430  public static final int HTTP_FORBIDDEN = 403;
431  public static final int HTTP_NOT_FOUND = 404;
432  public static final int HTTP_BAD_METHOD = 405;
433  public static final int HTTP_NOT_ACCEPTABLE = 406;
434  public static final int HTTP_PROXY_AUTH = 407;
435  public static final int HTTP_CLIENT_TIMEOUT = 408;
436  public static final int HTTP_CONFLICT = 409;
437  public static final int HTTP_GONE = 410;
438  public static final int HTTP_LENGTH_REQUIRED = 411;
439  public static final int HTTP_PRECON_FAILED = 412;
440  public static final int HTTP_ENTITY_TOO_LARGE = 413;
441  public static final int HTTP_REQ_TOO_LONG = 414;
442  public static final int HTTP_UNSUPPORTED_TYPE = 415;
443
444  /** 5XX: server error */
445  public static final int HTTP_SERVER_ERROR = 500;
446  public static final int HTTP_INTERNAL_ERROR = 501;
447  public static final int HTTP_BAD_GATEWAY = 502;
448  public static final int HTTP_UNAVAILABLE = 503;
449  public static final int HTTP_GATEWAY_TIMEOUT = 504;
450  public static final int HTTP_VERSION = 505;
451  }