comparison src/org/eclipse/jetty/server/Response.java @ 802:3428c60d7cfc

replace jetty jars with source
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 07 Sep 2016 21:15:48 -0600
parents
children 09d518d313b7
comparison
equal deleted inserted replaced
801:6a21393191c1 802:3428c60d7cfc
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 /* ------------------------------------------------------------ */
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 */
111 public Response(AbstractHttpConnection connection)
112 {
113 _connection=connection;
114 }
115
116
117 /* ------------------------------------------------------------ */
118 /*
119 * @see javax.servlet.ServletResponse#reset()
120 */
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 */
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 */
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 */
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 */
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 */
286 public String encodeRedirectURL(String url)
287 {
288 return encodeURL(url);
289 }
290
291 /* ------------------------------------------------------------ */
292 @Deprecated
293 public String encodeUrl(String url)
294 {
295 return encodeURL(url);
296 }
297
298 /* ------------------------------------------------------------ */
299 @Deprecated
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 /* ------------------------------------------------------------ */
554 public Collection<String> getHeaderNames()
555 {
556 final HttpFields fields=_connection.getResponseFields();
557 return fields.getFieldNamesCollection();
558 }
559
560 /* ------------------------------------------------------------ */
561 /*
562 */
563 public String getHeader(String name)
564 {
565 return _connection.getResponseFields().getStringField(name);
566 }
567
568 /* ------------------------------------------------------------ */
569 /*
570 */
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 */
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 */
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 */
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 */
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 */
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 */
662 public String getCharacterEncoding()
663 {
664 if (_characterEncoding==null)
665 _characterEncoding=StringUtil.__ISO_8859_1;
666 return _characterEncoding;
667 }
668
669 /* ------------------------------------------------------------ */
670 String getSetCharacterEncoding()
671 {
672 return _characterEncoding;
673 }
674
675 /* ------------------------------------------------------------ */
676 /*
677 * @see javax.servlet.ServletResponse#getContentType()
678 */
679 public String getContentType()
680 {
681 return _contentType;
682 }
683
684 /* ------------------------------------------------------------ */
685 /*
686 * @see javax.servlet.ServletResponse#getOutputStream()
687 */
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 /* ------------------------------------------------------------ */
699 public boolean isWriting()
700 {
701 return _outputState==WRITER;
702 }
703
704 /* ------------------------------------------------------------ */
705 public boolean isOutputing()
706 {
707 return _outputState!=NONE;
708 }
709
710 /* ------------------------------------------------------------ */
711 /*
712 * @see javax.servlet.ServletResponse#getWriter()
713 */
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 */
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 */
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 */
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 */
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 */
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 */
1055 public int getBufferSize()
1056 {
1057 return _connection.getGenerator().getContentBufferSize();
1058 }
1059
1060 /* ------------------------------------------------------------ */
1061 /*
1062 * @see javax.servlet.ServletResponse#flushBuffer()
1063 */
1064 public void flushBuffer() throws IOException
1065 {
1066 _connection.flushResponse();
1067 }
1068
1069 /* ------------------------------------------------------------ */
1070 /*
1071 * @see javax.servlet.ServletResponse#reset()
1072 */
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
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 */
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 */
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 */
1162 public boolean isCommitted()
1163 {
1164 return _connection.isResponseCommitted();
1165 }
1166
1167
1168 /* ------------------------------------------------------------ */
1169 /*
1170 * @see javax.servlet.ServletResponse#setLocale(java.util.Locale)
1171 */
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 */
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 */
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 */
1241 public String getReason()
1242 {
1243 return _reason;
1244 }
1245
1246 /* ------------------------------------------------------------ */
1247 /**
1248 */
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 */
1259 public long getContentCount()
1260 {
1261 if (_connection==null || _connection.getGenerator()==null)
1262 return -1;
1263 return _connection.getGenerator().getContentWritten();
1264 }
1265
1266 /* ------------------------------------------------------------ */
1267 public HttpFields getHttpFields()
1268 {
1269 return _connection.getResponseFields();
1270 }
1271
1272 /* ------------------------------------------------------------ */
1273 @Override
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
1286 public void write(int b) throws IOException
1287 {
1288 }
1289
1290 @Override
1291 public void print(String s) throws IOException
1292 {
1293 }
1294
1295 @Override
1296 public void println(String s) throws IOException
1297 {
1298 }
1299
1300 @Override
1301 public void write(byte[] b, int off, int len) throws IOException
1302 {
1303 }
1304
1305 }
1306
1307 }