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