View Javadoc

1   package com.bradmcevoy.http.http11;
2   
3   import com.bradmcevoy.http.*;
4   import com.bradmcevoy.http.Request.Method;
5   import com.bradmcevoy.http.exceptions.BadRequestException;
6   import com.bradmcevoy.http.exceptions.ConflictException;
7   import com.bradmcevoy.http.exceptions.NotAuthorizedException;
8   import java.util.Date;
9   import java.util.Map;
10  
11  import java.util.regex.Matcher;
12  import java.util.regex.Pattern;
13  import org.slf4j.Logger;
14  import org.slf4j.LoggerFactory;
15  
16  public class GetHandler implements ExistingEntityHandler {
17  
18      private static final Logger log = LoggerFactory.getLogger( GetHandler.class );
19      private final Http11ResponseHandler responseHandler;
20      private final HandlerHelper handlerHelper;
21      private final ResourceHandlerHelper resourceHandlerHelper;
22  
23      public GetHandler( Http11ResponseHandler responseHandler, HandlerHelper handlerHelper ) {
24          this.responseHandler = responseHandler;
25          this.handlerHelper = handlerHelper;
26          this.resourceHandlerHelper = new ResourceHandlerHelper( handlerHelper, responseHandler );
27      }
28  
29      @Override
30      public void process( HttpManager manager, Request request, Response response ) throws NotAuthorizedException, ConflictException, BadRequestException {
31          log.debug( "process" );
32          this.resourceHandlerHelper.process( manager, request, response, this );
33      }
34  
35      @Override
36      public void processResource( HttpManager manager, Request request, Response response, Resource r ) throws NotAuthorizedException, ConflictException, BadRequestException {
37          manager.onGet( request, response, r, request.getParams() );
38          resourceHandlerHelper.processResource( manager, request, response, r, this, true, request.getParams(), null );
39      }
40  
41      public void processExistingResource( HttpManager manager, Request request, Response response, Resource resource ) throws NotAuthorizedException, BadRequestException, ConflictException {
42          if( log.isTraceEnabled() ) {
43              log.trace( "process: " + request.getAbsolutePath() );
44          }
45          GetableResource r = (GetableResource) resource;
46          if( checkConditional( r, request ) ) {
47              if( log.isTraceEnabled() ) {
48                  log.trace( "respond not modified with: " + responseHandler.getClass().getCanonicalName() );
49              }
50              responseHandler.respondNotModified( r, response, request );
51              return;
52          }
53  
54          sendContent( manager, request, response, r, request.getParams() );
55      }
56  
57      public Range getRange( Request requestInfo ) {
58          // Thanks Igor!
59          String rangeHeader = requestInfo.getRangeHeader();
60          if( rangeHeader == null ) return null;
61          final Matcher matcher = Pattern.compile( "\\s*bytes\\s*=\\s*(\\d+)-(\\d+)" ).matcher( rangeHeader );
62          if( matcher.matches() ) {
63              return new Range( Long.parseLong( matcher.group( 1 ) ), Long.parseLong( matcher.group( 2 ) ) );
64          }
65          return null;
66      }
67  
68      /** Return true if the resource has not been modified
69       */
70      private boolean checkConditional( GetableResource resource, Request request ) {
71          // If maxAgeSeconds is null then we do not cache
72          if( resource.getMaxAgeSeconds( request.getAuthorization() ) == null ) {
73              log.trace( "resource has null max age, so not modified response is disabled" );
74              return false;
75          }
76          if( checkIfMatch( resource, request ) ) {
77              return true;
78          }
79          if( checkIfModifiedSince( resource, request ) ) {
80              log.trace( "is not modified since" );
81              return true;
82          }
83          if( checkIfNoneMatch( resource, request ) ) {
84              return true;
85          }
86          return false;
87      }
88  
89      private boolean checkIfMatch( GetableResource handler, Request requestInfo ) {
90          return false;   // TODO: not implemented
91      }
92  
93      /**
94       *
95       * @param resource
96       * @param requestInfo
97       * @return - true if the resource has NOT been modified since that date in the request
98       */
99      private boolean checkIfModifiedSince( GetableResource resource, Request requestInfo ) {
100         log.trace( "checkIfModifiedSince" );
101         Long maxAgeSecs = resource.getMaxAgeSeconds( requestInfo.getAuthorization() );
102 
103         if( maxAgeSecs == null ) {
104             log.trace( "checkIfModifiedSince: null max age" );
105             return false; // if null, always generate a fresh response
106         } else {
107             log.trace( "checkIfModifiedSince with maxAge" );
108             Date dtRequest = requestInfo.getIfModifiedHeader();
109             if( dtRequest == null ) {
110                 log.trace( " no modified date header" );
111                 return false;
112             }
113             long timeNow = System.currentTimeMillis();
114             long timeRequest = dtRequest.getTime() + 1000; // allow for rounding to nearest second
115             long timeElapsed = timeNow - timeRequest;
116             if( timeElapsed > maxAgeSecs ) {
117                 log.trace( "its been longer then the max age period, so generate fresh response" );
118                 return false;
119             } else {
120                 Date dtCurrent = resource.getModifiedDate();
121                 if( dtCurrent == null ) {
122                     if( log.isTraceEnabled() ) {
123                         log.trace( "no modified date on resource: " + resource.getClass().getCanonicalName() );
124                     }
125                     return true;
126                 }
127 
128                 long timeActual = dtCurrent.getTime();
129                 boolean unchangedSince = ( timeRequest >= timeActual );
130                 if( log.isTraceEnabled() ) {
131                     log.trace( "times as long: " + dtCurrent.getTime() + " - " + dtRequest.getTime() );
132                     log.trace( "checkModifiedSince: actual: " + dtCurrent + " - request:" + dtRequest + " = " + unchangedSince + " (true indicates no change)" );
133                 }
134 
135                 // If the modified time requested is greater or equal then the actual modified time, do not generate response
136                 return unchangedSince;
137             }
138         }
139     }
140 
141     private boolean checkIfNoneMatch( GetableResource handler, Request requestInfo ) {
142         return false;   // TODO: not implemented
143     }
144 
145     @Override
146     public String[] getMethods() {
147         return new String[]{Request.Method.GET.code, Request.Method.HEAD.code};
148     }
149 
150     @Override
151     public boolean isCompatible( Resource handler ) {
152         return ( handler instanceof GetableResource );
153     }
154 
155     private void sendContent( HttpManager manager, Request request, Response response, GetableResource resource, Map<String, String> params ) throws NotAuthorizedException, BadRequestException {
156         try {
157             if( request.getMethod().equals( Method.HEAD ) ) {
158                 responseHandler.respondHead( resource, response, request );
159             } else {
160                 Range range = getRange( request );
161                 if( range != null ) {
162                     log.trace( "partial" );
163                     responseHandler.respondPartialContent( resource, response, request, params, range );
164                 } else {
165                     if( log.isTraceEnabled() ) {
166                         log.trace( "normal content: " + responseHandler.getClass().getCanonicalName() );
167                     }
168                     responseHandler.respondContent( resource, response, request, params );
169                 }
170             }
171         } catch( NotAuthorizedException notAuthorizedException ) {
172             throw notAuthorizedException;
173         } catch( BadRequestException badRequestException ) {
174             throw badRequestException;
175         } catch( Throwable e ) {
176             log.error( "Exception sending content for:" + request.getAbsolutePath() + " of resource type: " + resource.getClass().getCanonicalName() );
177             throw new RuntimeException( e );
178         }
179     }
180 }