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