View Javadoc

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