KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > connector > Response


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.catalina.connector;
20
21
22 import java.io.IOException JavaDoc;
23 import java.io.OutputStream JavaDoc;
24 import java.io.PrintWriter JavaDoc;
25 import java.net.MalformedURLException JavaDoc;
26 import java.security.AccessController JavaDoc;
27 import java.security.PrivilegedAction JavaDoc;
28 import java.security.PrivilegedActionException JavaDoc;
29 import java.security.PrivilegedExceptionAction JavaDoc;
30 import java.text.SimpleDateFormat JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Enumeration JavaDoc;
33 import java.util.Locale JavaDoc;
34 import java.util.TimeZone JavaDoc;
35 import java.util.Vector JavaDoc;
36
37 import javax.servlet.ServletOutputStream JavaDoc;
38 import javax.servlet.http.Cookie JavaDoc;
39 import javax.servlet.http.HttpServletResponse JavaDoc;
40
41 import org.apache.catalina.Context;
42 import org.apache.catalina.Globals;
43 import org.apache.catalina.Session;
44 import org.apache.catalina.Wrapper;
45 import org.apache.catalina.security.SecurityUtil;
46 import org.apache.catalina.util.CharsetMapper;
47 import org.apache.catalina.util.DateTool;
48 import org.apache.catalina.util.StringManager;
49 import org.apache.tomcat.util.buf.CharChunk;
50 import org.apache.tomcat.util.buf.UEncoder;
51 import org.apache.tomcat.util.http.FastHttpDateFormat;
52 import org.apache.tomcat.util.http.MimeHeaders;
53 import org.apache.tomcat.util.http.ServerCookie;
54 import org.apache.tomcat.util.net.URL;
55
56 /**
57  * Wrapper object for the Coyote response.
58  *
59  * @author Remy Maucherat
60  * @author Craig R. McClanahan
61  * @version $Revision: 470756 $ $Date: 2006-11-03 11:56:25 +0100 (ven., 03 nov. 2006) $
62  */

