package com.bradmcevoy.http.http11;
import com.bradmcevoy.http.*;
import com.bradmcevoy.http.Response.Status;
import com.bradmcevoy.http.exceptions.BadRequestException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bradmcevoy.http.exceptions.NotAuthorizedException;
/**
*
*/
public class DefaultHttp11ResponseHandler implements Http11ResponseHandler {
private static final Logger log = LoggerFactory.getLogger( DefaultHttp11ResponseHandler.class );
public static final String METHOD_NOT_ALLOWED_HTML = "<html><body><h1>Method Not Allowed</h1></body></html>";
public static final String NOT_FOUND_HTML = "<html><body><h1>${url} Not Found (404)</h1></body></html>";
public static final String METHOD_NOT_IMPLEMENTED_HTML = "<html><body><h1>Method Not Implemented</h1></body></html>";
public static final String CONFLICT_HTML = "<html><body><h1>Conflict</h1></body></html>";
public static final String SERVER_ERROR_HTML = "<html><body><h1>Server Error</h1></body></html>";
private final AuthenticationService authenticationService;
private final ETagGenerator eTagGenerator;
public DefaultHttp11ResponseHandler( AuthenticationService authenticationService ) {
this.authenticationService = authenticationService;
this.eTagGenerator = new DefaultETagGenerator();
}
public DefaultHttp11ResponseHandler( AuthenticationService authenticationService, ETagGenerator eTagGenerator ) {
this.authenticationService = authenticationService;
this.eTagGenerator = eTagGenerator;
}
public String generateEtag( Resource r ) {
return eTagGenerator.generateEtag( r );
}
public void respondWithOptions( Resource resource, Response response, Request request, List<String> methodsAllowed ) {
response.setStatus( Response.Status.SC_OK );
response.setAllowHeader( methodsAllowed );
response.setContentLengthHeader( (long) 0 );
}
public void respondNotFound( Response response, Request request ) {
response.setStatus( Response.Status.SC_NOT_FOUND );
response.setContentTypeHeader( "text/html" );
response.setStatus( Response.Status.SC_NOT_FOUND );
PrintWriter pw = new PrintWriter( response.getOutputStream(), true );
String s = NOT_FOUND_HTML.replace( "${url}", request.getAbsolutePath() );
pw.print( s );
pw.flush();
}
public void respondUnauthorised( Resource resource, Response response, Request request ) {
response.setStatus( Response.Status.SC_UNAUTHORIZED );
List<String> challenges = authenticationService.getChallenges( resource, request );
response.setAuthenticateHeader( challenges );
}
public void respondMethodNotImplemented( Resource resource, Response response, Request request ) {
// log.debug( "method not implemented. resource: " + resource.getClass().getName() + " - method " + request.getMethod() );
try {
response.setStatus( Response.Status.SC_NOT_IMPLEMENTED );
OutputStream out = response.getOutputStream();
out.write( METHOD_NOT_IMPLEMENTED_HTML.getBytes() );
} catch( IOException ex ) {
log.warn( "exception writing content" );
}
}
public void respondMethodNotAllowed( Resource res, Response response, Request request ) {
log.debug( "method not allowed. handler: " + this.getClass().getName() + " resource: " + res.getClass().getName() );
try {
response.setStatus( Response.Status.SC_METHOD_NOT_ALLOWED );
OutputStream out = response.getOutputStream();
out.write( METHOD_NOT_ALLOWED_HTML.getBytes() );
} catch( IOException ex ) {
log.warn( "exception writing content" );
}
}
/**
*
* @param resource
* @param response
* @param message - optional message to output in the body content
*/
public void respondConflict( Resource resource, Response response, Request request, String message ) {
log.debug( "respondConflict" );
try {
response.setStatus( Response.Status.SC_CONFLICT );
OutputStream out = response.getOutputStream();
out.write( CONFLICT_HTML.getBytes() );
} catch( IOException ex ) {
log.warn( "exception writing content" );
}
}
public void respondRedirect( Response response, Request request, String redirectUrl ) {
if( redirectUrl == null ) {
throw new NullPointerException( "redirectUrl cannot be null" );
}
response.setStatus( Response.Status.SC_MOVED_TEMPORARILY );
response.setLocationHeader( redirectUrl );
}
public void respondExpectationFailed( Response response, Request request ) {
response.setStatus( Response.Status.SC_EXPECTATION_FAILED );
}
public void respondCreated( Resource resource, Response response, Request request ) {
// log.debug( "respondCreated" );
response.setStatus( Response.Status.SC_CREATED );
}
public void respondNoContent( Resource resource, Response response, Request request ) {
// log.debug( "respondNoContent" );
response.setStatus( Response.Status.SC_OK );
}
public void respondPartialContent( GetableResource resource, Response response, Request request, Map<String, String> params, Range range ) throws NotAuthorizedException, BadRequestException {
log.debug( "respondPartialContent: " + range.getStart() + " - " + range.getFinish() );
response.setStatus( Response.Status.SC_PARTIAL_CONTENT );
response.setContentRangeHeader( range.getStart(), range.getFinish(), resource.getContentLength() );
response.setDateHeader( new Date() );
String etag = eTagGenerator.generateEtag( resource );
if( etag != null ) {
response.setEtag( etag );
}
String acc = request.getAcceptHeader();
String ct = resource.getContentType( acc );
if( ct != null ) {
response.setContentTypeHeader( ct );
}
try {
resource.sendContent( response.getOutputStream(), range, params, ct );
} catch( IOException ex ) {
log.warn( "IOException writing to output, probably client terminated connection", ex );
}
}
public void respondHead( Resource resource, Response response, Request request ) {
setRespondContentCommonHeaders( response, resource, Response.Status.SC_NO_CONTENT );
}
public void respondContent( Resource resource, Response response, Request request, Map<String, String> params ) throws NotAuthorizedException, BadRequestException {
// log.debug( "respondContent: " + resource.getClass() );
setRespondContentCommonHeaders( response, resource );
if( resource instanceof GetableResource ) {
GetableResource gr = (GetableResource) resource;
Long contentLength = gr.getContentLength();
if( contentLength != null ) { // often won't know until rendered
response.setContentLengthHeader( contentLength );
}
String acc = request.getAcceptHeader();
String ct = gr.getContentType( acc );
if( ct != null ) {
response.setContentTypeHeader( ct );
}
setCacheControl( gr, response, request.getAuthorization() );
sendContent( request, response, (GetableResource) resource, params, null, ct );
}
}
public void respondNotModified( GetableResource resource, Response response, Request request ) {
// log.debug( "not modified" );
response.setStatus( Response.Status.SC_NOT_MODIFIED );
response.setDateHeader( new Date() );
String etag = eTagGenerator.generateEtag( resource );
if( etag != null ) {
response.setEtag( etag );
}
Date mod = resource.getModifiedDate();
if( mod != null ) {
response.setLastModifiedHeader( resource.getModifiedDate() );
}
setCacheControl( resource, response, request.getAuthorization() );
}
public static void setCacheControl( final GetableResource resource, final Response response, Auth auth ) {
Long delta = resource.getMaxAgeSeconds( auth );
// log.debug( "setCacheControl: " + delta + " - " + resource.getClass() );
if( delta != null ) {
if( auth != null ) {
response.setCacheControlPrivateMaxAgeHeader( delta );
//response.setCacheControlMaxAgeHeader(delta);
} else {
response.setCacheControlMaxAgeHeader( delta );
}
Date expiresAt = calcExpiresAt( resource.getModifiedDate(), delta.longValue() );
response.setExpiresHeader( expiresAt );
} else {
response.setCacheControlNoCacheHeader();
}
}
public static Date calcExpiresAt( Date modifiedDate, long deltaSeconds ) {
long deltaMs = deltaSeconds * 1000;
long expiresAt = System.currentTimeMillis() + deltaMs;
return new Date( expiresAt );
}
protected void sendContent( Request request, Response response, GetableResource resource, Map<String, String> params, Range range, String contentType ) throws NotAuthorizedException, BadRequestException {
OutputStream out = outputStreamForResponse( request, response, resource );
try {
resource.sendContent( out, null, params, contentType );
out.flush();
} catch( IOException ex ) {
log.warn( "IOException sending content", ex );
}
}
protected OutputStream outputStreamForResponse( Request request, Response response, GetableResource resource ) {
OutputStream outToUse = response.getOutputStream();
return outToUse;
}
protected void output( final Response response, final String s ) {
PrintWriter pw = new PrintWriter( response.getOutputStream(), true );
pw.print( s );
pw.flush();
}
protected void setRespondContentCommonHeaders( Response response, Resource resource ) {
setRespondContentCommonHeaders( response, resource, Response.Status.SC_OK );
}
protected void setRespondContentCommonHeaders( Response response, Resource resource, Response.Status status ) {
response.setStatus( status );
response.setDateHeader( new Date() );
String etag = eTagGenerator.generateEtag( resource );
if( etag != null ) {
response.setEtag( etag );
}
if( resource.getModifiedDate() != null ) {
response.setLastModifiedHeader( resource.getModifiedDate() );
}
}
public void respondBadRequest( Resource resource, Response response, Request request ) {
response.setStatus( Response.Status.SC_BAD_REQUEST );
}
public void respondForbidden( Resource resource, Response response, Request request ) {
response.setStatus( Response.Status.SC_FORBIDDEN );
}
public void respondDeleteFailed( Request request, Response response, Resource resource, Status status ) {
response.setStatus( status );
}
public AuthenticationService getAuthenticationService() {
return authenticationService;
}
public void respondServerError( Request request, Response response, String reason ) {
try {
response.setStatus( Status.SC_INTERNAL_SERVER_ERROR );
OutputStream out = response.getOutputStream();
out.write( SERVER_ERROR_HTML.getBytes() );
} catch( IOException ex ) {
throw new RuntimeException( ex );
}
}
}