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