63
64 public class Response
65     implements HttpServletResponse JavaDoc {
66
67
68     // ----------------------------------------------------------- Constructors
69

70     static {
71         // Ensure that URL is loaded for SM
72
URL.isSchemeChar('c');
73     }
74
75     public Response() {
76         urlEncoder.addSafeCharacter('/');
77     }
78
79
80     // ----------------------------------------------------- Class Variables
81

82
83     /**
84      * Descriptive information about this Response implementation.
85      */

86     protected static final String JavaDoc info =
87         "org.apache.coyote.tomcat5.CoyoteResponse/1.0";
88
89
90     /**
91      * The string manager for this package.
92      */

93     protected static StringManager sm =
94         StringManager.getManager(Constants.Package);
95
96
97     // ----------------------------------------------------- Instance Variables
98

99     /**
100      * The date format we will use for creating date headers.
101      */

102     protected SimpleDateFormat JavaDoc format = null;
103
104
105     // ------------------------------------------------------------- Properties
106

107
108     /**
109      * Associated Catalina connector.
110      */

111     protected Connector connector;
112
113     /**
114      * Return the Connector through which this Request was received.
115      */

116     public Connector getConnector() {
117         return (this.connector);
118     }
119
120     /**
121      * Set the Connector through which this Request was received.
122      *
123      * @param connector The new connector
124      */

125     public void setConnector(Connector connector) {
126         this.connector = connector;
127         if("AJP/1.3".equals(connector.getProtocol())) {
128             // default size to size of one ajp-packet
129
outputBuffer = new OutputBuffer(8184);
130         } else {
131             outputBuffer = new OutputBuffer();
132         }
133         outputStream = new CoyoteOutputStream(outputBuffer);
134         writer = new CoyoteWriter(outputBuffer);
135     }
136
137
138     /**
139      * Coyote response.
140      */

141     protected org.apache.coyote.Response coyoteResponse;
142
143     /**
144      * Set the Coyote response.
145      *
146      * @param coyoteResponse The Coyote response
147      */

148     public void setCoyoteResponse(org.apache.coyote.Response coyoteResponse) {
149         this.coyoteResponse = coyoteResponse;
150         outputBuffer.setResponse(coyoteResponse);
151     }
152
153     /**
154      * Get the Coyote response.
155      */

156     public org.apache.coyote.Response getCoyoteResponse() {
157         return (coyoteResponse);
158     }
159
160
161     /**
162      * Return the Context within which this Request is being processed.
163      */

164     public Context getContext() {
165         return (request.getContext());
166     }
167
168     /**
169      * Set the Context within which this Request is being processed. This
170      * must be called as soon as the appropriate Context is identified, because
171      * it identifies the value to be returned by <code>getContextPath()</code>,
172      * and thus enables parsing of the request URI.
173      *
174      * @param context The newly associated Context
175      */

176     public void setContext(Context context) {
177         request.setContext(context);
178     }
179
180
181     /**
182      * The associated output buffer.
183      */

184     protected OutputBuffer outputBuffer;
185
186
187     /**
188      * The associated output stream.
189      */

190     protected CoyoteOutputStream outputStream;
191
192
193     /**
194      * The associated writer.
195      */

196     protected CoyoteWriter writer;
197
198
199     /**
200      * The application commit flag.
201      */

202     protected boolean appCommitted = false;
203
204
205     /**
206      * The included flag.
207      */

208     protected boolean included = false;
209
210     
211     /**
212      * The characterEncoding flag
213      */

214     private boolean isCharacterEncodingSet = false;
215     
216     /**
217      * The error flag.
218      */

219     protected boolean error = false;
220
221
222     /**
223      * The set of Cookies associated with this Response.
224      */

225     protected ArrayList JavaDoc cookies = new ArrayList JavaDoc();
226
227
228     /**
229      * Using output stream flag.
230      */

231     protected boolean usingOutputStream = false;
232
233
234     /**
235      * Using writer flag.
236      */

237     protected boolean usingWriter = false;
238
239
240     /**
241      * URL encoder.
242      */

243     protected UEncoder urlEncoder = new UEncoder();
244
245
246     /**
247      * Recyclable buffer to hold the redirect URL.
248      */

249     protected CharChunk redirectURLCC = new CharChunk();
250
251
252     // --------------------------------------------------------- Public Methods
253

254
255     /**
256      * Release all object references, and initialize instance variables, in
257      * preparation for reuse of this object.
258      */

259     public void recycle() {
260
261         outputBuffer.recycle();
262         usingOutputStream = false;
263         usingWriter = false;
264         appCommitted = false;
265         included = false;
266         error = false;
267         isCharacterEncodingSet = false;
268         
269         cookies.clear();
270
271         if (Constants.SECURITY || Connector.RECYCLE_FACADES) {
272             if (facade != null) {
273                 facade.clear();
274                 facade = null;
275             }
276             if (outputStream != null) {
277                 outputStream.clear();
278                 outputStream = null;
279             }
280             if (writer != null) {
281                 writer.clear();
282                 writer = null;
283             }
284         } else {
285             writer.recycle();
286         }
287
288     }
289
290
291     /**
292      * Clear cached encoders (to save memory for Comet requests).
293      */

294     public void clearEncoders() {
295         outputBuffer.clearEncoders();
296     }
297     
298
299     // ------------------------------------------------------- Response Methods
300

301
302     /**
303      * Return the number of bytes actually written to the output stream.
304      */

305     public int getContentCount() {
306         return outputBuffer.getContentWritten();
307     }
308
309
310     /**
311      * Set the application commit flag.
312      *
313      * @param appCommitted The new application committed flag value
314      */

315     public void setAppCommitted(boolean appCommitted) {
316         this.appCommitted = appCommitted;
317     }
318
319
320     /**
321      * Application commit flag accessor.
322      */

323     public boolean isAppCommitted() {
324         return (this.appCommitted || isCommitted() || isSuspended()
325                 || ((getContentLength() > 0)
326                     && (getContentCount() >= getContentLength())));
327     }
328
329
330     /**
331      * Return the "processing inside an include" flag.
332      */

333     public boolean getIncluded() {
334         return included;
335     }
336
337
338     /**
339      * Set the "processing inside an include" flag.
340      *
341      * @param included <code>true</code> if we are currently inside a
342      * RequestDispatcher.include(), else <code>false</code>
343      */

344     public void setIncluded(boolean included) {
345         this.included = included;
346     }
347
348
349     /**
350      * Return descriptive information about this Response implementation and
351      * the corresponding version number, in the format
352      * <code>&lt;description&gt;/&lt;version&gt;</code>.
353      */

354     public String JavaDoc getInfo() {
355         return (info);
356     }
357
358
359     /**
360      * The request with which this response is associated.
361      */

362     protected Request request = null;
363
364     /**
365      * Return the Request with which this Response is associated.
366      */

367     public org.apache.catalina.connector.Request getRequest() {
368         return (this.request);
369     }
370
371     /**
372      * Set the Request with which this Response is associated.
373      *
374      * @param request The new associated request
375      */

376     public void setRequest(org.apache.catalina.connector.Request request) {
377         this.request = (Request) request;
378     }
379
380
381     /**
382      * The facade associated with this response.
383      */

384     protected ResponseFacade facade = null;
385
386     /**
387      * Return the <code>ServletResponse</code> for which this object
388      * is the facade.
389      */

390     public HttpServletResponse JavaDoc getResponse() {
391         if (facade == null) {
392             facade = new ResponseFacade(this);
393         }
394         return (facade);
395     }
396
397
398     /**
399      * Return the output stream associated with this Response.
400      */

401     public OutputStream JavaDoc getStream() {
402         if (outputStream == null) {
403             outputStream = new CoyoteOutputStream(outputBuffer);
404         }
405         return outputStream;
406     }
407
408
409     /**
410      * Set the output stream associated with this Response.
411      *
412      * @param stream The new output stream
413      */

414     public void setStream(OutputStream JavaDoc stream) {
415         // This method is evil
416
}
417
418
419     /**
420      * Set the suspended flag.
421      *
422      * @param suspended The new suspended flag value
423      */

424     public void setSuspended(boolean suspended) {
425         outputBuffer.setSuspended(suspended);
426     }
427
428
429     /**
430      * Suspended flag accessor.
431      */

432     public boolean isSuspended() {
433         return outputBuffer.isSuspended();
434     }
435
436
437     /**
438      * Closed flag accessor.
439      */

440     public boolean isClosed() {
441         return outputBuffer.isClosed();
442     }
443
444
445     /**
446      * Set the error flag.
447      */

448     public void setError() {
449         error = true;
450     }
451
452
453     /**
454      * Error flag accessor.
455      */

456     public boolean isError() {
457         return error;
458     }
459
460
461     /**
462      * Create and return a ServletOutputStream to write the content
463      * associated with this Response.
464      *
465      * @exception IOException if an input/output error occurs
466      */

467     public ServletOutputStream JavaDoc createOutputStream()
468         throws IOException JavaDoc {
469         // Probably useless
470
if (outputStream == null) {
471             outputStream = new CoyoteOutputStream(outputBuffer);
472         }
473         return outputStream;
474     }
475
476
477     /**
478      * Perform whatever actions are required to flush and close the output
479      * stream or writer, in a single operation.
480      *
481      * @exception IOException if an input/output error occurs
482      */

483     public void finishResponse()
484         throws IOException JavaDoc {
485         // Writing leftover bytes
486
outputBuffer.close();
487     }
488
489
490     /**
491      * Return the content length that was set or calculated for this Response.
492      */

493     public int getContentLength() {
494         return (coyoteResponse.getContentLength());
495     }
496
497
498     /**
499      * Return the content type that was set or calculated for this response,
500      * or <code>null</code> if no content type was set.
501      */

502     public String JavaDoc getContentType() {
503         return (coyoteResponse.getContentType());
504     }
505
506
507     /**
508      * Return a PrintWriter that can be used to render error messages,
509      * regardless of whether a stream or writer has already been acquired.
510      *
511      * @return Writer which can be used for error reports. If the response is
512      * not an error report returned using sendError or triggered by an
513      * unexpected exception thrown during the servlet processing
514      * (and only in that case), null will be returned if the response stream
515      * has already been used.
516      *
517      * @exception IOException if an input/output error occurs
518      */

519     public PrintWriter JavaDoc getReporter() throws IOException JavaDoc {
520         if (outputBuffer.isNew()) {
521             outputBuffer.checkConverter();
522             if (writer == null) {
523                 writer = new CoyoteWriter(outputBuffer);
524             }
525             return writer;
526         } else {
527             return null;
528         }
529     }
530
531
532     // ------------------------------------------------ ServletResponse Methods
533

534
535     /**
536      * Flush the buffer and commit this response.
537      *
538      * @exception IOException if an input/output error occurs
539      */

540     public void flushBuffer()
541         throws IOException JavaDoc {
542         outputBuffer.flush();
543     }
544
545
546     /**
547      * Return the actual buffer size used for this Response.
548      */

549     public int getBufferSize() {
550         return outputBuffer.getBufferSize();
551     }
552
553
554     /**
555      * Return the character encoding used for this Response.
556      */

557     public String JavaDoc getCharacterEncoding() {
558         return (coyoteResponse.getCharacterEncoding());
559     }
560
561
562     /**
563      * Return the servlet output stream associated with this Response.
564      *
565      * @exception IllegalStateException if <code>getWriter</code> has
566      * already been called for this response
567      * @exception IOException if an input/output error occurs
568      */

569     public ServletOutputStream JavaDoc getOutputStream()
570         throws IOException JavaDoc {
571
572         if (usingWriter)
573             throw new IllegalStateException JavaDoc
574                 (sm.getString("coyoteResponse.getOutputStream.ise"));
575
576         usingOutputStream = true;
577         if (outputStream == null) {
578             outputStream = new CoyoteOutputStream(outputBuffer);
579         }
580         return outputStream;
581
582     }
583
584
585     /**
586      * Return the Locale assigned to this response.
587      */

588     public Locale JavaDoc getLocale() {
589         return (coyoteResponse.getLocale());
590     }
591
592
593     /**
594      * Return the writer associated with this Response.
595      *
596      * @exception IllegalStateException if <code>getOutputStream</code> has
597      * already been called for this response
598      * @exception IOException if an input/output error occurs
599      */

600     public PrintWriter JavaDoc getWriter()
601         throws IOException JavaDoc {
602
603         if (usingOutputStream)
604             throw new IllegalStateException JavaDoc
605                 (sm.getString("coyoteResponse.getWriter.ise"));
606
607         if (Globals.STRICT_SERVLET_COMPLIANCE) {
608             /*
609              * If the response's character encoding has not been specified as
610              * described in <code>getCharacterEncoding</code> (i.e., the method
611              * just returns the default value <code>ISO-8859-1</code>),
612              * <code>getWriter</code> updates it to <code>ISO-8859-1</code>
613              * (with the effect that a subsequent call to getContentType() will
614              * include a charset=ISO-8859-1 component which will also be
615              * reflected in the Content-Type response header, thereby satisfying
616              * the Servlet spec requirement that containers must communicate the
617              * character encoding used for the servlet response's writer to the
618              * client).
619              */

620             setCharacterEncoding(getCharacterEncoding());
621         }
622
623         usingWriter = true;
624         outputBuffer.checkConverter();
625         if (writer == null) {
626             writer = new CoyoteWriter(outputBuffer);
627         }
628         return writer;
629
630     }
631
632
633     /**
634      * Has the output of this response already been committed?
635      */

636     public boolean isCommitted() {
637         return (coyoteResponse.isCommitted());
638     }
639
640
641     /**
642      * Clear any content written to the buffer.
643      *
644      * @exception IllegalStateException if this response has already
645      * been committed
646      */

647     public void reset() {
648
649         if (included)
650             return; // Ignore any call from an included servlet
651

652         coyoteResponse.reset();
653         outputBuffer.reset();
654     }
655
656
657     /**
658      * Reset the data buffer but not any status or header information.
659      *
660      * @exception IllegalStateException if the response has already
661      * been committed
662      */

663     public void resetBuffer() {
664
665         if (isCommitted())
666             throw new IllegalStateException JavaDoc
667                 (sm.getString("coyoteResponse.resetBuffer.ise"));
668
669         outputBuffer.reset();
670
671     }
672
673
674     /**
675      * Set the buffer size to be used for this Response.
676      *
677      * @param size The new buffer size
678      *
679      * @exception IllegalStateException if this method is called after
680      * output has been committed for this response
681      */

682     public void setBufferSize(int size) {
683
684         if (isCommitted() || !outputBuffer.isNew())
685             throw new IllegalStateException JavaDoc
686                 (sm.getString("coyoteResponse.setBufferSize.ise"));
687
688         outputBuffer.setBufferSize(size);
689
690     }
691
692
693     /**
694      * Set the content length (in bytes) for this Response.
695      *
696      * @param length The new content length
697      */

698     public void setContentLength(int length) {
699
700         if (isCommitted())
701             return;
702
703         // Ignore any call from an included servlet
704
if (included)
705             return;
706         
707         if (usingWriter)
708             return;
709         
710         coyoteResponse.setContentLength(length);
711
712     }
713
714
715     /**
716      * Set the content type for this Response.
717      *
718      * @param type The new content type
719      */

720     public void setContentType(String JavaDoc type) {
721
722         if (isCommitted())
723             return;
724
725         // Ignore any call from an included servlet
726
if (included)
727             return;
728
729         // Ignore charset if getWriter() has already been called
730
if (usingWriter) {
731             if (type != null) {
732                 int index = type.indexOf(";");
733                 if (index != -1) {
734                     type = type.substring(0, index);
735                 }
736             }
737         }
738
739         coyoteResponse.setContentType(type);
740
741         // Check to see if content type contains charset
742
if (type != null) {
743             int index = type.indexOf(";");
744             if (index != -1) {
745                 int len = type.length();
746                 index++;
747                 while (index < len && Character.isSpace(type.charAt(index))) {
748                     index++;
749                 }
750                 if (index+7 < len
751                         && type.charAt(index) == 'c'
752                         && type.charAt(index+1) == 'h'
753                         && type.charAt(index+2) == 'a'
754                         && type.charAt(index+3) == 'r'
755                         && type.charAt(index+4) == 's'
756                         && type.charAt(index+5) == 'e'
757                         && type.charAt(index+6) == 't'
758                         && type.charAt(index+7) == '=') {
759                     isCharacterEncodingSet = true;
760                 }
761             }
762         }
763     }
764
765
766     /*
767      * Overrides the name of the character encoding used in the body
768      * of the request. This method must be called prior to reading
769      * request parameters or reading input using getReader().
770      *
771      * @param charset String containing the name of the chararacter encoding.
772      */

773     public void setCharacterEncoding(String JavaDoc charset) {
774
775         if (isCommitted())
776             return;
777         
778         // Ignore any call from an included servlet
779
if (included)
780             return;
781         
782         // Ignore any call made after the getWriter has been invoked
783
// The default should be used
784
if (usingWriter)
785             return;
786
787         coyoteResponse.setCharacterEncoding(charset);
788         isCharacterEncodingSet = true;
789     }
790
791     
792     
793     /**
794      * Set the Locale that is appropriate for this response, including
795      * setting the appropriate character encoding.
796      *
797      * @param locale The new locale
798      */

799     public void setLocale(Locale JavaDoc locale) {
800
801         if (isCommitted())
802             return;
803
804         // Ignore any call from an included servlet
805
if (included)
806             return;
807
808         coyoteResponse.setLocale(locale);
809
810         // Ignore any call made after the getWriter has been invoked.
811
// The default should be used
812
if (usingWriter)
813             return;
814
815         if (isCharacterEncodingSet) {
816             return;
817         }
818
819         CharsetMapper cm = getContext().getCharsetMapper();
820         String JavaDoc charset = cm.getCharset( locale );
821         if ( charset != null ){
822             coyoteResponse.setCharacterEncoding(charset);
823         }
824
825     }
826
827
828     // --------------------------------------------------- HttpResponse Methods
829

830
831     /**
832      * Return an array of all cookies set for this response, or
833      * a zero-length array if no cookies have been set.
834      */

835     public Cookie JavaDoc[] getCookies() {
836         return ((Cookie JavaDoc[]) cookies.toArray(new Cookie JavaDoc[cookies.size()]));
837     }
838
839
840     /**
841      * Return the value for the specified header, or <code>null</code> if this
842      * header has not been set. If more than one value was added for this
843      * name, only the first is returned; use getHeaderValues() to retrieve all
844      * of them.
845      *
846      * @param name Header name to look up
847      */

848     public String JavaDoc getHeader(String JavaDoc name) {
849         return coyoteResponse.getMimeHeaders().getHeader(name);
850     }
851
852
853     /**
854      * Return an array of all the header names set for this response, or
855      * a zero-length array if no headers have been set.
856      */

857     public String JavaDoc[] getHeaderNames() {
858
859         MimeHeaders headers = coyoteResponse.getMimeHeaders();
860         int n = headers.size();
861         String JavaDoc[] result = new String JavaDoc[n];
862         for (int i = 0; i < n; i++) {
863             result[i] = headers.getName(i).toString();
864         }
865         return result;
866
867     }
868
869
870     /**
871      * Return an array of all the header values associated with the
872      * specified header name, or an zero-length array if there are no such
873      * header values.
874      *
875      * @param name Header name to look up
876      */

877     public String JavaDoc[] getHeaderValues(String JavaDoc name) {
878
879         Enumeration JavaDoc enumeration = coyoteResponse.getMimeHeaders().values(name);
880         Vector JavaDoc result = new Vector JavaDoc();
881         while (enumeration.hasMoreElements()) {
882             result.addElement(enumeration.nextElement());
883         }
884         String JavaDoc[] resultArray = new String JavaDoc[result.size()];
885         result.copyInto(resultArray);
886         return resultArray;
887
888     }
889
890
891     /**
892      * Return the error message that was set with <code>sendError()</code>
893      * for this Response.
894      */

895     public String JavaDoc getMessage() {
896         return coyoteResponse.getMessage();
897     }
898
899
900     /**
901      * Return the HTTP status code associated with this Response.
902      */

903     public int getStatus() {
904         return coyoteResponse.getStatus();
905     }
906
907
908     /**
909      * Reset this response, and specify the values for the HTTP status code
910      * and corresponding message.
911      *
912      * @exception IllegalStateException if this response has already been
913      * committed
914      */

915     public void reset(int status, String JavaDoc message) {
916         reset();
917         setStatus(status, message);
918     }
919
920
921     // -------------------------------------------- HttpServletResponse Methods
922

923
924     /**
925      * Add the specified Cookie to those that will be included with
926      * this Response.
927      *
928      * @param cookie Cookie to be added
929      */

930     public void addCookie(final Cookie JavaDoc cookie) {
931
932         // Ignore any call from an included servlet
933
if (included)
934             return;
935
936         addCookieInternal(cookie);
937
938     }
939
940
941     /**
942      * Add the specified Cookie to those that will be included with
943      * this Response.
944      *
945      * @param cookie Cookie to be added
946      */

947     public void addCookieInternal(final Cookie JavaDoc cookie) {
948
949         if (isCommitted())
950             return;
951
952         cookies.add(cookie);
953
954         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
955         if (SecurityUtil.isPackageProtectionEnabled()) {
956             AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
957                 public Object JavaDoc run(){
958                     ServerCookie.appendCookieValue
959                         (sb, cookie.getVersion(), cookie.getName(),
960                          cookie.getValue(), cookie.getPath(),
961                          cookie.getDomain(), cookie.getComment(),
962                          cookie.getMaxAge(), cookie.getSecure());
963                     return null;
964                 }
965             });
966         } else {
967             ServerCookie.appendCookieValue
968                 (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
969                      cookie.getPath(), cookie.getDomain(), cookie.getComment(),
970                      cookie.getMaxAge(), cookie.getSecure());
971         }
972
973         // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
974
// RFC2965 is not supported by browsers and the Servlet spec
975
// asks for 2109.
976
addHeader("Set-Cookie", sb.toString());
977
978     }
979
980
981     /**
982      * Add the specified date header to the specified value.
983      *
984      * @param name Name of the header to set
985      * @param value Date value to be set
986      */

