View Javadoc

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