Package com.bradmcevoy.http.http11

Source Code of com.bradmcevoy.http.http11.DefaultHttp11ResponseHandler

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;
import com.bradmcevoy.io.BufferingOutputStream;
import com.bradmcevoy.io.ReadingException;
import com.bradmcevoy.io.StreamUtils;
import com.bradmcevoy.io.WritingException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;

/**
*
*/
public class DefaultHttp11ResponseHandler implements Http11ResponseHandler {

    public enum BUFFERING {
        always,
        never,
        whenNeeded
    }

    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>";
    public static final String NOT_AUTHORISED_HTML = "<html><body><h1>Not authorised</h1></body></html>";
    private final AuthenticationService authenticationService;
    private final ETagGenerator eTagGenerator;
    private CacheControlHelper cacheControlHelper = new DefaultCacheControlHelper();
    private int maxMemorySize = 100000;
    private BUFFERING buffering;

    public DefaultHttp11ResponseHandler(AuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
        this.eTagGenerator = new DefaultETagGenerator();
    }

    public DefaultHttp11ResponseHandler(AuthenticationService authenticationService, ETagGenerator eTagGenerator) {
        this.authenticationService = authenticationService;
        this.eTagGenerator = eTagGenerator;
    }

    /**
     * Defaults to com.bradmcevoy.http.http11.DefaultCacheControlHelper
     * @return
     */
    public CacheControlHelper getCacheControlHelper() {
        return cacheControlHelper;
    }

    public void setCacheControlHelper(CacheControlHelper cacheControlHelper) {
        this.cacheControlHelper = cacheControlHelper;
    }

    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");
        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) {
        log.trace("respondUnauthorised");
        response.setStatus(Response.Status.SC_UNAUTHORIZED);
        List<String> challenges = authenticationService.getChallenges(resource, request);
        response.setAuthenticateHeader(challenges);

//        PrintWriter pw = new PrintWriter(response.getOutputStream(), true);
//
//        // http://jira.ettrema.com:8080/browse/MIL-39
//        String s = NOT_AUTHORISED_HTML.replace("${url}", request.getAbsolutePath());
//        response.setContentLengthHeader((long)s.length());
//        pw.print(s);
//        pw.flush();

    }

    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");
        }
        log.trace("respondRedirect");
        // delegate to the response, because this can be server dependent
        response.sendRedirect(redirectUrl);
