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