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