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