Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: raining/server/AdvServer.java


1   /*
2    * $Author: rahul_kumar $
3    * $Id: AdvServer.java,v 1.2 2003/10/18 11:32:15 rahul_kumar Exp rahul $
4    */
5    
6   package raining.server;
7   import raining.core.*;
8   import raining.protocol.http.*;
9   
10  import java.util.*;
11  import java.nio.channels.*;
12  import java.nio.*;
13  import java.text.SimpleDateFormat;
14  import EDU.oswego.cs.dl.util.concurrent.*;
15  
16  /**
17   * A more functional server that permits handlers and protocols to be
18   * defined and passed in.
19   * TODO: refactor the http processing out of here, so it can be reused.
20   * TODO: read from priority file
21   * TODO: handle different mime-types. ??
22   * TODO: handle errors 400: if request not a GET
23   * TODO: 304 if not modified since
24   * TODO: send last-modified
25   * TODO: try later, and close if beyond a limit
26   * TODO: flow control, if beyond a limit, rather than just close conn
27   * TODO: take care of absolute URL
28   *
29   *
30   */
31  
32  public class AdvServer extends NioSocket {
33      public static void main (String args[]){
34          try {
35              String host = "localhost";
36              int port = 8080;
37  
38              /* read from the properties file */
39              Properties serverProps = new Properties();
40              try {
41                  serverProps.load(new java.io.FileInputStream("server.properties"));
42                  if (serverProps != null){
43                      String sport = serverProps.getProperty("server.port", "8080");
44                      port = Integer.parseInt(sport);
45                  }
46              } catch (Exception exc) { System.err.println( P+" L 41 EXC:"+ exc.toString()); exc.printStackTrace();  } 
47  
48              /* if user has overridden port on commandline, use that. */
49              if (args.length > 0){
50                  host = args[0];
51                  port = Integer.parseInt(args[1]);
52              }
53  
54              AdvServer httpserver = new AdvServer(host,port);
55              if (serverProps != null){
56                  httpserver.setServerProperties(serverProps);
57              }
58  
59              NioSocket.setDebug(false);
60              NioSocket.start();
61          } catch (Exception exc) { System.err.println( P+" Main 22 EXC:"+ exc.toString()); exc.printStackTrace(); } 
62      }
63      public static final byte[] http503response;
64      static {
65          http503response =  Http503Response.getStaticContent() ;
66      }
67  
68  
69      /** Constructor taking port and host of server to connect to.
70       * It  will listen on port on localhost. 
71       * @param host not used 
72       * @param port port to listen on
73       */
74      public AdvServer (String host, int port) throws Exception {
75          super();
76          try {
77              this.create_server_socket(port);
78          } catch (Exception exc) { System.err.println( P+" 30 EXC:"+ exc.toString()); exc.printStackTrace(); } 
79  
80  
81  
82      }
83      /** Set the properties file to be used by the server.
84       */
85      public void setServerProperties (Properties props){
86          serverProps = props;
87          
88          try {
89              if (serverProps.getProperty("server.priority_handler") != null){
90                  Class c = Class.forName(serverProps.getProperty("server.priority_handler"));
91                  this.setPriorityHandler( (PriorityHandler) c.newInstance() );
92              }
93          } catch (Exception exc) 
94          { 
95              System.err.println( P+" L 86 EXC:"+ exc.toString()); exc.printStackTrace(); 
96          } 
97          conf_max_connections = Integer.parseInt(serverProps.getProperty("server.max_connections"));
98          conf_try_later = Integer.parseInt(serverProps.getProperty("server.try_later"));
99          
100     }
101 
102 
103     /** when a connection is accepted, create a new NIO object for the
104      * incoming socket.
105      */
106     public void handle_accept(){
107         try {
108             SocketChannel schannel = this.accept();
109 
110             /* if this connection exceeds the maximum configured
111              * connections, then close it.
112              */
113             if (activeSocketCount >= conf_max_connections){
114                 rejectcount++;
115                 try {
116                     schannel.close();
117                 } catch (Exception exc) { System.err.println( P+" L 108 EXC:"+ exc.toString());  } 
118                 return;
119             } else if (activeSocketCount >= conf_try_later){
120                 // we could create a new Incoming and ask it to
121                 // write. I am trying this, hoping it will be lighter.
122                 // This write could fail, or write partially - to be
123                 // tested.
124                 // Since i have not created an NioSocket therefore
125                 // activeSocketCount doesnt go up. I could increase it
126                 // from here itself - DIRTY then there would be a
127                 // discrepancy between the map size and
128                 // activeSocketCount!!
129                 // If this is a single thread, and i am writing and
130                 // closing here itself, then the count can NEVER go beyond try_later
131                 // to max. that would only be possible if i made it go
132                 // through the normal route, which i want to avoid.
133                 try {
134                     schannel.configureBlocking(false);
135                     int size = schannel.write ( ByteBuffer.wrap(http503response) );
136                     rejectcount++;
137                 } catch (Exception exc) { System.err.println( P+" L 129 EXC:"+ exc.toString());  } 
138                 finally {
139                     try {
140                     schannel.close();
141                     } catch (Exception exc) { } 
142                 }
143                 return;
144             }
145             if (handle_priority_on_accept){
146                 //priorityHandler = new PriorityHandler(schannel);
147                 if (priorityHandler.host_priority(schannel) == -1){
148                     schannel.close();
149                     return;
150                 }
151                 // can we put read in thread with LP
152             }
153             Incoming insock = new Incoming (schannel, priorityHandler);
154             insock.connected_time = System.currentTimeMillis();
155 
156             insock.handle_read();
157 
158         } catch (Exception exc) { System.err.println( "Nio handle accept 675 EXC:"+ exc.toString()); exc.printStackTrace(); } 
159         accept_accounting();
160     }
161 
162     /** set the given handler as a priorityHandler.
163      * @param ph priorityHandler to set
164      */
165     public void setPriorityHandler( PriorityHandler ph){
166         this.priorityHandler = ph;
167         handle_priority_on_accept = true;
168     }
169     
170     //// VARIABLES START
171     
172     private final static String P = "AdvServer";
173     /** flag to denote whether priority handling is to be done. */
174     public boolean handle_priority_on_accept = false;
175     public PriorityHandler priorityHandler;
176     /** are we precalculating headers, are we using "stale" headers.
177      * this should be from a props file.
178      */
179     public final static boolean precalcheaders = true;
180     /** the name of the default file to be served in / given in http
181      * request.
182      * TODO this is to be picked from server.properties.
183      */ 
184     public final static String defaultfile = "index.html";
185     public static Properties serverProps = null;
186     protected int rejectcount = 0;
187     protected int conf_try_later = 99999;
188     protected int conf_max_connections = 99999;
189 } // end of class AdvServer
190 
191 
192 /** this class represents an incoming TCP socket.
193  */
194 class Incoming extends NioSocket {
195 
196     public static final byte[] http400response;
197     public static final byte[] http501response;
198     static {
199         http400response =  Http400Response.getStaticContent() ;
200         http501response =  Http501Response.getStaticContent() ;
201     }
202     public Incoming (SocketChannel sch, PriorityHandler ph) throws Exception {
203         super(sch);
204         schannel = sch;
205         priorityHandler = ph;
206     }
207     public void setPriorityHandler( PriorityHandler ph){
208         this.priorityHandler = ph;
209     }
210 
211     /** Send some data back once the read has terminated.
212      * Usually you
213     * would evaluate the data and then decide what to send back.
214     * This was earlier handle_terminator, now read_complete
215      */
216     public void handle_read_complete(String mdata){
217 
218         // this can be used to close the channel if a read has not
219         // completed fast enough.
220         read_complete_time = System.currentTimeMillis();
221         read_complete = true;
222         //System.out.println("handle_read_complete"+this.id);
223         request = new HttpRequest ((SocketChannel)this.channel(), mdata);
224         // i could do this in processor, or here
225         if (!request.getOperation().equals("GET")){
226             try {
227                 schannel.configureBlocking(false);
228                 int size = schannel.write ( ByteBuffer.wrap(http501response) );
229                 rejectcount++;
230             } catch (Exception exc) { System.err.println( P+" L 129 EXC:"+ exc.toString());  } 
231             finally {
232                 try {
233                     schannel.close();
234                 } catch (Exception exc) { } 
235             }
236             return;
237         }
238         // i feel the next stuff should also go into the thread.
239         // we will add throttling here so this code should not be in
240         // main thread.
241         read_priority = priorityHandler.read_priority(request);
242         if (read_priority == -1){
243             handle_close();
244         }
245         // log to a file the host, timestamp and request.
246         //
247         // launch process, which then registers write with selector as
248         // per reactor
249         try {
250             pool.execute(new Processer(NioSocket.selector, request, this));
251         } catch (Exception exc) { System.err.println( P+":"+this.id+" L 132 EXC:"+ exc.toString()); exc.printStackTrace(); } 
252 
253         //setSendData("OK.\r\n\r\n"+this.id+"\r\n<html>Now the real stuff starts.</html>\r\n");
254     }
255 
256     /** Close the channel when a read completes.
257      * I am putting this here,
258     * just in case i decide to remove it from NioSocket someday.
259      */
260     public void handle_write_complete(){
261         //hwc++;
262         handle_close();
263     }
264 
265     /** Is the request  from client complete. 
266     * We could also kill the
267     * read if the size is beyond what we expect or 
268     * taking longer than  expected,  
269     * to take care of DOS attacks */
270     public boolean is_read_complete(){ 
271         return (rsb.indexOf(terminator) > -1);
272     }
273 
274     public long connected_time;
275     public long read_complete_time;
276     public boolean read_complete = false;
277     public int read_priority;
278     public Request request;
279     public PriorityHandler priorityHandler;
280     protected SocketChannel schannel;
281     public int rejectcount = 0; // this is a 400 reject count - i need to join the 2
282     //public static int hwc = 0; // this is a 400 reject count - i need to join the 2
283     public final static String terminator = "\r\n\r\n";
284     private final static String P = "Incoming";
285 
286 
287     /** Instance of pooled executor.
288      * We may make one of low prio and one
289      * for high prio jobs. 
290      * TODO set constr parameters as per requirement
291      * after studying the docs carefully.
292      * With no parameters, it has no max, a min of 1.
293      */
294     static PooledExecutor pool = new PooledExecutor();
295     
296 
297 } // end of class Incoming
298 
299 /** Processes incoming request as a runnable thread.
300  * This thread is fed to the PooledExecutor.
301  */
302 class Processer implements Runnable {
303     Selector selector;
304     Request request;
305     NioSocket nio;
306     static UrlCache urlCache = new UrlCache(1000);
307 
308     public Processer (Selector sel, Request request, NioSocket nio){
309         this.selector = sel;
310         this.request = request;
311         this.nio = nio;
312 
313     }
314     public void run() { process_and_handoff(); }
315     /** process incoming request object and
316      * send back result.
317      * We would read from a local file and send back result.
318      * I need to catch a FNF 404 and move things into try blk.
319      */
320     public void process_and_handoff(){
321 
322         //byte[] content = null;
323         ByteBuffer content = null;
324         try {
325 
326             String file = request.getURL();
327             // pick this up from a file.
328             if (file.length()==0)
329             {
330                 // need to handle a bad request.
331                 //nio.setSendData(http400response);
332                 nio.setSendData( Http400Response.getStaticContent());
333                 //rejectcount++;
334 
335             }
336             // defaults to come from a file.
337             if (file.length()==1 && file.charAt(0) == '/')
338                 file = '/'+ AdvServer.defaultfile;
339             else if (file.charAt(file.length()-1) == '/')
340                 file = file + AdvServer.defaultfile;
341 
342             // this contains content including a 200 OK response
343             FileInfo fi = urlCache.get(file);
344             String modsince = request.findValue("If-Modified-Since");
345             if (modsince != null){
346                 Date sincedate = RFC822Date(modsince);
347                 if (!fi.ifModifiedSince(sincedate.getTime())){
348                     nio.setSendData( Http304Response.getStaticContent() );
349                     return;
350                 }
351             }
352             content = fi.getContent();
353             // i am caching the headers, thus i can send the data off
354             if (AdvServer.precalcheaders){
355         nio.setSendData( content );
356             }
357       
358             else {
359               
360                 // XXX this is where a gathering write will be abso
361                 // essential
362                 Response response = new HttpOKResponse(request,content.array(), fi.lastModified);
363                 nio.setSendData(response.getBytes());
364             }
365         } catch (java.io.FileNotFoundException exc) {
366             //System.err.println( P+" L EXC:"+ exc.toString()); exc.printStackTrace();
367        
368             System.out.println(  "File not found! "+ exc.toString());
369             Response response = new Http404Response(request);
370             nio.setSendData(response.getBytes());
371         } catch (Exception exc) { 
372           // if a cancelled key is thrown, when the client closes, should i close
373           // the socket
374       System.err.println("Maybe i should close the channel here?? ");
375           System.err.println( P+" L EXC:"+ exc.toString()); exc.printStackTrace(); } 
376         //nio.setSendData("HTTP/1.1 200 OK.\r\nContent-Length: "+content.length()+"\r\n\r\n"+ content);
377     }
378     static final String P="Processor";
379 
380     /** parses an incoming date string as per RFC822.
381      * Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
382      * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
383      * Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
384      *
385      * Should go into a new class HttpUtils.
386      */
387     public static java.util.Date RFC822Date (String sdate){
388 
389         String df = null;
390         if (sdate.charAt(3)==',')
391             df="EEE, dd MMM yyyy HH:mm:ss 'GMT'";
392         else 
393         if (sdate.charAt(6)==',')
394             df="EEEE, dd-MMM-yy HH:mm:ss 'GMT'";
395         else
396             df="EEE MMM dd HH:mm:ss yyyy";
397         SimpleDateFormat fo =
398             new SimpleDateFormat (df, Locale.US);
399         //fo.setTimeZone(TimeZone.getTimeZone("GMT"));
400         try {
401             Date d = fo.parse (sdate);
402             return d;
403         } catch (Exception exc) { 
404             System.err.println(  "403 RFC822Date :"+ exc.toString());
405         }
406         return null;
407 
408     }
409 } // end of class
410