Java tutorial
// ======================================================================== // $Id: HttpResponse.java,v 1.61 2005/10/26 08:10:14 gregwilkins Exp $ // Copyright 199-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package net.lightbody.bmp.proxy.jetty.http; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import net.lightbody.bmp.proxy.jetty.util.LogSupport; import net.lightbody.bmp.proxy.jetty.util.StringUtil; import net.lightbody.bmp.proxy.jetty.util.TypeUtil; import net.lightbody.bmp.proxy.jetty.util.UrlEncoded; import org.apache.commons.logging.Log; import javax.servlet.http.Cookie; import java.io.IOException; import java.io.Writer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; /* ------------------------------------------------------------ */ /** HTTP Response. * This class manages the headers, trailers and content streams * of a HTTP response. It can be used for receiving or generating * requests. * * This class is not synchronized. It should be explicitly * synchronized if it is used by multiple threads. * * @see HttpRequest * @version $Id: HttpResponse.java,v 1.61 2005/10/26 08:10:14 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ public class HttpResponse extends HttpMessage { private static Log log = LogFactory.getLog(HttpResponse.class); public final static int __100_Continue = 100, __101_Switching_Protocols = 101, __102_Processing = 102, __200_OK = 200, __201_Created = 201, __202_Accepted = 202, __203_Non_Authoritative_Information = 203, __204_No_Content = 204, __205_Reset_Content = 205, __206_Partial_Content = 206, __207_Multi_Status = 207, __300_Multiple_Choices = 300, __301_Moved_Permanently = 301, __302_Moved_Temporarily = 302, __302_Found = 302, __303_See_Other = 303, __304_Not_Modified = 304, __305_Use_Proxy = 305, __400_Bad_Request = 400, __401_Unauthorized = 401, __402_Payment_Required = 402, __403_Forbidden = 403, __404_Not_Found = 404, __405_Method_Not_Allowed = 405, __406_Not_Acceptable = 406, __407_Proxy_Authentication_Required = 407, __408_Request_Timeout = 408, __409_Conflict = 409, __410_Gone = 410, __411_Length_Required = 411, __412_Precondition_Failed = 412, __413_Request_Entity_Too_Large = 413, __414_Request_URI_Too_Large = 414, __415_Unsupported_Media_Type = 415, __416_Requested_Range_Not_Satisfiable = 416, __417_Expectation_Failed = 417, __422_Unprocessable_Entity = 422, __423_Locked = 423, __424_Failed_Dependency = 424, __500_Internal_Server_Error = 500, __501_Not_Implemented = 501, __502_Bad_Gateway = 502, __503_Service_Unavailable = 503, __504_Gateway_Timeout = 504, __505_HTTP_Version_Not_Supported = 505, __507_Insufficient_Storage = 507; /* -------------------------------------------------------------- */ public final static HashMap __statusMsg = new HashMap(); static { // Build error code map using reflection try { Field[] fields = net.lightbody.bmp.proxy.jetty.http.HttpResponse.class.getDeclaredFields(); for (int f = fields.length; f-- > 0;) { int m = fields[f].getModifiers(); String name = fields[f].getName(); if (Modifier.isFinal(m) && Modifier.isStatic(m) && fields[f].getType().equals(Integer.TYPE) && name.startsWith("__") && Character.isDigit(name.charAt(2))) { String message = name.substring(6); message = message.replace('_', ' '); __statusMsg.put(fields[f].get(null), message); } } } catch (Exception e) { log.warn(LogSupport.EXCEPTION, e); } } /* ------------------------------------------------------------ */ static byte[] __Continue; static { try { __Continue = "HTTP/1.1 100 Continue\015\012\015\012".getBytes(StringUtil.__ISO_8859_1); } catch (Exception e) { log.fatal(e); System.exit(1); } } /* -------------------------------------------------------------- */ private int _status = __200_OK; private String _reason; private HttpContext _httpContext; /* ------------------------------------------------------------ */ /** Constructor. */ public HttpResponse() { _version = __HTTP_1_1; _dotVersion = 1; _state = __MSG_EDITABLE; } /* ------------------------------------------------------------ */ /** Constructor. * @param connection */ public HttpResponse(HttpConnection connection) { super(connection); _version = __HTTP_1_1; _dotVersion = 1; _state = __MSG_EDITABLE; } /* ------------------------------------------------------------ */ /** Get the HttpContext handling this reponse. * @return The HttpContext that is handling this request. */ public HttpContext getHttpContext() { return _httpContext; } /* ------------------------------------------------------------ */ /** Set the HttpContext handling this reponse. * @param context The HttpContext handling this reponse. */ public void setHttpContext(HttpContext context) { _httpContext = context; } /* ------------------------------------------------------------ */ /** * @return true if the message has been modified. */ public boolean isDirty() { return _status != __200_OK || super.isDirty(); } /* ------------------------------------------------------------ */ /** Reset the response. * Clears any data that exists in the buffer as well as the status * code. If the response has been committed, this method throws an * <code>IllegalStateException</code>. * * @exception IllegalStateException if the response has already been * committed */ public void reset() { if (isCommitted()) throw new IllegalStateException("Already committed"); try { ((HttpOutputStream) getOutputStream()).resetBuffer(); _status = __200_OK; _reason = null; super.reset(); setField(HttpFields.__Date, getRequest().getTimeStampStr()); if (!Version.isParanoid()) setField(HttpFields.__Server, Version.getDetail()); } catch (Exception e) { log.warn(LogSupport.EXCEPTION, e); throw new IllegalStateException(e.toString()); } } /* ------------------------------------------------------------ */ /** * @deprecated use getHttpRequest() */ public HttpRequest getRequest() { return getHttpRequest(); } /* ------------------------------------------------------------ */ /** Get the HTTP Request. * Get the HTTP Request associated with this response. * @return associated request */ public HttpRequest getHttpRequest() { if (_connection == null) return null; return _connection.getRequest(); } /* ------------------------------------------------------------ */ /** Not Implemented. * @param in * @exception IOException */ public void readHeader(HttpInputStream in) throws IOException { _state = __MSG_BAD; log.warn(LogSupport.NOT_IMPLEMENTED); } /* -------------------------------------------------------------- */ public void writeHeader(Writer writer) throws IOException { if (_state != __MSG_EDITABLE) throw new IllegalStateException(__state[_state] + " is not EDITABLE"); if (_header == null) throw new IllegalStateException("Response is destroyed"); if (getHttpRequest().getDotVersion() >= 0) { _state = __MSG_BAD; writer.write(_version); writer.write(' '); writer.write('0' + ((_status / 100) % 10)); writer.write('0' + ((_status / 10) % 10)); writer.write('0' + (_status % 10)); writer.write(' '); writer.write(getReason()); writer.write(HttpFields.__CRLF); _header.write(writer); } _state = __MSG_SENDING; } /* -------------------------------------------------------------- */ public int getStatus() { return _status; } /* -------------------------------------------------------------- */ public void setStatus(int status) { _status = status; } /* -------------------------------------------------------------- */ public String getReason() { if (_reason != null) return _reason; _reason = (String) __statusMsg.get(TypeUtil.newInteger(_status)); if (_reason == null) _reason = "unknown"; return _reason; } /* -------------------------------------------------------------- */ public void setReason(String reason) { _reason = reason; } /* -------------------------------------------------------------- */ public void setStatus(int code, String message) { setStatus(code); Integer code_integer = TypeUtil.newInteger(code); if (message == null) { message = (String) __statusMsg.get(code_integer); if (message == null) message = "" + code; setReason(message); } else setReason(UrlEncoded.encodeString(message)); } /* ------------------------------------------------------------- */ /** Send Error Response. */ public void sendError(int code, String message) throws IOException { setStatus(code, message); // Generate normal error page. HttpRequest request = getHttpRequest(); // If we are allowed to have a body if (code != __204_No_Content && code != __304_Not_Modified && code != __206_Partial_Content && code >= 200) { if (getHttpContext() != null) { Object o = getHttpContext().getAttribute(HttpContext.__ErrorHandler); if (o != null && o instanceof HttpHandler) ((HttpHandler) o).handle(request.getPath(), null, request, this); } } else if (code != __206_Partial_Content) { _header.remove(HttpFields.__ContentType); _header.remove(HttpFields.__ContentLength); _characterEncoding = null; _mimeType = null; } commit(); } /* ------------------------------------------------------------- */ /** * Sends an error response to the client using the specified status * code and no default message. * @param code the status code * @exception IOException If an I/O error has occurred. */ public void sendError(int code) throws IOException { sendError(code, null); } /* ------------------------------------------------------------- */ /** * Sends a redirect response to the client using the specified redirect * location URL. * @param location the redirect location URL * @exception IOException If an I/O error has occurred. */ public void sendRedirect(String location) throws IOException { if (isCommitted()) throw new IllegalStateException("Commited"); _header.put(HttpFields.__Location, location); setStatus(__302_Moved_Temporarily); commit(); } /* -------------------------------------------------------------- */ /** Add a Set-Cookie field. */ public void addSetCookie(String name, String value) { _header.addSetCookie(new Cookie(name, value)); } /* -------------------------------------------------------------- */ /** Add a Set-Cookie field. */ public void addSetCookie(Cookie cookie) { _header.addSetCookie(cookie); } /* ------------------------------------------------------------ */ public void completing() { getHttpConnection().completing(); } /* ------------------------------------------------------------ */ /** * @exception IOException */ public void commit() throws IOException { if (!isCommitted()) getOutputStream().flush(); getHttpConnection().commit(); } /* ------------------------------------------------------------ */ /** Recycle the response. */ void recycle(HttpConnection connection) { super.recycle(connection); _status = __200_OK; _reason = null; _httpContext = null; } /* ------------------------------------------------------------ */ /** Destroy the response. * Help the garbage collector by null everything that we can. */ public void destroy() { _reason = null; super.destroy(); } }