987     public void addDateHeader(String JavaDoc name, long value) {
988
989         if (isCommitted())
990             return;
991
992         // Ignore any call from an included servlet
993
if (included) {
994             return;
995         }
996
997         if (format == null) {
998             format = new SimpleDateFormat JavaDoc(DateTool.HTTP_RESPONSE_DATE_HEADER,
999                                           Locale.US);
1000            format.setTimeZone(TimeZone.getTimeZone("GMT"));
1001        }
1002
1003        addHeader(name, FastHttpDateFormat.formatDate(value, format));
1004
1005    }
1006
1007
1008    /**
1009     * Add the specified header to the specified value.
1010     *
1011     * @param name Name of the header to set
1012     * @param value Value to be set
1013     */

1014    public void addHeader(String JavaDoc name, String JavaDoc value) {
1015
1016        if (isCommitted())
1017            return;
1018
1019        // Ignore any call from an included servlet
1020
if (included)
1021            return;
1022
1023        coyoteResponse.addHeader(name, value);
1024
1025    }
1026
1027
1028    /**
1029     * Add the specified integer header to the specified value.
1030     *
1031     * @param name Name of the header to set
1032     * @param value Integer value to be set
1033     */

1034    public void addIntHeader(String JavaDoc name, int value) {
1035
1036        if (isCommitted())
1037            return;
1038
1039        // Ignore any call from an included servlet
1040
if (included)
1041            return;
1042
1043        addHeader(name, "" + value);
1044
1045    }
1046
1047
1048    /**
1049     * Has the specified header been set already in this response?
1050     *
1051     * @param name Name of the header to check
1052     */

