Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » connector » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   package org.apache.catalina.connector;
   20   
   21   
   22   import java.io.IOException;
   23   import java.io.OutputStream;
   24   import java.io.PrintWriter;
   25   import java.net.MalformedURLException;
   26   import java.security.AccessController;
   27   import java.security.PrivilegedAction;
   28   import java.security.PrivilegedActionException;
   29   import java.security.PrivilegedExceptionAction;
   30   import java.text.SimpleDateFormat;
   31   import java.util.ArrayList;
   32   import java.util.Enumeration;
   33   import java.util.Locale;
   34   import java.util.TimeZone;
   35   import java.util.Vector;
   36   
   37   import javax.servlet.ServletOutputStream;
   38   import javax.servlet.http.Cookie;
   39   import javax.servlet.http.HttpServletResponse;
   40   
   41   import org.apache.catalina.Context;
   42   import org.apache.catalina.Globals;
   43   import org.apache.catalina.Session;
   44   import org.apache.catalina.Wrapper;
   45   import org.apache.catalina.security.SecurityUtil;
   46   import org.apache.catalina.util.CharsetMapper;
   47   import org.apache.catalina.util.DateTool;
   48   import org.apache.catalina.util.StringManager;
   49   import org.apache.tomcat.util.buf.CharChunk;
   50   import org.apache.tomcat.util.buf.UEncoder;
   51   import org.apache.tomcat.util.http.FastHttpDateFormat;
   52   import org.apache.tomcat.util.http.MimeHeaders;
   53   import org.apache.tomcat.util.http.ServerCookie;
   54   import org.apache.tomcat.util.net.URL;
   55   
   56   /**
   57    * Wrapper object for the Coyote response.
   58    *
   59    * @author Remy Maucherat
   60    * @author Craig R. McClanahan
   61    * @version $Revision: 606610 $ $Date: 2007-12-23 21:16:08 +0100 (dim., 23 déc. 2007) $
   62    */
   63   
   64   public class Response
   65       implements HttpServletResponse {
   66   
   67   
   68       // ----------------------------------------------------------- Constructors
   69   
   70       static {
   71           // Ensure that URL is loaded for SM
   72           URL.isSchemeChar('c');
   73       }
   74   
   75       public Response() {
   76           urlEncoder.addSafeCharacter('/');
   77       }
   78   
   79   
   80       // ----------------------------------------------------- Class Variables
   81   
   82   
   83       /**
   84        * Descriptive information about this Response implementation.
   85        */
   86       protected static final String info =
   87           "org.apache.coyote.tomcat5.CoyoteResponse/1.0";
   88   
   89   
   90       /**
   91        * The string manager for this package.
   92        */
   93       protected static StringManager sm =
   94           StringManager.getManager(Constants.Package);
   95   
   96   
   97       // ----------------------------------------------------- Instance Variables
   98   
   99       /**
  100        * The date format we will use for creating date headers.
  101        */
  102       protected SimpleDateFormat format = null;
  103   
  104   
  105       // ------------------------------------------------------------- Properties
  106   
  107   
  108       /**
  109        * Associated Catalina connector.
  110        */
  111       protected Connector connector;
  112   
  113       /**
  114        * Return the Connector through which this Request was received.
  115        */
  116       public Connector getConnector() {
  117           return (this.connector);
  118       }
  119   
  120       /**
  121        * Set the Connector through which this Request was received.
  122        *
  123        * @param connector The new connector
  124        */
  125       public void setConnector(Connector connector) {
  126           this.connector = connector;
  127           if("AJP/1.3".equals(connector.getProtocol())) {
  128               // default size to size of one ajp-packet
  129               outputBuffer = new OutputBuffer(8184);
  130           } else {
  131               outputBuffer = new OutputBuffer();
  132           }
  133           outputStream = new CoyoteOutputStream(outputBuffer);
  134           writer = new CoyoteWriter(outputBuffer);
  135       }
  136   
  137   
  138       /**
  139        * Coyote response.
  140        */
  141       protected org.apache.coyote.Response coyoteResponse;
  142   
  143       /**
  144        * Set the Coyote response.
  145        * 
  146        * @param coyoteResponse The Coyote response
  147        */
  148       public void setCoyoteResponse(org.apache.coyote.Response coyoteResponse) {
  149           this.coyoteResponse = coyoteResponse;
  150           outputBuffer.setResponse(coyoteResponse);
  151       }
  152   
  153       /**
  154        * Get the Coyote response.
  155        */
  156       public org.apache.coyote.Response getCoyoteResponse() {
  157           return (coyoteResponse);
  158       }
  159   
  160   
  161       /**
  162        * Return the Context within which this Request is being processed.
  163        */
  164       public Context getContext() {
  165           return (request.getContext());
  166       }
  167   
  168       /**
  169        * Set the Context within which this Request is being processed.  This
  170        * must be called as soon as the appropriate Context is identified, because
  171        * it identifies the value to be returned by <code>getContextPath()</code>,
  172        * and thus enables parsing of the request URI.
  173        *
  174        * @param context The newly associated Context
  175        */
  176       public void setContext(Context context) {
  177           request.setContext(context);
  178       }
  179   
  180   
  181       /**
  182        * The associated output buffer.
  183        */
  184       protected OutputBuffer outputBuffer;
  185   
  186   
  187       /**
  188        * The associated output stream.
  189        */
  190       protected CoyoteOutputStream outputStream;
  191   
  192   
  193       /**
  194        * The associated writer.
  195        */
  196       protected CoyoteWriter writer;
  197   
  198   
  199       /**
  200        * The application commit flag.
  201        */
  202       protected boolean appCommitted = false;
  203   
  204   
  205       /**
  206        * The included flag.
  207        */
  208       protected boolean included = false;
  209   
  210       
  211       /**
  212        * The characterEncoding flag
  213        */
  214       private boolean isCharacterEncodingSet = false;
  215       
  216       /**
  217        * The error flag.
  218        */
  219       protected boolean error = false;
  220   
  221   
  222       /**
  223        * The set of Cookies associated with this Response.
  224        */
  225       protected ArrayList cookies = new ArrayList();
  226   
  227   
  228       /**
  229        * Using output stream flag.
  230        */
  231       protected boolean usingOutputStream = false;
  232   
  233   
  234       /**
  235        * Using writer flag.
  236        */
  237       protected boolean usingWriter = false;
  238   
  239   
  240       /**
  241        * URL encoder.
  242        */
  243       protected UEncoder urlEncoder = new UEncoder();
  244   
  245   
  246       /**
  247        * Recyclable buffer to hold the redirect URL.
  248        */
  249       protected CharChunk redirectURLCC = new CharChunk();
  250   
  251   
  252       // --------------------------------------------------------- Public Methods
  253   
  254   
  255       /**
  256        * Release all object references, and initialize instance variables, in
  257        * preparation for reuse of this object.
  258        */
  259       public void recycle() {
  260   
  261           outputBuffer.recycle();
  262           usingOutputStream = false;
  263           usingWriter = false;
  264           appCommitted = false;
  265           included = false;
  266           error = false;
  267           isCharacterEncodingSet = false;
  268           
  269           cookies.clear();
  270   
  271           if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
  272               if (facade != null) {
  273                   facade.clear();
  274                   facade = null;
  275               }
  276               if (outputStream != null) {
  277                   outputStream.clear();
  278                   outputStream = null;
  279               }
  280               if (writer != null) {
  281                   writer.clear();
  282                   writer = null;
  283               }
  284           } else {
  285               writer.recycle();
  286           }
  287   
  288       }
  289   
  290   
  291       /**
  292        * Clear cached encoders (to save memory for Comet requests).
  293        */
  294       public void clearEncoders() {
  295           outputBuffer.clearEncoders();
  296       }
  297       
  298   
  299       // ------------------------------------------------------- Response Methods
  300   
  301   
  302       /**
  303        * Return the number of bytes actually written to the output stream.
  304        */
  305       public int getContentCount() {
  306           return outputBuffer.getContentWritten();
  307       }
  308       
  309       /**
  310        * Return the number of bytes actually written to the output stream.
  311        */
  312       public long getContentCountLong() {
  313           return outputBuffer.getContentWrittenLong();
  314       }
  315   
  316       /**
  317        * Set the application commit flag.
  318        * 
  319        * @param appCommitted The new application committed flag value
  320        */
  321       public void setAppCommitted(boolean appCommitted) {
  322           this.appCommitted = appCommitted;
  323       }
  324   
  325   
  326       /**
  327        * Application commit flag accessor.
  328        */
  329       public boolean isAppCommitted() {
  330           return (this.appCommitted || isCommitted() || isSuspended()
  331                   || ((getContentLength() > 0) 
  332                       && (getContentCount() >= getContentLength())));
  333       }
  334   
  335   
  336       /**
  337        * Return the "processing inside an include" flag.
  338        */
  339       public boolean getIncluded() {
  340           return included;
  341       }
  342   
  343   
  344       /**
  345        * Set the "processing inside an include" flag.
  346        *
  347        * @param included <code>true</code> if we are currently inside a
  348        *  RequestDispatcher.include(), else <code>false</code>
  349        */
  350       public void setIncluded(boolean included) {
  351           this.included = included;
  352       }
  353   
  354   
  355       /**
  356        * Return descriptive information about this Response implementation and
  357        * the corresponding version number, in the format
  358        * <code>&lt;description&gt;/&lt;version&gt;</code>.
  359        */
  360       public String getInfo() {
  361           return (info);
  362       }
  363   
  364   
  365       /**
  366        * The request with which this response is associated.
  367        */
  368       protected Request request = null;
  369   
  370       /**
  371        * Return the Request with which this Response is associated.
  372        */
  373       public org.apache.catalina.connector.Request getRequest() {
  374           return (this.request);
  375       }
  376   
  377       /**
  378        * Set the Request with which this Response is associated.
  379        *
  380        * @param request The new associated request
  381        */
  382       public void setRequest(org.apache.catalina.connector.Request request) {
  383           this.request = (Request) request;
  384       }
  385   
  386   
  387       /**
  388        * The facade associated with this response.
  389        */
  390       protected ResponseFacade facade = null;
  391   
  392       /**
  393        * Return the <code>ServletResponse</code> for which this object
  394        * is the facade.
  395        */
  396       public HttpServletResponse getResponse() {
  397           if (facade == null) {
  398               facade = new ResponseFacade(this);
  399           }
  400           return (facade);
  401       }
  402   
  403   
  404       /**
  405        * Return the output stream associated with this Response.
  406        */
  407       public OutputStream getStream() {
  408           if (outputStream == null) {
  409               outputStream = new CoyoteOutputStream(outputBuffer);
  410           }
  411           return outputStream;
  412       }
  413   
  414   
  415       /**
  416        * Set the output stream associated with this Response.
  417        *
  418        * @param stream The new output stream
  419        */
  420       public void setStream(OutputStream stream) {
  421           // This method is evil
  422       }
  423   
  424   
  425       /**
  426        * Set the suspended flag.
  427        * 
  428        * @param suspended The new suspended flag value
  429        */
  430       public void setSuspended(boolean suspended) {
  431           outputBuffer.setSuspended(suspended);
  432       }
  433   
  434   
  435       /**
  436        * Suspended flag accessor.
  437        */
  438       public boolean isSuspended() {
  439           return outputBuffer.isSuspended();
  440       }
  441   
  442   
  443       /**
  444        * Closed flag accessor.
  445        */
  446       public boolean isClosed() {
  447           return outputBuffer.isClosed();
  448       }
  449   
  450   
  451       /**
  452        * Set the error flag.
  453        */
  454       public void setError() {
  455           error = true;
  456       }
  457   
  458   
  459       /**
  460        * Error flag accessor.
  461        */
  462       public boolean isError() {
  463           return error;
  464       }
  465   
  466   
  467       /**
  468        * Create and return a ServletOutputStream to write the content
  469        * associated with this Response.
  470        *
  471        * @exception IOException if an input/output error occurs
  472        */
  473       public ServletOutputStream createOutputStream() 
  474           throws IOException {
  475           // Probably useless
  476           if (outputStream == null) {
  477               outputStream = new CoyoteOutputStream(outputBuffer);
  478           }
  479           return outputStream;
  480       }
  481   
  482   
  483       /**
  484        * Perform whatever actions are required to flush and close the output
  485        * stream or writer, in a single operation.
  486        *
  487        * @exception IOException if an input/output error occurs
  488        */
  489       public void finishResponse() 
  490           throws IOException {
  491           // Writing leftover bytes
  492           outputBuffer.close();
  493       }
  494   
  495   
  496       /**
  497        * Return the content length that was set or calculated for this Response.
  498        */
  499       public int getContentLength() {
  500           return (coyoteResponse.getContentLength());
  501       }
  502   
  503   
  504       /**
  505        * Return the content type that was set or calculated for this response,
  506        * or <code>null</code> if no content type was set.
  507        */
  508       public String getContentType() {
  509           return (coyoteResponse.getContentType());
  510       }
  511   
  512   
  513       /**
  514        * Return a PrintWriter that can be used to render error messages,
  515        * regardless of whether a stream or writer has already been acquired.
  516        *
  517        * @return Writer which can be used for error reports. If the response is
  518        * not an error report returned using sendError or triggered by an
  519        * unexpected exception thrown during the servlet processing
  520        * (and only in that case), null will be returned if the response stream
  521        * has already been used.
  522        *
  523        * @exception IOException if an input/output error occurs
  524        */
  525       public PrintWriter getReporter() throws IOException {
  526           if (outputBuffer.isNew()) {
  527               outputBuffer.checkConverter();
  528               if (writer == null) {
  529                   writer = new CoyoteWriter(outputBuffer);
  530               }
  531               return writer;
  532           } else {
  533               return null;
  534           }
  535       }
  536   
  537   
  538       // ------------------------------------------------ ServletResponse Methods
  539   
  540   
  541       /**
  542        * Flush the buffer and commit this response.
  543        *
  544        * @exception IOException if an input/output error occurs
  545        */
  546       public void flushBuffer() 
  547           throws IOException {
  548           outputBuffer.flush();
  549       }
  550   
  551   
  552       /**
  553        * Return the actual buffer size used for this Response.
  554        */
  555       public int getBufferSize() {
  556           return outputBuffer.getBufferSize();
  557       }
  558   
  559   
  560       /**
  561        * Return the character encoding used for this Response.
  562        */
  563       public String getCharacterEncoding() {
  564           return (coyoteResponse.getCharacterEncoding());
  565       }
  566   
  567   
  568       /**
  569        * Return the servlet output stream associated with this Response.
  570        *
  571        * @exception IllegalStateException if <code>getWriter</code> has
  572        *  already been called for this response
  573        * @exception IOException if an input/output error occurs
  574        */
  575       public ServletOutputStream getOutputStream() 
  576           throws IOException {
  577   
  578           if (usingWriter)
  579               throw new IllegalStateException
  580                   (sm.getString("coyoteResponse.getOutputStream.ise"));
  581   
  582           usingOutputStream = true;
  583           if (outputStream == null) {
  584               outputStream = new CoyoteOutputStream(outputBuffer);
  585           }
  586           return outputStream;
  587   
  588       }
  589   
  590   
  591       /**
  592        * Return the Locale assigned to this response.
  593        */
  594       public Locale getLocale() {
  595           return (coyoteResponse.getLocale());
  596       }
  597   
  598   
  599       /**
  600        * Return the writer associated with this Response.
  601        *
  602        * @exception IllegalStateException if <code>getOutputStream</code> has
  603        *  already been called for this response
  604        * @exception IOException if an input/output error occurs
  605        */
  606       public PrintWriter getWriter() 
  607           throws IOException {
  608   
  609           if (usingOutputStream)
  610               throw new IllegalStateException
  611                   (sm.getString("coyoteResponse.getWriter.ise"));
  612   
  613           if (Globals.STRICT_SERVLET_COMPLIANCE) {
  614               /*
  615                * If the response's character encoding has not been specified as
  616                * described in <code>getCharacterEncoding</code> (i.e., the method
  617                * just returns the default value <code>ISO-8859-1</code>),
  618                * <code>getWriter</code> updates it to <code>ISO-8859-1</code>
  619                * (with the effect that a subsequent call to getContentType() will
  620                * include a charset=ISO-8859-1 component which will also be
  621                * reflected in the Content-Type response header, thereby satisfying
  622                * the Servlet spec requirement that containers must communicate the
  623                * character encoding used for the servlet response's writer to the
  624                * client).
  625                */
  626               setCharacterEncoding(getCharacterEncoding());
  627           }
  628   
  629           usingWriter = true;
  630           outputBuffer.checkConverter();
  631           if (writer == null) {
  632               writer = new CoyoteWriter(outputBuffer);
  633           }
  634           return writer;
  635   
  636       }
  637   
  638   
  639       /**
  640        * Has the output of this response already been committed?
  641        */
  642       public boolean isCommitted() {
  643           return (coyoteResponse.isCommitted());
  644       }
  645   
  646   
  647       /**
  648        * Clear any content written to the buffer.
  649        *
  650        * @exception IllegalStateException if this response has already
  651        *  been committed
  652        */
  653       public void reset() {
  654   
  655           if (included)
  656               return;     // Ignore any call from an included servlet
  657   
  658           coyoteResponse.reset();
  659           outputBuffer.reset();
  660           usingOutputStream = false;
  661           usingWriter = false;
  662           isCharacterEncodingSet = false;
  663       }
  664   
  665   
  666       /**
  667        * Reset the data buffer but not any status or header information.
  668        *
  669        * @exception IllegalStateException if the response has already
  670        *  been committed
  671        */
  672       public void resetBuffer() {
  673   
  674           if (isCommitted())
  675               throw new IllegalStateException
  676                   (sm.getString("coyoteResponse.resetBuffer.ise"));
  677   
  678           outputBuffer.reset();
  679   
  680       }
  681   
  682   
  683       /**
  684        * Set the buffer size to be used for this Response.
  685        *
  686        * @param size The new buffer size
  687        *
  688        * @exception IllegalStateException if this method is called after
  689        *  output has been committed for this response
  690        */
  691       public void setBufferSize(int size) {
  692   
  693           if (isCommitted() || !outputBuffer.isNew())
  694               throw new IllegalStateException
  695                   (sm.getString("coyoteResponse.setBufferSize.ise"));
  696   
  697           outputBuffer.setBufferSize(size);
  698   
  699       }
  700   
  701   
  702       /**
  703        * Set the content length (in bytes) for this Response.
  704        *
  705        * @param length The new content length
  706        */
  707       public void setContentLength(int length) {
  708   
  709           if (isCommitted())
  710               return;
  711   
  712           // Ignore any call from an included servlet
  713           if (included)
  714               return;
  715           
  716           if (usingWriter)
  717               return;
  718           
  719           coyoteResponse.setContentLength(length);
  720   
  721       }
  722   
  723   
  724       /**
  725        * Set the content type for this Response.
  726        *
  727        * @param type The new content type
  728        */
  729       public void setContentType(String type) {
  730   
  731           if (isCommitted())
  732               return;
  733   
  734           // Ignore any call from an included servlet
  735           if (included)
  736               return;
  737   
  738           // Ignore charset if getWriter() has already been called
  739           if (usingWriter) {
  740               if (type != null) {
  741                   int index = type.indexOf(";");
  742                   if (index != -1) {
  743                       type = type.substring(0, index);
  744                   }
  745               }
  746           }
  747   
  748           coyoteResponse.setContentType(type);
  749   
  750           // Check to see if content type contains charset
  751           if (type != null) {
  752               int index = type.indexOf(";");
  753               if (index != -1) {
  754                   int len = type.length();
  755                   index++;
  756                   while (index < len && Character.isSpace(type.charAt(index))) {
  757                       index++;
  758                   }
  759                   if (index+7 < len
  760                           && type.charAt(index) == 'c'
  761                           && type.charAt(index+1) == 'h'
  762                           && type.charAt(index+2) == 'a'
  763                           && type.charAt(index+3) == 'r'
  764                           && type.charAt(index+4) == 's'
  765                           && type.charAt(index+5) == 'e'
  766                           && type.charAt(index+6) == 't'
  767                           && type.charAt(index+7) == '=') {
  768                       isCharacterEncodingSet = true;
  769                   }
  770               }
  771           }
  772       }
  773   
  774   
  775       /*
  776        * Overrides the name of the character encoding used in the body
  777        * of the request. This method must be called prior to reading
  778        * request parameters or reading input using getReader().
  779        *
  780        * @param charset String containing the name of the chararacter encoding.
  781        */
  782       public void setCharacterEncoding(String charset) {
  783   
  784           if (isCommitted())
  785               return;
  786           
  787           // Ignore any call from an included servlet
  788           if (included)
  789               return;     
  790           
  791           // Ignore any call made after the getWriter has been invoked
  792           // The default should be used
  793           if (usingWriter)
  794               return;
  795   
  796           coyoteResponse.setCharacterEncoding(charset);
  797           isCharacterEncodingSet = true;
  798       }
  799   
  800       
  801       
  802       /**
  803        * Set the Locale that is appropriate for this response, including
  804        * setting the appropriate character encoding.
  805        *
  806        * @param locale The new locale
  807        */
  808       public void setLocale(Locale locale) {
  809   
  810           if (isCommitted())
  811               return;
  812   
  813           // Ignore any call from an included servlet
  814           if (included)
  815               return;
  816   
  817           coyoteResponse.setLocale(locale);
  818   
  819           // Ignore any call made after the getWriter has been invoked.
  820           // The default should be used
  821           if (usingWriter)
  822               return;
  823   
  824           if (isCharacterEncodingSet) {
  825               return;
  826           }
  827   
  828           CharsetMapper cm = getContext().getCharsetMapper();
  829           String charset = cm.getCharset( locale );
  830           if ( charset != null ){
  831               coyoteResponse.setCharacterEncoding(charset);
  832           }
  833   
  834       }
  835   
  836   
  837       // --------------------------------------------------- HttpResponse Methods
  838   
  839   
  840       /**
  841        * Return an array of all cookies set for this response, or
  842        * a zero-length array if no cookies have been set.
  843        */
  844       public Cookie[] getCookies() {
  845           return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
  846       }
  847   
  848   
  849       /**
  850        * Return the value for the specified header, or <code>null</code> if this
  851        * header has not been set.  If more than one value was added for this
  852        * name, only the first is returned; use getHeaderValues() to retrieve all
  853        * of them.
  854        *
  855        * @param name Header name to look up
  856        */
  857       public String getHeader(String name) {
  858           return coyoteResponse.getMimeHeaders().getHeader(name);
  859       }
  860   
  861   
  862       /**
  863        * Return an array of all the header names set for this response, or
  864        * a zero-length array if no headers have been set.
  865        */
  866       public String[] getHeaderNames() {
  867   
  868           MimeHeaders headers = coyoteResponse.getMimeHeaders();
  869           int n = headers.size();
  870           String[] result = new String[n];
  871           for (int i = 0; i < n; i++) {
  872               result[i] = headers.getName(i).toString();
  873           }
  874           return result;
  875   
  876       }
  877   
  878   
  879       /**
  880        * Return an array of all the header values associated with the
  881        * specified header name, or an zero-length array if there are no such
  882        * header values.
  883        *
  884        * @param name Header name to look up
  885        */
  886       public String[] getHeaderValues(String name) {
  887   
  888           Enumeration enumeration = coyoteResponse.getMimeHeaders().values(name);
  889           Vector result = new Vector();
  890           while (enumeration.hasMoreElements()) {
  891               result.addElement(enumeration.nextElement());
  892           }
  893           String[] resultArray = new String[result.size()];
  894           result.copyInto(resultArray);
  895           return resultArray;
  896   
  897       }
  898   
  899   
  900       /**
  901        * Return the error message that was set with <code>sendError()</code>
  902        * for this Response.
  903        */
  904       public String getMessage() {
  905           return coyoteResponse.getMessage();
  906       }
  907   
  908   
  909       /**
  910        * Return the HTTP status code associated with this Response.
  911        */
  912       public int getStatus() {
  913           return coyoteResponse.getStatus();
  914       }
  915   
  916   
  917       /**
  918        * Reset this response, and specify the values for the HTTP status code
  919        * and corresponding message.
  920        *
  921        * @exception IllegalStateException if this response has already been
  922        *  committed
  923        */
  924       public void reset(int status, String message) {
  925           reset();
  926           setStatus(status, message);
  927       }
  928   
  929   
  930       // -------------------------------------------- HttpServletResponse Methods
  931   
  932   
  933       /**
  934        * Add the specified Cookie to those that will be included with
  935        * this Response.
  936        *
  937        * @param cookie Cookie to be added
  938        */
  939       public void addCookie(final Cookie cookie) {
  940   
  941           // Ignore any call from an included servlet
  942           if (included)
  943               return;
  944   
  945           addCookieInternal(cookie);
  946   
  947       }
  948   
  949   
  950       /**
  951        * Add the specified Cookie to those that will be included with
  952        * this Response.
  953        *
  954        * @param cookie Cookie to be added
  955        */
  956       public void addCookieInternal(final Cookie cookie) {
  957   
  958           if (isCommitted())
  959               return;
  960   
  961           final StringBuffer sb = new StringBuffer();
  962           //web application code can receive a IllegalArgumentException 
  963           //from the appendCookieValue invokation
  964           if (SecurityUtil.isPackageProtectionEnabled()) {
  965               AccessController.doPrivileged(new PrivilegedAction() {
  966                   public Object run(){
  967                       ServerCookie.appendCookieValue
  968                           (sb, cookie.getVersion(), cookie.getName(), 
  969                            cookie.getValue(), cookie.getPath(), 
  970                            cookie.getDomain(), cookie.getComment(), 
  971                            cookie.getMaxAge(), cookie.getSecure());
  972                       return null;
  973                   }
  974               });
  975           } else {
  976               ServerCookie.appendCookieValue
  977                   (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
  978                        cookie.getPath(), cookie.getDomain(), cookie.getComment(), 
  979                        cookie.getMaxAge(), cookie.getSecure());
  980           }
  981           //if we reached here, no exception, cookie is valid
  982           // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
  983           // RFC2965 is not supported by browsers and the Servlet spec
  984           // asks for 2109.
  985           addHeader("Set-Cookie", sb.toString());
  986   
  987           cookies.add(cookie);
  988       }
  989   
  990   
  991       /**
  992        * Add the specified date header to the specified value.
  993        *
  994        * @param name Name of the header to set
  995        * @param value Date value to be set
  996        */
  997       public void addDateHeader(String name, long value) {
  998   
  999           if (isCommitted())
 1000               return;
 1001   
 1002           // Ignore any call from an included servlet
 1003           if (included) {
 1004               return;
 1005           }
 1006   
 1007           if (format == null) {
 1008               format = new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER,
 1009                                             Locale.US);
 1010               format.setTimeZone(TimeZone.getTimeZone("GMT"));
 1011           }
 1012   
 1013           addHeader(name, FastHttpDateFormat.formatDate(value, format));
 1014   
 1015       }
 1016   
 1017   
 1018       /**
 1019        * Add the specified header to the specified value.
 1020        *
 1021        * @param name Name of the header to set
 1022        * @param value Value to be set
 1023        */
 1024       public void addHeader(String name, String value) {
 1025   
 1026           if (isCommitted())
 1027               return;
 1028   
 1029           // Ignore any call from an included servlet
 1030           if (included)
 1031               return;
 1032   
 1033           coyoteResponse.addHeader(name, value);
 1034   
 1035       }
 1036   
 1037   
 1038       /**
 1039        * Add the specified integer header to the specified value.
 1040        *
 1041        * @param name Name of the header to set
 1042        * @param value Integer value to be set
 1043        */
 1044       public void addIntHeader(String name, int value) {
 1045   
 1046           if (isCommitted())
 1047               return;
 1048   
 1049           // Ignore any call from an included servlet
 1050           if (included)
 1051               return;
 1052   
 1053           addHeader(name, "" + value);
 1054   
 1055       }
 1056   
 1057   
 1058       /**
 1059        * Has the specified header been set already in this response?
 1060        *
 1061        * @param name Name of the header to check
 1062        */
 1063       public boolean containsHeader(String name) {
 1064           // Need special handling for Content-Type and Content-Length due to
 1065           // special handling of these in coyoteResponse
 1066           char cc=name.charAt(0);
 1067           if(cc=='C' || cc=='c') {
 1068               if(name.equalsIgnoreCase("Content-Type")) {
 1069                   // Will return null if this has not been set
 1070                   return (coyoteResponse.getContentType() != null);
 1071               }
 1072               if(name.equalsIgnoreCase("Content-Length")) {
 1073                   // -1 means not known and is not sent to client
 1074                   return (coyoteResponse.getContentLengthLong() != -1);
 1075               }
 1076           }
 1077   
 1078           return coyoteResponse.containsHeader(name);
 1079       }
 1080   
 1081   
 1082       /**
 1083        * Encode the session identifier associated with this response
 1084        * into the specified redirect URL, if necessary.
 1085        *
 1086        * @param url URL to be encoded
 1087        */
 1088       public String encodeRedirectURL(String url) {
 1089   
 1090           if (isEncodeable(toAbsolute(url))) {
 1091               return (toEncoded(url, request.getSessionInternal().getIdInternal()));
 1092           } else {
 1093               return (url);
 1094           }
 1095   
 1096       }
 1097   
 1098   
 1099       /**
 1100        * Encode the session identifier associated with this response
 1101        * into the specified redirect URL, if necessary.
 1102        *
 1103        * @param url URL to be encoded
 1104        *
 1105        * @deprecated As of Version 2.1 of the Java Servlet API, use
 1106        *  <code>encodeRedirectURL()</code> instead.
 1107        */
 1108       public String encodeRedirectUrl(String url) {
 1109           return (encodeRedirectURL(url));
 1110       }
 1111   
 1112   
 1113       /**
 1114        * Encode the session identifier associated with this response
 1115        * into the specified URL, if necessary.
 1116        *
 1117        * @param url URL to be encoded
 1118        */
 1119       public String encodeURL(String url) {
 1120           
 1121           String absolute = toAbsolute(url);
 1122           if (isEncodeable(absolute)) {
 1123               // W3c spec clearly said 
 1124               if (url.equalsIgnoreCase("")){
 1125                   url = absolute;
 1126               }
 1127               return (toEncoded(url, request.getSessionInternal().getIdInternal()));
 1128           } else {
 1129               return (url);
 1130           }
 1131   
 1132       }
 1133   
 1134   
 1135       /**
 1136        * Encode the session identifier associated with this response
 1137        * into the specified URL, if necessary.
 1138        *
 1139        * @param url URL to be encoded
 1140        *
 1141        * @deprecated As of Version 2.1 of the Java Servlet API, use
 1142        *  <code>encodeURL()</code> instead.
 1143        */
 1144       public String encodeUrl(String url) {
 1145           return (encodeURL(url));
 1146       }
 1147   
 1148   
 1149       /**
 1150        * Send an acknowledgment of a request.
 1151        * 
 1152        * @exception IOException if an input/output error occurs
 1153        */
 1154       public void sendAcknowledgement()
 1155           throws IOException {
 1156   
 1157           if (isCommitted())
 1158               return;
 1159   
 1160           // Ignore any call from an included servlet
 1161           if (included)
 1162               return; 
 1163   
 1164           coyoteResponse.acknowledge();
 1165   
 1166       }
 1167   
 1168   
 1169       /**
 1170        * Send an error response with the specified status and a
 1171        * default message.
 1172        *
 1173        * @param status HTTP status code to send
 1174        *
 1175        * @exception IllegalStateException if this response has
 1176        *  already been committed
 1177        * @exception IOException if an input/output error occurs
 1178        */
 1179       public void sendError(int status) 
 1180           throws IOException {
 1181           sendError(status, null);
 1182       }
 1183   
 1184   
 1185       /**
 1186        * Send an error response with the specified status and message.
 1187        *
 1188        * @param status HTTP status code to send
 1189        * @param message Corresponding message to send
 1190        *
 1191        * @exception IllegalStateException if this response has
 1192        *  already been committed
 1193        * @exception IOException if an input/output error occurs
 1194        */
 1195       public void sendError(int status, String message) 
 1196           throws IOException {
 1197   
 1198           if (isCommitted())
 1199               throw new IllegalStateException
 1200                   (sm.getString("coyoteResponse.sendError.ise"));
 1201   
 1202           // Ignore any call from an included servlet
 1203           if (included)
 1204               return; 
 1205   
 1206           Wrapper wrapper = getRequest().getWrapper();
 1207           if (wrapper != null) {
 1208               wrapper.incrementErrorCount();
 1209           } 
 1210   
 1211           setError();
 1212   
 1213           coyoteResponse.setStatus(status);
 1214           coyoteResponse.setMessage(message);
 1215   
 1216           // Clear any data content that has been buffered
 1217           resetBuffer();
 1218   
 1219           // Cause the response to be finished (from the application perspective)
 1220           setSuspended(true);
 1221   
 1222       }
 1223   
 1224   
 1225       /**
 1226        * Send a temporary redirect to the specified redirect location URL.
 1227        *
 1228        * @param location Location URL to redirect to
 1229        *
 1230        * @exception IllegalStateException if this response has
 1231        *  already been committed
 1232        * @exception IOException if an input/output error occurs
 1233        */
 1234       public void sendRedirect(String location) 
 1235           throws IOException {
 1236   
 1237           if (isCommitted())
 1238               throw new IllegalStateException
 1239                   (sm.getString("coyoteResponse.sendRedirect.ise"));
 1240   
 1241           // Ignore any call from an included servlet
 1242           if (included)
 1243               return; 
 1244   
 1245           // Clear any data content that has been buffered
 1246           resetBuffer();
 1247   
 1248           // Generate a temporary redirect to the specified location
 1249           try {
 1250               String absolute = toAbsolute(location);
 1251               setStatus(SC_FOUND);
 1252               setHeader("Location", absolute);
 1253           } catch (IllegalArgumentException e) {
 1254               setStatus(SC_NOT_FOUND);
 1255           }
 1256   
 1257           // Cause the response to be finished (from the application perspective)
 1258           setSuspended(true);
 1259   
 1260       }
 1261   
 1262   
 1263       /**
 1264        * Set the specified date header to the specified value.
 1265        *
 1266        * @param name Name of the header to set
 1267        * @param value Date value to be set
 1268        */
 1269       public void setDateHeader(String name, long value) {
 1270   
 1271           if (isCommitted())
 1272               return;
 1273   
 1274           // Ignore any call from an included servlet
 1275           if (included) {
 1276               return;
 1277           }
 1278   
 1279           if (format == null) {
 1280               format = new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER,
 1281                                             Locale.US);
 1282               format.setTimeZone(TimeZone.getTimeZone("GMT"));
 1283           }
 1284   
 1285           setHeader(name, FastHttpDateFormat.formatDate(value, format));
 1286   
 1287       }
 1288   
 1289   
 1290       /**
 1291        * Set the specified header to the specified value.
 1292        *
 1293        * @param name Name of the header to set
 1294        * @param value Value to be set
 1295        */
 1296       public void setHeader(String name, String value) {
 1297   
 1298           if (isCommitted())
 1299               return;
 1300   
 1301           // Ignore any call from an included servlet
 1302           if (included)
 1303               return;
 1304   
 1305           coyoteResponse.setHeader(name, value);
 1306   
 1307       }
 1308   
 1309   
 1310       /**
 1311        * Set the specified integer header to the specified value.
 1312        *
 1313        * @param name Name of the header to set
 1314        * @param value Integer value to be set
 1315        */
 1316       public void setIntHeader(String name, int value) {
 1317   
 1318           if (isCommitted())
 1319               return;
 1320   
 1321           // Ignore any call from an included servlet
 1322           if (included)
 1323               return;
 1324   
 1325           setHeader(name, "" + value);
 1326   
 1327       }
 1328   
 1329   
 1330       /**
 1331        * Set the HTTP status to be returned with this response.
 1332        *
 1333        * @param status The new HTTP status
 1334        */
 1335       public void setStatus(int status) {
 1336           setStatus(status, null);
 1337       }
 1338   
 1339   
 1340       /**
 1341        * Set the HTTP status and message to be returned with this response.
 1342        *
 1343        * @param status The new HTTP status
 1344        * @param message The associated text message
 1345        *
 1346        * @deprecated As of Version 2.1 of the Java Servlet API, this method
 1347        *  has been deprecated due to the ambiguous meaning of the message
 1348        *  parameter.
 1349        */
 1350       public void setStatus(int status, String message) {
 1351   
 1352           if (isCommitted())
 1353               return;
 1354   
 1355           // Ignore any call from an included servlet
 1356           if (included)
 1357               return;
 1358   
 1359           coyoteResponse.setStatus(status);
 1360           coyoteResponse.setMessage(message);
 1361   
 1362       }
 1363   
 1364   
 1365       // ------------------------------------------------------ Protected Methods
 1366   
 1367   
 1368       /**
 1369        * Return <code>true</code> if the specified URL should be encoded with
 1370        * a session identifier.  This will be true if all of the following
 1371        * conditions are met:
 1372        * <ul>
 1373        * <li>The request we are responding to asked for a valid session
 1374        * <li>The requested session ID was not received via a cookie
 1375        * <li>The specified URL points back to somewhere within the web
 1376        *     application that is responding to this request
 1377        * </ul>
 1378        *
 1379        * @param location Absolute URL to be validated
 1380        */
 1381       protected boolean isEncodeable(final String location) {
 1382   
 1383           if (location == null)
 1384               return (false);
 1385   
 1386           // Is this an intra-document reference?
 1387           if (location.startsWith("#"))
 1388               return (false);
 1389   
 1390           // Are we in a valid session that is not using cookies?
 1391           final Request hreq = request;
 1392           final Session session = hreq.getSessionInternal(false);
 1393           if (session == null)
 1394               return (false);
 1395           if (hreq.isRequestedSessionIdFromCookie())
 1396               return (false);
 1397           
 1398           if (SecurityUtil.isPackageProtectionEnabled()) {
 1399               return ((Boolean)
 1400                   AccessController.doPrivileged(new PrivilegedAction() {
 1401   
 1402                   public Object run(){
 1403                       return new Boolean(doIsEncodeable(hreq, session, location));
 1404                   }
 1405               })).booleanValue();
 1406           } else {
 1407               return doIsEncodeable(hreq, session, location);
 1408           }
 1409       }
 1410   
 1411       private boolean doIsEncodeable(Request hreq, Session session, 
 1412                                      String location) {
 1413           // Is this a valid absolute URL?
 1414           URL url = null;
 1415           try {
 1416               url = new URL(location);
 1417           } catch (MalformedURLException e) {
 1418               return (false);
 1419           }
 1420   
 1421           // Does this URL match down to (and including) the context path?
 1422           if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol()))
 1423               return (false);
 1424           if (!hreq.getServerName().equalsIgnoreCase(url.getHost()))
 1425               return (false);
 1426           int serverPort = hreq.getServerPort();
 1427           if (serverPort == -1) {
 1428               if ("https".equals(hreq.getScheme()))
 1429                   serverPort = 443;
 1430               else
 1431                   serverPort = 80;
 1432           }
 1433           int urlPort = url.getPort();
 1434           if (urlPort == -1) {
 1435               if ("https".equals(url.getProtocol()))
 1436                   urlPort = 443;
 1437               else
 1438                   urlPort = 80;
 1439           }
 1440           if (serverPort != urlPort)
 1441               return (false);
 1442   
 1443           String contextPath = getContext().getPath();
 1444           if (contextPath != null) {
 1445               String file = url.getFile();
 1446               if ((file == null) || !file.startsWith(contextPath))
 1447                   return (false);
 1448               String tok = ";" + Globals.SESSION_PARAMETER_NAME + "=" + session.getIdInternal();
 1449               if( file.indexOf(tok, contextPath.length()) >= 0 )
 1450                   return (false);
 1451           }
 1452   
 1453           // This URL belongs to our web application, so it is encodeable
 1454           return (true);
 1455   
 1456       }
 1457   
 1458   
 1459       /**
 1460        * Convert (if necessary) and return the absolute URL that represents the
 1461        * resource referenced by this possibly relative URL.  If this URL is
 1462        * already absolute, return it unchanged.
 1463        *
 1464        * @param location URL to be (possibly) converted and then returned
 1465        *
 1466        * @exception IllegalArgumentException if a MalformedURLException is
 1467        *  thrown when converting the relative URL to an absolute one
 1468        */
 1469       private String toAbsolute(String location) {
 1470   
 1471           if (location == null)
 1472               return (location);
 1473   
 1474           boolean leadingSlash = location.startsWith("/");
 1475   
 1476           if (leadingSlash || !hasScheme(location)) {
 1477   
 1478               redirectURLCC.recycle();
 1479   
 1480               String scheme = request.getScheme();
 1481               String name = request.getServerName();
 1482               int port = request.getServerPort();
 1483   
 1484               try {
 1485                   redirectURLCC.append(scheme, 0, scheme.length());
 1486                   redirectURLCC.append("://", 0, 3);
 1487                   redirectURLCC.append(name, 0, name.length());
 1488                   if ((scheme.equals("http") && port != 80)
 1489                       || (scheme.equals("https") && port != 443)) {
 1490                       redirectURLCC.append(':');
 1491                       String portS = port + "";
 1492                       redirectURLCC.append(portS, 0, portS.length());
 1493                   }
 1494                   if (!leadingSlash) {
 1495                       String relativePath = request.getDecodedRequestURI();
 1496                       int pos = relativePath.lastIndexOf('/');
 1497                       relativePath = relativePath.substring(0, pos);
 1498                       
 1499                       String encodedURI = null;
 1500                       final String frelativePath = relativePath;
 1501                       if (SecurityUtil.isPackageProtectionEnabled() ){
 1502                           try{
 1503                               encodedURI = (String)AccessController.doPrivileged( 
 1504                                   new PrivilegedExceptionAction(){                                
 1505                                       public Object run() throws IOException{
 1506                                           return urlEncoder.encodeURL(frelativePath);
 1507                                       }
 1508                              });   
 1509                           } catch (PrivilegedActionException pae){
 1510                               IllegalArgumentException iae =
 1511                                   new IllegalArgumentException(location);
 1512                               iae.initCause(pae.getException());
 1513                               throw iae;
 1514                           }
 1515                       } else {
 1516                           encodedURI = urlEncoder.encodeURL(relativePath);
 1517                       }
 1518                       redirectURLCC.append(encodedURI, 0, encodedURI.length());
 1519                       redirectURLCC.append('/');
 1520                   }
 1521                   redirectURLCC.append(location, 0, location.length());
 1522               } catch (IOException e) {
 1523                   IllegalArgumentException iae =
 1524                       new IllegalArgumentException(location);
 1525                   iae.initCause(e);
 1526                   throw iae;
 1527               }
 1528   
 1529               return redirectURLCC.toString();
 1530   
 1531           } else {
 1532   
 1533               return (location);
 1534   
 1535           }
 1536   
 1537       }
 1538   
 1539   
 1540       /**
 1541        * Determine if a URI string has a <code>scheme</code> component.
 1542        */
 1543       private boolean hasScheme(String uri) {
 1544           int len = uri.length();
 1545           for(int i=0; i < len ; i++) {
 1546               char c = uri.charAt(i);
 1547               if(c == ':') {
 1548                   return i > 0;
 1549               } else if(!URL.isSchemeChar(c)) {
 1550                   return false;
 1551               }
 1552           }
 1553           return false;
 1554       }
 1555   
 1556       /**
 1557        * Return the specified URL with the specified session identifier
 1558        * suitably encoded.
 1559        *
 1560        * @param url URL to be encoded with the session id
 1561        * @param sessionId Session id to be included in the encoded URL
 1562        */
 1563       protected String toEncoded(String url, String sessionId) {
 1564   
 1565           if ((url == null) || (sessionId == null))
 1566               return (url);
 1567   
 1568           String path = url;
 1569           String query = "";
 1570           String anchor = "";
 1571           int question = url.indexOf('?');
 1572           if (question >= 0) {
 1573               path = url.substring(0, question);
 1574               query = url.substring(question);
 1575           }
 1576           int pound = path.indexOf('#');
 1577           if (pound >= 0) {
 1578               anchor = path.substring(pound);
 1579               path = path.substring(0, pound);
 1580           }
 1581           StringBuffer sb = new StringBuffer(path);
 1582           if( sb.length() > 0 ) { // jsessionid can't be first.
 1583               sb.append(";");
 1584               sb.append(Globals.SESSION_PARAMETER_NAME);
 1585               sb.append("=");
 1586               sb.append(sessionId);
 1587           }
 1588           sb.append(anchor);
 1589           sb.append(query);
 1590           return (sb.toString());
 1591   
 1592       }
 1593   
 1594   
 1595   }
 1596   

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » connector » [javadoc | source]