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     String getSetCharacterEncoding()
547     {
548         return _characterEncoding;
549     }    
550 
551     /* ------------------------------------------------------------ */
552     /*
553      * @see javax.servlet.ServletResponse#getContentType()
554      */
555     public String getContentType()
556     {
557         return _contentType;
558     }
559 
560     /* ------------------------------------------------------------ */
561     /*
562      * @see javax.servlet.ServletResponse#getOutputStream()
563      */
564     public ServletOutputStream getOutputStream() throws IOException
565     {
566         if (_outputState==DISABLED)
567             return __nullServletOut;
568 
569         if (_outputState!=NONE && _outputState!=STREAM)
570             throw new IllegalStateException("WRITER");
571 
572         _outputState=STREAM;
573         return _connection.getOutputStream();
574     }
575 
576     /* ------------------------------------------------------------ */
577     public boolean isWriting()
578     {
579         return _outputState==WRITER;
580     }
581     
582     /* ------------------------------------------------------------ */
583     /*
584      * @see javax.servlet.ServletResponse#getWriter()
585      */
586     public PrintWriter getWriter() throws IOException
587     {
588         if (_outputState==DISABLED)
589             return __nullPrintWriter;
590 
591         if (_outputState!=NONE && _outputState!=WRITER)
592             throw new IllegalStateException("STREAM");
593 
594         /* if there is no writer yet */
595         if (_writer==null)
596         {
597             /* get encoding from Content-Type header */
598             String encoding = _characterEncoding;
599 
600             if (encoding==null)
601             {
602                 /* implementation of educated defaults */
603                 if(_mimeType!=null)
604                     encoding = null; // TODO getHttpContext().getEncodingByMimeType(_mimeType);
605 
606                 if (encoding==null)
607                     encoding = StringUtil.__ISO_8859_1;
608 
609                 setCharacterEncoding(encoding);
610             }
611 
612             /* construct Writer using correct encoding */
613             _writer = _connection.getPrintWriter(encoding);
614         }
615         _outputState=WRITER;
616         return _writer;
617     }
618 
619     /* ------------------------------------------------------------ */
620     /*
621      * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String)
622      */
623     public void setCharacterEncoding(String encoding)
624     {
625     	if (_connection.isIncluding())
626     		return;
627         
628         if (this._outputState==0 && !isCommitted())
629         {
630             _explicitEncoding=true;
631 
632             if (encoding==null)
633             {
634                 // Clear any encoding.
635                 if (_characterEncoding!=null)
636                 {
637                     _characterEncoding=null;
638                     if (_cachedMimeType!=null)
639                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
640                     else
641                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_mimeType);
642                 }
643             }
644             else
645             {
646                 // No, so just add this one to the mimetype
647                 _characterEncoding=encoding;
648                 if (_contentType!=null)
649                 {
650                     int i0=_contentType.indexOf(';');
651                     if (i0<0)
652                     {   
653                         _contentType=null;
654                         if(_cachedMimeType!=null)
655                         {
656                             CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
657                             if (content_type!=null)
658                             {
659                                 _contentType=content_type.toString();
660                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
661                             }
662                         }
663                         
664                         if (_contentType==null)
665                         {
666                             _contentType = _mimeType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); 
667                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
668                         }
669                     }
670                     else
671                     {
672                         int i1=_contentType.indexOf("charset=",i0);
673                         if (i1<0)
674                         {
675                             _contentType = _contentType+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); 
676                         }
677                         else
678                         {
679                             int i8=i1+8;
680                             int i2=_contentType.indexOf(" ",i8);
681                             if (i2<0)
682                                 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ");
683                             else
684                                 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ")+_contentType.substring(i2);
685                         }
686                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
687                     }
688                 }
689             }
690         }
691     }
692 
693     /* ------------------------------------------------------------ */
694     /*
695      * @see javax.servlet.ServletResponse#setContentLength(int)
696      */
697     public void setContentLength(int len)
698     {
699         // Protect from setting after committed as default handling
700         // of a servlet HEAD request ALWAYS sets _content length, even
701         // if the getHandling committed the response!
702         if (isCommitted() || _connection.isIncluding())
703             return;
704         _connection._generator.setContentLength(len);
705         if (len>=0)
706         {
707             _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
708             if (_connection._generator.isContentWritten())
709             {
710                 if (_outputState==WRITER)
711                     _writer.close();
712                 else if (_outputState==STREAM)
713                 {
714                     try
715                     {
716                         getOutputStream().close();
717                     }
718                     catch(IOException e)
719                     {
720                         throw new RuntimeException(e);
721                     }
722                 }
723             }
724         }
725     }
726 
727     /* ------------------------------------------------------------ */
728     /*
729      * @see javax.servlet.ServletResponse#setContentLength(int)
730      */
731     public void setLongContentLength(long len)
732     {
733         // Protect from setting after committed as default handling
734         // of a servlet HEAD request ALWAYS sets _content length, even
735         // if the getHandling committed the response!
736         if (isCommitted() || _connection.isIncluding())
737         	return;
738         _connection._generator.setContentLength(len);
739         _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
740     }
741     
742     /* ------------------------------------------------------------ */
743     /*
744      * @see javax.servlet.ServletResponse#setContentType(java.lang.String)
745      */
746     public void setContentType(String contentType)
747     {
748         if (isCommitted() || _connection.isIncluding())
749             return;
750         
751         // Yes this method is horribly complex.... but there are lots of special cases and
752         // as this method is called on every request, it is worth trying to save string creation.
753         //
754         
755         if (contentType==null)
756         {
757             if (_locale==null)
758                 _characterEncoding=null;
759             _mimeType=null;
760             _cachedMimeType=null;
761             _contentType=null;
762             _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
763         }
764         else
765         {
766             // Look for encoding in contentType
767             int i0=contentType.indexOf(';');
768 
769             if (i0>0)
770             {
771                 // we have content type parameters
772             
773                 // Extract params off mimetype
774                 _mimeType=contentType.substring(0,i0).trim();
775                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
776 
777                 // Look for charset
778                 int i1=contentType.indexOf("charset=",i0+1);
779                 if (i1>=0)
780                 {
781                     _explicitEncoding=true;
782                     int i8=i1+8;
783                     int i2 = contentType.indexOf(' ',i8);
784 
785                     if (_outputState==WRITER)
786                     {
787                         // strip the charset and ignore;
788                         if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
789                         {
790                             if (_cachedMimeType!=null)
791                             {
792                                 CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
793                                 if (content_type!=null)
794                                 {
795                                     _contentType=content_type.toString();
796                                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
797                                 }
798                                 else
799                                 {
800                                     _contentType=_mimeType+"; charset="+_characterEncoding;
801                                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
802                                 }
803                             }
804                             else
805                             {
806                                 _contentType=_mimeType+"; charset="+_characterEncoding;
807                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
808                             }
809                         }
810                         else if (i2<0)
811                         {
812                             _contentType=contentType.substring(0,i1)+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
813                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
814                         }
815                         else
816                         {
817                             _contentType=contentType.substring(0,i1)+contentType.substring(i2)+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
818                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
819                         }
820                     }
821                     else if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
822                     {
823                         // The params are just the char encoding
824                         _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
825                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8));
826                         
827                         if (_cachedMimeType!=null)
828                         {
829                             CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
830                             if (content_type!=null)
831                             {
832                                 _contentType=content_type.toString();
833                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
834                             }
835                             else
836                             {
837                                 _contentType=contentType;
838                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
839                             }
840                         }
841                         else
842                         {
843                             _contentType=contentType;
844                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
845                         }
846                     }
847                     else if (i2>0)
848                     {
849                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8,i2));
850                         _contentType=contentType;
851                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
852                     }
853                     else
854                     {
855                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8)); 
856                         _contentType=contentType;
857                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
858                     }
859                 }
860                 else // No encoding in the params.
861                 {
862                     _cachedMimeType=null;
863                     _contentType=_characterEncoding==null?contentType:contentType+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
864                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
865                 }
866             }
867             else // No params at all
868             {
869                 _mimeType=contentType;
870                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
871                 
872                 if (_characterEncoding!=null)
873                 {
874                     if (_cachedMimeType!=null)
875                     {
876                         CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
877                         if (content_type!=null)
878                         {
879                             _contentType=content_type.toString();
880                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
881                         }
882                         else
883                         {
884                             _contentType=_mimeType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
885                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
886                         }
887                     }
888                     else
889                     {
890                         _contentType=contentType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
891                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
892                     }
893                 }
894                 else if (_cachedMimeType!=null)
895                 {
896                     _contentType=_cachedMimeType.toString();
897                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
898                 }
899                 else
900                 {
901                     _contentType=contentType;
902                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
903                 }
904             }   
905         }
906     }
907 
908     /* ------------------------------------------------------------ */
909     /*
910      * @see javax.servlet.ServletResponse#setBufferSize(int)
911      */
912     public void setBufferSize(int size)
913     {
914         if (isCommitted() || getContentCount()>0)
915             throw new IllegalStateException("Committed or content written");
916         _connection.getGenerator().increaseContentBufferSize(size);
917     }
918 
919     /* ------------------------------------------------------------ */
920     /*
921      * @see javax.servlet.ServletResponse#getBufferSize()
922      */
923     public int getBufferSize()
924     {
925         return _connection.getGenerator().getContentBufferSize();
926     }
927 
928     /* ------------------------------------------------------------ */
929     /*
930      * @see javax.servlet.ServletResponse#flushBuffer()
931      */
932     public void flushBuffer() throws IOException
933     {
934         _connection.flushResponse();
935     }
936 
937     /* ------------------------------------------------------------ */
938     /*
939      * @see javax.servlet.ServletResponse#reset()
940      */
941     public void reset()
942     {
943         resetBuffer();
944         
945         HttpFields response_fields=_connection.getResponseFields();
946         response_fields.clear();
947         String connection=_connection.getRequestFields().getStringField(HttpHeaders.CONNECTION_BUFFER);
948         if (connection!=null)
949         {
950             String[] values = connection.split(",");
951             for  (int i=0;values!=null && i<values.length;i++)
952             {
953                 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[0].trim());
954 
955                 if (cb!=null)
956                 {
957                     switch(cb.getOrdinal())
958                     {
959                         case HttpHeaderValues.CLOSE_ORDINAL:
960                             response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
961                             break;
962 
963                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
964                             if (HttpVersions.HTTP_1_0.equalsIgnoreCase(_connection.getRequest().getProtocol()))
965                                 response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE);
966                             break;
967                         case HttpHeaderValues.TE_ORDINAL:
968                             response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.TE);
969                             break;
970                     }
971                 }
972             }
973         }
974         
975         if (_connection.getConnector().getServer().getSendDateHeader())
976         {
977             Request request=_connection.getRequest();
978             response_fields.put(HttpHeaders.DATE_BUFFER, request.getTimeStampBuffer(),request.getTimeStamp());
979         }
980         
981         _status=200;
982         _reason=null;
983         _mimeType=null;
984         _cachedMimeType=null;
985         _contentType=null;
986         _characterEncoding=null;
987         _explicitEncoding=false;
988         _locale=null;
989         _outputState=NONE;
990         _writer=null;
991     }
992 
993     /* ------------------------------------------------------------ */
994     /*
995      * @see javax.servlet.ServletResponse#resetBuffer()
996      */
997     public void resetBuffer()
998     {
999         if (isCommitted())
1000             throw new IllegalStateException("Committed");
1001         _connection.getGenerator().resetBuffer();
1002     }
1003 
1004     /* ------------------------------------------------------------ */
1005     /*
1006      * @see javax.servlet.ServletResponse#isCommitted()
1007      */
1008     public boolean isCommitted()
1009     {
1010         return _connection.isResponseCommitted();
1011     }
1012 
1013 
1014     /* ------------------------------------------------------------ */
1015     /*
1016      * @see javax.servlet.ServletResponse#setLocale(java.util.Locale)
1017      */
1018     public void setLocale(Locale locale)
1019     {
1020         if (locale == null || isCommitted() ||_connection.isIncluding())
1021             return;
1022 
1023         _locale = locale;
1024         _connection.getResponseFields().put(HttpHeaders.CONTENT_LANGUAGE_BUFFER,locale.toString().replace('_','-'));
1025         
1026         if (_explicitEncoding || _outputState!=0 )
1027             return;
1028 
1029         if (_connection.getRequest().getContext()==null)
1030             return;
1031         
1032         String charset = _connection.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);
1033         
1034         if (charset!=null && charset.length()>0)
1035         {
1036             _characterEncoding=charset;
1037 
1038             /* get current MIME type from Content-Type header */
1039             String type=getContentType();
1040             if (type!=null)
1041             {
1042                 _characterEncoding=charset;
1043                 int semi=type.indexOf(';');
1044                 if (semi<0)
1045                 {
1046                     _mimeType=type;
1047                     _contentType= type += "; charset="+charset;
1048                 }
1049                 else
1050                 {
1051                     _mimeType=type.substring(0,semi);
1052                     _contentType= _mimeType += "; charset="+charset;
1053                 }
1054 
1055                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
1056                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
1057             }
1058         }
1059     }
1060 
1061     /* ------------------------------------------------------------ */
1062     /*
1063      * @see javax.servlet.ServletResponse#getLocale()
1064      */
1065     public Locale getLocale()
1066     {
1067         if (_locale==null)
1068             return Locale.getDefault();
1069         return _locale;
1070     }
1071 
1072     /* ------------------------------------------------------------ */
1073     /**
1074      * @return The HTTP status code that has been set for this request. This will be <code>200<code> 
1075      *    ({@link HttpServletResponse#SC_OK}), unless explicitly set through one of the <code>setStatus</code> methods.
1076      */
1077     public int getStatus()
1078     {
1079         return _status;
1080     }
1081 
1082     /* ------------------------------------------------------------ */
1083     /**
1084      * @return The reason associated with the current {@link #getStatus() status}. This will be <code>null</code>, 
1085      *    unless one of the <code>setStatus</code> methods have been called.
1086      */
1087     public String getReason()
1088     {
1089         return _reason;
1090     }
1091 
1092 
1093 
1094 
1095     /* ------------------------------------------------------------ */
1096     /**
1097      *
1098      */
1099     public void complete()
1100         throws IOException
1101     {
1102         _connection.completeResponse();
1103     }
1104 
1105     /* ------------------------------------------------------------- */
1106     /**
1107      * @return the number of bytes actually written in response body
1108      */
1109     public long getContentCount()
1110     {
1111         if (_connection==null || _connection.getGenerator()==null)
1112             return -1;
1113         return _connection.getGenerator().getContentWritten();
1114     }
1115 
1116     /* ------------------------------------------------------------ */
1117     public HttpFields getHttpFields()
1118     {
1119         return _connection.getResponseFields();
1120     }
1121     
1122     /* ------------------------------------------------------------ */
1123     public String toString()
1124     {
1125         return "HTTP/1.1 "+_status+" "+ (_reason==null?"":_reason) +System.getProperty("line.separator")+
1126         _connection.getResponseFields().toString();
1127     }
1128 
1129     /* ------------------------------------------------------------ */
1130     /* ------------------------------------------------------------ */
1131     /* ------------------------------------------------------------ */
1132     private static class NullOutput extends ServletOutputStream
1133     {
1134         public void write(int b) throws IOException
1135         {
1136         }
1137     }
1138 
1139 
1140 }