1053    public boolean containsHeader(String JavaDoc name) {
1054        // Need special handling for Content-Type and Content-Length due to
1055
// special handling of these in coyoteResponse
1056
char cc=name.charAt(0);
1057        if(cc=='C' || cc=='c') {
1058            if(name.equalsIgnoreCase("Content-Type")) {
1059                // Will return null if this has not been set
1060
return (coyoteResponse.getContentType() != null);
1061            }
1062            if(name.equalsIgnoreCase("Content-Length")) {
1063                // -1 means not known and is not sent to client
1064
return (coyoteResponse.getContentLengthLong() != -1);
1065            }
1066        }
1067
1068        return coyoteResponse.containsHeader(name);
1069    }
1070
1071
1072    /**
1073     * Encode the session identifier associated with this response
1074     * into the specified redirect URL, if necessary.
1075     *
1076     * @param url URL to be encoded
1077     */

1078    public String JavaDoc encodeRedirectURL(String JavaDoc url) {
1079
1080        if (isEncodeable(toAbsolute(url))) {
1081            return (toEncoded(url, request.getSessionInternal().getIdInternal()));
1082        } else {
1083            return (url);
1084        }
1085
1086    }
1087
1088
1089    /**
1090     * Encode the session identifier associated with this response
1091     * into the specified redirect URL, if necessary.
1092     *
1093     * @param url URL to be encoded
1094     *
1095     * @deprecated As of Version 2.1 of the Java Servlet API, use
1096     * <code>encodeRedirectURL()</code> instead.
1097     */

