1 /*
2 * Copyright 1999-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.coyote.tomcat4;
18
19
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.io.PrintWriter;
23 import java.net.MalformedURLException;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Date;
27 import java.util.Enumeration;
28 import java.util.Locale;
29 import java.util.TimeZone;
30 import java.util.Vector;
31
32 import javax.servlet.ServletOutputStream;
33 import javax.servlet.ServletResponse;
34 import javax.servlet.http.Cookie;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37 import javax.servlet.http.HttpSession;
38
39 import org.apache.catalina.Connector;
40 import org.apache.catalina.Context;
41 import org.apache.catalina.HttpResponse;
42 import org.apache.catalina.util.CharsetMapper;
43 import org.apache.catalina.util.StringManager;
44 import org.apache.coyote.Response;
45 import org.apache.tomcat.util.buf.CharChunk;
46 import org.apache.tomcat.util.buf.UEncoder;
47 import org.apache.tomcat.util.http.MimeHeaders;
48 import org.apache.tomcat.util.http.ServerCookie;
49 import org.apache.tomcat.util.net.URL;
50
51
52 /**
53 * Wrapper object for the Coyote response.
54 *
55 * @author Remy Maucherat
56 * @author Craig R. McClanahan
57 * @version $Revision: 299753 $ $Date: 2004-08-29 13:14:42 -0400 (Sun, 29 Aug 2004) $
58 */
59
60 public class CoyoteResponse
61 implements HttpResponse, HttpServletResponse {
62
63
64 // ----------------------------------------------------------- Constructors
65
66
67 public CoyoteResponse() {
68
69 format.setTimeZone(TimeZone.getTimeZone("GMT"));
70 urlEncoder.addSafeCharacter('/');
71
72 }
73
74
75 // ----------------------------------------------------- Instance Variables
76
77
78 /**
79 * The date format we will use for creating date headers.
80 */
81 protected final SimpleDateFormat format =
82 new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
83
84
85 /**
86 * Descriptive information about this Response implementation.
87 */
88 protected static final String info =
89 "org.apache.coyote.tomcat4.CoyoteResponse/1.0";
90
91
92 /**
93 * The string manager for this package.
94 */
95 protected static StringManager sm =
96 StringManager.getManager(Constants.Package);
97
98
99 // ------------------------------------------------------------- Properties
100
101
102 /**
103 * Associated Catalina connector.
104 */
105 protected Connector connector;
106
107 /**
108 * Return the Connector through which this Request was received.
109 */
110 public Connector getConnector() {
111 return (this.connector);
112 }
113
114 /**
115 * Set the Connector through which this Request was received.
116 *
117 * @param connector The new connector
118 */
119 public void setConnector(Connector connector) {
120 this.connector = connector;
121 }
122
123
124 /**
125 * Coyote response.
126 */
127 protected Response coyoteResponse;
128
129 /**
130 * Set the Coyote response.
131 *
132 * @param coyoteResponse The Coyote response
133 */
134 public void setCoyoteResponse(Response coyoteResponse) {
135 this.coyoteResponse = coyoteResponse;
136 outputBuffer.setResponse(coyoteResponse);
137 }
138
139 /**
140 * Get the Coyote response.
141 */
142 public Response getCoyoteResponse() {
143 return (coyoteResponse);
144 }
145
146
147 /**
148 * The Context within which this Request is being processed.
149 */
150 protected Context context = null;
151
152 /**
153 * Return the Context within which this Request is being processed.
154 */
155 public Context getContext() {
156 return (this.context);
157 }
158
159 /**
160 * Set the Context within which this Request is being processed. This
161 * must be called as soon as the appropriate Context is identified, because
162 * it identifies the value to be returned by <code>getContextPath()</code>,
163 * and thus enables parsing of the request URI.
164 *
165 * @param context The newly associated Context
166 */
167 public void setContext(Context context) {
168 this.context = context;
169 }
170
171
172 /**
173 * The associated output buffer.
174 */
175 protected OutputBuffer outputBuffer = new OutputBuffer();
176
177
178 /**
179 * The associated output stream.
180 */
181 protected CoyoteOutputStream outputStream =
182 new CoyoteOutputStream(outputBuffer);
183
184
185 /**
186 * The associated writer.
187 */
188 protected CoyoteWriter writer = new CoyoteWriter(outputBuffer);
189
190
191 /**
192 * The application commit flag.
193 */
194 protected boolean appCommitted = false;
195
196
197 /**
198 * The included flag.
199 */
200 protected boolean included = false;
201
202
203 /**
204 * The error flag.
205 */
206 protected boolean error = false;
207
208
209 /**
210 * The set of Cookies associated with this Response.
211 */
212 protected ArrayList cookies = new ArrayList();
213
214
215 /**
216 * Using output stream flag.
217 */
218 protected boolean usingOutputStream = false;
219
220
221 /**
222 * Using writer flag.
223 */
224 protected boolean usingWriter = false;
225
226
227 /**
228 * URL encoder.
229 */
230 protected UEncoder urlEncoder = new UEncoder();
231
232
233 /**
234 * Recyclable buffer to hold the redirect URL.
235 */
236 protected CharChunk redirectURLCC = new CharChunk();
237
238
239 // --------------------------------------------------------- Public Methods
240
241
242 /**
243 * Release all object references, and initialize instance variables, in
244 * preparation for reuse of this object.
245 */
246 public void recycle() {
247
248 outputBuffer.recycle();
249 usingOutputStream = false;
250 usingWriter = false;
251 appCommitted = false;
252 included = false;
253 error = false;
254 cookies.clear();
255
256 if (facade != null) {
257 facade.clear();
258 facade = null;
259 }
260
261 writer.recycle();
262
263 }
264
265
266 // ------------------------------------------------------- Response Methods
267
268
269 /**
270 * Return the number of bytes actually written to the output stream.
271 */
272 public int getContentCount() {
273 return outputBuffer.getContentWritten();
274 }
275
276
277 /**
278 * Set the application commit flag.
279 *
280 * @param appCommitted The new application committed flag value
281 */
282 public void setAppCommitted(boolean appCommitted) {
283 this.appCommitted = appCommitted;
284 }
285
286
287 /**
288 * Application commit flag accessor.
289 */
290 public boolean isAppCommitted() {
291 return (this.appCommitted || isCommitted() || isSuspended());
292 }
293
294
295 /**
296 * Return the "processing inside an include" flag.
297 */
298 public boolean getIncluded() {
299 return included;
300 }
301
302
303 /**
304 * Set the "processing inside an include" flag.
305 *
306 * @param included <code>true</code> if we are currently inside a
307 * RequestDispatcher.include(), else <code>false</code>
308 */
309 public void setIncluded(boolean included) {
310 this.included = included;
311 }
312
313
314 /**
315 * Return descriptive information about this Response implementation and
316 * the corresponding version number, in the format
317 * <code><description>/<version></code>.
318 */
319 public String getInfo() {
320 return (info);
321 }
322
323
324 /**
325 * The request with which this response is associated.
326 */
327 protected CoyoteRequest request = null;
328
329 /**
330 * Return the Request with which this Response is associated.
331 */
332 public org.apache.catalina.Request getRequest() {
333 return (this.request);
334 }
335
336 /**
337 * Set the Request with which this Response is associated.
338 *
339 * @param request The new associated request
340 */
341 public void setRequest(org.apache.catalina.Request request) {
342 this.request = (CoyoteRequest) request;
343 }
344
345
346 /**
347 * The facade associated with this response.
348 */
349 protected CoyoteResponseFacade facade = null;
350
351 /**
352 * Return the <code>ServletResponse</code> for which this object
353 * is the facade.
354 */
355 public ServletResponse getResponse() {
356 if (facade == null) {
357 facade = new CoyoteResponseFacade(this);
358 }
359 return (facade);
360 }
361
362
363 /**
364 * Return the output stream associated with this Response.
365 */
366 public OutputStream getStream() {
367 return outputStream;
368 }
369
370
371 /**
372 * Set the output stream associated with this Response.
373 *
374 * @param stream The new output stream
375 */
376 public void setStream(OutputStream stream) {
377 // This method is evil
378 }
379
380
381 /**
382 * Set the suspended flag.
383 *
384 * @param suspended The new suspended flag value
385 */
386 public void setSuspended(boolean suspended) {
387 outputBuffer.setSuspended(suspended);
388 }
389
390
391 /**
392 * Suspended flag accessor.
393 */
394 public boolean isSuspended() {
395 return outputBuffer.isSuspended();
396 }
397
398
399 /**
400 * Set the error flag.
401 */
402 public void setError() {
403 error = true;
404 }
405
406
407 /**
408 * Error flag accessor.
409 */
410 public boolean isError() {
411 return error;
412 }
413
414
415 /**
416 * Create and return a ServletOutputStream to write the content
417 * associated with this Response.
418 *
419 * @exception IOException if an input/output error occurs
420 */
421 public ServletOutputStream createOutputStream()
422 throws IOException {
423 // Probably useless
424 return outputStream;
425 }
426
427
428 /**
429 * Perform whatever actions are required to flush and close the output
430 * stream or writer, in a single operation.
431 *
432 * @exception IOException if an input/output error occurs
433 */
434 public void finishResponse()
435 throws IOException {
436 // Writing leftover bytes
437 try {
438 outputBuffer.close();
439 } catch(IOException e) {
440 ;
441 } catch(Throwable t) {
442 t.printStackTrace();
443 }
444 }
445
446
447 /**
448 * Return the content length that was set or calculated for this Response.
449 */
450 public int getContentLength() {
451 return (coyoteResponse.getContentLength());
452 }
453
454
455 /**
456 * Return the content type that was set or calculated for this response,
457 * or <code>null</code> if no content type was set.
458 */
459 public String getContentType() {
460 return (coyoteResponse.getContentType());
461 }
462
463
464 /**
465 * Return a PrintWriter that can be used to render error messages,
466 * regardless of whether a stream or writer has already been acquired.
467 *
468 * @return Writer which can be used for error reports. If the response is
469 * not an error report returned using sendError or triggered by an
470 * unexpected exception thrown during the servlet processing
471 * (and only in that case), null will be returned if the response stream
472 * has already been used.
473 */
474 public PrintWriter getReporter() {
475 if (outputBuffer.isNew()) {
476 return writer;
477 } else {
478 return null;
479 }
480 }
481
482
483 // ------------------------------------------------ ServletResponse Methods
484
485
486 /**
487 * Flush the buffer and commit this response.
488 *
489 * @exception IOException if an input/output error occurs
490 */
491 public void flushBuffer()
492 throws IOException {
493 outputBuffer.flush();
494 }
495
496
497 /**
498 * Return the actual buffer size used for this Response.
499 */
500 public int getBufferSize() {
501 return outputBuffer.getBufferSize();
502 }
503
504
505 /**
506 * Return the character encoding used for this Response.
507 */
508 public String getCharacterEncoding() {
509 return (coyoteResponse.getCharacterEncoding());
510 }
511
512
513 /**
514 * Return the servlet output stream associated with this Response.
515 *
516 * @exception IllegalStateException if <code>getWriter</code> has
517 * already been called for this response
518 * @exception IOException if an input/output error occurs
519 */
520 public ServletOutputStream getOutputStream()
521 throws IOException {
522
523 if (usingWriter)
524 throw new IllegalStateException
525 (sm.getString("coyoteResponse.getOutputStream.ise"));
526
527 usingOutputStream = true;
528 return outputStream;
529
530 }
531
532
533 /**
534 * Return the Locale assigned to this response.
535 */
536 public Locale getLocale() {
537 return (coyoteResponse.getLocale());
538 }
539
540
541 /**
542 * Return the writer associated with this Response.
543 *
544 * @exception IllegalStateException if <code>getOutputStream</code> has
545 * already been called for this response
546 * @exception IOException if an input/output error occurs
547 */
548 public PrintWriter getWriter()
549 throws IOException {
550
551 if (usingOutputStream)
552 throw new IllegalStateException
553 (sm.getString("coyoteResponse.getWriter.ise"));
554
555 usingWriter = true;
556 return writer;
557
558 }
559
560
561 /**
562 * Has the output of this response already been committed?
563 */
564 public boolean isCommitted() {
565 return (coyoteResponse.isCommitted());
566 }
567
568
569 /**
570 * Clear any content written to the buffer.
571 *
572 * @exception IllegalStateException if this response has already
573 * been committed
574 */
575 public void reset() {
576
577 if (included)
578 return; // Ignore any call from an included servlet
579
580 coyoteResponse.reset();
581 outputBuffer.reset();
582
583 }
584
585
586 /**
587 * Reset the data buffer but not any status or header information.
588 *
589 * @exception IllegalStateException if the response has already
590 * been committed
591 */
592 public void resetBuffer() {
593
594 if (isCommitted())
595 throw new IllegalStateException
596 (sm.getString("coyoteResponse.resetBuffer.ise"));
597
598 outputBuffer.reset();
599
600 }
601
602
603 /**
604 * Set the buffer size to be used for this Response.
605 *
606 * @param size The new buffer size
607 *
608 * @exception IllegalStateException if this method is called after
609 * output has been committed for this response
610 */
611 public void setBufferSize(int size) {
612
613 if (isCommitted() || !outputBuffer.isNew())
614 throw new IllegalStateException
615 (sm.getString("coyoteResponse.setBufferSize.ise"));
616
617 outputBuffer.setBufferSize(size);
618
619 }
620
621
622 /**
623 * Set the content length (in bytes) for this Response.
624 *
625 * @param length The new content length
626 */
627 public void setContentLength(int length) {
628
629 if (isCommitted())
630 return;
631
632 // Ignore any call from an included servlet
633 if (included)
634 return;
635
636 coyoteResponse.setContentLength(length);
637
638 }
639
640
641 /**
642 * Set the content type for this Response.
643 *
644 * @param type The new content type
645 */
646 public void setContentType(String type) {
647
648 if (isCommitted())
649 return;
650
651 // Ignore any call from an included servlet
652 if (included)
653 return;
654
655 coyoteResponse.setContentType(type);
656
657 }
658
659
660 /**
661 * Set the Locale that is appropriate for this response, including
662 * setting the appropriate character encoding.
663 *
664 * @param locale The new locale
665 */
666 public void setLocale(Locale locale) {
667
668 if (isCommitted())
669 return;
670
671 // Ignore any call from an included servlet
672 if (included)
673 return;
674
675 coyoteResponse.setLocale(locale);
676
677 // Set the specified locale's default encoding of a response
678 CharsetMapper cm = context.getCharsetMapper();
679 String charset = cm.getCharset(locale);
680
681 if (charset != null) {
682 coyoteResponse.setCharacterEncoding(charset);
683 }
684
685 }
686
687
688 // --------------------------------------------------- HttpResponse Methods
689
690
691 /**
692 * Return an array of all cookies set for this response, or
693 * a zero-length array if no cookies have been set.
694 */
695 public Cookie[] getCookies() {
696 return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
697 }
698
699
700 /**
701 * Return the value for the specified header, or <code>null</code> if this
702 * header has not been set. If more than one value was added for this
703 * name, only the first is returned; use getHeaderValues() to retrieve all
704 * of them.
705 *
706 * @param name Header name to look up
707 */
708 public String getHeader(String name) {
709 return coyoteResponse.getMimeHeaders().getHeader(name);
710 }
711
712
713 /**
714 * Return an array of all the header names set for this response, or
715 * a zero-length array if no headers have been set.
716 */
717 public String[] getHeaderNames() {
718
719 MimeHeaders headers = coyoteResponse.getMimeHeaders();
720 int n = headers.size();
721 String[] result = new String[n];
722 for (int i = 0; i < n; i++) {
723 result[i] = headers.getName(i).toString();
724 }
725 return result;
726
727 }
728
729
730 /**
731 * Return an array of all the header values associated with the
732 * specified header name, or an zero-length array if there are no such
733 * header values.
734 *
735 * @param name Header name to look up
736 */
737 public String[] getHeaderValues(String name) {
738
739 Enumeration headerValues = coyoteResponse.getMimeHeaders().values(name);
740 Vector result = new Vector();
741 while (headerValues.hasMoreElements()) {
742 result.addElement(headerValues.nextElement());
743 }
744 String[] resultArray = new String[result.size()];
745 result.copyInto(resultArray);
746 return resultArray;
747
748 }
749
750
751 /**
752 * Return the error message that was set with <code>sendError()</code>
753 * for this Response.
754 */
755 public String getMessage() {
756 return coyoteResponse.getMessage();
757 }
758
759
760 /**
761 * Return the HTTP status code associated with this Response.
762 */
763 public int getStatus() {
764 return coyoteResponse.getStatus();
765 }
766
767
768 /**
769 * Reset this response, and specify the values for the HTTP status code
770 * and corresponding message.
771 *
772 * @exception IllegalStateException if this response has already been
773 * committed
774 */
775 public void reset(int status, String message) {
776 reset();
777 setStatus(status, message);
778 }
779
780
781 // -------------------------------------------- HttpServletResponse Methods
782
783
784 /**
785 * Add the specified Cookie to those that will be included with
786 * this Response.
787 *
788 * @param cookie Cookie to be added
789 */
790 public void addCookie(Cookie cookie) {
791
792 if (isCommitted())
793 return;
794
795 // Ignore any call from an included servlet
796 if (included)
797 return;
798
799 cookies.add(cookie);
800
801 StringBuffer sb = new StringBuffer();
802 ServerCookie.appendCookieValue
803 (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
804 cookie.getPath(), cookie.getDomain(), cookie.getComment(),
805 cookie.getMaxAge(), cookie.getSecure());
806 // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
807 // RFC2965 is not supported by browsers and the Servlet spec
808 // asks for 2109.
809 addHeader("Set-Cookie", sb.toString());
810
811 }
812
813
814 /**
815 * Add the specified date header to the specified value.
816 *
817 * @param name Name of the header to set
818 * @param value Date value to be set
819 */
820 public void addDateHeader(String name, long value) {
821
822 if (isCommitted())
823 return;
824
825 // Ignore any call from an included servlet
826 if (included)
827 return;
828
829 addHeader(name, format.format(new Date(value)));
830
831 }
832
833
834 /**
835 * Add the specified header to the specified value.
836 *
837 * @param name Name of the header to set
838 * @param value Value to be set
839 */
840 public void addHeader(String name, String value) {
841
842 if (isCommitted())
843 return;
844
845 // Ignore any call from an included servlet
846 if (included)
847 return;
848
849 coyoteResponse.addHeader(name, value);
850
851 }
852
853
854 /**
855 * Add the specified integer header to the specified value.
856 *
857 * @param name Name of the header to set
858 * @param value Integer value to be set
859 */
860 public void addIntHeader(String name, int value) {
861
862 if (isCommitted())
863 return;
864
865 // Ignore any call from an included servlet
866 if (included)
867 return;
868
869 addHeader(name, "" + value);
870
871 }
872
873
874 /**
875 * Has the specified header been set already in this response?
876 *
877 * @param name Name of the header to check
878 */
879 public boolean containsHeader(String name) {
880 return coyoteResponse.containsHeader(name);
881 }
882
883
884 /**
885 * Encode the session identifier associated with this response
886 * into the specified redirect URL, if necessary.
887 *
888 * @param url URL to be encoded
889 */
890 public String encodeRedirectURL(String url) {
891
892 if (isEncodeable(toAbsolute(url))) {
893 HttpServletRequest hreq =
894 (HttpServletRequest) request.getRequest();
895 return (toEncoded(url, hreq.getSession().getId()));
896 } else {
897 return (url);
898 }
899
900 }
901
902
903 /**
904 * Encode the session identifier associated with this response
905 * into the specified redirect URL, if necessary.
906 *
907 * @param url URL to be encoded
908 *
909 * @deprecated As of Version 2.1 of the Java Servlet API, use
910 * <code>encodeRedirectURL()</code> instead.
911 */
912 public String encodeRedirectUrl(String url) {
913 return (encodeRedirectURL(url));
914 }
915
916
917 /**
918 * Encode the session identifier associated with this response
919 * into the specified URL, if necessary.
920 *
921 * @param url URL to be encoded
922 */
923 public String encodeURL(String url) {
924
925 String absolute = toAbsolute(url);
926 if (isEncodeable(absolute)) {
927 HttpServletRequest hreq =
928 (HttpServletRequest) request.getRequest();
929
930 // W3c spec clearly said
931 if (url.equalsIgnoreCase("")){
932 url = absolute;
933 }
934 return (toEncoded(url, hreq.getSession().getId()));
935 } else {
936 return (url);
937 }
938
939 }
940
941
942 /**
943 * Encode the session identifier associated with this response
944 * into the specified URL, if necessary.
945 *
946 * @param url URL to be encoded
947 *
948 * @deprecated As of Version 2.1 of the Java Servlet API, use
949 * <code>encodeURL()</code> instead.
950 */
951 public String encodeUrl(String url) {
952 return (encodeURL(url));
953 }
954
955
956 /**
957 * Send an acknowledgment of a request.
958 *
959 * @exception IOException if an input/output error occurs
960 */
961 public void sendAcknowledgement()
962 throws IOException {
963
964 if (isCommitted())
965 return;
966
967 // Ignore any call from an included servlet
968 if (included)
969 return;
970
971 coyoteResponse.acknowledge();
972
973 }
974
975
976 /**
977 * Send an error response with the specified status and a
978 * default message.
979 *
980 * @param status HTTP status code to send
981 *
982 * @exception IllegalStateException if this response has
983 * already been committed
984 * @exception IOException if an input/output error occurs
985 */
986 public void sendError(int status)
987 throws IOException {
988 sendError(status, null);
989 }
990
991
992 /**
993 * Send an error response with the specified status and message.
994 *
995 * @param status HTTP status code to send
996 * @param message Corresponding message to send
997 *
998 * @exception IllegalStateException if this response has
999 * already been committed
1000 * @exception IOException if an input/output error occurs
1001 */
1002 public void sendError(int status, String message)
1003 throws IOException {
1004
1005 if (isCommitted())
1006 throw new IllegalStateException
1007 (sm.getString("coyoteResponse.sendError.ise"));
1008
1009 // Ignore any call from an included servlet
1010 if (included)
1011 return;
1012
1013 setError();
1014
1015 coyoteResponse.setStatus(status);
1016 coyoteResponse.setMessage(message);
1017
1018 // Clear any data content that has been buffered
1019 resetBuffer();
1020
1021 // Cause the response to be finished (from the application perspective)
1022 setSuspended(true);
1023
1024 }
1025
1026
1027 /**
1028 * Send a temporary redirect to the specified redirect location URL.
1029 *
1030 * @param location Location URL to redirect to
1031 *
1032 * @exception IllegalStateException if this response has
1033 * already been committed
1034 * @exception IOException if an input/output error occurs
1035 */
1036 public void sendRedirect(String location)
1037 throws IOException {
1038
1039 if (isCommitted())
1040 throw new IllegalStateException
1041 (sm.getString("coyoteResponse.sendRedirect.ise"));
1042
1043 // Ignore any call from an included servlet
1044 if (included)
1045 return;
1046
1047 // Clear any data content that has been buffered
1048 resetBuffer();
1049
1050 // Generate a temporary redirect to the specified location
1051 try {
1052 String absolute = toAbsolute(location);
1053 setStatus(SC_MOVED_TEMPORARILY);
1054 setHeader("Location", absolute);
1055 } catch (IllegalArgumentException e) {
1056 setStatus(SC_NOT_FOUND);
1057 }
1058
1059 // Cause the response to be finished (from the application perspective)
1060 setSuspended(true);
1061
1062 }
1063
1064
1065 /**
1066 * Set the specified date header to the specified value.
1067 *
1068 * @param name Name of the header to set
1069 * @param value Date value to be set
1070 */
1071 public void setDateHeader(String name, long value) {
1072
1073 if (isCommitted())
1074 return;
1075
1076 // Ignore any call from an included servlet
1077 if (included)
1078 return;
1079
1080 setHeader(name, format.format(new Date(value)));
1081
1082 }
1083
1084
1085 /**
1086 * Set the specified header to the specified value.
1087 *
1088 * @param name Name of the header to set
1089 * @param value Value to be set
1090 */
1091 public void setHeader(String name, String value) {
1092
1093 if (isCommitted())
1094 return;
1095
1096 // Ignore any call from an included servlet
1097 if (included)
1098 return;
1099
1100 coyoteResponse.setHeader(name, value);
1101
1102 }
1103
1104
1105 /**
1106 * Set the specified integer header to the specified value.
1107 *
1108 * @param name Name of the header to set
1109 * @param value Integer value to be set
1110 */
1111 public void setIntHeader(String name, int value) {
1112
1113 if (isCommitted())
1114 return;
1115
1116 // Ignore any call from an included servlet
1117 if (included)
1118 return;
1119
1120 setHeader(name, "" + value);
1121
1122 }
1123
1124
1125 /**
1126 * Set the HTTP status to be returned with this response.
1127 *
1128 * @param status The new HTTP status
1129 */
1130 public void setStatus(int status) {
1131 setStatus(status, null);
1132 }
1133
1134
1135 /**
1136 * Set the HTTP status and message to be returned with this response.
1137 *
1138 * @param status The new HTTP status
1139 * @param message The associated text message
1140 *
1141 * @deprecated As of Version 2.1 of the Java Servlet API, this method
1142 * has been deprecated due to the ambiguous meaning of the message
1143 * parameter.
1144 */
1145 public void setStatus(int status, String message) {
1146
1147 if (isCommitted())
1148 return;
1149
1150 // Ignore any call from an included servlet
1151 if (included)
1152 return;
1153
1154 coyoteResponse.setStatus(status);
1155 coyoteResponse.setMessage(message);
1156
1157 }
1158
1159
1160 // ------------------------------------------------------ Protected Methods
1161
1162
1163 /**
1164 * Return <code>true</code> if the specified URL should be encoded with
1165 * a session identifier. This will be true if all of the following
1166 * conditions are met:
1167 * <ul>
1168 * <li>The request we are responding to asked for a valid session
1169 * <li>The requested session ID was not received via a cookie
1170 * <li>The specified URL points back to somewhere within the web
1171 * application that is responding to this request
1172 * </ul>
1173 *
1174 * @param location Absolute URL to be validated
1175 */
1176 protected boolean isEncodeable(String location) {
1177
1178 if (location == null)
1179 return (false);
1180
1181 // Is this an intra-document reference?
1182 if (location.startsWith("#"))
1183 return (false);
1184
1185 // Are we in a valid session that is not using cookies?
1186 HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
1187 HttpSession session = hreq.getSession(false);
1188 if (session == null)
1189 return (false);
1190 if (hreq.isRequestedSessionIdFromCookie())
1191 return (false);
1192
1193 // Is this a valid absolute URL?
1194 URL url = null;
1195 try {
1196 url = new URL(location);
1197 } catch (MalformedURLException e) {
1198 return (false);
1199 }
1200
1201 // Does this URL match down to (and including) the context path?
1202 if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol()))
1203 return (false);
1204 if (!hreq.getServerName().equalsIgnoreCase(url.getHost()))
1205 return (false);
1206 int serverPort = hreq.getServerPort();
1207 if (serverPort == -1) {
1208 if ("https".equals(hreq.getScheme()))
1209 serverPort = 443;
1210 else
1211 serverPort = 80;
1212 }
1213 int urlPort = url.getPort();
1214 if (urlPort == -1) {
1215 if ("https".equals(url.getProtocol()))
1216 urlPort = 443;
1217 else
1218 urlPort = 80;
1219 }
1220 if (serverPort != urlPort)
1221 return (false);
1222
1223 String contextPath = getContext().getPath();
1224 if (contextPath != null) {
1225 String file = url.getFile();
1226 if ((file == null) || !file.startsWith(contextPath))
1227 return (false);
1228 if( file.indexOf(";jsessionid=" + session.getId()) >= 0 )
1229 return (false);
1230 }
1231
1232 // This URL belongs to our web application, so it is encodeable
1233 return (true);
1234
1235 }
1236
1237
1238 /**
1239 * Convert (if necessary) and return the absolute URL that represents the
1240 * resource referenced by this possibly relative URL. If this URL is
1241 * already absolute, return it unchanged.
1242 *
1243 * @param location URL to be (possibly) converted and then returned
1244 *
1245 * @exception IllegalArgumentException if a MalformedURLException is
1246 * thrown when converting the relative URL to an absolute one
1247 */
1248 private String toAbsolute(String location) {
1249
1250 if (location == null)
1251 return (location);
1252
1253 // Construct a new absolute URL if possible (cribbed from
1254 // the DefaultErrorPage servlet)
1255 URL url = null;
1256 try {
1257 url = new URL(location);
1258
1259 if (url.getAuthority() == null)
1260 return location;
1261
1262 } catch (MalformedURLException e1) {
1263 HttpServletRequest hreq =
1264 (HttpServletRequest) request.getRequest();
1265 String requrl = request.getRequestURL().toString();
1266 try {
1267 url = new URL(new URL(requrl), location);
1268 } catch (MalformedURLException e2) {
1269 throw new IllegalArgumentException(location);
1270 }
1271 }
1272 return (url.toExternalForm());
1273 }
1274
1275
1276 /**
1277 * Return the specified URL with the specified session identifier
1278 * suitably encoded.
1279 *
1280 * @param url URL to be encoded with the session id
1281 * @param sessionId Session id to be included in the encoded URL
1282 */
1283 private String toEncoded(String url, String sessionId) {
1284
1285 if ((url == null) || (sessionId == null))
1286 return (url);
1287
1288 String path = url;
1289 String query = "";
1290 String anchor = "";
1291 int question = url.indexOf('?');
1292 if (question >= 0) {
1293 path = url.substring(0, question);
1294 query = url.substring(question);
1295 }
1296 int pound = path.indexOf('#');
1297 if (pound >= 0) {
1298 anchor = path.substring(pound);
1299 path = path.substring(0, pound);
1300 }
1301 StringBuffer sb = new StringBuffer(path);
1302 if( sb.length() > 0 ) { // jsessionid can't be first.
1303 sb.append(";jsessionid=");
1304 sb.append(sessionId);
1305 }
1306 sb.append(anchor);
1307 sb.append(query);
1308 return (sb.toString());
1309
1310 }
1311
1312
1313 }