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