package com.bradmcevoy.http.http11;
import com.bradmcevoy.http.*;
import com.bradmcevoy.http.Request.Method;
import com.bradmcevoy.http.exceptions.BadRequestException;
import com.bradmcevoy.http.exceptions.ConflictException;
import com.bradmcevoy.http.exceptions.NotAuthorizedException;
import java.util.Date;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GetHandler implements ExistingEntityHandler {
private static final Logger log = LoggerFactory.getLogger( GetHandler.class );
private final Http11ResponseHandler responseHandler;
private final HandlerHelper handlerHelper;
private final ResourceHandlerHelper resourceHandlerHelper;
public GetHandler( Http11ResponseHandler responseHandler, HandlerHelper handlerHelper ) {
this.responseHandler = responseHandler;
this.handlerHelper = handlerHelper;
this.resourceHandlerHelper = new ResourceHandlerHelper( handlerHelper, responseHandler );
}
public void process( HttpManager manager, Request request, Response response ) throws NotAuthorizedException, ConflictException, BadRequestException {
this.resourceHandlerHelper.process( manager, request, response, this );
}
public void processResource( HttpManager manager, Request request, Response response, Resource r ) throws NotAuthorizedException, ConflictException, BadRequestException {
manager.onGet( request, response, r, request.getParams() );
resourceHandlerHelper.processResource( manager, request, response, r, this, true, request.getParams(), null);
}
public void processExistingResource( HttpManager manager, Request request, Response response, Resource resource) throws NotAuthorizedException, BadRequestException, ConflictException {
// log.debug( "process: " + request.getAbsolutePath() );
GetableResource r = (GetableResource) resource;
if( checkConditional( r, request ) ) {
responseHandler.respondNotModified( r, response, request );
return;
}
sendContent( manager, request, response, r, request.getParams() );
}
public Range getRange( Request requestInfo ) {
// Thanks Igor!
String rangeHeader = requestInfo.getRangeHeader();
if( rangeHeader == null ) return null;
final Matcher matcher = Pattern.compile( "\\s*bytes\\s*=\\s*(\\d+)-(\\d+)" ).matcher( rangeHeader );
if( matcher.matches() ) {
return new Range( Long.parseLong( matcher.group( 1 ) ), Long.parseLong( matcher.group( 2 ) ) );
}
return null;
}
/** Return true if the resource has not been modified
*/
private boolean checkConditional( GetableResource resource, Request request ) {
// If maxAgeSeconds is null then we do not cache
if( resource.getMaxAgeSeconds( request.getAuthorization()) == null ) {
return false;
}
if( checkIfMatch( resource, request ) ) {
return true;
}
if( checkIfModifiedSince( resource, request ) ) {
return true;
}
if( checkIfNoneMatch( resource, request ) ) {
return true;
}
return false;
}
private boolean checkIfMatch( GetableResource handler, Request requestInfo ) {
return false; // TODO: not implemented
}
/**
*
* @param resource
* @param requestInfo
* @return - true if the resource has NOT been modified since that date in the request
*/
private boolean checkIfModifiedSince( GetableResource resource, Request requestInfo ) {
Long maxAgeSecs = resource.getMaxAgeSeconds( requestInfo.getAuthorization());
if( maxAgeSecs == null ) {
return false; // if null, always generate a fresh response
} else {
Date dtRequest = requestInfo.getIfModifiedHeader();
if( dtRequest == null ) return false;
Date dtCurrent = resource.getModifiedDate();
if( dtCurrent == null ) return true;
long timeNow = System.currentTimeMillis();
long timeRequest = dtRequest.getTime() + 1000; // allow for rounding to nearest second
long timeElapsed = timeNow - timeRequest;
if( timeElapsed > maxAgeSecs) {
// its been longer then the max age period, so generate fresh response
return false;
} else {
long timeActual = dtCurrent.getTime();
// log.debug("times as long: " + dtCurrent.getTime() + " - " + dtRequest.getTime());
boolean unchangedSince = ( timeRequest >= timeActual );
// log.debug("checkModifiedSince: actual: " + dtCurrent + " - request:" + dtRequest + " = " + unchangedSince + " (true indicates no change)");
// If the modified time requested is greater or equal then the actual modified time, do not generate response
return unchangedSince;
}
}
}
private boolean checkIfNoneMatch( GetableResource handler, Request requestInfo ) {
return false; // TODO: not implemented
}
public String[] getMethods() {
return new String[]{Request.Method.GET.code, Request.Method.HEAD.code};
}
public boolean isCompatible( Resource handler ) {
return ( handler instanceof GetableResource );
}
private void sendContent( HttpManager manager, Request request, Response response, GetableResource resource, Map<String, String> params ) throws NotAuthorizedException, BadRequestException {
try {
if( request.getMethod().equals( Method.HEAD ) ) {
responseHandler.respondHead( resource, response, request );
} else {
Range range = getRange( request );
if( range != null ) {
log.trace( "partial");
responseHandler.respondPartialContent( resource, response, request, params, range );
} else {
log.trace( "normal content");
responseHandler.respondContent( resource, response, request, params );
}
}
} catch( NotAuthorizedException notAuthorizedException ) {
throw notAuthorizedException;
} catch( BadRequestException badRequestException ) {
throw badRequestException;
} catch(Throwable e) {
log.error( "Exception sending content for:" + request.getAbsolutePath() + " of resource type: " + resource.getClass().getCanonicalName());
throw new RuntimeException( e );
}
}
}