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