Sun Java Solaris Communities My SDN Account
 
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  }

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.