1098    public String JavaDoc encodeRedirectUrl(String JavaDoc url) {
1099        return (encodeRedirectURL(url));
1100    }
1101
1102
1103    /**
1104     * Encode the session identifier associated with this response
1105     * into the specified URL, if necessary.
1106     *
1107     * @param url URL to be encoded
1108     */

1109    public String JavaDoc encodeURL(String JavaDoc url) {
1110        
1111        String JavaDoc absolute = toAbsolute(url);
1112        if (isEncodeable(absolute)) {
1113            // W3c spec clearly said
1114
if (url.equalsIgnoreCase("")){
1115                url = absolute;
1116            }
1117            return (toEncoded(url, request.getSessionInternal().getIdInternal()));
1118        } else {
1119            return (url);
1120        }
1121
1122    }
1123
1124
1125    /**
1126     * Encode the session identifier associated with this response
1127     * into the specified URL, if necessary.
1128     *
1129     * @param url URL to be encoded
1130     *
1131     * @deprecated As of Version 2.1 of the Java Servlet API, use
1132     * <code>encodeURL()</code> instead.
1133     */

1134    public String JavaDoc encodeUrl(String JavaDoc url) {
1135        return (encodeURL(url));
1136    }
1137
1138
1139    /**
1140     * Send an acknowledgment of a request.
1141     *
1142     * @exception IOException if an input/output error occurs
1143     */

