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>\n<p>Problem accessing ");
305                 writer.write(uri);
306                 writer.write(". Reason:\n<pre>    ");
307                 writer.write(message);
308                 writer.write("</pre>");
309                 writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>");
310                 
311                 for (int i= 0; i < 20; i++)
312                     writer.write("\n                                                ");
313                 writer.write("\n</body>\n</html>\n");
314                 
315                 writer.flush();
316                 setContentLength(writer.size());
317                 writer.writeTo(getOutputStream());
318                 writer.destroy();
319             }
320         }
321         else if (code!=SC_PARTIAL_CONTENT)
322         {
323             _connection.getRequestFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
324             _connection.getRequestFields().remove(HttpHeaders.CONTENT_LENGTH_BUFFER);
325             _characterEncoding=null;
326             _mimeType=null;
327             _cachedMimeType=null;
328         }
329 
330         complete();
331     }
332 
333     /* ------------------------------------------------------------ */
334     /*
335      * @see javax.servlet.http.HttpServletResponse#sendError(int)
336      */
337     public void sendError(int sc) throws IOException
338     {
339         if (sc==102)
340             sendProcessing();
341         else
342             sendError(sc,null);
343     }
344 
345     /* ------------------------------------------------------------ */
346     /* Send a 102-Processing response.
347      * If the connection is a HTTP connection, the version is 1.1 and the
348      * request has a Expect header starting with 102, then a 102 response is
349      * sent. This indicates that the request still be processed and real response
350      * can still be sent.   This method is called by sendError if it is passed 102.
351      * @see javax.servlet.http.HttpServletResponse#sendError(int)
352      */
353     public void sendProcessing() throws IOException
354     {
355         Generator g = _connection.getGenerator();
356         if (g instanceof HttpGenerator)
357         {
358             HttpGenerator generator = (HttpGenerator)g;
359             
360             String expect = _connection.getRequest().getHeader(HttpHeaders.EXPECT);
361             
362             if (expect!=null && expect.startsWith("102") && generator.getVersion()>=HttpVersions.HTTP_1_1_ORDINAL)
363             {
364                 boolean was_persistent=generator.isPersistent();
365                 generator.setResponse(HttpStatus.ORDINAL_102_Processing,null);
366                 generator.completeHeader(null,true);
367                 generator.setPersistent(true);
368                 generator.complete();
369                 generator.flush();
370                 generator.reset(false);
371                 generator.setPersistent(was_persistent);
372             }
373         }
374     }
375 
376     /* ------------------------------------------------------------ */
377     /*
378      * @see javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String)
379      */
380     public void sendRedirect(String location) throws IOException
381     {
382     	if (_connection.isIncluding())
383     		return;
384     	
385         if (location==null)
386             throw new IllegalArgumentException();
387 
388         if (!URIUtil.hasScheme(location))
389         {
390             StringBuffer buf = _connection.getRequest().getRootURL();
391             if (location.startsWith("/"))
392                 buf.append(URIUtil.canonicalPath(location));
393             else
394             {
395                 String path=_connection.getRequest().getRequestURI();
396                 String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
397                 location=URIUtil.canonicalPath(URIUtil.addPaths(parent,location));
398                 if(location==null)
399                     throw new IllegalStateException("path cannot be above root");
400                 if (!location.startsWith("/"))
401                     buf.append('/');
402                 buf.append(location);
403             }
404 
405             location=buf.toString();
406         }
407         resetBuffer();
408 
409         setHeader(HttpHeaders.LOCATION,location);
410         setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
411         complete();
412 
413     }
414 
415     /* ------------------------------------------------------------ */
416     /*
417      * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long)
418      */
419     public void setDateHeader(String name, long date)
420     {
421         if (!_connection.isIncluding())
422             _connection.getResponseFields().putDateField(name, date);
423     }
424 
425     /* ------------------------------------------------------------ */
426     /*
427      * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long)
428      */
429     public void addDateHeader(String name, long date)
430     {
431         if (!_connection.isIncluding())
432             _connection.getResponseFields().addDateField(name, date);
433     }
434 
435     /* ------------------------------------------------------------ */
436     /*
437      * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String)
438      */
439     public void setHeader(String name, String value)
440     {
441         if (!_connection.isIncluding())
442         {
443             _connection.getResponseFields().put(name, value);
444             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
445             {
446                 if (value==null)
447                     _connection._generator.setContentLength(-1);
448                 else
449                     _connection._generator.setContentLength(Long.parseLong(value));
450             }
451         }
452     }
453     
454     /* ------------------------------------------------------------ */
455     /*
456      */
457     public String getHeader(String name)
458     {
459         return _connection.getResponseFields().getStringField(name);
460     }
461 
462     /* ------------------------------------------------------------ */
463     /* 
464      */
465     public Enumeration getHeaders(String name)
466     {
467         Enumeration e = _connection.getResponseFields().getValues(name);
468         if (e==null)
469             return Collections.enumeration(Collections.EMPTY_LIST);
470         return e;
471     }
472 
473     /* ------------------------------------------------------------ */
474     /*
475      * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String)
476      */
477     public void addHeader(String name, String value)
478     {
479         if (!_connection.isIncluding())
480         {
481             _connection.getResponseFields().add(name, value);
482             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
483                 _connection._generator.setContentLength(Long.parseLong(value));
484         }
485     }
486 
487     /* ------------------------------------------------------------ */
488     /*
489      * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int)
490      */
491     public void setIntHeader(String name, int value)
492     {
493         if (!_connection.isIncluding())
494         {
495             _connection.getResponseFields().putLongField(name, value);
496             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
497                 _connection._generator.setContentLength(value);
498         }
499     }
500 
501     /* ------------------------------------------------------------ */
502     /*
503      * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int)
504      */
505     public void addIntHeader(String name, int value)
506     {
507         if (!_connection.isIncluding())
508         {
509             _connection.getResponseFields().addLongField(name, value);
510             if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
511                 _connection._generator.setContentLength(value);
512         }
513     }
514 
515     /* ------------------------------------------------------------ */
516     /*
517      * @see javax.servlet.http.HttpServletResponse#setStatus(int)
518      */
519     public void setStatus(int sc)
520     {
521         setStatus(sc,null);
522     }
523 
524     /* ------------------------------------------------------------ */
525     /*
526      * @see javax.servlet.http.HttpServletResponse#setStatus(int, java.lang.String)
527      */
528     public void setStatus(int sc, String sm)
529     {
530         if (!_connection.isIncluding())
531         {
532             _status=sc;
533             _reason=sm;
534         }
535     }
536 
537     /* ------------------------------------------------------------ */
538     /*
539      * @see javax.servlet.ServletResponse#getCharacterEncoding()
540      */
541     public String getCharacterEncoding()
542     {
543         if (_characterEncoding==null)
544             _characterEncoding=StringUtil.__ISO_8859_1;
545         return _characterEncoding;
546     }
547     
548     /* ------------------------------------------------------------ */
549     String getSetCharacterEncoding()
550     {
551         return _characterEncoding;
552     }    
553 
554     /* ------------------------------------------------------------ */
555     /*
556      * @see javax.servlet.ServletResponse#getContentType()
557      */
558     public String getContentType()
559     {
560         return _contentType;
561     }
562 
563     /* ------------------------------------------------------------ */
564     /*
565      * @see javax.servlet.ServletResponse#getOutputStream()
566      */
567     public ServletOutputStream getOutputStream() throws IOException
568     {
569         if (_outputState==DISABLED)
570             return __nullServletOut;
571 
572         if (_outputState!=NONE && _outputState!=STREAM)
573             throw new IllegalStateException("WRITER");
574 
575         _outputState=STREAM;
576         return _connection.getOutputStream();
577     }
578 
579     /* ------------------------------------------------------------ */
580     public boolean isWriting()
581     {
582         return _outputState==WRITER;
583     }
584     
585     /* ------------------------------------------------------------ */
586     /*
587      * @see javax.servlet.ServletResponse#getWriter()
588      */
589     public PrintWriter getWriter() throws IOException
590     {
591         if (_outputState==DISABLED)
592             return __nullPrintWriter;
593 
594         if (_outputState!=NONE && _outputState!=WRITER)
595             throw new IllegalStateException("STREAM");
596 
597         /* if there is no writer yet */
598         if (_writer==null)
599         {
600             /* get encoding from Content-Type header */
601             String encoding = _characterEncoding;
602 
603             if (encoding==null)
604             {
605                 /* implementation of educated defaults */
606                 if(_mimeType!=null)
607                     encoding = null; // TODO getHttpContext().getEncodingByMimeType(_mimeType);
608 
609                 if (encoding==null)
610                     encoding = StringUtil.__ISO_8859_1;
611 
612                 setCharacterEncoding(encoding);
613             }
614 
615             /* construct Writer using correct encoding */
616             _writer = _connection.getPrintWriter(encoding);
617         }
618         _outputState=WRITER;
619         return _writer;
620     }
621 
622     /* ------------------------------------------------------------ */
623     /*
624      * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String)
625      */
626     public void setCharacterEncoding(String encoding)
627     {
628     	if (_connection.isIncluding())
629     		return;
630         
631         if (this._outputState==0 && !isCommitted())
632         {
633             _explicitEncoding=true;
634 
635             if (encoding==null)
636             {
637                 // Clear any encoding.
638                 if (_characterEncoding!=null)
639                 {
640                     _characterEncoding=null;
641                     if (_cachedMimeType!=null)
642                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
643                     else
644                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_mimeType);
645                 }
646             }
647             else
648             {
649                 // No, so just add this one to the mimetype
650                 _characterEncoding=encoding;
651                 if (_contentType!=null)
652                 {
653                     int i0=_contentType.indexOf(';');
654                     if (i0<0)
655                     {   
656                         _contentType=null;
657                         if(_cachedMimeType!=null)
658                         {
659                             CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
660                             if (content_type!=null)
661                             {
662                                 _contentType=content_type.toString();
663                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
664                             }
665                         }
666                         
667                         if (_contentType==null)
668                         {
669                             _contentType = _mimeType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); 
670                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
671                         }
672                     }
673                     else
674                     {
675                         int i1=_contentType.indexOf("charset=",i0);
676                         if (i1<0)
677                         {
678                             _contentType = _contentType+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); 
679                         }
680                         else
681                         {
682                             int i8=i1+8;
683                             int i2=_contentType.indexOf(" ",i8);
684                             if (i2<0)
685                                 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ");
686                             else
687                                 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ")+_contentType.substring(i2);
688                         }
689                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
690                     }
691                 }
692             }
693         }
694     }
695 
696     /* ------------------------------------------------------------ */
697     /*
698      * @see javax.servlet.ServletResponse#setContentLength(int)
699      */
700     public void setContentLength(int len)
701     {
702         // Protect from setting after committed as default handling
703         // of a servlet HEAD request ALWAYS sets _content length, even
704         // if the getHandling committed the response!
705         if (isCommitted() || _connection.isIncluding())
706             return;
707         _connection._generator.setContentLength(len);
708         if (len>=0)
709         {
710             _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
711             if (_connection._generator.isContentWritten())
712             {
713                 if (_outputState==WRITER)
714                     _writer.close();
715                 else if (_outputState==STREAM)
716                 {
717                     try
718                     {
719                         getOutputStream().close();
720                     }
721                     catch(IOException e)
722                     {
723                         throw new RuntimeException(e);
724                     }
725                 }
726             }
727         }
728     }
729 
730     /* ------------------------------------------------------------ */
731     /*
732      * @see javax.servlet.ServletResponse#setContentLength(int)
733      */
734     public void setLongContentLength(long len)
735     {
736         // Protect from setting after committed as default handling
737         // of a servlet HEAD request ALWAYS sets _content length, even
738         // if the getHandling committed the response!
739         if (isCommitted() || _connection.isIncluding())
740         	return;
741         _connection._generator.setContentLength(len);
742         _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
743     }
744     
745     /* ------------------------------------------------------------ */
746     /*
747      * @see javax.servlet.ServletResponse#setContentType(java.lang.String)
748      */
749     public void setContentType(String contentType)
750     {
751         if (isCommitted() || _connection.isIncluding())
752             return;
753         
754         // Yes this method is horribly complex.... but there are lots of special cases and
755         // as this method is called on every request, it is worth trying to save string creation.
756         //
757         
758         if (contentType==null)
759         {
760             if (_locale==null)
761                 _characterEncoding=null;
762             _mimeType=null;
763             _cachedMimeType=null;
764             _contentType=null;
765             _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
766         }
767         else
768         {
769             // Look for encoding in contentType
770             int i0=contentType.indexOf(';');
771 
772             if (i0>0)
773             {
774                 // we have content type parameters
775             
776                 // Extract params off mimetype
777                 _mimeType=contentType.substring(0,i0).trim();
778                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
779 
780                 // Look for charset
781                 int i1=contentType.indexOf("charset=",i0+1);
782                 if (i1>=0)
783                 {
784                     _explicitEncoding=true;
785                     int i8=i1+8;
786                     int i2 = contentType.indexOf(' ',i8);
787 
788                     if (_outputState==WRITER)
789                     {
790                         // strip the charset and ignore;
791                         if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
792                         {
793                             if (_cachedMimeType!=null)
794                             {
795                                 CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
796                                 if (content_type!=null)
797                                 {
798                                     _contentType=content_type.toString();
799                                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
800                                 }
801                                 else
802                                 {
803                                     _contentType=_mimeType+"; charset="+_characterEncoding;
804                                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
805                                 }
806                             }
807                             else
808                             {
809                                 _contentType=_mimeType+"; charset="+_characterEncoding;
810                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
811                             }
812                         }
813                         else if (i2<0)
814                         {
815                             _contentType=contentType.substring(0,i1)+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
816                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
817                         }
818                         else
819                         {
820                             _contentType=contentType.substring(0,i1)+contentType.substring(i2)+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
821                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
822                         }
823                     }
824                     else if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
825                     {
826                         // The params are just the char encoding
827                         _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
828                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8));
829                         
830                         if (_cachedMimeType!=null)
831                         {
832                             CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
833                             if (content_type!=null)
834                             {
835                                 _contentType=content_type.toString();
836                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
837                             }
838                             else
839                             {
840                                 _contentType=contentType;
841                                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
842                             }
843                         }
844                         else
845                         {
846                             _contentType=contentType;
847                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
848                         }
849                     }
850                     else if (i2>0)
851                     {
852                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8,i2));
853                         _contentType=contentType;
854                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
855                     }
856                     else
857                     {
858                         _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8)); 
859                         _contentType=contentType;
860                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
861                     }
862                 }
863                 else // No encoding in the params.
864                 {
865                     _cachedMimeType=null;
866                     _contentType=_characterEncoding==null?contentType:contentType+" charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
867                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
868                 }
869             }
870             else // No params at all
871             {
872                 _mimeType=contentType;
873                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
874                 
875                 if (_characterEncoding!=null)
876                 {
877                     if (_cachedMimeType!=null)
878                     {
879                         CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
880                         if (content_type!=null)
881                         {
882                             _contentType=content_type.toString();
883                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
884                         }
885                         else
886                         {
887                             _contentType=_mimeType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
888                             _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
889                         }
890                     }
891                     else
892                     {
893                         _contentType=contentType+"; charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
894                         _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
895                     }
896                 }
897                 else if (_cachedMimeType!=null)
898                 {
899                     _contentType=_cachedMimeType.toString();
900                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
901                 }
902                 else
903                 {
904                     _contentType=contentType;
905                     _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
906                 }
907             }   
908         }
909     }
910 
911     /* ------------------------------------------------------------ */
912     /*
913      * @see javax.servlet.ServletResponse#setBufferSize(int)
914      */
915     public void setBufferSize(int size)
916     {
917         if (isCommitted() || getContentCount()>0)
918             throw new IllegalStateException("Committed or content written");
919         _connection.getGenerator().increaseContentBufferSize(size);
920     }
921 
922     /* ------------------------------------------------------------ */
923     /*
924      * @see javax.servlet.ServletResponse#getBufferSize()
925      */
926     public int getBufferSize()
927     {
928         return _connection.getGenerator().getContentBufferSize();
929     }
930 
931     /* ------------------------------------------------------------ */
932     /*
933      * @see javax.servlet.ServletResponse#flushBuffer()
934      */
935     public void flushBuffer() throws IOException
936     {
937         _connection.flushResponse();
938     }
939 
940     /* ------------------------------------------------------------ */
941     /*
942      * @see javax.servlet.ServletResponse#reset()
943      */
944     public void reset()
945     {
946         resetBuffer();
947         
948         HttpFields response_fields=_connection.getResponseFields();
949         response_fields.clear();
950         String connection=_connection.getRequestFields().getStringField(HttpHeaders.CONNECTION_BUFFER);
951         if (connection!=null)
952         {
953             String[] values = connection.split(",");
954             for  (int i=0;values!=null && i<values.length;i++)
955             {
956                 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[0].trim());
957 
958                 if (cb!=null)
959                 {
960                     switch(cb.getOrdinal())
961                     {
962                         case HttpHeaderValues.CLOSE_ORDINAL:
963                             response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
964                             break;
965 
966                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
967                             if (HttpVersions.HTTP_1_0.equalsIgnoreCase(_connection.getRequest().getProtocol()))
968                                 response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE);
969                             break;
970                         case HttpHeaderValues.TE_ORDINAL:
971                             response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.TE);
972                             break;
973                     }
974                 }
975             }
976         }
977         
978         if (_connection.getConnector().getServer().getSendDateHeader())
979         {
980             Request request=_connection.getRequest();
981             response_fields.put(HttpHeaders.DATE_BUFFER, request.getTimeStampBuffer(),request.getTimeStamp());
982         }
983         
984         _status=200;
985         _reason=null;
986         _mimeType=null;
987         _cachedMimeType=null;
988         _contentType=null;
989         _characterEncoding=null;
990         _explicitEncoding=false;
991         _locale=null;
992         _outputState=NONE;
993         _writer=null;
994     }
995 
996     /* ------------------------------------------------------------ */
997     /*
998      * @see javax.servlet.ServletResponse#resetBuffer()
999      */
1000     public void resetBuffer()
1001     {
1002         if (isCommitted())
1003             throw new IllegalStateException("Committed");
1004         _connection.getGenerator().resetBuffer();
1005     }
1006 
1007     /* ------------------------------------------------------------ */
1008     /*
1009      * @see javax.servlet.ServletResponse#isCommitted()
1010      */
1011     public boolean isCommitted()
1012     {
1013         return _connection.isResponseCommitted();
1014     }
1015 
1016 
1017     /* ------------------------------------------------------------ */
1018     /*
1019      * @see javax.servlet.ServletResponse#setLocale(java.util.Locale)
1020      */
1021     public void setLocale(Locale locale)
1022     {
1023         if (locale == null || isCommitted() ||_connection.isIncluding())
1024             return;
1025 
1026         _locale = locale;
1027         _connection.getResponseFields().put(HttpHeaders.CONTENT_LANGUAGE_BUFFER,locale.toString().replace('_','-'));
1028         
1029         if (_explicitEncoding || _outputState!=0 )
1030             return;
1031 
1032         if (_connection.getRequest().getContext()==null)
1033             return;
1034         
1035         String charset = _connection.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);
1036         
1037         if (charset!=null && charset.length()>0)
1038         {
1039             _characterEncoding=charset;
1040 
1041             /* get current MIME type from Content-Type header */
1042             String type=getContentType();
1043             if (type!=null)
1044             {
1045                 _characterEncoding=charset;
1046                 int semi=type.indexOf(';');
1047                 if (semi<0)
1048                 {
1049                     _mimeType=type;
1050                     _contentType= type += "; charset="+charset;
1051                 }
1052                 else
1053                 {
1054                     _mimeType=type.substring(0,semi);
1055                     _contentType= _mimeType += "; charset="+charset;
1056                 }
1057 
1058                 _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
1059                 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
1060             }
1061         }
1062     }
1063 
1064     /* ------------------------------------------------------------ */
1065     /*
1066      * @see javax.servlet.ServletResponse#getLocale()
1067      */
1068     public Locale getLocale()
1069     {
1070         if (_locale==null)
1071             return Locale.getDefault();
1072         return _locale;
1073     }
1074 
1075     /* ------------------------------------------------------------ */
1076     /**
1077      * @return The HTTP status code that has been set for this request. This will be <code>200<code> 
1078      *    ({@link HttpServletResponse#SC_OK}), unless explicitly set through one of the <code>setStatus</code> methods.
1079      */
1080     public int getStatus()
1081     {
1082         return _status;
1083     }
1084 
1085     /* ------------------------------------------------------------ */
1086     /**
1087      * @return The reason associated with the current {@link #getStatus() status}. This will be <code>null</code>, 
1088      *    unless one of the <code>setStatus</code> methods have been called.
1089      */
1090     public String getReason()
1091     {
1092         return _reason;
1093     }
1094 
1095 
1096 
1097 
1098     /* ------------------------------------------------------------ */
1099     /**
1100      *
1101      */
1102     public void complete()
1103         throws IOException
1104     {
1105         _connection.completeResponse();
1106     }
1107 
1108     /* ------------------------------------------------------------- */
1109     /**
1110      * @return the number of bytes actually written in response body
1111      */
1112     public long getContentCount()
1113     {
1114         if (_connection==null || _connection.getGenerator()==null)
1115             return -1;
1116         return _connection.getGenerator().getContentWritten();
1117     }
1118 
1119     /* ------------------------------------------------------------ */
1120     public HttpFields getHttpFields()
1121     {
1122         return _connection.getResponseFields();
1123     }
1124     
1125     /* ------------------------------------------------------------ */
1126     public String toString()
1127     {
1128         return "HTTP/1.1 "+_status+" "+ (_reason==null?"":_reason) +System.getProperty("line.separator")+
1129         _connection.getResponseFields().toString();
1130     }
1131 
1132     /* ------------------------------------------------------------ */
1133     /* ------------------------------------------------------------ */
1134     /* ------------------------------------------------------------ */
1135     private static class NullOutput extends ServletOutputStream
1136     {
1137         public void write(int b) throws IOException
1138         {
1139         }
1140     }
1141 
1142 
1143 }