//        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);
        // see comments in http://www.ettrema.com:8080/browse/MIL-87
        response.setStatus(Response.Status.SC_NO_CONTENT);
    }

    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, request.getAuthorization());
    }

    public void respondContent(Resource resource, Response response, Request request, Map<String, String> params) throws NotAuthorizedException, BadRequestException {
        log.debug("respondContent: " + resource.getClass());
        Auth auth = request.getAuthorization();
        setRespondContentCommonHeaders(response, resource, auth);
        if (resource instanceof GetableResource) {
            GetableResource gr = (GetableResource) resource;
            String acc = request.getAcceptHeader();
            String ct = gr.getContentType(acc);
            if (ct != null) {
                ct = pickBestContentType(ct);
                response.setContentTypeHeader(ct);
            }
            cacheControlHelper.setCacheControl(gr, response, request.getAuthorization());

            Long contentLength = gr.getContentLength();
            if (buffering == BUFFERING.always || (contentLength != null && buffering == BUFFERING.whenNeeded)) { // often won't know until rendered
                log.trace("sending content with known content length: " + contentLength);
                response.setContentLengthHeader(contentLength);
                sendContent(request, response, (GetableResource) resource, params, null, ct);
            } else {
                log.trace("buffering content...");
                BufferingOutputStream tempOut = new BufferingOutputStream(maxMemorySize);
                try {
                    ((GetableResource) resource).sendContent(tempOut, null, params, ct);
                    tempOut.close();
                } catch (IOException ex) {
                    tempOut.deleteTempFileIfExists();
                    throw new RuntimeException("Exception generating buffered content", ex);
                }
                Long bufContentLength = tempOut.getSize();
                if (contentLength != null) {
                    if (!contentLength.equals(bufContentLength)) {
                        throw new RuntimeException("Lengthd dont match: " + contentLength + " != " + bufContentLength);
                    }
                }
                log.trace("sending buffered content...");
                response.setContentLengthHeader(bufContentLength);
                InputStream in = tempOut.getInputStream();
                try {
                    StreamUtils.readTo(in, response.getOutputStream());
                } catch (ReadingException ex) {
                    throw new RuntimeException(ex);
                } catch (WritingException ex) {
                    log.warn("exception writing, client probably closed connection", ex);
                } finally {
                    IOUtils.closeQuietly(in); // make sure we close to delete temporary file
                }
                return;


            }

        }
    }

    public void respondNotModified(GetableResource resource, Response response, Request request) {
        log.trace("respondNotModified");
        response.setStatus(Response.Status.SC_NOT_MODIFIED);
        response.setDateHeader(new Date());
        String etag = eTagGenerator.generateEtag(resource);
        if (etag != null) {
            response.setEtag(etag);
        }

        // Note that we use a simpler modified date handling here then when
        // responding with content, because in a not-modified situation the
        // modified date MUST be that of the actual resource
        Date modDate = resource.getModifiedDate();
        response.setLastModifiedHeader(modDate);

        cacheControlHelper.setCacheControl(resource, response, request.getAuthorization());
    }

    protected void sendContent(Request request, Response response, GetableResource resource, Map<String, String> params, Range range, String contentType) throws NotAuthorizedException, BadRequestException {
        long l = System.currentTimeMillis();
        log.trace("sendContent");
        OutputStream out = outputStreamForResponse(request, response, resource);
        try {
            resource.sendContent(out, null, params, contentType);
            out.flush();
            if (log.isTraceEnabled()) {
                l = System.currentTimeMillis() - l;
                log.trace("sendContent finished in " + l + "ms");
            }
        } 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, Auth auth) {
        setRespondContentCommonHeaders(response, resource, Response.Status.SC_OK, auth);
    }

    protected void setRespondContentCommonHeaders(Response response, Resource resource, Response.Status status, Auth auth) {
        response.setStatus(status);
        response.setDateHeader(new Date());
        String etag = eTagGenerator.generateEtag(resource);
        if (etag != null) {
            response.setEtag(etag);
        }
        setModifiedDate(response, resource, auth);
    }

    /**
    The modified date response header is used by the client for content
    caching. It seems obvious that if we have a modified date on the resource
    we should set it.
    BUT, because of the interaction with max-age we should always set it
    to the current date if we have max-age
    The problem, is that if we find that a condition GET has an expired mod-date
    (based on maxAge) then we want to respond with content (even if our mod-date
    hasnt changed. But if we use the actual mod-date in that case, then the
    browser will continue to use the old mod-date, so will forever more respond
    with content. So we send a mod-date of now to ensure that future requests
    will be given a 304 not modified.*
     *
     * @param response
     * @param resource
     * @param auth
     */
    public static void setModifiedDate(Response response, Resource resource, Auth auth) {
        Date modDate = resource.getModifiedDate();
        if (modDate != null) {
            // HACH - see if this helps IE
            response.setLastModifiedHeader(modDate);
//            if (resource instanceof GetableResource) {
//                GetableResource gr = (GetableResource) resource;
//                Long maxAge = gr.getMaxAgeSeconds(auth);
//                if (maxAge != null && maxAge > 0) {
//                    log.trace("setModifiedDate: has a modified date and a positive maxAge, so adjust modDate");
//                    long tm = System.currentTimeMillis() - 60000; // modified 1 minute ago
//                    modDate = new Date(tm); // have max-age, so use current date
//                }
//            }
//            response.setLastModifiedHeader(modDate);
        }
    }

    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);
        }
    }

    /**
     * Maximum size of data to hold in memory per request when buffering output
     * data.
     *
     * @return
     */
    public int getMaxMemorySize() {
        return maxMemorySize;
    }

    public void setMaxMemorySize(int maxMemorySize) {
        this.maxMemorySize = maxMemorySize;
    }

    public BUFFERING getBuffering() {
        return buffering;
    }

    public void setBuffering(BUFFERING buffering) {
        this.buffering = buffering;
    }

    /**
     * Sometimes we'll get a content type list, such as image/jpeg,image/pjpeg
     *
     * In this case we should pick the first in the list
     *
     * @param ct
     * @return
     */
    private String pickBestContentType(String ct) {
        if( ct == null ) {
            return null;
        } else if( ct.contains(",")) {
            return ct.split(",")[0];
        } else {
            return ct;
        }
    }

}
TOP

Related Classes of com.bradmcevoy.http.http11.DefaultHttp11ResponseHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.