1144    public void sendAcknowledgement()
1145        throws IOException JavaDoc {
1146
1147        if (isCommitted())
1148            return;
1149
1150        // Ignore any call from an included servlet
1151
if (included)
1152            return;
1153
1154        coyoteResponse.acknowledge();
1155
1156    }
1157
1158
1159    /**
1160     * Send an error response with the specified status and a
1161     * default message.
1162     *
1163     * @param status HTTP status code to send
1164     *
1165     * @exception IllegalStateException if this response has
1166     * already been committed
1167     * @exception IOException if an input/output error occurs
1168     */

1169    public void sendError(int status)
1170        throws IOException JavaDoc {
1171        sendError(status, null);
1172    }
1173
1174
1175    /**
1176     * Send an error response with the specified status and message.
1177     *
1178     * @param status HTTP status code to send
1179     * @param message Corresponding message to send
1180     *
1181     * @exception IllegalStateException if this response has
1182     * already been committed
1183     * @exception IOException if an input/output error occurs
1184     */

1185    public void sendError(int status, String JavaDoc message)
1186        throws IOException JavaDoc {
1187
1188        if (isCommitted())
1189            throw new IllegalStateException JavaDoc
1190                (sm.getString("coyoteResponse.sendError.ise"));
1191
1192        // Ignore any call from an included servlet
1193
if (included)
1194            return;
1195
1196        Wrapper wrapper = getRequest().getWrapper();
1197        if (wrapper != null) {
1198            wrapper.incrementErrorCount();
1199        }
1200
1201        setError();
1202
1203        coyoteResponse.setStatus(status);
1204        coyoteResponse.setMessage(message);
1205
1206        // Clear any data content that has been buffered
1207
resetBuffer();
1208
1209        // Cause the response to be finished (from the application perspective)
1210
setSuspended(true);
1211
1212    }
1213
1214
1215    /**
1216     * Send a temporary redirect to the specified redirect location URL.
1217     *
1218     * @param location Location URL to redirect to
1219     *
1220     * @exception IllegalStateException if this response has
1221     * already been committed
1222     * @exception IOException if an input/output error occurs
1223     */

1224    public void sendRedirect(String JavaDoc location)
1225        throws IOException JavaDoc {
1226
1227        if (isCommitted())
1228            throw new IllegalStateException JavaDoc
1229                (sm.getString("coyoteResponse.sendRedirect.ise"));
1230
1231        // Ignore any call from an included servlet
1232
if (included)
1233            return;
1234
1235        // Clear any data content that has been buffered
1236
resetBuffer();
1237
1238        // Generate a temporary redirect to the specified location
1239
try {
1240            String JavaDoc absolute = toAbsolute(location);
1241            setStatus(SC_FOUND);
1242            setHeader("Location", absolute);
1243        } catch (IllegalArgumentException JavaDoc e) {
1244            setStatus(SC_NOT_FOUND);
1245        }
1246
1247        // Cause the response to be finished (from the application perspective)
1248
setSuspended(true);
1249
1250    }
1251
1252
1253    /**
1254     * Set the specified date header to the specified value.
1255     *
1256     * @param name Name of the header to set
1257     * @param value Date value to be set
1258     */

