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