1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server;
20
21 import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
22
23 import java.io.IOException;
24 import java.io.PrintWriter;
25 import java.nio.channels.IllegalSelectorException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Enumeration;
30 import java.util.Iterator;
31 import java.util.Locale;
32 import java.util.concurrent.atomic.AtomicInteger;
33
34 import javax.servlet.RequestDispatcher;
35 import javax.servlet.ServletOutputStream;
36 import javax.servlet.http.Cookie;
37 import javax.servlet.http.HttpServletResponse;
38 import javax.servlet.http.HttpSession;
39
40 import org.eclipse.jetty.http.DateGenerator;
41 import org.eclipse.jetty.http.HttpContent;
42 import org.eclipse.jetty.http.HttpCookie;
43 import org.eclipse.jetty.http.HttpField;
44 import org.eclipse.jetty.http.HttpFields;
45 import org.eclipse.jetty.http.HttpGenerator;
46 import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
47 import org.eclipse.jetty.http.HttpHeader;
48 import org.eclipse.jetty.http.HttpHeaderValue;
49 import org.eclipse.jetty.http.HttpParser;
50 import org.eclipse.jetty.http.HttpScheme;
51 import org.eclipse.jetty.http.HttpStatus;
52 import org.eclipse.jetty.http.HttpURI;
53 import org.eclipse.jetty.http.HttpVersion;
54 import org.eclipse.jetty.http.MimeTypes;
55 import org.eclipse.jetty.io.RuntimeIOException;
56 import org.eclipse.jetty.server.handler.ContextHandler;
57 import org.eclipse.jetty.server.handler.ErrorHandler;
58 import org.eclipse.jetty.util.ByteArrayISO8859Writer;
59 import org.eclipse.jetty.util.QuotedStringTokenizer;
60 import org.eclipse.jetty.util.StringUtil;
61 import org.eclipse.jetty.util.URIUtil;
62 import org.eclipse.jetty.util.log.Log;
63 import org.eclipse.jetty.util.log.Logger;
64
65
66
67
68 public class Response implements HttpServletResponse
69 {
70 private static final Logger LOG = Log.getLogger(Response.class);
71 private static final String __COOKIE_DELIM="\",;\\ \t";
72 private final static String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim();
73
74
75 private static final ThreadLocal<StringBuilder> __cookieBuilder = new ThreadLocal<StringBuilder>()
76 {
77 @Override
78 protected StringBuilder initialValue()
79 {
80 return new StringBuilder(128);
81 }
82 };
83
84
85 public static Response getResponse(HttpServletResponse response)
86 {
87 if (response instanceof Response)
88 return (Response)response;
89 return HttpChannel.getCurrentHttpChannel().getResponse();
90 }
91
92
93 public enum OutputType
94 {
95 NONE, STREAM, WRITER
96 }
97
98
99
100
101
102
103 public final static String SET_INCLUDE_HEADER_PREFIX = "org.eclipse.jetty.server.include.";
104
105
106
107
108
109 public final static String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
110
111 private final HttpChannel<?> _channel;
112 private final HttpOutput _out;
113 private final HttpFields _fields = new HttpFields();
114 private final AtomicInteger _include = new AtomicInteger();
115 private int _status = HttpStatus.NOT_SET_000;
116 private String _reason;
117 private Locale _locale;
118 private MimeTypes.Type _mimeType;
119 private String _characterEncoding;
120 private boolean _explicitEncoding;
121 private String _contentType;
122 private OutputType _outputType = OutputType.NONE;
123 private ResponseWriter _writer;
124 private long _contentLength = -1;
125
126
127 public Response(HttpChannel<?> channel, HttpOutput out)
128 {
129 _channel = channel;
130 _out = out;
131 }
132
133 protected HttpChannel<?> getHttpChannel()
134 {
135 return _channel;
136 }
137
138 protected void recycle()
139 {
140 _status = HttpStatus.NOT_SET_000;
141 _reason = null;
142 _locale = null;
143 _mimeType = null;
144 _characterEncoding = null;
145 _contentType = null;
146 _outputType = OutputType.NONE;
147 _contentLength = -1;
148 _out.reset();
149 _fields.clear();
150 _explicitEncoding=false;
151 }
152
153 public void setHeaders(HttpContent httpContent)
154 {
155 Response response = _channel.getResponse();
156 String contentType = httpContent.getContentType();
157 if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString()))
158 setContentType(contentType);
159
160 if (httpContent.getContentLength() > 0)
161 setLongContentLength(httpContent.getContentLength());
162
163 String lm = httpContent.getLastModified();
164 if (lm != null)
165 response.getHttpFields().put(HttpHeader.LAST_MODIFIED, lm);
166 else if (httpContent.getResource() != null)
167 {
168 long lml = httpContent.getResource().lastModified();
169 if (lml != -1)
170 response.getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, lml);
171 }
172
173 String etag=httpContent.getETag();
174 if (etag!=null)
175 response.getHttpFields().put(HttpHeader.ETAG,etag);
176 }
177
178 public HttpOutput getHttpOutput()
179 {
180 return _out;
181 }
182
183 public boolean isIncluding()
184 {
185 return _include.get() > 0;
186 }
187
188 public void include()
189 {
190 _include.incrementAndGet();
191 }
192
193 public void included()
194 {
195 _include.decrementAndGet();
196 if (_outputType == OutputType.WRITER)
197 {
198 _writer.reopen();
199 }
200 _out.reopen();
201 }
202
203 public void addCookie(HttpCookie cookie)
204 {
205 addSetCookie(
206 cookie.getName(),
207 cookie.getValue(),
208 cookie.getDomain(),
209 cookie.getPath(),
210 cookie.getMaxAge(),
211 cookie.getComment(),
212 cookie.isSecure(),
213 cookie.isHttpOnly(),
214 cookie.getVersion());;
215 }
216
217 @Override
218 public void addCookie(Cookie cookie)
219 {
220 String comment = cookie.getComment();
221 boolean httpOnly = false;
222
223 if (comment != null)
224 {
225 int i = comment.indexOf(HTTP_ONLY_COMMENT);
226 if (i >= 0)
227 {
228 httpOnly = true;
229 comment = comment.replace(HTTP_ONLY_COMMENT, "").trim();
230 if (comment.length() == 0)
231 comment = null;
232 }
233 }
234 addSetCookie(cookie.getName(),
235 cookie.getValue(),
236 cookie.getDomain(),
237 cookie.getPath(),
238 cookie.getMaxAge(),
239 comment,
240 cookie.getSecure(),
241 httpOnly || cookie.isHttpOnly(),
242 cookie.getVersion());
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 public void addSetCookie(
260 final String name,
261 final String value,
262 final String domain,
263 final String path,
264 final long maxAge,
265 final String comment,
266 final boolean isSecure,
267 final boolean isHttpOnly,
268 int version)
269 {
270
271 if (name == null || name.length() == 0)
272 throw new IllegalArgumentException("Bad cookie name");
273
274
275 StringBuilder buf = __cookieBuilder.get();
276 buf.setLength(0);
277
278
279 boolean quote_name=isQuoteNeededForCookie(name);
280 quoteOnlyOrAppend(buf,name,quote_name);
281
282 buf.append('=');
283
284
285 String name_equals=buf.toString();
286
287
288 boolean quote_value=isQuoteNeededForCookie(value);
289 quoteOnlyOrAppend(buf,value,quote_value);
290
291
292 boolean has_domain = domain!=null && domain.length()>0;
293 boolean quote_domain = has_domain && isQuoteNeededForCookie(domain);
294 boolean has_path = path!=null && path.length()>0;
295 boolean quote_path = has_path && isQuoteNeededForCookie(path);
296
297
298 if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path || isQuoted(name) || isQuoted(value) || isQuoted(path) || isQuoted(domain)))
299 version=1;
300
301
302 if (version==1)
303 buf.append (";Version=1");
304 else if (version>1)
305 buf.append (";Version=").append(version);
306
307
308 if (has_path)
309 {
310 buf.append(";Path=");
311 quoteOnlyOrAppend(buf,path,quote_path);
312 }
313
314
315 if (has_domain)
316 {
317 buf.append(";Domain=");
318 quoteOnlyOrAppend(buf,domain,quote_domain);
319 }
320
321
322 if (maxAge >= 0)
323 {
324
325
326 buf.append(";Expires=");
327 if (maxAge == 0)
328 buf.append(__01Jan1970_COOKIE);
329 else
330 DateGenerator.formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
331
332
333 if (version>=1)
334 {
335 buf.append(";Max-Age=");
336 buf.append(maxAge);
337 }
338 }
339
340
341 if (isSecure)
342 buf.append(";Secure");
343 if (isHttpOnly)
344 buf.append(";HttpOnly");
345 if (comment != null)
346 {
347 buf.append(";Comment=");
348 quoteOnlyOrAppend(buf,comment,isQuoteNeededForCookie(comment));
349 }
350
351
352 Iterator<HttpField> i=_fields.iterator();
353 while (i.hasNext())
354 {
355 HttpField field=i.next();
356 if (field.getHeader()==HttpHeader.SET_COOKIE)
357 {
358 String val = field.getValue();
359 if (val!=null && val.startsWith(name_equals))
360 {
361
362 if (((!has_domain && !val.contains("Domain")) || (has_domain && val.contains(domain))) &&
363 ((!has_path && !val.contains("Path")) || (has_path && val.contains(path))))
364 {
365 i.remove();
366 }
367 }
368 }
369 }
370
371
372 _fields.add(HttpHeader.SET_COOKIE.toString(), buf.toString());
373
374
375 _fields.put(HttpHeader.EXPIRES.toString(), DateGenerator.__01Jan1970);
376 }
377
378
379
380
381
382
383
384
385 private static boolean isQuoteNeededForCookie(String s)
386 {
387 if (s==null || s.length()==0)
388 return true;
389
390 if (QuotedStringTokenizer.isQuoted(s))
391 return false;
392
393 for (int i=0;i<s.length();i++)
394 {
395 char c = s.charAt(i);
396 if (__COOKIE_DELIM.indexOf(c)>=0)
397 return true;
398
399 if (c<0x20 || c>=0x7f)
400 throw new IllegalArgumentException("Illegal character in cookie value");
401 }
402
403 return false;
404 }
405
406
407 private static void quoteOnlyOrAppend(StringBuilder buf, String s, boolean quote)
408 {
409 if (quote)
410 QuotedStringTokenizer.quoteOnly(buf,s);
411 else
412 buf.append(s);
413 }
414
415 @Override
416 public boolean containsHeader(String name)
417 {
418 return _fields.containsKey(name);
419 }
420
421 @Override
422 public String encodeURL(String url)
423 {
424 final Request request = _channel.getRequest();
425 SessionManager sessionManager = request.getSessionManager();
426 if (sessionManager == null)
427 return url;
428
429 HttpURI uri = null;
430 if (sessionManager.isCheckingRemoteSessionIdEncoding() && URIUtil.hasScheme(url))
431 {
432 uri = new HttpURI(url);
433 String path = uri.getPath();
434 path = (path == null ? "" : path);
435 int port = uri.getPort();
436 if (port < 0)
437 port = HttpScheme.HTTPS.asString().equalsIgnoreCase(uri.getScheme()) ? 443 : 80;
438 if (!request.getServerName().equalsIgnoreCase(uri.getHost()) ||
439 request.getServerPort() != port ||
440 !path.startsWith(request.getContextPath()))
441 return url;
442 }
443
444 String sessionURLPrefix = sessionManager.getSessionIdPathParameterNamePrefix();
445 if (sessionURLPrefix == null)
446 return url;
447
448 if (url == null)
449 return null;
450
451
452 if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs())
453 {
454 int prefix = url.indexOf(sessionURLPrefix);
455 if (prefix != -1)
456 {
457 int suffix = url.indexOf("?", prefix);
458 if (suffix < 0)
459 suffix = url.indexOf("#", prefix);
460
461 if (suffix <= prefix)
462 return url.substring(0, prefix);
463 return url.substring(0, prefix) + url.substring(suffix);
464 }
465 return url;
466 }
467
468
469 HttpSession session = request.getSession(false);
470
471
472 if (session == null)
473 return url;
474
475
476 if (!sessionManager.isValid(session))
477 return url;
478
479 String id = sessionManager.getNodeId(session);
480
481 if (uri == null)
482 uri = new HttpURI(url);
483
484
485
486 int prefix = url.indexOf(sessionURLPrefix);
487 if (prefix != -1)
488 {
489 int suffix = url.indexOf("?", prefix);
490 if (suffix < 0)
491 suffix = url.indexOf("#", prefix);
492
493 if (suffix <= prefix)
494 return url.substring(0, prefix + sessionURLPrefix.length()) + id;
495 return url.substring(0, prefix + sessionURLPrefix.length()) + id +
496 url.substring(suffix);
497 }
498
499
500 int suffix = url.indexOf('?');
501 if (suffix < 0)
502 suffix = url.indexOf('#');
503 if (suffix < 0)
504 {
505 return url +
506 ((HttpScheme.HTTPS.is(uri.getScheme()) || HttpScheme.HTTP.is(uri.getScheme())) && uri.getPath() == null ? "/" : "") +
507 sessionURLPrefix + id;
508 }
509
510
511 return url.substring(0, suffix) +
512 ((HttpScheme.HTTPS.is(uri.getScheme()) || HttpScheme.HTTP.is(uri.getScheme())) && uri.getPath() == null ? "/" : "") +
513 sessionURLPrefix + id + url.substring(suffix);
514 }
515
516 @Override
517 public String encodeRedirectURL(String url)
518 {
519 return encodeURL(url);
520 }
521
522 @Override
523 @Deprecated
524 public String encodeUrl(String url)
525 {
526 return encodeURL(url);
527 }
528
529 @Override
530 @Deprecated
531 public String encodeRedirectUrl(String url)
532 {
533 return encodeRedirectURL(url);
534 }
535
536 @Override
537 public void sendError(int sc) throws IOException
538 {
539 if (sc == 102)
540 sendProcessing();
541 else
542 sendError(sc, null);
543 }
544
545 @Override
546 public void sendError(int code, String message) throws IOException
547 {
548 if (isIncluding())
549 return;
550
551 if (isCommitted())
552 LOG.warn("Committed before "+code+" "+message);
553
554 resetBuffer();
555 _characterEncoding=null;
556 setHeader(HttpHeader.EXPIRES,null);
557 setHeader(HttpHeader.LAST_MODIFIED,null);
558 setHeader(HttpHeader.CACHE_CONTROL,null);
559 setHeader(HttpHeader.CONTENT_TYPE,null);
560 setHeader(HttpHeader.CONTENT_LENGTH,null);
561
562 _outputType = OutputType.NONE;
563 setStatus(code);
564 _reason=message;
565
566 Request request = _channel.getRequest();
567 Throwable cause = (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
568 if (message==null)
569 message=cause==null?HttpStatus.getMessage(code):cause.toString();
570
571
572 if (code!=SC_NO_CONTENT &&
573 code!=SC_NOT_MODIFIED &&
574 code!=SC_PARTIAL_CONTENT &&
575 code>=SC_OK)
576 {
577
578 ErrorHandler error_handler = null;
579 ContextHandler.Context context = request.getContext();
580 if (context!=null)
581 error_handler=context.getContextHandler().getErrorHandler();
582 if (error_handler==null)
583 error_handler = _channel.getServer().getBean(ErrorHandler.class);
584 if (error_handler!=null)
585 {
586 request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(code));
587 request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
588 request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
589 request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,request.getServletName());
590 error_handler.handle(null,_channel.getRequest(),_channel.getRequest(),this );
591 }
592 else
593 {
594 setHeader(HttpHeader.CACHE_CONTROL, "must-revalidate,no-cache,no-store");
595 setContentType(MimeTypes.Type.TEXT_HTML_8859_1.toString());
596 try (ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048);)
597 {
598 if (message != null)
599 {
600 message= StringUtil.replace(message, "&", "&");
601 message= StringUtil.replace(message, "<", "<");
602 message= StringUtil.replace(message, ">", ">");
603 }
604 String uri= request.getRequestURI();
605 if (uri!=null)
606 {
607 uri= StringUtil.replace(uri, "&", "&");
608 uri= StringUtil.replace(uri, "<", "<");
609 uri= StringUtil.replace(uri, ">", ">");
610 }
611
612 writer.write("<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n");
613 writer.write("<title>Error ");
614 writer.write(Integer.toString(code));
615 writer.write(' ');
616 if (message==null)
617 writer.write(message);
618 writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: ");
619 writer.write(Integer.toString(code));
620 writer.write("</h2>\n<p>Problem accessing ");
621 writer.write(uri);
622 writer.write(". Reason:\n<pre> ");
623 writer.write(message);
624 writer.write("</pre>");
625 writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>");
626 writer.write("\n</body>\n</html>\n");
627
628 writer.flush();
629 setContentLength(writer.size());
630 try (ServletOutputStream outputStream = getOutputStream())
631 {
632 writer.writeTo(outputStream);
633 writer.destroy();
634 }
635 }
636 }
637 }
638 else if (code!=SC_PARTIAL_CONTENT)
639 {
640
641 _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE);
642 _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH);
643 _characterEncoding=null;
644 _mimeType=null;
645 }
646
647 closeOutput();
648 }
649
650
651
652
653
654
655
656
657
658 public void sendProcessing() throws IOException
659 {
660 if (_channel.isExpecting102Processing() && !isCommitted())
661 {
662 _channel.sendResponse(HttpGenerator.PROGRESS_102_INFO, null, true);
663 }
664 }
665
666
667
668
669
670
671
672 public void sendRedirect(int code, String location) throws IOException
673 {
674 if ((code < HttpServletResponse.SC_MULTIPLE_CHOICES) || (code >= HttpServletResponse.SC_BAD_REQUEST))
675 throw new IllegalArgumentException("Not a 3xx redirect code");
676
677 if (isIncluding())
678 return;
679
680 if (location == null)
681 throw new IllegalArgumentException();
682
683 if (!URIUtil.hasScheme(location))
684 {
685 StringBuilder buf = _channel.getRequest().getRootURL();
686
687 if (location.startsWith("//"))
688 {
689 buf.delete(0, buf.length());
690 buf.append(_channel.getRequest().getScheme());
691 buf.append(":");
692 buf.append(location);
693 }
694 else if (location.startsWith("/"))
695 buf.append(location);
696 else
697 {
698 String path = _channel.getRequest().getRequestURI();
699 String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path);
700 location = URIUtil.addPaths(parent, location);
701 if (location == null)
702 throw new IllegalStateException("path cannot be above root");
703 if (!location.startsWith("/"))
704 buf.append('/');
705 buf.append(location);
706 }
707
708 location = buf.toString();
709 HttpURI uri = new HttpURI(location);
710 String path = uri.getDecodedPath();
711 String canonical = URIUtil.canonicalPath(path);
712 if (canonical == null)
713 throw new IllegalArgumentException();
714 if (!canonical.equals(path))
715 {
716 buf = _channel.getRequest().getRootURL();
717 buf.append(URIUtil.encodePath(canonical));
718 String param=uri.getParam();
719 if (param!=null)
720 {
721 buf.append(';');
722 buf.append(param);
723 }
724 String query=uri.getQuery();
725 if (query!=null)
726 {
727 buf.append('?');
728 buf.append(query);
729 }
730 String fragment=uri.getFragment();
731 if (fragment!=null)
732 {
733 buf.append('#');
734 buf.append(fragment);
735 }
736 location = buf.toString();
737 }
738 }
739
740 resetBuffer();
741 setHeader(HttpHeader.LOCATION, location);
742 setStatus(code);
743 closeOutput();
744 }
745
746 @Override
747 public void sendRedirect(String location) throws IOException
748 {
749 sendRedirect(HttpServletResponse.SC_MOVED_TEMPORARILY, location);
750 }
751
752 @Override
753 public void setDateHeader(String name, long date)
754 {
755 if (!isIncluding())
756 _fields.putDateField(name, date);
757 }
758
759 @Override
760 public void addDateHeader(String name, long date)
761 {
762 if (!isIncluding())
763 _fields.addDateField(name, date);
764 }
765
766 public void setHeader(HttpHeader name, String value)
767 {
768 if (HttpHeader.CONTENT_TYPE == name)
769 setContentType(value);
770 else
771 {
772 if (isIncluding())
773 return;
774
775 _fields.put(name, value);
776
777 if (HttpHeader.CONTENT_LENGTH == name)
778 {
779 if (value == null)
780 _contentLength = -1l;
781 else
782 _contentLength = Long.parseLong(value);
783 }
784 }
785 }
786
787 @Override
788 public void setHeader(String name, String value)
789 {
790 if (HttpHeader.CONTENT_TYPE.is(name))
791 setContentType(value);
792 else
793 {
794 if (isIncluding())
795 {
796 if (name.startsWith(SET_INCLUDE_HEADER_PREFIX))
797 name = name.substring(SET_INCLUDE_HEADER_PREFIX.length());
798 else
799 return;
800 }
801 _fields.put(name, value);
802 if (HttpHeader.CONTENT_LENGTH.is(name))
803 {
804 if (value == null)
805 _contentLength = -1l;
806 else
807 _contentLength = Long.parseLong(value);
808 }
809 }
810 }
811
812 @Override
813 public Collection<String> getHeaderNames()
814 {
815 final HttpFields fields = _fields;
816 return fields.getFieldNamesCollection();
817 }
818
819 @Override
820 public String getHeader(String name)
821 {
822 return _fields.getStringField(name);
823 }
824
825 @Override
826 public Collection<String> getHeaders(String name)
827 {
828 final HttpFields fields = _fields;
829 Collection<String> i = fields.getValuesList(name);
830 if (i == null)
831 return Collections.emptyList();
832 return i;
833 }
834
835 @Override
836 public void addHeader(String name, String value)
837 {
838 if (isIncluding())
839 {
840 if (name.startsWith(SET_INCLUDE_HEADER_PREFIX))
841 name = name.substring(SET_INCLUDE_HEADER_PREFIX.length());
842 else
843 return;
844 }
845
846 if (HttpHeader.CONTENT_TYPE.is(name))
847 {
848 setContentType(value);
849 return;
850 }
851
852 if (HttpHeader.CONTENT_LENGTH.is(name))
853 {
854 setHeader(name,value);
855 return;
856 }
857
858 _fields.add(name, value);
859 }
860
861 @Override
862 public void setIntHeader(String name, int value)
863 {
864 if (!isIncluding())
865 {
866 _fields.putLongField(name, value);
867 if (HttpHeader.CONTENT_LENGTH.is(name))
868 _contentLength = value;
869 }
870 }
871
872 @Override
873 public void addIntHeader(String name, int value)
874 {
875 if (!isIncluding())
876 {
877 _fields.add(name, Integer.toString(value));
878 if (HttpHeader.CONTENT_LENGTH.is(name))
879 _contentLength = value;
880 }
881 }
882
883 @Override
884 public void setStatus(int sc)
885 {
886 if (sc <= 0)
887 throw new IllegalArgumentException();
888 if (!isIncluding())
889 {
890 _status = sc;
891 _reason = null;
892 }
893 }
894
895 @Override
896 @Deprecated
897 public void setStatus(int sc, String sm)
898 {
899 setStatusWithReason(sc,sm);
900 }
901
902 public void setStatusWithReason(int sc, String sm)
903 {
904 if (sc <= 0)
905 throw new IllegalArgumentException();
906 if (!isIncluding())
907 {
908 _status = sc;
909 _reason = sm;
910 }
911 }
912
913 @Override
914 public String getCharacterEncoding()
915 {
916 if (_characterEncoding == null)
917 _characterEncoding = StringUtil.__ISO_8859_1;
918 return _characterEncoding;
919 }
920
921 @Override
922 public String getContentType()
923 {
924 return _contentType;
925 }
926
927 @Override
928 public ServletOutputStream getOutputStream() throws IOException
929 {
930 if (_outputType == OutputType.WRITER)
931 throw new IllegalStateException("WRITER");
932 _outputType = OutputType.STREAM;
933 return _out;
934 }
935
936 public boolean isWriting()
937 {
938 return _outputType == OutputType.WRITER;
939 }
940
941 @Override
942 public PrintWriter getWriter() throws IOException
943 {
944 if (_outputType == OutputType.STREAM)
945 throw new IllegalStateException("STREAM");
946
947 if (_outputType == OutputType.NONE)
948 {
949
950 String encoding = _characterEncoding;
951 if (encoding == null)
952 {
953 encoding = MimeTypes.inferCharsetFromContentType(_contentType);
954 if (encoding == null)
955 encoding = StringUtil.__ISO_8859_1;
956 setCharacterEncoding(encoding,false);
957 }
958
959 if (_writer != null && _writer.isFor(encoding))
960 _writer.reopen();
961 else
962 {
963 if (StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
964 _writer = new ResponseWriter(new Iso88591HttpWriter(_out),encoding);
965 else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
966 _writer = new ResponseWriter(new Utf8HttpWriter(_out),encoding);
967 else
968 _writer = new ResponseWriter(new EncodingHttpWriter(_out, encoding),encoding);
969 }
970
971
972 _outputType = OutputType.WRITER;
973 }
974 return _writer;
975 }
976
977 @Override
978 public void setContentLength(int len)
979 {
980
981
982
983 if (isCommitted() || isIncluding())
984 return;
985
986 long written = _out.getWritten();
987 if (written > len)
988 throw new IllegalArgumentException("setContentLength(" + len + ") when already written " + written);
989
990 _contentLength = len;
991 _fields.putLongField(HttpHeader.CONTENT_LENGTH.toString(), len);
992
993 if (_contentLength > 0)
994 {
995 if (isAllContentWritten(written))
996 {
997 try
998 {
999 closeOutput();
1000 }
1001 catch(IOException e)
1002 {
1003 throw new RuntimeIOException(e);
1004 }
1005 }
1006 }
1007 }
1008
1009 public boolean isAllContentWritten(long written)
1010 {
1011 return (_contentLength >= 0 && written >= _contentLength);
1012 }
1013
1014 public void closeOutput() throws IOException
1015 {
1016 switch (_outputType)
1017 {
1018 case WRITER:
1019 _writer.close();
1020 if (!_out.isClosed())
1021 _out.close();
1022 break;
1023 case STREAM:
1024 getOutputStream().close();
1025 break;
1026 default:
1027 _out.close();
1028 }
1029 }
1030
1031 public long getLongContentLength()
1032 {
1033 return _contentLength;
1034 }
1035
1036 public void setLongContentLength(long len)
1037 {
1038
1039
1040
1041 if (isCommitted() || isIncluding())
1042 return;
1043 _contentLength = len;
1044 _fields.putLongField(HttpHeader.CONTENT_LENGTH.toString(), len);
1045 }
1046
1047 @Override
1048 public void setContentLengthLong(long length)
1049 {
1050 setLongContentLength(length);
1051 }
1052
1053 @Override
1054 public void setCharacterEncoding(String encoding)
1055 {
1056 setCharacterEncoding(encoding,true);
1057 }
1058
1059 private void setCharacterEncoding(String encoding, boolean explicit)
1060 {
1061 if (isIncluding())
1062 return;
1063
1064 if (_outputType == OutputType.NONE && !isCommitted())
1065 {
1066 if (encoding == null)
1067 {
1068 _explicitEncoding=false;
1069
1070
1071 if (_characterEncoding != null)
1072 {
1073 _characterEncoding = null;
1074 if (_contentType != null)
1075 {
1076 _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType);
1077 HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
1078 if (field!=null)
1079 _fields.put(field);
1080 else
1081 _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
1082 }
1083 }
1084 }
1085 else
1086 {
1087
1088 _explicitEncoding=explicit;
1089 _characterEncoding = StringUtil.normalizeCharset(encoding);
1090 if (_contentType != null)
1091 {
1092 _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType) + ";charset=" + _characterEncoding;
1093 HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
1094 if (field!=null)
1095 _fields.put(field);
1096 else
1097 _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
1098 }
1099 }
1100 }
1101 }
1102
1103 @Override
1104 public void setContentType(String contentType)
1105 {
1106 if (isCommitted() || isIncluding())
1107 return;
1108
1109 if (contentType == null)
1110 {
1111 if (isWriting() && _characterEncoding != null)
1112 throw new IllegalSelectorException();
1113
1114 if (_locale == null)
1115 _characterEncoding = null;
1116 _mimeType = null;
1117 _contentType = null;
1118 _fields.remove(HttpHeader.CONTENT_TYPE);
1119 }
1120 else
1121 {
1122 _contentType = contentType;
1123 _mimeType = MimeTypes.CACHE.get(contentType);
1124 String charset;
1125 if (_mimeType != null && _mimeType.getCharset() != null)
1126 charset = _mimeType.getCharset().toString();
1127 else
1128 charset = MimeTypes.getCharsetFromContentType(contentType);
1129
1130 if (charset == null)
1131 {
1132 if (_characterEncoding != null)
1133 {
1134 _contentType = contentType + ";charset=" + _characterEncoding;
1135 _mimeType = null;
1136 }
1137 }
1138 else if (isWriting() && !charset.equals(_characterEncoding))
1139 {
1140
1141 _mimeType = null;
1142 _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType);
1143 if (_characterEncoding != null)
1144 _contentType = _contentType + ";charset=" + _characterEncoding;
1145 }
1146 else
1147 {
1148 _characterEncoding = charset;
1149 _explicitEncoding = true;
1150 }
1151
1152 HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
1153 if (field!=null)
1154 _fields.put(field);
1155 else
1156 _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
1157 }
1158 }
1159
1160 @Override
1161 public void setBufferSize(int size)
1162 {
1163 if (isCommitted() || getContentCount() > 0)
1164 throw new IllegalStateException("Committed or content written");
1165 _out.setBufferSize(size);
1166 }
1167
1168 @Override
1169 public int getBufferSize()
1170 {
1171 return _out.getBufferSize();
1172 }
1173
1174 @Override
1175 public void flushBuffer() throws IOException
1176 {
1177 if (!_out.isClosed())
1178 _out.flush();
1179 }
1180
1181 @Override
1182 public void reset()
1183 {
1184 resetForForward();
1185 _status = 200;
1186 _reason = null;
1187 _contentLength = -1;
1188 _fields.clear();
1189
1190 String connection = _channel.getRequest().getHttpFields().getStringField(HttpHeader.CONNECTION);
1191 if (connection != null)
1192 {
1193 String[] values = connection.split(",");
1194 for (int i = 0; values != null && i < values.length; i++)
1195 {
1196 HttpHeaderValue cb = HttpHeaderValue.CACHE.get(values[0].trim());
1197
1198 if (cb != null)
1199 {
1200 switch (cb)
1201 {
1202 case CLOSE:
1203 _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.toString());
1204 break;
1205
1206 case KEEP_ALIVE:
1207 if (HttpVersion.HTTP_1_0.is(_channel.getRequest().getProtocol()))
1208 _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.toString());
1209 break;
1210 case TE:
1211 _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.TE.toString());
1212 break;
1213 default:
1214 }
1215 }
1216 }
1217 }
1218 }
1219
1220 public void reset(boolean preserveCookies)
1221 {
1222 if (!preserveCookies)
1223 reset();
1224 else
1225 {
1226 ArrayList<String> cookieValues = new ArrayList<String>(5);
1227 Enumeration<String> vals = _fields.getValues(HttpHeader.SET_COOKIE.asString());
1228 while (vals.hasMoreElements())
1229 cookieValues.add(vals.nextElement());
1230 reset();
1231 for (String v:cookieValues)
1232 _fields.add(HttpHeader.SET_COOKIE, v);
1233 }
1234 }
1235
1236 public void resetForForward()
1237 {
1238 resetBuffer();
1239 _outputType = OutputType.NONE;
1240 }
1241
1242 @Override
1243 public void resetBuffer()
1244 {
1245 if (isCommitted())
1246 throw new IllegalStateException("Committed");
1247
1248 switch (_outputType)
1249 {
1250 case STREAM:
1251 case WRITER:
1252 _out.reset();
1253 break;
1254 default:
1255 }
1256
1257 _out.resetBuffer();
1258 }
1259
1260 protected ResponseInfo newResponseInfo()
1261 {
1262 if (_status == HttpStatus.NOT_SET_000)
1263 _status = HttpStatus.OK_200;
1264 return new ResponseInfo(_channel.getRequest().getHttpVersion(), _fields, getLongContentLength(), getStatus(), getReason(), _channel.getRequest().isHead());
1265 }
1266
1267 @Override
1268 public boolean isCommitted()
1269 {
1270 return _channel.isCommitted();
1271 }
1272
1273 @Override
1274 public void setLocale(Locale locale)
1275 {
1276 if (locale == null || isCommitted() || isIncluding())
1277 return;
1278
1279 _locale = locale;
1280 _fields.put(HttpHeader.CONTENT_LANGUAGE, locale.toString().replace('_', '-'));
1281
1282 if (_outputType != OutputType.NONE)
1283 return;
1284
1285 if (_channel.getRequest().getContext() == null)
1286 return;
1287
1288 String charset = _channel.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);
1289
1290 if (charset != null && charset.length() > 0 && !_explicitEncoding)
1291 setCharacterEncoding(charset,false);
1292 }
1293
1294 @Override
1295 public Locale getLocale()
1296 {
1297 if (_locale == null)
1298 return Locale.getDefault();
1299 return _locale;
1300 }
1301
1302 @Override
1303 public int getStatus()
1304 {
1305 return _status;
1306 }
1307
1308 public String getReason()
1309 {
1310 return _reason;
1311 }
1312
1313 public HttpFields getHttpFields()
1314 {
1315 return _fields;
1316 }
1317
1318 public long getContentCount()
1319 {
1320 return _out.getWritten();
1321 }
1322
1323 @Override
1324 public String toString()
1325 {
1326 return String.format("%s %d %s%n%s", _channel.getRequest().getHttpVersion(), _status, _reason == null ? "" : _reason, _fields);
1327 }
1328
1329
1330 private static class ResponseWriter extends PrintWriter
1331 {
1332 private final String _encoding;
1333 private final HttpWriter _httpWriter;
1334
1335 public ResponseWriter(HttpWriter httpWriter,String encoding)
1336 {
1337 super(httpWriter);
1338 _httpWriter=httpWriter;
1339 _encoding=encoding;
1340 }
1341
1342 public boolean isFor(String encoding)
1343 {
1344 return _encoding.equalsIgnoreCase(encoding);
1345 }
1346
1347 protected void reopen()
1348 {
1349 super.clearError();
1350 out=_httpWriter;
1351 }
1352 }
1353 }