1259    public void setDateHeader(String JavaDoc name, long value) {
1260
1261        if (isCommitted())
1262            return;
1263
1264        // Ignore any call from an included servlet
1265
if (included) {
1266            return;
1267        }
1268
1269        if (format == null) {
1270            format = new SimpleDateFormat JavaDoc(DateTool.HTTP_RESPONSE_DATE_HEADER,
1271                                          Locale.US);
1272            format.setTimeZone(TimeZone.getTimeZone("GMT"));
1273        }
1274
1275        setHeader(name, FastHttpDateFormat.formatDate(value, format));
1276
1277    }
1278
1279
1280    /**
1281     * Set the specified header to the specified value.
1282     *
1283     * @param name Name of the header to set
1284     * @param value Value to be set
1285     */

1286    public void setHeader(String JavaDoc name, String JavaDoc value) {
1287
1288        if (isCommitted())
1289            return;
1290
1291        // Ignore any call from an included servlet
1292
if (included)
1293            return;
1294
1295        coyoteResponse.setHeader(name, value);
1296
1297    }
1298
1299
1300    /**
1301     * Set the specified integer header to the specified value.
1302     *
1303     * @param name Name of the header to set
1304     * @param value Integer value to be set
1305     */

1306    public void setIntHeader(String JavaDoc name, int value) {
1307
1308        if (isCommitted())
1309            return;
1310
1311        // Ignore any call from an included servlet
1312
if (included)
1313            return;
1314
1315        setHeader(name, "" + value);
1316
1317    }
1318
1319
1320    /**
1321     * Set the HTTP status to be returned with this response.
1322     *
1323     * @param status The new HTTP status
1324     */

1325    public void setStatus(int status) {
1326        setStatus(status, null);
1327    }
1328
1329
1330    /**
1331     * Set the HTTP status and message to be returned with this response.
1332     *
1333     * @param status The new HTTP status
1334     * @param message The associated text message
1335     *
1336     * @deprecated As of Version 2.1 of the Java Servlet API, this method
1337     * has been deprecated due to the ambiguous meaning of the message
1338     * parameter.
1339     */

1340    public void setStatus(int status, String JavaDoc message) {
1341
1342        if (isCommitted())
1343            return;
1344
1345        // Ignore any call from an included servlet
1346
if (included)
1347            return;
1348
1349        coyoteResponse.setStatus(status);
1350        coyoteResponse.setMessage(message);
1351
1352    }
1353
1354
1355    // ------------------------------------------------------ Protected Methods
1356

1357
1358    /**
1359     * Return <code>true</code> if the specified URL should be encoded with
1360     * a session identifier. This will be true if all of the following
1361     * conditions are met:
1362     * <ul>
1363     * <li>The request we are responding to asked for a valid session
1364     * <li>The requested session ID was not received via a cookie
1365     * <li>The specified URL points back to somewhere within the web
1366     * application that is responding to this request
1367     * </ul>
1368     *
1369     * @param location Absolute URL to be validated
1370     */

1371    protected boolean isEncodeable(final String JavaDoc location) {
1372
1373        if (location == null)
1374            return (false);
1375
1376        // Is this an intra-document reference?
1377
if (location.startsWith("#"))
1378            return (false);
1379
1380        // Are we in a valid session that is not using cookies?
1381
final Request hreq = request;
1382        final Session session = hreq.getSessionInternal(false);
1383        if (session == null)
1384            return (false);
1385        if (hreq.isRequestedSessionIdFromCookie())
1386            return (false);
1387        
1388        if (SecurityUtil.isPackageProtectionEnabled()) {
1389            return ((Boolean JavaDoc)
1390                AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
1391
1392                public Object JavaDoc run(){
1393                    return new Boolean JavaDoc(doIsEncodeable(hreq, session, location));
1394                }
1395            })).booleanValue();
1396        } else {
1397            return doIsEncodeable(hreq, session, location);
1398        }
1399    }
1400
1401    private boolean doIsEncodeable(Request hreq, Session session,
1402                                   String JavaDoc location) {
1403        // Is this a valid absolute URL?
1404
URL url = null;
1405        try {
1406            url = new URL(location);
1407        } catch (MalformedURLException JavaDoc e) {
1408            return (false);
1409        }
1410
1411        // Does this URL match down to (and including) the context path?
1412
if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol()))
1413            return (false);
1414        if (!hreq.getServerName().equalsIgnoreCase(url.getHost()))
1415            return (false);
1416        int serverPort = hreq.getServerPort();
1417        if (serverPort == -1) {
1418            if ("https".equals(hreq.getScheme()))
1419                serverPort = 443;
1420            else
1421                serverPort = 80;
1422        }
1423        int urlPort = url.getPort();
1424        if (urlPort == -1) {
1425            if ("https".equals(url.getProtocol()))
1426                urlPort = 443;
1427            else
1428                urlPort = 80;
1429        }
1430        if (serverPort != urlPort)
1431            return (false);
1432
1433        String JavaDoc contextPath = getContext().getPath();
1434        if (contextPath != null) {
1435            String JavaDoc file = url.getFile();
1436            if ((file == null) || !file.startsWith(contextPath))
1437                return (false);
1438            if( file.indexOf(";jsessionid=" + session.getIdInternal()) >= 0 )
1439                return (false);
1440        }
1441
1442        // This URL belongs to our web application, so it is encodeable
1443
return (true);
1444
1445    }
1446
1447
1448    /**
1449     * Convert (if necessary) and return the absolute URL that represents the
1450     * resource referenced by this possibly relative URL. If this URL is
1451     * already absolute, return it unchanged.
1452     *
1453     * @param location URL to be (possibly) converted and then returned
1454     *
1455     * @exception IllegalArgumentException if a MalformedURLException is
1456     * thrown when converting the relative URL to an absolute one
1457     */

