View Javadoc

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