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><pre>");
    305                 writer.write(message);
    306                 writer.write("</pre>\n<p>RequestURI=");
    307                 writer.write(uri);
    308                 writer.write("</p>\n<p><i><small><a href=\"http://jetty.mortbay.org\">Powered by jetty://</a></small></i></p>");
    309
    310                 for (int i= 0; i < 20; i++)
    311                     writer.write("\n                                                ");
    312                 writer.write("\n</body>\n</html>\n");
    313
    314                 writer.flush();
    315                 setContentLength(writer.size());
    316                 writer.writeTo(getOutputStream());
    317                 writer.destroy();
    318             }
    319         }
    320         else if (code!=SC_PARTIAL_CONTENT)
    321         {
    322             _connection.getRequestFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
    323             _connection.getRequestFields().remove(HttpHeaders.CONTENT_LENGTH_BUFFER);
    324             _characterEncoding=null;
    325             _mimeType=null;
    326             _cachedMimeType=null;
    327         }
    328
    329         complete();
    330     }
    331
    332     /* ------------------------------------------------------------ */
    333     /*
    334      * @see javax.servlet.http.HttpServletResponse#sendError(int)
    335      */
    336     public void sendError(int sc) throws IOException
    337     {
    338         if (sc==102)
    339             sendProcessing();
    340         else
    341             sendError(sc,null);
    342     }
    343
    344     /* ------------------------------------------------------------ */
    345     /* Send a 102-Processing response.
    346      * If the connection is a HTTP connection, the version is 1.1 and the
    347      * request has a Expect header starting with 102, then a 102 response is
    348      * sent. This indicates that the request still be processed and real response
    349      * can still be sent.   This method is called by sendError if it is passed 102.
    350      * @see javax.servlet.http.HttpServletResponse#sendError(int)
    351      */
    352     public void sendProcessing() throws IOException
    353     {
    354         Generator g = _connection.getGenerator();
    355         if (g instanceof HttpGenerator)
    356         {
    357             HttpGenerator generator = (HttpGenerator)g;
    358
    359             String expect = _connection.getRequest().getHeader(HttpHeaders.EXPECT);
    360
    361             if (expect!=null && expect.startsWith("102") && generator.getVersion()>=HttpVersions.HTTP_1_1_ORDINAL)
    362             {
    363                 boolean was_persistent=generator.isPersistent();
    364                 generator.setResponse(HttpStatus.ORDINAL_102_Processing,null);
    365                 generator.completeHeader(null,true);
    366                 generator.setPersistent(true);
    367                 generator.complete();
    368                 generator.flush();
    369                 generator.reset(false);
    370                 generator.setPersistent(was_persistent);
    371             }
    372         }
    373     }
    374
    375     /* ------------------------------------------------------------ */
    376     /*
    377      * @see javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String)
    378      */
    379     public void sendRedirect(String location) throws IOException
    380     {
    381     	if (_connection.isIncluding())
    382     		return;
    383
    384         if (location==null)
    385             throw new IllegalArgumentException();
    386
    387         if (!URIUtil.hasScheme(location))
    388         {
    389             StringBuffer buf = _connection.getRequest().getRootURL();
    390             if (location.startsWith("/"))
    391                 buf.append(URIUtil.canonicalPath(location));
    392             else
    393             {
    394                 String path=_connection.getRequest().getRequestURI();
    395                 String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
    396                 location=URIUtil.canonicalPath(URIUtil.addPaths(parent,location));
    397                 if (!location.startsWith("/"))
    398                     buf.append('/');
    399                 buf.append(location);
    400             }
    401
    402             location=buf.toString();
    403         }
    404         resetBuffer();
    405
    406         setHeader(HttpHeaders.LOCATION,location);
    407         setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
    408         complete();
    409
    410     }
    411
    412     /* ------------------------------------------------------------ */
    413     /*
    414      * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long)
    415      */
    416     public void setDateHeader(String name, long date)
    417     {
    418         if (!_connection.isIncluding())
    419             _connection.getResponseFields().putDateField(name, date);
    420     }
    421
    422     /* ------------------------------------------------------------ */
    423     /*
    424      * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long)
    425      */
    426     public void addDateHeader(String name, long date)
    427     {
    428         if (!_connection.isIncluding())
    429             _connection.getResponseFields().addDateField(name, date);
    430     }
    431
    432     /* ------------------------------------------------------------ */
    433     /*
    434      * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String)
    435      */
    436     public void setHeader(String name, String value)
    437     {
    438         if (!_connection.isIncluding())
    439         {
    440             _connection.getResponseFields().put(name, value);
    441             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
    442             {
    443                 if (value==null)
    444                     _connection._generator.setContentLength(-1);
    445                 else
    446                     _connection._generator.setContentLength(Long.parseLong(value));
    447             }
    448         }
    449     }
    450
    451     /* ------------------------------------------------------------ */
    452     /*
    453      */
    454     public String getHeader(String name)
    455     {
    456         return _connection.getResponseFields().getStringField(name);
    457     }
    458
    459     /* ------------------------------------------------------------ */
    460     /*
    461      */
    462     public Enumeration getHeaders(String name)
    463     {
    464         Enumeration e = _connection.getResponseFields().getValues(name);
    465         if (e==null)
    466             return Collections.enumeration(Collections.EMPTY_LIST);
    467         return e;
    468     }
    469
    470     /* ------------------------------------------------------------ */
    471     /*
    472      * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String)
    473      */
    474     public void addHeader(String name, String value)
    475     {
    476         if (!_connection.isIncluding())
    477         {
    478             _connection.getResponseFields().add(name, value);
    479             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
    480                 _connection._generator.setContentLength(Long.parseLong(value));
    481         }
    482     }
    483
    484     /* ------------------------------------------------------------ */
    485     /*
    486      * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int)
    487      */
    488     public void setIntHeader(String name, int value)
    489     {
    490         if (!_connection.isIncluding())
    491         {
    492             _connection.getResponseFields().putLongField(name, value);
    493             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
    494                 _connection._generator.setContentLength(value);
    495         }
    496     }
    497
    498     /* ------------------------------------------------------------ */
    499     /*
    500      * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int)
    501      */
    502     public void addIntHeader(String name, int value)
    503     {
    504         if (!_connection.isIncluding())
    505         {
    506             _connection.getResponseFields().addLongField(name, value);
    507             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
    508                 _connection._generator.setContentLength(value);
    509         }
    510     }
    511
    512     /* ------------------------------------------------------------ */
    513     /*
    514      * @see javax.servlet.http.HttpServletResponse#setStatus(int)
    515      */
    516     public void setStatus(int sc)
    517     {
    518         setStatus(sc,null);
    519     }
    520
    521     /* ------------------------------------------------------------ */
    522     /*
    523      * @see javax.servlet.http.HttpServletResponse#setStatus(int, java.lang.String)
    524      */
    525     public void setStatus(int sc, String sm)
    526     {
    527         if (!_connection.isIncluding())
    528         {
    529             _status=sc;
    530             _reason=sm;
    531         }
    532     }
    533
    534     /* ------------------------------------------------------------ */
    535     /*
    536      * @see javax.servlet.ServletResponse#getCharacterEncoding()
    537      */
    538     public String getCharacterEncoding()
    539     {
    540         if (_characterEncoding==null)
    541             _characterEncoding=StringUtil.__ISO_8859_1;
    542         return _characterEncoding;
    543     }
    544
    545     /* ------------------------------------------------------------ */
    546     /*
    547      * @see javax.servlet.ServletResponse#getContentType()
    548      */
    549     public String getContentType()
    550     {
    551         return _contentType;
    552     }
    553
    554     /* ------------------------------------------------------------ */
    555     /*
    556      * @see javax.servlet.ServletResponse#getOutputStream()
    557      */
    558     public ServletOutputStream getOutputStream() throws IOException
    559     {
    560         if (_outputState==DISABLED)
    561             return __nullServletOut;
    562
    563         if (_outputState!=NONE && _outputState!=STREAM)
    564             throw new IllegalStateException("WRITER");
    565
    566         _outputState=STREAM;
    567         return _connection.getOutputStream();
    568     }
    569
    570     /* ------------------------------------------------------------ */
    571     public boolean isWriting()
    572     {
    573         return _outputState==WRITER;
    574     }
    575
    576     /* ------------------------------------------------------------ */
    577     /*
    578      * @see javax.servlet.ServletResponse#getWriter()
    579      */
    580     public PrintWriter getWriter() throws IOException
    581     {
    582         if (_outputState==DISABLED)
    583             return __nullPrintWriter;
    584
    585         if (_outputState!=NONE && _outputState!=WRITER)
    586             throw new IllegalStateException("STREAM");
    587
    588         /* if there is no writer yet */
    589         if (_writer==null)
    590         {
    591             /* get encoding from Content-Type header */
    592             String encoding = _characterEncoding;
    593
    594             if (encoding==null)
    595             {
    596                 /* implementation of educated defaults */
    597                 if(_mimeType!=null)
    598                     encoding = null; // TODO getHttpContext().getEncodingByMimeType(_mimeType);
    599
    600                 if (encoding==null)
    601                     encoding = StringUtil.__ISO_8859_1;
    602
    603                 setCharacterEncoding(encoding);
    604             }
    605
    606             /* construct Writer using correct encoding */
    607             _writer = _connection.getPrintWriter(encoding);
    608         }
    609         _outputState=WRITER;
    610         return _writer;
    611     }
    612
    613     /* ------------------------------------------------------------ */
    614     /*
    615      * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String)
    616      */
    617     public void setCharacterEncoding(String encoding)
    618     {
    619     	if (_connection.isIncluding())
    620     		return;
    621
    622         if (this._outputState==0 && !isCommitted())
    623         {
    624             _explicitEncoding=true;
    625
    626             if (encoding==null)
    627             {
    628                 // Clear any encoding.
    629                 if (_characterEncoding!=null)
    630                 {
    631                     _characterEncoding=null;
    632                     if (_cachedMimeType!=null)
    633                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
    634                     else
    635                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_mimeType);
    636                 }
    637             }
    638             else
    639             {
    640                 // No, so just add this one to the mimetype
    641                 _characterEncoding=encoding;
    642                 if (_contentType!=null)
    643                 {
    644                     int i0=_contentType.indexOf(';');
    645                     if (i0<0)
    646                     {
    647                         _contentType=null;
    648                         if(_cachedMimeType!=null)
    649                         {
    650                             CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
    651                             if (content_type!=null)
    652                             {
    653                                 _contentType=content_type.toString();
    654                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
    655                             }
    656                         }
    657
    658                         if (_contentType==null)
    659                         {
    660                             _contentType = _mimeType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    661                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    662                         }
    663                     }
    664                     else
    665                     {
    666                         int i1=_contentType.indexOf("charset=",i0);
    667                         if (i1<0)
    668                         {
    669                             _contentType = _contentType+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    670                         }
    671                         else
    672                         {
    673                             int i8=i1+8;
    674                             int i2=_contentType.indexOf(" ",i8);
    675                             if (i2<0)
    676                                 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    677                             else
    678                                 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ")+_contentType.substring(i2);
    679                         }
    680                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    681                     }
    682                 }
    683             }
    684         }
    685     }
    686
    687     /* ------------------------------------------------------------ */
    688     /*
    689      * @see javax.servlet.ServletResponse#setContentLength(int)
    690      */
    691     public void setContentLength(int len)
    692     {
    693         // Protect from setting after committed as default handling
    694         // of a servlet HEAD request ALWAYS sets _content length, even
    695         // if the getHandling committed the response!
    696         if (isCommitted() || _connection.isIncluding())
    697             return;
    698         _connection._generator.setContentLength(len);
    699         if (len>=0)
    700         {
    701             _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
    702             if (_connection._generator.isContentWritten())
    703             {
    704                 if (_outputState==WRITER)
    705                     _writer.close();
    706                 else if (_outputState==STREAM)
    707                 {
    708                     try
    709                     {
    710                         getOutputStream().close();
    711                     }
    712                     catch(IOException e)
    713                     {
    714                         throw new RuntimeException(e);
    715                     }
    716                 }
    717             }
    718         }
    719     }
    720
    721     /* ------------------------------------------------------------ */
    722     /*
    723      * @see javax.servlet.ServletResponse#setContentLength(int)
    724      */
    725     public void setLongContentLength(long len)
    726     {
    727         // Protect from setting after committed as default handling
    728         // of a servlet HEAD request ALWAYS sets _content length, even
    729         // if the getHandling committed the response!
    730         if (isCommitted() || _connection.isIncluding())
    731         	return;
    732         _connection._generator.setContentLength(len);
    733         _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
    734     }
    735
    736     /* ------------------------------------------------------------ */
    737     /*
    738      * @see javax.servlet.ServletResponse#setContentType(java.lang.String)
    739      */
    740     public void setContentType(String contentType)
    741     {
    742         if (isCommitted() || _connection.isIncluding())
    743             return;
    744
    745         // Yes this method is horribly complex.... but there are lots of special cases and
    746         // as this method is called on every request, it is worth trying to save string creation.
    747         //
    748
    749         if (contentType==null)
    750         {
    751             if (_locale==null)
    752                 _characterEncoding=null;
    753             _mimeType=null;
    754             _cachedMimeType=null;
    755             _contentType=null;
    756             _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
    757         }
    758         else
    759         {
    760             // Look for encoding in contentType
    761             int i0=contentType.indexOf(';');
    762
    763             if (i0>0)
    764             {
    765                 // we have content type parameters
    766
    767                 // Extract params off mimetype
    768                 _mimeType=contentType.substring(0,i0).trim();
    769                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
    770
    771                 // Look for charset
    772                 int i1=contentType.indexOf("charset=",i0+1);
    773                 if (i1>=0)
    774                 {
    775                     _explicitEncoding=true;
    776                     int i8=i1+8;
    777                     int i2 = contentType.indexOf(' ',i8);
    778
    779                     if (_outputState==WRITER)
    780                     {
    781                         // strip the charset and ignore;
    782                         if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
    783                         {
    784                             if (_cachedMimeType!=null)
    785                             {
    786                                 CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
    787                                 if (content_type!=null)
    788                                 {
    789                                     _contentType=content_type.toString();
    790                                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
    791                                 }
    792                                 else
    793                                 {
    794                                     _contentType=_mimeType+"; charset="+_characterEncoding;
    795                                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    796                                 }
    797                             }
    798                             else
    799                             {
    800                                 _contentType=_mimeType+"; charset="+_characterEncoding;
    801                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    802                             }
    803                         }
    804                         else if (i2<0)
    805                         {
    806                             _contentType=contentType.substring(0,i1)+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    807                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    808                         }
    809                         else
    810                         {
    811                             _contentType=contentType.substring(0,i1)+contentType.substring(i2)+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    812                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    813                         }
    814                     }
    815                     else if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
    816                     {
    817                         // The params are just the char encoding
    818                         _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
    819                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8));
    820
    821                         if (_cachedMimeType!=null)
    822                         {
    823                             CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
    824                             if (content_type!=null)
    825                             {
    826                                 _contentType=content_type.toString();
    827                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
    828                             }
    829                             else
    830                             {
    831                                 _contentType=contentType;
    832                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    833                             }
    834                         }
    835                         else
    836                         {
    837                             _contentType=contentType;
    838                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    839                         }
    840                     }
    841                     else if (i2>0)
    842                     {
    843                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8,i2));
    844                         _contentType=contentType;
    845                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    846                     }
    847                     else
    848                     {
    849                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8));
    850                         _contentType=contentType;
    851                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    852                     }
    853                 }
    854                 else // No encoding in the params.
    855                 {
    856                     _cachedMimeType=null;
    857                     _contentType=_characterEncoding==null?contentType:contentType+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    858                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    859                 }
    860             }
    861             else // No params at all
    862             {
    863                 _mimeType=contentType;
    864                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
    865
    866                 if (_characterEncoding!=null)
    867                 {
    868                     if (_cachedMimeType!=null)
    869                     {
    870                         CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
    871                         if (content_type!=null)
    872                         {
    873                             _contentType=content_type.toString();
    874                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
    875                         }
    876                         else
    877                         {
    878                             _contentType=_mimeType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    879                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    880                         }
    881                     }
    882                     else
    883                     {
    884                         _contentType=contentType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
    885                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    886                     }
    887                 }
    888                 else if (_cachedMimeType!=null)
    889                 {
    890                     _contentType=_cachedMimeType.toString();
    891                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
    892                 }
    893                 else
    894                 {
    895                     _contentType=contentType;
    896                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    897                 }
    898             }
    899         }
    900     }
    901
    902     /* ------------------------------------------------------------ */
    903     /*
    904      * @see javax.servlet.ServletResponse#setBufferSize(int)
    905      */
    906     public void setBufferSize(int size)
    907     {
    908         if (isCommitted() || getContentCount()>0)
    909             throw new IllegalStateException("Committed or content written");
    910         _connection.getGenerator().increaseContentBufferSize(size);
    911     }
    912
    913     /* ------------------------------------------------------------ */
    914     /*
    915      * @see javax.servlet.ServletResponse#getBufferSize()
    916      */
    917     public int getBufferSize()
    918     {
    919         return _connection.getGenerator().getContentBufferSize();
    920     }
    921
    922     /* ------------------------------------------------------------ */
    923     /*
    924      * @see javax.servlet.ServletResponse#flushBuffer()
    925      */
    926     public void flushBuffer() throws IOException
    927     {
    928         _connection.flushResponse();
    929     }
    930
    931     /* ------------------------------------------------------------ */
    932     /*
    933      * @see javax.servlet.ServletResponse#reset()
    934      */
    935     public void reset()
    936     {
    937         resetBuffer();
    938
    939         HttpFields response_fields=_connection.getResponseFields();
    940         response_fields.clear();
    941         String connection=_connection.getRequestFields().getStringField(HttpHeaders.CONNECTION_BUFFER);
    942         if (connection!=null)
    943         {
    944             String[] values = connection.split(",");
    945             for  (int i=0;values!=null && i<values.length;i++)
    946             {
    947                 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[0].trim());
    948
    949                 if (cb!=null)
    950                 {
    951                     switch(cb.getOrdinal())
    952                     {
    953                         case HttpHeaderValues.CLOSE_ORDINAL:
    954                             response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
    955                             break;
    956
    957                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
    958                             if (HttpVersions.HTTP_1_0.equalsIgnoreCase(_connection.getRequest().getProtocol()))
    959                                 response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE);
    960                             break;
    961                         case HttpHeaderValues.TE_ORDINAL:
    962                             response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.TE);
    963                             break;
    964                     }
    965                 }
    966             }
    967         }
    968
    969         if (_connection.getConnector().getServer().getSendDateHeader())
    970         {
    971             Request request=_connection.getRequest();
    972             response_fields.put(HttpHeaders.DATE_BUFFER, request.getTimeStampBuffer(),request.getTimeStamp());
    973         }
    974
    975         _status=200;
    976         _reason=null;
    977         _mimeType=null;
    978         _cachedMimeType=null;
    979         _contentType=null;
    980         _characterEncoding=null;
    981         _explicitEncoding=false;
    982         _locale=null;
    983         _outputState=NONE;
    984         _writer=null;
    985     }
    986
    987     /* ------------------------------------------------------------ */
    988     /*
    989      * @see javax.servlet.ServletResponse#resetBuffer()
    990      */
    991     public void resetBuffer()
    992     {
    993         if (isCommitted())
    994             throw new IllegalStateException("Committed");
    995         _connection.getGenerator().resetBuffer();
    996     }
    997
    998     /* ------------------------------------------------------------ */
    999     /*
    1000      * @see javax.servlet.ServletResponse#isCommitted()
    1001      */
    1002     public boolean isCommitted()
    1003     {
    1004         return _connection.isResponseCommitted();
    1005     }
    1006
    1007
    1008     /* ------------------------------------------------------------ */
    1009     /*
    1010      * @see javax.servlet.ServletResponse#setLocale(java.util.Locale)
    1011      */
    1012     public void setLocale(Locale locale)
    1013     {
    1014         if (locale == null || isCommitted() ||_connection.isIncluding())
    1015             return;
    1016
    1017         _locale = locale;
    1018         _connection.getResponseFields().put(HttpHeaders.CONTENT_LANGUAGE_BUFFER,locale.toString().replace('_','-'));
    1019
    1020         if (_explicitEncoding || _outputState!=0 )
    1021             return;
    1022
    1023         if (_connection.getRequest().getContext()==null)
    1024             return;
    1025
    1026         String charset = _connection.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);
    1027
    1028         if (charset!=null && charset.length()>0)
    1029         {
    1030             _characterEncoding=charset;
    1031
    1032             /* get current MIME type from Content-Type header */
    1033             String type=getContentType();
    1034             if (type!=null)
    1035             {
    1036                 _characterEncoding=charset;
    1037                 int semi=type.indexOf(';');
    1038                 if (semi<0)
    1039                 {
    1040                     _mimeType=type;
    1041                     _contentType= type += "; charset="+charset;
    1042                 }
    1043                 else
    1044                 {
    1045                     _mimeType=type.substring(0,semi);
    1046                     _contentType= _mimeType += "; charset="+charset;
    1047                 }
    1048
    1049                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
    1050                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
    1051             }
    1052         }
    1053     }
    1054
    1055     /* ------------------------------------------------------------ */
    1056     /*
    1057      * @see javax.servlet.ServletResponse#getLocale()
    1058      */
    1059     public Locale getLocale()
    1060     {
    1061         if (_locale==null)
    1062             return Locale.getDefault();
    1063         return _locale;
    1064     }
    1065
    1066     /* ------------------------------------------------------------ */
    1067     /**
    1068      * @return The HTTP status code that has been set for this request. This will be <code>200<code>
    1069      *    ({@link HttpServletResponse#SC_OK}), unless explicitly set through one of the <code>setStatus</code> methods.
    1070      */
    1071     public int getStatus()
    1072     {
    1073         return _status;
    1074     }
    1075
    1076     /* ------------------------------------------------------------ */
    1077     /**
    1078      * @return The reason associated with the current {@link #getStatus() status}. This will be <code>null</code>,
    1079      *    unless one of the <code>setStatus</code> methods have been called.
    1080      */
    1081     public String getReason()
    1082     {
    1083         return _reason;
    1084     }
    1085
    1086
    1087
    1088
    1089     /* ------------------------------------------------------------ */
    1090     /**
    1091      *
    1092      */
    1093     public void complete()
    1094         throws IOException
    1095     {
    1096         _connection.completeResponse();
    1097     }
    1098
    1099     /* ------------------------------------------------------------- */
    1100     /**
    1101      * @return the number of bytes actually written in response body
    1102      */
    1103     public long getContentCount()
    1104     {
    1105         if (_connection==null || _connection.getGenerator()==null)
    1106             return -1;
    1107         return _connection.getGenerator().getContentWritten();
    1108     }
    1109
    1110     /* ------------------------------------------------------------ */
    1111     public HttpFields getHttpFields()
    1112     {
    1113         return _connection.getResponseFields();
    1114     }
    1115
    1116     /* ------------------------------------------------------------ */
    1117     public String toString()
    1118     {
    1119         return "HTTP/1.1 "+_status+" "+ (_reason==null?"":_reason) +System.getProperty("line.separator")+
    1120         _connection.getResponseFields().toString();
    1121     }
    1122
    1123     /* ------------------------------------------------------------ */
    1124     /* ------------------------------------------------------------ */
    1125     /* ------------------------------------------------------------ */
    1126     private static class NullOutput extends ServletOutputStream
    1127     {
    1128         public void write(int b) throws IOException
    1129         {
    1130         }
    1131     }
    1132
    1133
    1134 }