1458    private String JavaDoc toAbsolute(String JavaDoc location) {
1459
1460        if (location == null)
1461            return (location);
1462
1463        boolean leadingSlash = location.startsWith("/");
1464
1465        if (leadingSlash || !hasScheme(location)) {
1466
1467            redirectURLCC.recycle();
1468
1469            String JavaDoc scheme = request.getScheme();
1470            String JavaDoc name = request.getServerName();
1471            int port = request.getServerPort();
1472
1473            try {
1474                redirectURLCC.append(scheme, 0, scheme.length());
1475                redirectURLCC.append("://", 0, 3);
1476                redirectURLCC.append(name, 0, name.length());
1477                if ((scheme.equals("http") && port != 80)
1478                    || (scheme.equals("https") && port != 443)) {
1479                    redirectURLCC.append(':');
1480                    String JavaDoc portS = port + "";
1481                    redirectURLCC.append(portS, 0, portS.length());
1482                }
1483                if (!leadingSlash) {
1484                    String JavaDoc relativePath = request.getDecodedRequestURI();
1485                    int pos = relativePath.lastIndexOf('/');
1486                    relativePath = relativePath.substring(0, pos);
1487                    
1488                    String JavaDoc encodedURI = null;
1489                    final String JavaDoc frelativePath = relativePath;
1490                    if (SecurityUtil.isPackageProtectionEnabled() ){
1491                        try{
1492                            encodedURI = (String JavaDoc)AccessController.doPrivileged(
1493                                new PrivilegedExceptionAction JavaDoc(){
1494                                    public Object JavaDoc run() throws IOException JavaDoc{
1495                                        return urlEncoder.encodeURL(frelativePath);
1496                                    }
1497                           });
1498                        } catch (PrivilegedActionException JavaDoc pae){
1499                            IllegalArgumentException JavaDoc iae =
1500                                new IllegalArgumentException JavaDoc(location);
1501                            iae.initCause(pae.getException());
1502                            throw iae;
1503                        }
1504                    } else {
1505                        encodedURI = urlEncoder.encodeURL(relativePath);
1506                    }
1507                    redirectURLCC.append(encodedURI, 0, encodedURI.length());
1508                    redirectURLCC.append('/');
1509                }
1510                redirectURLCC.append(location, 0, location.length());
1511            } catch (IOException JavaDoc e) {
1512                IllegalArgumentException JavaDoc iae =
1513                    new IllegalArgumentException JavaDoc(location);
1514                iae.initCause(e);
1515                throw iae;
1516            }
1517
1518            return redirectURLCC.toString();
1519
1520        } else {
1521
1522            return (location);
1523
1524        }
1525
1526    }
1527
1528
1529    /**
1530     * Determine if a URI string has a <code>scheme</code> component.
1531     */

1532    private boolean hasScheme(String JavaDoc uri) {
1533        int len = uri.length();
1534        for(int i=0; i < len ; i++) {
1535            char c = uri.charAt(i);
1536            if(c == ':') {
1537                return i > 0;
1538            } else if(!URL.isSchemeChar(c)) {
1539                return false;
1540            }
1541        }
1542        return false;
1543    }
1544
1545    /**
1546     * Return the specified URL with the specified session identifier
1547     * suitably encoded.
1548     *
1549     * @param url URL to be encoded with the session id
1550     * @param sessionId Session id to be included in the encoded URL
1551     */

1552    protected String JavaDoc toEncoded(String JavaDoc url, String JavaDoc sessionId) {
1553
1554        if ((url == null) || (sessionId == null))
1555            return (url);
1556
1557        String JavaDoc path = url;
1558        String JavaDoc query = "";
1559        String JavaDoc anchor = "";
1560        int question = url.indexOf('?');
1561        if (question >= 0) {
1562            path = url.substring(0, question);
1563            query = url.substring(question);
1564        }
1565        int pound = path.indexOf('#');
1566        if (pound >= 0) {
1567            anchor = path.substring(pound);
1568            path = path.substring(0, pound);
1569        }
1570        StringBuffer JavaDoc sb = new StringBuffer JavaDoc(path);
1571        if( sb.length() > 0 ) { // jsessionid can't be first.
1572
sb.append(";jsessionid=");
1573            sb.append(sessionId);
1574        }
1575        sb.append(anchor);
1576        sb.append(query);
1577        return (sb.toString());
1578
1579    }
1580
1581
1582}
1583
1584
Popular Tags