0001: /*
0002: * Copyright 1999-2004 The Apache Software Foundation
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.coyote.tomcat4;
0018:
0019: import java.io.IOException;
0020: import java.io.OutputStream;
0021: import java.io.PrintWriter;
0022: import java.net.MalformedURLException;
0023: import java.text.SimpleDateFormat;
0024: import java.util.ArrayList;
0025: import java.util.Date;
0026: import java.util.Enumeration;
0027: import java.util.Locale;
0028: import java.util.TimeZone;
0029: import java.util.Vector;
0030:
0031: import javax.servlet.ServletOutputStream;
0032: import javax.servlet.ServletResponse;
0033: import javax.servlet.http.Cookie;
0034: import javax.servlet.http.HttpServletRequest;
0035: import javax.servlet.http.HttpServletResponse;
0036: import javax.servlet.http.HttpSession;
0037:
0038: import org.apache.catalina.Connector;
0039: import org.apache.catalina.Context;
0040: import org.apache.catalina.HttpResponse;
0041: import org.apache.catalina.util.CharsetMapper;
0042: import org.apache.catalina.util.StringManager;
0043: import org.apache.coyote.Response;
0044: import org.apache.tomcat.util.buf.CharChunk;
0045: import org.apache.tomcat.util.buf.UEncoder;
0046: import org.apache.tomcat.util.http.MimeHeaders;
0047: import org.apache.tomcat.util.http.ServerCookie;
0048: import org.apache.tomcat.util.net.URL;
0049:
0050: /**
0051: * Wrapper object for the Coyote response.
0052: *
0053: * @author Remy Maucherat
0054: * @author Craig R. McClanahan
0055: * @version $Revision: 1.35 $ $Date: 2004/05/16 16:38:12 $
0056: */
0057:
0058: public class CoyoteResponse implements HttpResponse,
0059: HttpServletResponse {
0060:
0061: // ----------------------------------------------------------- Constructors
0062:
0063: public CoyoteResponse() {
0064:
0065: format.setTimeZone(TimeZone.getTimeZone("GMT"));
0066: urlEncoder.addSafeCharacter('/');
0067:
0068: }
0069:
0070: // ----------------------------------------------------- Instance Variables
0071:
0072: /**
0073: * The date format we will use for creating date headers.
0074: */
0075: protected final SimpleDateFormat format = new SimpleDateFormat(
0076: "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
0077:
0078: /**
0079: * Descriptive information about this Response implementation.
0080: */
0081: protected static final String info = "org.apache.coyote.tomcat4.CoyoteResponse/1.0";
0082:
0083: /**
0084: * The string manager for this package.
0085: */
0086: protected static StringManager sm = StringManager
0087: .getManager(Constants.Package);
0088:
0089: // ------------------------------------------------------------- Properties
0090:
0091: /**
0092: * Associated Catalina connector.
0093: */
0094: protected Connector connector;
0095:
0096: /**
0097: * Return the Connector through which this Request was received.
0098: */
0099: public Connector getConnector() {
0100: return (this .connector);
0101: }
0102:
0103: /**
0104: * Set the Connector through which this Request was received.
0105: *
0106: * @param connector The new connector
0107: */
0108: public void setConnector(Connector connector) {
0109: this .connector = connector;
0110: }
0111:
0112: /**
0113: * Coyote response.
0114: */
0115: protected Response coyoteResponse;
0116:
0117: /**
0118: * Set the Coyote response.
0119: *
0120: * @param response The Coyote response
0121: */
0122: public void setCoyoteResponse(Response coyoteResponse) {
0123: this .coyoteResponse = coyoteResponse;
0124: outputBuffer.setResponse(coyoteResponse);
0125: }
0126:
0127: /**
0128: * Get the Coyote response.
0129: */
0130: public Response getCoyoteResponse() {
0131: return (coyoteResponse);
0132: }
0133:
0134: /**
0135: * The Context within which this Request is being processed.
0136: */
0137: protected Context context = null;
0138:
0139: /**
0140: * Return the Context within which this Request is being processed.
0141: */
0142: public Context getContext() {
0143: return (this .context);
0144: }
0145:
0146: /**
0147: * Set the Context within which this Request is being processed. This
0148: * must be called as soon as the appropriate Context is identified, because
0149: * it identifies the value to be returned by <code>getContextPath()</code>,
0150: * and thus enables parsing of the request URI.
0151: *
0152: * @param context The newly associated Context
0153: */
0154: public void setContext(Context context) {
0155: this .context = context;
0156: }
0157:
0158: /**
0159: * The associated output buffer.
0160: */
0161: protected OutputBuffer outputBuffer = new OutputBuffer();
0162:
0163: /**
0164: * The associated output stream.
0165: */
0166: protected CoyoteOutputStream outputStream = new CoyoteOutputStream(
0167: outputBuffer);
0168:
0169: /**
0170: * The associated writer.
0171: */
0172: protected CoyoteWriter writer = new CoyoteWriter(outputBuffer);
0173:
0174: /**
0175: * The application commit flag.
0176: */
0177: protected boolean appCommitted = false;
0178:
0179: /**
0180: * The included flag.
0181: */
0182: protected boolean included = false;
0183:
0184: /**
0185: * The error flag.
0186: */
0187: protected boolean error = false;
0188:
0189: /**
0190: * The set of Cookies associated with this Response.
0191: */
0192: protected ArrayList cookies = new ArrayList();
0193:
0194: /**
0195: * Using output stream flag.
0196: */
0197: protected boolean usingOutputStream = false;
0198:
0199: /**
0200: * Using writer flag.
0201: */
0202: protected boolean usingWriter = false;
0203:
0204: /**
0205: * URL encoder.
0206: */
0207: protected UEncoder urlEncoder = new UEncoder();
0208:
0209: /**
0210: * Recyclable buffer to hold the redirect URL.
0211: */
0212: protected CharChunk redirectURLCC = new CharChunk();
0213:
0214: // --------------------------------------------------------- Public Methods
0215:
0216: /**
0217: * Release all object references, and initialize instance variables, in
0218: * preparation for reuse of this object.
0219: */
0220: public void recycle() {
0221:
0222: outputBuffer.recycle();
0223: usingOutputStream = false;
0224: usingWriter = false;
0225: appCommitted = false;
0226: included = false;
0227: error = false;
0228: cookies.clear();
0229:
0230: if (facade != null) {
0231: facade.clear();
0232: facade = null;
0233: }
0234:
0235: writer.recycle();
0236:
0237: }
0238:
0239: // ------------------------------------------------------- Response Methods
0240:
0241: /**
0242: * Return the number of bytes actually written to the output stream.
0243: */
0244: public int getContentCount() {
0245: return outputBuffer.getContentWritten();
0246: }
0247:
0248: /**
0249: * Set the application commit flag.
0250: *
0251: * @param appCommitted The new application committed flag value
0252: */
0253: public void setAppCommitted(boolean appCommitted) {
0254: this .appCommitted = appCommitted;
0255: }
0256:
0257: /**
0258: * Application commit flag accessor.
0259: */
0260: public boolean isAppCommitted() {
0261: return (this .appCommitted || isCommitted() || isSuspended());
0262: }
0263:
0264: /**
0265: * Return the "processing inside an include" flag.
0266: */
0267: public boolean getIncluded() {
0268: return included;
0269: }
0270:
0271: /**
0272: * Set the "processing inside an include" flag.
0273: *
0274: * @param included <code>true</code> if we are currently inside a
0275: * RequestDispatcher.include(), else <code>false</code>
0276: */
0277: public void setIncluded(boolean included) {
0278: this .included = included;
0279: }
0280:
0281: /**
0282: * Return descriptive information about this Response implementation and
0283: * the corresponding version number, in the format
0284: * <code><description>/<version></code>.
0285: */
0286: public String getInfo() {
0287: return (info);
0288: }
0289:
0290: /**
0291: * The request with which this response is associated.
0292: */
0293: protected CoyoteRequest request = null;
0294:
0295: /**
0296: * Return the Request with which this Response is associated.
0297: */
0298: public org.apache.catalina.Request getRequest() {
0299: return (this .request);
0300: }
0301:
0302: /**
0303: * Set the Request with which this Response is associated.
0304: *
0305: * @param request The new associated request
0306: */
0307: public void setRequest(org.apache.catalina.Request request) {
0308: this .request = (CoyoteRequest) request;
0309: }
0310:
0311: /**
0312: * The facade associated with this response.
0313: */
0314: protected CoyoteResponseFacade facade = null;
0315:
0316: /**
0317: * Return the <code>ServletResponse</code> for which this object
0318: * is the facade.
0319: */
0320: public ServletResponse getResponse() {
0321: if (facade == null) {
0322: facade = new CoyoteResponseFacade(this );
0323: }
0324: return (facade);
0325: }
0326:
0327: /**
0328: * Return the output stream associated with this Response.
0329: */
0330: public OutputStream getStream() {
0331: return outputStream;
0332: }
0333:
0334: /**
0335: * Set the output stream associated with this Response.
0336: *
0337: * @param stream The new output stream
0338: */
0339: public void setStream(OutputStream stream) {
0340: // This method is evil
0341: }
0342:
0343: /**
0344: * Set the suspended flag.
0345: *
0346: * @param suspended The new suspended flag value
0347: */
0348: public void setSuspended(boolean suspended) {
0349: outputBuffer.setSuspended(suspended);
0350: }
0351:
0352: /**
0353: * Suspended flag accessor.
0354: */
0355: public boolean isSuspended() {
0356: return outputBuffer.isSuspended();
0357: }
0358:
0359: /**
0360: * Set the error flag.
0361: */
0362: public void setError() {
0363: error = true;
0364: }
0365:
0366: /**
0367: * Error flag accessor.
0368: */
0369: public boolean isError() {
0370: return error;
0371: }
0372:
0373: /**
0374: * Create and return a ServletOutputStream to write the content
0375: * associated with this Response.
0376: *
0377: * @exception IOException if an input/output error occurs
0378: */
0379: public ServletOutputStream createOutputStream() throws IOException {
0380: // Probably useless
0381: return outputStream;
0382: }
0383:
0384: /**
0385: * Perform whatever actions are required to flush and close the output
0386: * stream or writer, in a single operation.
0387: *
0388: * @exception IOException if an input/output error occurs
0389: */
0390: public void finishResponse() throws IOException {
0391: // Writing leftover bytes
0392: try {
0393: outputBuffer.close();
0394: } catch (IOException e) {
0395: ;
0396: } catch (Throwable t) {
0397: t.printStackTrace();
0398: }
0399: }
0400:
0401: /**
0402: * Return the content length that was set or calculated for this Response.
0403: */
0404: public int getContentLength() {
0405: return (coyoteResponse.getContentLength());
0406: }
0407:
0408: /**
0409: * Return the content type that was set or calculated for this response,
0410: * or <code>null</code> if no content type was set.
0411: */
0412: public String getContentType() {
0413: return (coyoteResponse.getContentType());
0414: }
0415:
0416: /**
0417: * Return a PrintWriter that can be used to render error messages,
0418: * regardless of whether a stream or writer has already been acquired.
0419: *
0420: * @return Writer which can be used for error reports. If the response is
0421: * not an error report returned using sendError or triggered by an
0422: * unexpected exception thrown during the servlet processing
0423: * (and only in that case), null will be returned if the response stream
0424: * has already been used.
0425: */
0426: public PrintWriter getReporter() {
0427: if (outputBuffer.isNew()) {
0428: return writer;
0429: } else {
0430: return null;
0431: }
0432: }
0433:
0434: // ------------------------------------------------ ServletResponse Methods
0435:
0436: /**
0437: * Flush the buffer and commit this response.
0438: *
0439: * @exception IOException if an input/output error occurs
0440: */
0441: public void flushBuffer() throws IOException {
0442: outputBuffer.flush();
0443: }
0444:
0445: /**
0446: * Return the actual buffer size used for this Response.
0447: */
0448: public int getBufferSize() {
0449: return outputBuffer.getBufferSize();
0450: }
0451:
0452: /**
0453: * Return the character encoding used for this Response.
0454: */
0455: public String getCharacterEncoding() {
0456: return (coyoteResponse.getCharacterEncoding());
0457: }
0458:
0459: /**
0460: * Return the servlet output stream associated with this Response.
0461: *
0462: * @exception IllegalStateException if <code>getWriter</code> has
0463: * already been called for this response
0464: * @exception IOException if an input/output error occurs
0465: */
0466: public ServletOutputStream getOutputStream() throws IOException {
0467:
0468: if (usingWriter)
0469: throw new IllegalStateException(sm
0470: .getString("coyoteResponse.getOutputStream.ise"));
0471:
0472: usingOutputStream = true;
0473: return outputStream;
0474:
0475: }
0476:
0477: /**
0478: * Return the Locale assigned to this response.
0479: */
0480: public Locale getLocale() {
0481: return (coyoteResponse.getLocale());
0482: }
0483:
0484: /**
0485: * Return the writer associated with this Response.
0486: *
0487: * @exception IllegalStateException if <code>getOutputStream</code> has
0488: * already been called for this response
0489: * @exception IOException if an input/output error occurs
0490: */
0491: public PrintWriter getWriter() throws IOException {
0492:
0493: if (usingOutputStream)
0494: throw new IllegalStateException(sm
0495: .getString("coyoteResponse.getWriter.ise"));
0496:
0497: usingWriter = true;
0498: return writer;
0499:
0500: }
0501:
0502: /**
0503: * Has the output of this response already been committed?
0504: */
0505: public boolean isCommitted() {
0506: return (coyoteResponse.isCommitted());
0507: }
0508:
0509: /**
0510: * Clear any content written to the buffer.
0511: *
0512: * @exception IllegalStateException if this response has already
0513: * been committed
0514: */
0515: public void reset() {
0516:
0517: if (included)
0518: return; // Ignore any call from an included servlet
0519:
0520: coyoteResponse.reset();
0521: outputBuffer.reset();
0522:
0523: }
0524:
0525: /**
0526: * Reset the data buffer but not any status or header information.
0527: *
0528: * @exception IllegalStateException if the response has already
0529: * been committed
0530: */
0531: public void resetBuffer() {
0532:
0533: if (isCommitted())
0534: throw new IllegalStateException(sm
0535: .getString("coyoteResponse.resetBuffer.ise"));
0536:
0537: outputBuffer.reset();
0538:
0539: }
0540:
0541: /**
0542: * Set the buffer size to be used for this Response.
0543: *
0544: * @param size The new buffer size
0545: *
0546: * @exception IllegalStateException if this method is called after
0547: * output has been committed for this response
0548: */
0549: public void setBufferSize(int size) {
0550:
0551: if (isCommitted() || !outputBuffer.isNew())
0552: throw new IllegalStateException(sm
0553: .getString("coyoteResponse.setBufferSize.ise"));
0554:
0555: outputBuffer.setBufferSize(size);
0556:
0557: }
0558:
0559: /**
0560: * Set the content length (in bytes) for this Response.
0561: *
0562: * @param length The new content length
0563: */
0564: public void setContentLength(int length) {
0565:
0566: if (isCommitted())
0567: return;
0568:
0569: // Ignore any call from an included servlet
0570: if (included)
0571: return;
0572:
0573: coyoteResponse.setContentLength(length);
0574:
0575: }
0576:
0577: /**
0578: * Set the content type for this Response.
0579: *
0580: * @param type The new content type
0581: */
0582: public void setContentType(String type) {
0583:
0584: if (isCommitted())
0585: return;
0586:
0587: // Ignore any call from an included servlet
0588: if (included)
0589: return;
0590:
0591: coyoteResponse.setContentType(type);
0592:
0593: }
0594:
0595: /**
0596: * Set the Locale that is appropriate for this response, including
0597: * setting the appropriate character encoding.
0598: *
0599: * @param locale The new locale
0600: */
0601: public void setLocale(Locale locale) {
0602:
0603: if (isCommitted())
0604: return;
0605:
0606: // Ignore any call from an included servlet
0607: if (included)
0608: return;
0609:
0610: coyoteResponse.setLocale(locale);
0611:
0612: // Set the specified locale's default encoding of a response
0613: CharsetMapper cm = context.getCharsetMapper();
0614: String charset = cm.getCharset(locale);
0615:
0616: if (charset != null) {
0617: coyoteResponse.setCharacterEncoding(charset);
0618: }
0619:
0620: }
0621:
0622: // --------------------------------------------------- HttpResponse Methods
0623:
0624: /**
0625: * Return an array of all cookies set for this response, or
0626: * a zero-length array if no cookies have been set.
0627: */
0628: public Cookie[] getCookies() {
0629: return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
0630: }
0631:
0632: /**
0633: * Return the value for the specified header, or <code>null</code> if this
0634: * header has not been set. If more than one value was added for this
0635: * name, only the first is returned; use getHeaderValues() to retrieve all
0636: * of them.
0637: *
0638: * @param name Header name to look up
0639: */
0640: public String getHeader(String name) {
0641: return coyoteResponse.getMimeHeaders().getHeader(name);
0642: }
0643:
0644: /**
0645: * Return an array of all the header names set for this response, or
0646: * a zero-length array if no headers have been set.
0647: */
0648: public String[] getHeaderNames() {
0649:
0650: MimeHeaders headers = coyoteResponse.getMimeHeaders();
0651: int n = headers.size();
0652: String[] result = new String[n];
0653: for (int i = 0; i < n; i++) {
0654: result[i] = headers.getName(i).toString();
0655: }
0656: return result;
0657:
0658: }
0659:
0660: /**
0661: * Return an array of all the header values associated with the
0662: * specified header name, or an zero-length array if there are no such
0663: * header values.
0664: *
0665: * @param name Header name to look up
0666: */
0667: public String[] getHeaderValues(String name) {
0668:
0669: Enumeration enum = coyoteResponse.getMimeHeaders().values(name);
0670: Vector result = new Vector();
0671: while (enum.hasMoreElements()) {
0672: result.addElement(enum.nextElement());
0673: }
0674: String[] resultArray = new String[result.size()];
0675: result.copyInto(resultArray);
0676: return resultArray;
0677:
0678: }
0679:
0680: /**
0681: * Return the error message that was set with <code>sendError()</code>
0682: * for this Response.
0683: */
0684: public String getMessage() {
0685: return coyoteResponse.getMessage();
0686: }
0687:
0688: /**
0689: * Return the HTTP status code associated with this Response.
0690: */
0691: public int getStatus() {
0692: return coyoteResponse.getStatus();
0693: }
0694:
0695: /**
0696: * Reset this response, and specify the values for the HTTP status code
0697: * and corresponding message.
0698: *
0699: * @exception IllegalStateException if this response has already been
0700: * committed
0701: */
0702: public void reset(int status, String message) {
0703: reset();
0704: setStatus(status, message);
0705: }
0706:
0707: // -------------------------------------------- HttpServletResponse Methods
0708:
0709: /**
0710: * Add the specified Cookie to those that will be included with
0711: * this Response.
0712: *
0713: * @param cookie Cookie to be added
0714: */
0715: public void addCookie(Cookie cookie) {
0716:
0717: if (isCommitted())
0718: return;
0719:
0720: // Ignore any call from an included servlet
0721: if (included)
0722: return;
0723:
0724: cookies.add(cookie);
0725:
0726: StringBuffer sb = new StringBuffer();
0727: ServerCookie.appendCookieValue(sb, cookie.getVersion(), cookie
0728: .getName(), cookie.getValue(), cookie.getPath(), cookie
0729: .getDomain(), cookie.getComment(), cookie.getMaxAge(),
0730: cookie.getSecure());
0731: // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
0732: // RFC2965 is not supported by browsers and the Servlet spec
0733: // asks for 2109.
0734: addHeader("Set-Cookie", sb.toString());
0735:
0736: }
0737:
0738: /**
0739: * Add the specified date header to the specified value.
0740: *
0741: * @param name Name of the header to set
0742: * @param value Date value to be set
0743: */
0744: public void addDateHeader(String name, long value) {
0745:
0746: if (isCommitted())
0747: return;
0748:
0749: // Ignore any call from an included servlet
0750: if (included)
0751: return;
0752:
0753: addHeader(name, format.format(new Date(value)));
0754:
0755: }
0756:
0757: /**
0758: * Add the specified header to the specified value.
0759: *
0760: * @param name Name of the header to set
0761: * @param value Value to be set
0762: */
0763: public void addHeader(String name, String value) {
0764:
0765: if (isCommitted())
0766: return;
0767:
0768: // Ignore any call from an included servlet
0769: if (included)
0770: return;
0771:
0772: coyoteResponse.addHeader(name, value);
0773:
0774: }
0775:
0776: /**
0777: * Add the specified integer header to the specified value.
0778: *
0779: * @param name Name of the header to set
0780: * @param value Integer value to be set
0781: */
0782: public void addIntHeader(String name, int value) {
0783:
0784: if (isCommitted())
0785: return;
0786:
0787: // Ignore any call from an included servlet
0788: if (included)
0789: return;
0790:
0791: addHeader(name, "" + value);
0792:
0793: }
0794:
0795: /**
0796: * Has the specified header been set already in this response?
0797: *
0798: * @param name Name of the header to check
0799: */
0800: public boolean containsHeader(String name) {
0801: return coyoteResponse.containsHeader(name);
0802: }
0803:
0804: /**
0805: * Encode the session identifier associated with this response
0806: * into the specified redirect URL, if necessary.
0807: *
0808: * @param url URL to be encoded
0809: */
0810: public String encodeRedirectURL(String url) {
0811:
0812: if (isEncodeable(toAbsolute(url))) {
0813: HttpServletRequest hreq = (HttpServletRequest) request
0814: .getRequest();
0815: return (toEncoded(url, hreq.getSession().getId()));
0816: } else {
0817: return (url);
0818: }
0819:
0820: }
0821:
0822: /**
0823: * Encode the session identifier associated with this response
0824: * into the specified redirect URL, if necessary.
0825: *
0826: * @param url URL to be encoded
0827: *
0828: * @deprecated As of Version 2.1 of the Java Servlet API, use
0829: * <code>encodeRedirectURL()</code> instead.
0830: */
0831: public String encodeRedirectUrl(String url) {
0832: return (encodeRedirectURL(url));
0833: }
0834:
0835: /**
0836: * Encode the session identifier associated with this response
0837: * into the specified URL, if necessary.
0838: *
0839: * @param url URL to be encoded
0840: */
0841: public String encodeURL(String url) {
0842:
0843: String absolute = toAbsolute(url);
0844: if (isEncodeable(absolute)) {
0845: HttpServletRequest hreq = (HttpServletRequest) request
0846: .getRequest();
0847:
0848: // W3c spec clearly said
0849: if (url.equalsIgnoreCase("")) {
0850: url = absolute;
0851: }
0852: return (toEncoded(url, hreq.getSession().getId()));
0853: } else {
0854: return (url);
0855: }
0856:
0857: }
0858:
0859: /**
0860: * Encode the session identifier associated with this response
0861: * into the specified URL, if necessary.
0862: *
0863: * @param url URL to be encoded
0864: *
0865: * @deprecated As of Version 2.1 of the Java Servlet API, use
0866: * <code>encodeURL()</code> instead.
0867: */
0868: public String encodeUrl(String url) {
0869: return (encodeURL(url));
0870: }
0871:
0872: /**
0873: * Send an acknowledgment of a request.
0874: *
0875: * @exception IOException if an input/output error occurs
0876: */
0877: public void sendAcknowledgement() throws IOException {
0878:
0879: if (isCommitted())
0880: return;
0881:
0882: // Ignore any call from an included servlet
0883: if (included)
0884: return;
0885:
0886: coyoteResponse.acknowledge();
0887:
0888: }
0889:
0890: /**
0891: * Send an error response with the specified status and a
0892: * default message.
0893: *
0894: * @param status HTTP status code to send
0895: *
0896: * @exception IllegalStateException if this response has
0897: * already been committed
0898: * @exception IOException if an input/output error occurs
0899: */
0900: public void sendError(int status) throws IOException {
0901: sendError(status, null);
0902: }
0903:
0904: /**
0905: * Send an error response with the specified status and message.
0906: *
0907: * @param status HTTP status code to send
0908: * @param message Corresponding message to send
0909: *
0910: * @exception IllegalStateException if this response has
0911: * already been committed
0912: * @exception IOException if an input/output error occurs
0913: */
0914: public void sendError(int status, String message)
0915: throws IOException {
0916:
0917: if (isCommitted())
0918: throw new IllegalStateException(sm
0919: .getString("coyoteResponse.sendError.ise"));
0920:
0921: // Ignore any call from an included servlet
0922: if (included)
0923: return;
0924:
0925: setError();
0926:
0927: coyoteResponse.setStatus(status);
0928: coyoteResponse.setMessage(message);
0929:
0930: // Clear any data content that has been buffered
0931: resetBuffer();
0932:
0933: // Cause the response to be finished (from the application perspective)
0934: setSuspended(true);
0935:
0936: }
0937:
0938: /**
0939: * Send a temporary redirect to the specified redirect location URL.
0940: *
0941: * @param location Location URL to redirect to
0942: *
0943: * @exception IllegalStateException if this response has
0944: * already been committed
0945: * @exception IOException if an input/output error occurs
0946: */
0947: public void sendRedirect(String location) throws IOException {
0948:
0949: if (isCommitted())
0950: throw new IllegalStateException(sm
0951: .getString("coyoteResponse.sendRedirect.ise"));
0952:
0953: // Ignore any call from an included servlet
0954: if (included)
0955: return;
0956:
0957: // Clear any data content that has been buffered
0958: resetBuffer();
0959:
0960: // Generate a temporary redirect to the specified location
0961: try {
0962: String absolute = toAbsolute(location);
0963: setStatus(SC_MOVED_TEMPORARILY);
0964: setHeader("Location", absolute);
0965: } catch (IllegalArgumentException e) {
0966: setStatus(SC_NOT_FOUND);
0967: }
0968:
0969: // Cause the response to be finished (from the application perspective)
0970: setSuspended(true);
0971:
0972: }
0973:
0974: /**
0975: * Set the specified date header to the specified value.
0976: *
0977: * @param name Name of the header to set
0978: * @param value Date value to be set
0979: */
0980: public void setDateHeader(String name, long value) {
0981:
0982: if (isCommitted())
0983: return;
0984:
0985: // Ignore any call from an included servlet
0986: if (included)
0987: return;
0988:
0989: setHeader(name, format.format(new Date(value)));
0990:
0991: }
0992:
0993: /**
0994: * Set the specified header to the specified value.
0995: *
0996: * @param name Name of the header to set
0997: * @param value Value to be set
0998: */
0999: public void setHeader(String name, String value) {
1000:
1001: if (isCommitted())
1002: return;
1003:
1004: // Ignore any call from an included servlet
1005: if (included)
1006: return;
1007:
1008: coyoteResponse.setHeader(name, value);
1009:
1010: }
1011:
1012: /**
1013: * Set the specified integer header to the specified value.
1014: *
1015: * @param name Name of the header to set
1016: * @param value Integer value to be set
1017: */
1018: public void setIntHeader(String name, int value) {
1019:
1020: if (isCommitted())
1021: return;
1022:
1023: // Ignore any call from an included servlet
1024: if (included)
1025: return;
1026:
1027: setHeader(name, "" + value);
1028:
1029: }
1030:
1031: /**
1032: * Set the HTTP status to be returned with this response.
1033: *
1034: * @param status The new HTTP status
1035: */
1036: public void setStatus(int status) {
1037: setStatus(status, null);
1038: }
1039:
1040: /**
1041: * Set the HTTP status and message to be returned with this response.
1042: *
1043: * @param status The new HTTP status
1044: * @param message The associated text message
1045: *
1046: * @deprecated As of Version 2.1 of the Java Servlet API, this method
1047: * has been deprecated due to the ambiguous meaning of the message
1048: * parameter.
1049: */
1050: public void setStatus(int status, String message) {
1051:
1052: if (isCommitted())
1053: return;
1054:
1055: // Ignore any call from an included servlet
1056: if (included)
1057: return;
1058:
1059: coyoteResponse.setStatus(status);
1060: coyoteResponse.setMessage(message);
1061:
1062: }
1063:
1064: // ------------------------------------------------------ Protected Methods
1065:
1066: /**
1067: * Return <code>true</code> if the specified URL should be encoded with
1068: * a session identifier. This will be true if all of the following
1069: * conditions are met:
1070: * <ul>
1071: * <li>The request we are responding to asked for a valid session
1072: * <li>The requested session ID was not received via a cookie
1073: * <li>The specified URL points back to somewhere within the web
1074: * application that is responding to this request
1075: * </ul>
1076: *
1077: * @param location Absolute URL to be validated
1078: */
1079: protected boolean isEncodeable(String location) {
1080:
1081: if (location == null)
1082: return (false);
1083:
1084: // Is this an intra-document reference?
1085: if (location.startsWith("#"))
1086: return (false);
1087:
1088: // Are we in a valid session that is not using cookies?
1089: HttpServletRequest hreq = (HttpServletRequest) request
1090: .getRequest();
1091: HttpSession session = hreq.getSession(false);
1092: if (session == null)
1093: return (false);
1094: if (hreq.isRequestedSessionIdFromCookie())
1095: return (false);
1096:
1097: // Is this a valid absolute URL?
1098: URL url = null;
1099: try {
1100: url = new URL(location);
1101: } catch (MalformedURLException e) {
1102: return (false);
1103: }
1104:
1105: // Does this URL match down to (and including) the context path?
1106: if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol()))
1107: return (false);
1108: if (!hreq.getServerName().equalsIgnoreCase(url.getHost()))
1109: return (false);
1110: int serverPort = hreq.getServerPort();
1111: if (serverPort == -1) {
1112: if ("https".equals(hreq.getScheme()))
1113: serverPort = 443;
1114: else
1115: serverPort = 80;
1116: }
1117: int urlPort = url.getPort();
1118: if (urlPort == -1) {
1119: if ("https".equals(url.getProtocol()))
1120: urlPort = 443;
1121: else
1122: urlPort = 80;
1123: }
1124: if (serverPort != urlPort)
1125: return (false);
1126:
1127: String contextPath = getContext().getPath();
1128: if (contextPath != null) {
1129: String file = url.getFile();
1130: if ((file == null) || !file.startsWith(contextPath))
1131: return (false);
1132: if (file.indexOf(";jsessionid=" + session.getId()) >= 0)
1133: return (false);
1134: }
1135:
1136: // This URL belongs to our web application, so it is encodeable
1137: return (true);
1138:
1139: }
1140:
1141: /**
1142: * Convert (if necessary) and return the absolute URL that represents the
1143: * resource referenced by this possibly relative URL. If this URL is
1144: * already absolute, return it unchanged.
1145: *
1146: * @param location URL to be (possibly) converted and then returned
1147: *
1148: * @exception IllegalArgumentException if a MalformedURLException is
1149: * thrown when converting the relative URL to an absolute one
1150: */
1151: private String toAbsolute(String location) {
1152:
1153: if (location == null)
1154: return (location);
1155:
1156: // Construct a new absolute URL if possible (cribbed from
1157: // the DefaultErrorPage servlet)
1158: URL url = null;
1159: try {
1160: url = new URL(location);
1161: } catch (MalformedURLException e1) {
1162: HttpServletRequest hreq = (HttpServletRequest) request
1163: .getRequest();
1164: String requrl = request.getRequestURL().toString();
1165: try {
1166: url = new URL(new URL(requrl), location);
1167: } catch (MalformedURLException e2) {
1168: throw new IllegalArgumentException(location);
1169: }
1170: }
1171: return (url.toExternalForm());
1172: }
1173:
1174: /**
1175: * Return the specified URL with the specified session identifier
1176: * suitably encoded.
1177: *
1178: * @param url URL to be encoded with the session id
1179: * @param sessionId Session id to be included in the encoded URL
1180: */
1181: private String toEncoded(String url, String sessionId) {
1182:
1183: if ((url == null) || (sessionId == null))
1184: return (url);
1185:
1186: String path = url;
1187: String query = "";
1188: String anchor = "";
1189: int question = url.indexOf('?');
1190: if (question >= 0) {
1191: path = url.substring(0, question);
1192: query = url.substring(question);
1193: }
1194: int pound = path.indexOf('#');
1195: if (pound >= 0) {
1196: anchor = path.substring(pound);
1197: path = path.substring(0, pound);
1198: }
1199: StringBuffer sb = new StringBuffer(path);
1200: if (sb.length() > 0) { // jsessionid can't be first.
1201: sb.append(";jsessionid=");
1202: sb.append(sessionId);
1203: }
1204: sb.append(anchor);
1205: sb.append(query);
1206: return (sb.toString());
1207:
1208: }
1209:
1210: }
|