View Javadoc

    1   //========================================================================
    2   //$Id: Response.java,v 1.8 2005/11/25 21:01:45 gregwilkins Exp $
    3   //Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
    4   //------------------------------------------------------------------------
    5   //Licensed under the Apache License, Version 2.0 (the "License");
    6   //you may not use this file except in compliance with the License.
    7   //You may obtain a copy of the License at
    8   //http://www.apache.org/licenses/LICENSE-2.0
    9   //Unless required by applicable law or agreed to in writing, software
    10  //distributed under the License is distributed on an "AS IS" BASIS,
    11  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //See the License for the specific language governing permissions and
    13  //limitations under the License.
    14  //========================================================================
    15
    16  package org.mortbay.jetty;
    17
    18  import java.io.IOException;
    19  import java.io.PrintWriter;
    20  import java.util.Collections;
    21  import java.util.Enumeration;
    22  import java.util.Locale;
    23
    24  import javax.servlet.ServletOutputStream;
    25  import javax.servlet.http.Cookie;
    26  import javax.servlet.http.HttpServletResponse;
    27  import javax.servlet.http.HttpSession;
    28
    29  import org.mortbay.io.BufferCache.CachedBuffer;
    30  import org.mortbay.jetty.handler.ContextHandler;
    31  import org.mortbay.jetty.handler.ErrorHandler;
    32  import org.mortbay.jetty.servlet.ServletHandler;
    33  import org.mortbay.log.Log;
    34  import org.mortbay.util.ByteArrayISO8859Writer;
    35  import org.mortbay.util.IO;
    36  import org.mortbay.util.QuotedStringTokenizer;
    37  import org.mortbay.util.StringUtil;
    38  import org.mortbay.util.URIUtil;
    39
    40  /* ------------------------------------------------------------ */
    41  /** Response.
    42   * <p>
    43   * Implements {@link javax.servlet.HttpServletResponse} from the {@link javax.servlet} package.
    44   * </p>
    45   *
    46   * @author gregw
    47   *
    48   */
    49  public class Response implements HttpServletResponse
    50  {
    51      public static final int
    52          DISABLED=-1,
    53          NONE=0,
    54          STREAM=1,
    55          WRITER=2;
    56
    57      private static PrintWriter __nullPrintWriter;
    58      private static ServletOutputStream __nullServletOut;
    59
    60      static
    61      {
    62          try{
    63              __nullPrintWriter = new PrintWriter(IO.getNullWriter());
    64              __nullServletOut = new NullOutput();
    65          }
    66          catch (Exception e)
    67          {
    68              Log.warn(e);
    69          }
    70      }
    71
    72      private HttpConnection _connection;
    73      private int _status=SC_OK;
    74      private String _reason;
    75      private Locale _locale;
    76      private String _mimeType;
    77      private CachedBuffer _cachedMimeType;
    78      private String _characterEncoding;
    79      private boolean _explicitEncoding;
    80      private String _contentType;
    81      private int _outputState;
    82      private PrintWriter _writer;
    83
    84      /* ------------------------------------------------------------ */
    85      /**
    86       *
    87       */
    88      public Response(HttpConnection connection)
    89      {
    90          _connection=connection;
    91      }
    92
    93
    94      /* ------------------------------------------------------------ */
    95      /*
    96       * @see javax.servlet.ServletResponse#reset()
    97       */
    98      protected void recycle()
    99      {
    100         _status=SC_OK;
    101         _reason=null;
    102         _locale=null;
    103         _mimeType=null;
    104         _cachedMimeType=null;
    105         _characterEncoding=null;
    106         _explicitEncoding=false;
    107         _contentType=null;
    108         _outputState=NONE;
    109         _writer=null;
    110     }
    111
    112     /* ------------------------------------------------------------ */
    113     /*
    114      * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie)
    115      */
    116     public void addCookie(Cookie cookie)
    117     {
    118         _connection.getResponseFields().addSetCookie(cookie);
    119     }
    120
    121     /* ------------------------------------------------------------ */
    122     /*
    123      * @see javax.servlet.http.HttpServletResponse#containsHeader(java.lang.String)
    124      */
    125     public boolean containsHeader(String name)
    126     {
    127         return _connection.getResponseFields().containsKey(name);
    128     }
    129
    130     /* ------------------------------------------------------------ */
    131     /*
    132      * @see javax.servlet.http.HttpServletResponse#encodeURL(java.lang.String)
    133      */
    134     public String encodeURL(String url)
    135     {
    136         Request request=_connection.getRequest();
    137         SessionManager sessionManager = request.getSessionManager();
    138         if (sessionManager==null)
    139             return url;
    140         String sessionURLPrefix = sessionManager.getSessionURLPrefix();
    141         if (sessionURLPrefix==null)
    142             return url;
    143
    144         // should not encode if cookies in evidence
    145         if (url==null || request==null || request.isRequestedSessionIdFromCookie())
    146         {
    147             int prefix=url.indexOf(sessionURLPrefix);
    148             if (prefix!=-1)
    149             {
    150                 int suffix=url.indexOf("?",prefix);
    151                 if (suffix<0)
    152                     suffix=url.indexOf("#",prefix);
    153
    154                 if (suffix<=prefix)
    155                     return url.substring(0,prefix);
    156                 return url.substring(0,prefix)+url.substring(suffix);
    157             }
    158             return url;
    159         }
    160
    161         // get session;
    162         HttpSession session=request.getSession(false);
    163
    164         // no session
    165         if (session == null)
    166             return url;
    167
    168
    169         // invalid session
    170         if (!sessionManager.isValid(session))
    171             return url;
    172
    173         String id=sessionManager.getNodeId(session);
    174
    175
    176         // TODO Check host and port are for this server
    177         // Already encoded
    178         int prefix=url.indexOf(sessionURLPrefix);
    179         if (prefix!=-1)
    180         {
    181             int suffix=url.indexOf("?",prefix);
    182             if (suffix<0)
    183                 suffix=url.indexOf("#",prefix);
    184
    185             if (suffix<=prefix)
    186                 return url.substring(0,prefix+sessionURLPrefix.length())+id;
    187             return url.substring(0,prefix+sessionURLPrefix.length())+id+
    188                 url.substring(suffix);
    189         }
    190
    191         // edit the session
    192         int suffix=url.indexOf('?');
    193         if (suffix<0)
    194             suffix=url.indexOf('#');
    195         if (suffix<0)
    196             return url+sessionURLPrefix+id;
    197         return url.substring(0,suffix)+
    198             sessionURLPrefix+id+url.substring(suffix);
    199     }
    200
    201     /* ------------------------------------------------------------ */
    202     /*
    203      * @see javax.servlet.http.HttpServletResponse#encodeRedirectURL(java.lang.String)
    204      */
    205     public String encodeRedirectURL(String url)
    206     {
    207         return encodeURL(url);
    208     }
    209
    210     /* ------------------------------------------------------------ */
    211     /*
    212      * @see javax.servlet.http.HttpServletResponse#encodeUrl(java.lang.String)
    213      */
    214     public String encodeUrl(String url)
    215     {
    216         return encodeURL(url);
    217     }
    218
    219     /* ------------------------------------------------------------ */
    220     /*
    221      * @see javax.servlet.http.HttpServletResponse#encodeRedirectUrl(java.lang.String)
    222      */
    223     public String encodeRedirectUrl(String url)
    224     {
    225         return encodeURL(url);
    226     }
    227
    228     /* ------------------------------------------------------------ */
    229     /*
    230      * @see javax.servlet.http.HttpServletResponse#sendError(int, java.lang.String)
    231      */
    232     public void sendError(int code, String message) throws IOException
    233     {
    234     	if (_connection.isIncluding())
    235     		return;
    236
    237         if (isCommitted())
    238             Log.warn("Committed before "+code+" "+message);
    239
    240         resetBuffer();
    241         _characterEncoding=null;
    242         setHeader(HttpHeaders.EXPIRES,null);
    243         setHeader(HttpHeaders.LAST_MODIFIED,null);
    244         setHeader(HttpHeaders.CACHE_CONTROL,null);
    245         setHeader(HttpHeaders.CONTENT_TYPE,null);
    246         setHeader(HttpHeaders.CONTENT_LENGTH,null);
    247
    248         _outputState=NONE;
    249         setStatus(code,message);
    250
    251         if (message==null)
    252             message=HttpGenerator.getReason(code);
    253
    254         // If we are allowed to have a body
    255         if (code!=SC_NO_CONTENT &&
    256             code!=SC_NOT_MODIFIED &&
    257             code!=SC_PARTIAL_CONTENT &&
    258             code>=SC_OK)
    259         {
    260             Request request = _connection.getRequest();
    261
    262             ErrorHandler error_handler = null;
    263             ContextHandler.SContext context = request.getContext();
    264             if (context!=null)
    265                 error_handler=context.getContextHandler().getErrorHandler();
    266             if (error_handler!=null)
    267             {
    268                 // TODO - probably should reset these after the request?
    269                 request.setAttribute(ServletHandler.__J_S_ERROR_STATUS_CODE,new Integer(code));
    270                 request.setAttribute(ServletHandler.__J_S_ERROR_MESSAGE, message);
    271                 request.setAttribute(ServletHandler.__J_S_ERROR_REQUEST_URI, request.getRequestURI());
    272                 request.setAttribute(ServletHandler.__J_S_ERROR_SERVLET_NAME,request.getServletName());
    273
    274                 error_handler.handle(null,_connection.getRequest(),this, Handler.ERROR);
    275             }
    276             else
    277             {
    278                 setHeader(HttpHeaders.CACHE_CONTROL, "must-revalidate,no-cache,no-store");
    279                 setContentType(MimeTypes.TEXT_HTML_8859_1);
    280                 ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048);
    281                 if (message != null)
    282                 {
    283                     message= StringUtil.replace(message, "&", "&amp;");
    284                     message= StringUtil.replace(message, "<", "&lt;");
    285                     message= StringUtil.replace(message, ">", "&gt;");
    286                 }
    287                 String uri= request.getRequestURI();
    288                 if (uri!=null)
    289                 {
    290                     uri= StringUtil.replace(uri, "&", "&amp;");
    291                     uri= StringUtil.replace(uri, "<", "&lt;");
    292                     uri= StringUtil.replace(uri, ">", "&gt;");
    293                 }
    294
    295                 writer.write("<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/>\n");
    296                 writer.write("<title>Error ");
    297                 writer.write(Integer.toString(code));
    298                 writer.write(' ');
    299                 if (message==null)
    300                     message=HttpGenerator.getReason(code);
    301                 writer.write(message);
    302                 writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: ");
    303                 writer.write(Integer.toString(code));
    304                 writer.write("</h2>\n<p>Problem accessing ");
    305                 writer.write(uri);
    306                 writer.write(". Reason:\n<pre>    ");
    307                 writer.write(message);
    308                 writer.write("</pre>");
    309                 writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>");
    310
    311                 for (int i= 0; i < 20; i++)
    312                     writer.write("\n                                                ");
    313                 writer.write("\n</body>\n</html>\n");
    314
    315                 writer.flush();
    316                 setContentLength(writer.size());
    317                 writer.writeTo(getOutputStream());
    318                 writer.destroy();
    319             }
    320         }
    321         else if (code!=SC_PARTIAL_CONTENT)
    322         {
    323             _connection.getRequestFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
    324             _connection.getRequestFields().remove(HttpHeaders.CONTENT_LENGTH_BUFFER);
    325             _characterEncoding=null;
    326             _mimeType=null;
    327             _cachedMimeType=null;
    328         }
    329
    330         complete();
    331     }
    332
    333     /* ------------------------------------------------------------ */
    334     /*
    335      * @see javax.servlet.http.HttpServletResponse#sendError(int)
    336      */
    337     public void sendError(int sc) throws IOException
    338     {
    339         if (sc==102)
    340             sendProcessing();
    341         else
    342             sendError(sc,null);
    343     }
    344
    345     /* ------------------------------------------------------------ */
    346     /* Send a 102-Processing response.
    347      * If the connection is a HTTP connection, the version is 1.1 and the
    348      * request has a Expect header starting with 102, then a 102 response is
    349      * sent. This indicates that the request still be processed and real response
    350      * can still be sent.   This method is called by sendError if it is passed 102.
    351      * @see javax.servlet.http.HttpServletResponse#sendError(int)
    352      */
    353     public void sendProcessing() throws IOException
    354     {
    355         Generator g = _connection.getGenerator();
    356         if (g instanceof HttpGenerator)
    357         {
    358             HttpGenerator generator = (HttpGenerator)g;
    359
    360             String expect = _connection.getRequest().getHeader(HttpHeaders.EXPECT);
    361
    362             if (expect!=null && expect.startsWith("102") && generator.getVersion()>=HttpVersions.HTTP_1_1_ORDINAL)
    363             {
    364                 boolean was_persistent=generator.isPersistent();
    365                 generator.setResponse(HttpStatus.ORDINAL_102_Processing,null);
    366                 generator.completeHeader(null,true);
    367                 generator.setPersistent(true);
    368                 generator.complete();
    369                 generator.flush();
    370                 generator.reset(false);
    371                 generator.setPersistent(was_persistent);
    372             }
    373         }
    374     }
    375
    376     /* ------------------------------------------------------------ */
    377     /*
    378      * @see javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String)
    379      */
    380     public void sendRedirect(String location) throws IOException
    381     {
    382     	if (_connection.isIncluding())
    383     		return;
    384
    385         if (location==null)
    386             throw new IllegalArgumentException();
    387
    388         if (!URIUtil.hasScheme(location))
    389         {
    390             StringBuffer buf = _connection.getRequest().getRootURL();
    391             if (location.startsWith("/"))
    392                 buf.append(location);
    393             else
    394             {
    395                 String path=_connection.getRequest().getRequestURI();
    396                 String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
    397                 location=URIUtil.addPaths(parent,location);
    398                 if(location==null)
    399                     throw new IllegalStateException("path cannot be above root");
    400                 if (!location.startsWith("/"))
    401                     buf.append('/');
    402                 buf.append(location);
    403             }
    404
    405             location=buf.toString();
    406             HttpURI uri = new HttpURI(location);
    407             String path=uri.getDecodedPath();
    408             String canonical=URIUtil.canonicalPath(path);
    409             if (canonical==null)
    410                 throw new IllegalArgumentException();
    411             if (!canonical.equals(path))
    412             {
    413                 buf = _connection.getRequest().getRootURL();
    414                 buf.append(canonical);
    415                 if (uri.getQuery()!=null)
    416                 {
    417                     buf.append('?');
    418                     buf.append(uri.getQuery());
    419                 }
    420                 if (uri.getFragment()!=null)
    421                 {
    422                     buf.append('#');
    423                     buf.append(uri.getFragment());
    424                 }
    425                 location=buf.toString();
    426             }
    427         }
    428         resetBuffer();
    429
    430         setHeader(HttpHeaders.LOCATION,location);
    431         setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
    432         complete();
    433
    434     }
    435
    436     /* ------------------------------------------------------------ */
    437     /*
    438      * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long)
    439      */
    440     public void setDateHeader(String name, long date)
    441     {
    442         if (!_connection.isIncluding())
    443             _connection.getResponseFields().putDateField(name, date);
    444     }
    445
    446     /* ------------------------------------------------------------ */
    447     /*
    448      * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long)
    449      */
    450     public void addDateHeader(String name, long date)
    451     {
    452         if (!_connection.isIncluding())
    453             _connection.getResponseFields().addDateField(name, date);
    454     }
    455
    456     /* ------------------------------------------------------------ */
    457     /*
    458      * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String)
    459      */
    460     public void setHeader(String name, String value)
    461     {
    462         if (!_connection.isIncluding())
    463         {
    464             _connection.getResponseFields().put(name, value);
    465             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
    466             {
    467                 if (value==null)
    468                     _connection._generator.setContentLength(-1);
    469                 else
    470                     _connection._generator.setContentLength(Long.parseLong(value));
    471             }
    472         }
    473     }
    474
    475     /* ------------------------------------------------------------ */
    476     /*
    477      */
    478     public String getHeader(String name)
    479     {
    480         return _connection.getResponseFields().getStringField(name);
    481     }
    482
    483     /* ------------------------------------------------------------ */
    484     /*
    485      */
    486     public Enumeration getHeaders(String name)
    487     {
    488         Enumeration e = _connection.getResponseFields().getValues(name);
    489         if (e==null)
    490             return Collections.enumeration(Collections.EMPTY_LIST);
    491         return e;
    492     }
    493
    494     /* ------------------------------------------------------------ */
    495     /*
    496      * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String)
    497      */
    498     public void addHeader(String name, String value)
    499     {
    500         if (!_connection.isIncluding())
    501         {
    502             _connection.getResponseFields().add(name, value);
    503             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
    504                 _connection._generator.setContentLength(Long.parseLong(value));
    505         }
    506     }
    507
    508     /* ------------------------------------------------------------ */
    509     /*
    510      * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int)
    511      */
    512     public void setIntHeader(String name, int value)
    513     {
    514         if (!_connection.isIncluding())
    515         {
    516             _connection.getResponseFields().putLongField(name, value);
    517             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
    518                 _connection._generator.setContentLength(value);
    519         }
    520     }
    521
    522     /* ------------------------------------------------------------ */
    523     /*
    524      * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int)
    525      */
    526     public void addIntHeader(String name, int value)
    527     {
    528         if (!_connection.isIncluding())
    529         {
    530             _connection.getResponseFields().addLongField(name, value);
    531             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
    532                 _connection._generator.setContentLength(value);
    533         }
    534     }
    535
    536     /* ------------------------------------------------------------ */
    537     /*
    538      * @see javax.servlet.http.HttpServletResponse#setStatus(int)
    539      */
    540     public void setStatus(int sc)
    541     {
    542         setStatus(sc,null);
    543     }
    544
    545     /* ------------------------------------------------------------ */
    546     /*
    547      * @see javax.servlet.http.HttpServletResponse#setStatus(int, java.lang.String)
    548      */
    549     public void setStatus(int sc, String sm)
    550     {
    551         if (!_connection.isIncluding())
    552         {
    553             _status=sc;
    554             _reason=sm;
    555         }
    556     }
    557
    558     /* ------------------------------------------------------------ */
    559     /*
    560      * @see javax.servlet.ServletResponse#getCharacterEncoding()
    561      */
    562     public String getCharacterEncoding()
    563     {
    564         if (_characterEncoding==null)
    565             _characterEncoding=StringUtil.__ISO_8859_1;
    566         return _characterEncoding;
    567     }
    568
    569     /* ------------------------------------------------------------ */
    570     String getSetCharacterEncoding()
    571     {
    572         return _characterEncoding;
    573     }
    574
    575     /* ------------------------------------------------------------ */
    576     /*
    577      * @see javax.servlet.ServletResponse#getContentType()
    578      */
    579     public String getContentType()
    580     {
    581         return _contentType;
    582     }
    583
    584     /* ------------------------------------------------------------ */
    585     /*
    586      * @see javax.servlet.ServletResponse#getOutputStream()
    587      */
    588     public ServletOutputStream getOutputStream() throws IOException
    589     {
    590         if (_outputState==DISABLED)
    591             return __nullServletOut;
    592
    593         if (_outputState!=NONE && _outputState!=STREAM)
    594             throw new IllegalStateException("WRITER");
    595
    596         _outputState=STREAM;
    597         return _connection.getOutputStream();
    598     }
    599
    600     /* ------------------------------------------------------------ */
    601     public boolean isWriting()
    602     {
    603         return _outputState==WRITER;
    604     }
    605
    606     /* ------------------------------------------------------------ */
    607     /*
    608      * @see javax.servlet.ServletResponse#getWriter()
    609      */
    610     public PrintWriter getWriter() throws IOException
    611     {
    612         if (_outputState==DISABLED)
    613             return __nullPrintWriter;
    614
    615         if (_outputState!=NONE && _outputState!=WRITER)
    616             throw new IllegalStateException("STREAM");
    617
    618         /* if there is no writer yet */
    619         if (_writer==null)
    620         {
    621             /* get encoding from Content-Type header */
    622             String encoding = _characterEncoding;
    623
    624             if (encoding==null)
    625             {
    626                 /* implementation of educated defaults */
    627                 if(_mimeType!=null)
    628                     encoding = null; // TODO getHttpContext().getEncodingByMimeType(_mimeType);
    629
    630                 if (encoding==null)
    631                     encoding = StringUtil.__ISO_8859_1;
    632
    633                 setCharacterEncoding(encoding);
    634             }
    635
    636             /* construct Writer using correct encoding */
    637             _writer = _connection.getPrintWriter(encoding);
    638         }
    639         _outputState=WRITER;
    640         return _writer;
    641     }
    642
    643     /* ------------------------------------------------------------ */
    644     /*
    645      * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String)
    646      */
    647     public void setCharacterEncoding(String encoding)
    648     {
    649     	if (_connection.isIncluding())
    650     		return;
    651
    652         if (this._outputState==0 && !isCommitted())
    653         {
    654             _explicitEncoding=true;
    655
    656             if (encoding==null)
    657             {
    658                 // Clear any encoding.
    659                 if (_characterEncoding!=null)
    660                 {
    661                     _characterEncoding=null;
    662                     if (_cachedMimeType!=null)
    663                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
    664                     else
    665                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_mimeType);
    666                 }
    667             }
    668             else
    669             {
    670                 // No, so just add this one to the mimetype
    671                 _characterEncoding=encoding;
    672                 if (_contentType!=null)
    673                 {
    674                     int i0=_contentType.indexOf(';');
    675                     if (i0<0)
    676                     {
    677                         _contentType=null;
    678                         if(_cachedMimeType!=null)
    679                         {
    680                             CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
    681                             if (content_type!=null)
    682                             {
    683                                 _contentType=content_type.toString();
    684                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
    685                             }
    686                         }
    687
    688                         if (_contentType==null)
    689                         {
    690                             _contentType = _mimeType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    691                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    692                         }
    693                     }
    694                     else
    695                     {
    696                         int i1=_contentType.indexOf("charset=",i0);
    697                         if (i1<0)
    698                         {
    699                             _contentType = _contentType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    700                         }
    701                         else
    702                         {
    703                             int i8=i1+8;
    704                             int i2=_contentType.indexOf(" ",i8);
    705                             if (i2<0)
    706                                 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    707                             else
    708                                 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ")+_contentType.substring(i2);
    709                         }
    710                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    711                     }
    712                 }
    713             }
    714         }
    715     }
    716
    717     /* ------------------------------------------------------------ */
    718     /*
    719      * @see javax.servlet.ServletResponse#setContentLength(int)
    720      */
    721     public void setContentLength(int len)
    722     {
    723         // Protect from setting after committed as default handling
    724         // of a servlet HEAD request ALWAYS sets _content length, even
    725         // if the getHandling committed the response!
    726         if (isCommitted() || _connection.isIncluding())
    727             return;
    728         _connection._generator.setContentLength(len);
    729         if (len>=0)
    730         {
    731             _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
    732             if (_connection._generator.isContentWritten())
    733             {
    734                 if (_outputState==WRITER)
    735                     _writer.close();
    736                 else if (_outputState==STREAM)
    737                 {
    738                     try
    739                     {
    740                         getOutputStream().close();
    741                     }
    742                     catch(IOException e)
    743                     {
    744                         throw new RuntimeException(e);
    745                     }
    746                 }
    747             }
    748         }
    749     }
    750
    751     /* ------------------------------------------------------------ */
    752     /*
    753      * @see javax.servlet.ServletResponse#setContentLength(int)
    754      */
    755     public void setLongContentLength(long len)
    756     {
    757         // Protect from setting after committed as default handling
    758         // of a servlet HEAD request ALWAYS sets _content length, even
    759         // if the getHandling committed the response!
    760         if (isCommitted() || _connection.isIncluding())
    761         	return;
    762         _connection._generator.setContentLength(len);
    763         _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
    764     }
    765
    766     /* ------------------------------------------------------------ */
    767     /*
    768      * @see javax.servlet.ServletResponse#setContentType(java.lang.String)
    769      */
    770     public void setContentType(String contentType)
    771     {
    772         if (isCommitted() || _connection.isIncluding())
    773             return;
    774
    775         // Yes this method is horribly complex.... but there are lots of special cases and
    776         // as this method is called on every request, it is worth trying to save string creation.
    777         //
    778
    779         if (contentType==null)
    780         {
    781             if (_locale==null)
    782                 _characterEncoding=null;
    783             _mimeType=null;
    784             _cachedMimeType=null;
    785             _contentType=null;
    786             _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
    787         }
    788         else
    789         {
    790             // Look for encoding in contentType
    791             int i0=contentType.indexOf(';');
    792
    793             if (i0>0)
    794             {
    795                 // we have content type parameters
    796
    797                 // Extract params off mimetype
    798                 _mimeType=contentType.substring(0,i0).trim();
    799                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
    800
    801                 // Look for charset
    802                 int i1=contentType.indexOf("charset=",i0+1);
    803                 if (i1>=0)
    804                 {
    805                     _explicitEncoding=true;
    806                     int i8=i1+8;
    807                     int i2 = contentType.indexOf(' ',i8);
    808
    809                     if (_outputState==WRITER)
    810                     {
    811                         // strip the charset and ignore;
    812                         if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
    813                         {
    814                             if (_cachedMimeType!=null)
    815                             {
    816                                 CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
    817                                 if (content_type!=null)
    818                                 {
    819                                     _contentType=content_type.toString();
    820                                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
    821                                 }
    822                                 else
    823                                 {
    824                                     _contentType=_mimeType+"; charset="+_characterEncoding;
    825                                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    826                                 }
    827                             }
    828                             else
    829                             {
    830                                 _contentType=_mimeType+"; charset="+_characterEncoding;
    831                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    832                             }
    833                         }
    834                         else if (i2<0)
    835                         {
    836                             _contentType=contentType.substring(0,i1)+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    837                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    838                         }
    839                         else
    840                         {
    841                             _contentType=contentType.substring(0,i1)+contentType.substring(i2)+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    842                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    843                         }
    844                     }
    845                     else if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
    846                     {
    847                         // The params are just the char encoding
    848                         _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
    849                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8));
    850
    851                         if (_cachedMimeType!=null)
    852                         {
    853                             CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
    854                             if (content_type!=null)
    855                             {
    856                                 _contentType=content_type.toString();
    857                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
    858                             }
    859                             else
    860                             {
    861                                 _contentType=contentType;
    862                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    863                             }
    864                         }
    865                         else
    866                         {
    867                             _contentType=contentType;
    868                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    869                         }
    870                     }
    871                     else if (i2>0)
    872                     {
    873                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8,i2));
    874                         _contentType=contentType;
    875                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    876                     }
    877                     else
    878                     {
    879                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8));
    880                         _contentType=contentType;
    881                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    882                     }
    883                 }
    884                 else // No encoding in the params.
    885                 {
    886                     _cachedMimeType=null;
    887                     _contentType=_characterEncoding==null?contentType:contentType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    888                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    889                 }
    890             }
    891             else // No params at all
    892             {
    893                 _mimeType=contentType;
    894                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
    895
    896                 if (_characterEncoding!=null)
    897                 {
    898                     if (_cachedMimeType!=null)
    899                     {
    900                         CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
    901                         if (content_type!=null)
    902                         {
    903                             _contentType=content_type.toString();
    904                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
    905                         }
    906                         else
    907                         {
    908                             _contentType=_mimeType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    909                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    910                         }
    911                     }
    912                     else
    913                     {
    914                         _contentType=contentType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    915                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    916                     }
    917                 }
    918                 else if (_cachedMimeType!=null)
    919                 {
    920                     _contentType=_cachedMimeType.toString();
    921                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
    922                 }
    923                 else
    924                 {
    925                     _contentType=contentType;
    926                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    927                 }
    928             }
    929         }
    930     }
    931
    932     /* ------------------------------------------------------------ */
    933     /*
    934      * @see javax.servlet.ServletResponse#setBufferSize(int)
    935      */
    936     public void setBufferSize(int size)
    937     {
    938         if (isCommitted() || getContentCount()>0)
    939             throw new IllegalStateException("Committed or content written");
    940         _connection.getGenerator().increaseContentBufferSize(size);
    941     }
    942
    943     /* ------------------------------------------------------------ */
    944     /*
    945      * @see javax.servlet.ServletResponse#getBufferSize()
    946      */
    947     public int getBufferSize()
    948     {
    949         return _connection.getGenerator().getContentBufferSize();
    950     }
    951
    952     /* ------------------------------------------------------------ */
    953     /*
    954      * @see javax.servlet.ServletResponse#flushBuffer()
    955      */
    956     public void flushBuffer() throws IOException
    957     {
    958         _connection.flushResponse();
    959     }
    960
    961     /* ------------------------------------------------------------ */
    962     /*
    963      * @see javax.servlet.ServletResponse#reset()
    964      */
    965     public void reset()
    966     {
    967         resetBuffer();
    968
    969         HttpFields response_fields=_connection.getResponseFields();
    970         response_fields.clear();
    971         String connection=_connection.getRequestFields().getStringField(HttpHeaders.CONNECTION_BUFFER);
    972         if (connection!=null)
    973         {
    974             QuotedStringTokenizer tok = new QuotedStringTokenizer(connection, ",");
    975             while(tok.hasMoreTokens())
    976             {
    977                 CachedBuffer cb = HttpHeaderValues.CACHE.get(tok.nextToken().trim());
    978                 if (cb!=null)
    979                 {
    980                     switch(cb.getOrdinal())
    981                     {
    982                         case HttpHeaderValues.CLOSE_ORDINAL:
    983                             response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
    984                             break;
    985
    986                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
    987                             if (HttpVersions.HTTP_1_0.equalsIgnoreCase(_connection.getRequest().getProtocol()))
    988                                 response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE);
    989                             break;
    990                         case HttpHeaderValues.TE_ORDINAL:
    991                             response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.TE);
    992                             break;
    993                     }
    994                 }
    995             }
    996         }
    997
    998         if (_connection.getConnector().getServer().getSendDateHeader())
    999         {
    1000             Request request=_connection.getRequest();
    1001             response_fields.put(HttpHeaders.DATE_BUFFER, request.getTimeStampBuffer(),request.getTimeStamp());
    1002         }
    1003
    1004         _status=200;
    1005         _reason=null;
    1006         _mimeType=null;
    1007         _cachedMimeType=null;
    1008         _contentType=null;
    1009         _characterEncoding=null;
    1010         _explicitEncoding=false;
    1011         _locale=null;
    1012         _outputState=NONE;
    1013         _writer=null;
    1014     }
    1015
    1016     /* ------------------------------------------------------------ */
    1017     /*
    1018      * @see javax.servlet.ServletResponse#resetBuffer()
    1019      */
    1020     public void resetBuffer()
    1021     {
    1022         if (isCommitted())
    1023             throw new IllegalStateException("Committed");
    1024         _connection.getGenerator().resetBuffer();
    1025     }
    1026
    1027     /* ------------------------------------------------------------ */
    1028     /*
    1029      * @see javax.servlet.ServletResponse#isCommitted()
    1030      */
    1031     public boolean isCommitted()
    1032     {
    1033         return _connection.isResponseCommitted();
    1034     }
    1035
    1036
    1037     /* ------------------------------------------------------------ */
    1038     /*
    1039      * @see javax.servlet.ServletResponse#setLocale(java.util.Locale)
    1040      */
    1041     public void setLocale(Locale locale)
    1042     {
    1043         if (locale == null || isCommitted() ||_connection.isIncluding())
    1044             return;
    1045
    1046         _locale = locale;
    1047         _connection.getResponseFields().put(HttpHeaders.CONTENT_LANGUAGE_BUFFER,locale.toString().replace('_','-'));
    1048
    1049         if (_explicitEncoding || _outputState!=0 )
    1050             return;
    1051
    1052         if (_connection.getRequest().getContext()==null)
    1053             return;
    1054
    1055         String charset = _connection.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);
    1056
    1057         if (charset!=null && charset.length()>0)
    1058         {
    1059             _characterEncoding=charset;
    1060
    1061             /* get current MIME type from Content-Type header */
    1062             String type=getContentType();
    1063             if (type!=null)
    1064             {
    1065                 _characterEncoding=charset;
    1066                 int semi=type.indexOf(';');
    1067                 if (semi<0)
    1068                 {
    1069                     _mimeType=type;
    1070                     _contentType= type += "; charset="+charset;
    1071                 }
    1072                 else
    1073                 {
    1074                     _mimeType=type.substring(0,semi);
    1075                     _contentType= _mimeType += "; charset="+charset;
    1076                 }
    1077
    1078                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
    1079                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    1080             }
    1081         }
    1082     }
    1083
    1084     /* ------------------------------------------------------------ */
    1085     /*
    1086      * @see javax.servlet.ServletResponse#getLocale()
    1087      */
    1088     public Locale getLocale()
    1089     {
    1090         if (_locale==null)
    1091             return Locale.getDefault();
    1092         return _locale;
    1093     }
    1094
    1095     /* ------------------------------------------------------------ */
    1096     /**
    1097      * @return The HTTP status code that has been set for this request. This will be <code>200<code>
    1098      *    ({@link HttpServletResponse#SC_OK}), unless explicitly set through one of the <code>setStatus</code> methods.
    1099      */
    1100     public int getStatus()
    1101     {
    1102         return _status;
    1103     }
    1104
    1105     /* ------------------------------------------------------------ */
    1106     /**
    1107      * @return The reason associated with the current {@link #getStatus() status}. This will be <code>null</code>,
    1108      *    unless one of the <code>setStatus</code> methods have been called.
    1109      */
    1110     public String getReason()
    1111     {
    1112         return _reason;
    1113     }
    1114
    1115
    1116
    1117
    1118     /* ------------------------------------------------------------ */
    1119     /**
    1120      *
    1121      */
    1122     public void complete()
    1123         throws IOException
    1124     {
    1125         _connection.completeResponse();
    1126     }
    1127
    1128     /* ------------------------------------------------------------- */
    1129     /**
    1130      * @return the number of bytes actually written in response body
    1131      */
    1132     public long getContentCount()
    1133     {
    1134         if (_connection==null || _connection.getGenerator()==null)
    1135             return -1;
    1136         return _connection.getGenerator().getContentWritten();
    1137     }
    1138
    1139     /* ------------------------------------------------------------ */
    1140     public HttpFields getHttpFields()
    1141     {
    1142         return _connection.getResponseFields();
    1143     }
    1144
    1145     /* ------------------------------------------------------------ */
    1146     public String toString()
    1147     {
    1148         return "HTTP/1.1 "+_status+" "+ (_reason==null?"":_reason) +System.getProperty("line.separator")+
    1149         _connection.getResponseFields().toString();
    1150     }
    1151
    1152     /* ------------------------------------------------------------ */
    1153     /* ------------------------------------------------------------ */
    1154     /* ------------------------------------------------------------ */
    1155     private static class NullOutput extends ServletOutputStream
    1156     {
    1157         public void write(int b) throws IOException
    1158         {
    1159         }
    1160     }
    1161
    1162
    1163 }