KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > server > connection > AbstractHttpResponse


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.server.connection;
31
32 import com.caucho.log.Log;
33 import com.caucho.server.cache.AbstractCacheEntry;
34 import com.caucho.server.cache.AbstractCacheFilterChain;
35 import com.caucho.server.dispatch.BadRequestException;
36 import com.caucho.server.dispatch.InvocationDecoder;
37 import com.caucho.server.session.CookieImpl;
38 import com.caucho.server.session.SessionImpl;
39 import com.caucho.server.session.SessionManager;
40 import com.caucho.server.util.CauchoSystem;
41 import com.caucho.server.webapp.ErrorPageManager;
42 import com.caucho.server.webapp.WebApp;
43 import com.caucho.util.Alarm;
44 import com.caucho.util.CaseInsensitiveIntMap;
45 import com.caucho.util.CharBuffer;
46 import com.caucho.util.HTTPUtil;
47 import com.caucho.util.L10N;
48 import com.caucho.util.QDate;
49 import com.caucho.vfs.ClientDisconnectException;
50 import com.caucho.vfs.FlushBuffer;
51 import com.caucho.vfs.TempBuffer;
52 import com.caucho.vfs.WriteStream;
53 import com.caucho.xml.XmlChar;
54
55 import javax.servlet.ServletOutputStream JavaDoc;
56 import javax.servlet.ServletResponse JavaDoc;
57 import javax.servlet.http.Cookie JavaDoc;
58 import javax.servlet.http.HttpServletResponse JavaDoc;
59 import javax.servlet.http.HttpSession JavaDoc;
60 import java.io.IOException JavaDoc;
61 import java.io.OutputStream JavaDoc;
62 import java.io.PrintWriter JavaDoc;
63 import java.io.Writer JavaDoc;
64 import java.util.ArrayList JavaDoc;
65 import java.util.HashMap JavaDoc;
66 import java.util.Locale JavaDoc;
67 import java.util.logging.Level JavaDoc;
68 import java.util.logging.Logger JavaDoc;
69
70 /**
71  * Encapsulates the servlet response, controlling response headers and the
72  * response stream.
73  */

74 abstract public class AbstractHttpResponse implements CauchoResponse {
75   static final protected Logger JavaDoc log = Log.open(AbstractHttpResponse.class);
76   static final L10N L = new L10N(AbstractHttpResponse.class);
77
78   static final HashMap JavaDoc<String JavaDoc,String JavaDoc> _errors;
79
80   protected static final CaseInsensitiveIntMap _headerCodes;
81   protected static final int HEADER_CACHE_CONTROL = 1;
82   protected static final int HEADER_CONTENT_TYPE = HEADER_CACHE_CONTROL + 1;
83   protected static final int HEADER_CONTENT_LENGTH = HEADER_CONTENT_TYPE + 1;
84   protected static final int HEADER_DATE = HEADER_CONTENT_LENGTH + 1;
85   protected static final int HEADER_SERVER = HEADER_DATE + 1;
86
87   protected CauchoRequest _originalRequest;
88   protected CauchoRequest _request;
89
90   protected int _statusCode;
91   protected String JavaDoc _statusMessage;
92
93   protected String JavaDoc _contentType;
94   protected String JavaDoc _contentPrefix;
95   protected String JavaDoc _charEncoding;
96   protected boolean _hasCharEncoding;
97   
98   protected final ArrayList JavaDoc<String JavaDoc> _headerKeys = new ArrayList JavaDoc<String JavaDoc>();
99   protected final ArrayList JavaDoc<String JavaDoc> _headerValues = new ArrayList JavaDoc<String JavaDoc>();
100
101   protected final ArrayList JavaDoc<Cookie JavaDoc> _cookiesOut = new ArrayList JavaDoc<Cookie JavaDoc>();
102
103   private final AbstractResponseStream _originalResponseStream;
104   
105   private final ServletOutputStreamImpl _responseOutputStream;
106   private final ResponseWriter _responsePrintWriter;
107
108   private AbstractResponseStream _responseStream;
109
110   // the raw output stream.
111
protected WriteStream _rawWrite;
112
113   // any stream that needs flusing before getting the writer.
114
private FlushBuffer _flushBuffer;
115
116   private boolean _isHeaderWritten;
117   private boolean _isChunked;
118   protected final QDate _calendar = new QDate(false);
119
120   protected final CharBuffer _cb = new CharBuffer();
121   protected final char [] _headerBuffer = new char[256];
122
123   private String JavaDoc _sessionId;
124   private boolean _hasSessionCookie;
125
126   private Locale JavaDoc _locale;
127   protected boolean _disableHeaders;
128   protected boolean _disableCaching;
129   protected long _contentLength;
130   protected boolean _isClosed;
131   protected boolean _hasSentLog;
132   
133   protected boolean _hasWriter;
134   protected boolean _hasOutputStream;
135
136   private AbstractCacheFilterChain _cacheInvocation;
137   private AbstractCacheEntry _cacheEntry;
138   private OutputStream JavaDoc _cacheStream;
139   private Writer JavaDoc _cacheWriter;
140
141   protected boolean _isNoCache;
142   private boolean _allowCache;
143   private boolean _isPrivateCache;
144   private boolean _hasCacheControl;
145
146   protected boolean _isTopCache;
147
148   protected boolean _forbidForward;
149   protected boolean _hasError;
150
151   private final TempBuffer _tempBuffer = TempBuffer.allocate();
152
153   protected AbstractHttpResponse()
154   {
155     _originalResponseStream = createResponseStream();
156     
157     _responseOutputStream = new ServletOutputStreamImpl();
158     _responsePrintWriter = new ResponseWriter();
159
160     _responseOutputStream.init(_originalResponseStream);
161     _responsePrintWriter.init(_originalResponseStream);
162   }
163
164   protected AbstractResponseStream createResponseStream()
165   {
166     return new ResponseStream(this);
167   }
168
169   protected AbstractHttpResponse(CauchoRequest request)
170   {
171     this();
172     
173     _request = request;
174     _originalRequest = request;
175     
176     _responseOutputStream.init(_originalResponseStream);
177     _responsePrintWriter.init(_originalResponseStream);
178   }
179
180   /**
181    * If set true, client disconnect exceptions are no propagated to the
182    * server code.
183    */

184   public boolean isIgnoreClientDisconnect()
185   {
186     if (! (_originalRequest instanceof AbstractHttpRequest))
187       return true;
188     else {
189       return ((AbstractHttpRequest) _originalRequest).isIgnoreClientDisconnect();
190     }
191   }
192
193   /**
194    * Return true for the top request.
195    */

196   public boolean isTop()
197   {
198     if (! (_request instanceof AbstractHttpRequest))
199       return false;
200     else {
201       return ((AbstractHttpRequest) _request).isTop();
202     }
203   }
204
205   /**
206    * Returns the next response.
207    */

208   public ServletResponse JavaDoc getResponse()
209   {
210     return null;
211   }
212
213   /**
214    * Initialize the response for a new request.
215    *
216    * @param stream the underlying output stream.
217    */

218   public void init(WriteStream stream)
219   {
220     _rawWrite = stream;
221     if (_originalResponseStream instanceof ResponseStream)
222       ((ResponseStream) _originalResponseStream).init(_rawWrite);
223   }
224
225   /**
226    * Initialize the response for a new request.
227    *
228    * @param request the matching request.
229    */

230   public void init(CauchoRequest request)
231   {
232     _request = request;
233     _originalRequest = request;
234   }
235
236   /**
237    * Returns the corresponding request.
238    */

239   public CauchoRequest getRequest()
240   {
241     return _request;
242   }
243
244   /**
245    * Sets the corresponding request.
246    */

247   public void setRequest(CauchoRequest req)
248   {
249     _request = req;
250   }
251
252   /**
253    * Returns the corresponding original
254    */

255   public CauchoRequest getOriginalRequest()
256   {
257     return _originalRequest;
258   }
259
260   /**
261    * Closes the request.
262    */

263   public void close()
264     throws IOException JavaDoc
265   {
266     finish(true);
267     // getStream().flush();
268
}
269
270   /**
271    * Returns true for closed requests.
272    */

273   public boolean isClosed()
274   {
275     return _isClosed;
276   }
277
278   /**
279    * Initializes the Response at the beginning of the request.
280    */

281   public void start()
282     throws IOException JavaDoc
283   {
284     _statusCode = 200;
285     _statusMessage = "OK";
286
287     _headerKeys.clear();
288     _headerValues.clear();
289
290     _hasSessionCookie = false;
291     _cookiesOut.clear();
292
293     _isHeaderWritten = false;
294     _isChunked = false;
295     _charEncoding = null;
296     _hasCharEncoding = false;
297     _contentType = null;
298     _contentPrefix = null;
299     _locale = null;
300     if (_originalResponseStream instanceof ResponseStream)
301       ((ResponseStream) _originalResponseStream).init(_rawWrite);
302     _flushBuffer = null;
303
304     _contentLength = -1;
305     _disableHeaders = false;
306     _disableCaching = false;
307     _isClosed = false;
308     _hasSentLog = false;
309
310     _hasWriter = false;
311     _hasOutputStream = false;
312
313     _cacheInvocation = null;
314     _cacheEntry = null;
315     _cacheStream = null;
316     _cacheWriter = null;
317     _isPrivateCache = false;
318     _hasCacheControl = false;
319     _allowCache = true;
320     _isNoCache = false;
321     _isTopCache = false;
322
323     _sessionId = null;
324
325     _forbidForward = false;
326
327     _originalResponseStream.start();
328     
329     _responseStream = _originalResponseStream;
330
331     _responseOutputStream.init(_responseStream);
332     _responsePrintWriter.init(_responseStream);
333   }
334
335   /**
336    * For a HEAD request, the response stream should write no data.
337    */

338   void setHead()
339   {
340     _originalResponseStream.setHead();
341   }
342
343   /**
344    * For a HEAD request, the response stream should write no data.
345    */

346   protected final boolean isHead()
347   {
348     return _originalResponseStream.isHead();
349   }
350
351   /**
352    * When set to true, RequestDispatcher.forward() is disallowed on
353    * this stream.
354    */

355   public void setForbidForward(boolean forbid)
356   {
357     _forbidForward = forbid;
358   }
359
360   /**
361    * Returns true if RequestDispatcher.forward() is disallowed on
362    * this stream.
363    */

364   public boolean getForbidForward()
365   {
366     return _forbidForward;
367   }
368
369   /**
370    * Set to true while processing an error.
371    */

372   public void setHasError(boolean hasError)
373   {
374     _hasError = hasError;
375   }
376
377   /**
378    * Returns true if we're processing an error.
379    */

380   public boolean hasError()
381   {
382     return _hasError;
383   }
384
385   /**
386    * Sets the cache entry so we can use it if the servlet returns
387    * not_modified response.
388    *
389    * @param entry the saved cache entry
390    */

391   public void setCacheEntry(AbstractCacheEntry entry)
392   {
393     _cacheEntry = entry;
394   }
395
396   /**
397    * Sets the cache invocation to indicate that the response might be
398    * cacheable.
399    */

400   public void setCacheInvocation(AbstractCacheFilterChain cacheInvocation)
401   {
402     _cacheInvocation = cacheInvocation;
403   }
404
405   public void setTopCache(boolean isTopCache)
406   {
407     _isTopCache = isTopCache;
408   }
409
410   public void setStatus(int code)
411   {
412     setStatus(code, null);
413   }
414
415   public void setStatus(int code, String JavaDoc message)
416   {
417     if (code < 0)
418       code = 500;
419
420     if (message != null) {
421     }
422     else if (code == SC_OK)
423       message = "OK";
424     
425     else if (code == SC_NOT_MODIFIED)
426       message = "Not Modified";
427     
428     else if (message == null) {
429       message = (String JavaDoc) _errors.get(String.valueOf(code));
430
431       if (message == null)
432         message = L.l("Internal Server Error");
433     }
434
435     _statusCode = code;
436     _statusMessage = message;
437   }
438
439   public int getStatusCode()
440   {
441     return _statusCode;
442   }
443
444   public void sendError(int code)
445     throws IOException JavaDoc
446   {
447     sendError(code, null);
448   }
449
450   /**
451    * Sends an HTTP error to the browser.
452    *
453    * @param code the HTTP error code
454    * @param value a string message
455    */

456   public void sendError(int code, String JavaDoc value)
457     throws IOException JavaDoc
458   {
459     if (isCommitted())
460       throw new IllegalStateException JavaDoc(L.l("sendError() forbidden after buffer has been committed."));
461       
462     if (code == SC_NOT_MODIFIED && _cacheEntry != null) {
463       setStatus(code, value);
464       if (handleNotModified(_isTopCache))
465         return;
466     }
467
468     //_currentWriter = null;
469
//setStream(_originalStream);
470
resetBuffer();
471
472     if (code != SC_NOT_MODIFIED)
473       killCache();
474
475     /* XXX: if we've already got an error, won't this just mask it?
476     if (responseStream.isCommitted())
477       throw new IllegalStateException("response can't sendError() after commit");
478     */

479
480     WebApp app = getRequest().getWebApp();
481
482     ErrorPageManager errorManager = null;
483     if (app != null)
484       errorManager = app.getErrorPageManager();
485
486     setStatus(code, value);
487     try {
488       if (code == SC_NOT_MODIFIED || code == SC_NO_CONTENT) {
489         finish();
490         return;
491       }
492       else if (errorManager != null) {
493         errorManager.sendError(getRequest(), this, code, _statusMessage);
494         // _request.killKeepalive();
495
// close, but don't force a flush
496
// XXX: finish(false);
497
finish();
498         return;
499       }
500
501       setContentType("text/html");
502       ServletOutputStream JavaDoc s = getOutputStream();
503
504       s.println("<html>");
505       if (! isCommitted()) {
506         s.print("<head><title>");
507         s.print(code);
508         s.print(" ");
509         s.print(_statusMessage);
510         s.println("</title></head>");
511       }
512       s.println("<body>");
513
514       s.print("<h1>");
515       s.print(code);
516       s.print(" ");
517       s.print(_statusMessage);
518       s.println("</h1>");
519
520       if (code == HttpServletResponse.SC_NOT_FOUND) {
521         s.println(L.l("{0} was not found on this server.",
522                       HTTPUtil.encodeString(getRequest().getPageURI())));
523       }
524       else if (code == HttpServletResponse.SC_SERVICE_UNAVAILABLE) {
525         s.println(L.l("The server is temporarily unavailable due to maintenance downtime or excessive load."));
526       }
527
528       if (CauchoSystem.isTesting() || app == null) {
529       }
530       else {
531     s.println("<p /><hr />");
532     s.println("<small>");
533     
534     if (app.getServer() != null
535         && app.getServer().getServerHeader() != null) {
536       s.println(app.getServer().getServerHeader());
537     }
538     else
539       s.println(com.caucho.Version.FULL_VERSION);
540       
541     s.println("</small>");
542       }
543       
544       s.println("</body></html>");
545     } catch (Exception JavaDoc e) {
546       log.log(Level.FINE, e.toString(), e);
547     }
548
549     _request.killKeepalive();
550     // close, but don't force a flush
551
finish();
552   }
553
554   /**
555    * Sends a redirect to the browser. If the URL is relative, it gets
556    * combined with the current url.
557    *
558    * @param url the possibly relative url to send to the browser
559    */

560   public void sendRedirect(String JavaDoc url)
561     throws IOException JavaDoc
562   {
563     if (url == null)
564       throw new NullPointerException JavaDoc();
565     
566     if (_originalResponseStream.isCommitted())
567       throw new IllegalStateException JavaDoc(L.l("Can't sendRedirect() after data has committed to the client."));
568
569     _responseStream.clearBuffer();
570     _originalResponseStream.clearBuffer();
571     
572     _responseStream = _originalResponseStream;
573     resetBuffer();
574     
575     setStatus(SC_MOVED_TEMPORARILY);
576     String JavaDoc path = getAbsolutePath(url);
577
578     CharBuffer cb = new CharBuffer();
579
580     for (int i = 0; i < path.length(); i++) {
581       char ch = path.charAt(i);
582
583       if (ch == '<')
584     cb.append("%3c");
585       else
586     cb.append(ch);
587     }
588
589     path = cb.toString();
590     
591     setHeader("Location", path);
592     
593     // The data is required for some WAP devices that can't handle an
594
// empty response.
595
ServletOutputStream JavaDoc out = getOutputStream();
596     out.println("The URL has moved <a HREF=\"" + path + "\">here</a>");
597     // closeConnection();
598

599     if (_request instanceof AbstractHttpRequest) {
600       AbstractHttpRequest request = (AbstractHttpRequest) _request;
601
602       request.saveSession(); // #503
603
}
604
605     close();
606   }
607
608   /**
609    * Switch to raw socket mode.
610    */

611   public void switchToRaw()
612     throws IOException JavaDoc
613   {
614     throw new UnsupportedOperationException JavaDoc(L.l("raw mode is not supported in this configuration"));
615   }
616
617   /**
618    * Switch to raw socket mode.
619    */

620   public WriteStream getRawOutput()
621     throws IOException JavaDoc
622   {
623     throw new UnsupportedOperationException JavaDoc(L.l("raw mode is not supported in this configuration"));
624   }
625
626   /**
627    * Returns the absolute path for a given relative path.
628    *
629    * @param path the possibly relative url to send to the browser
630    */

631   private String JavaDoc getAbsolutePath(String JavaDoc path)
632   {
633     int slash = path.indexOf('/');
634     
635     int len = path.length();
636
637     for (int i = 0; i < len; i++) {
638       char ch = path.charAt(i);
639
640       if (ch == ':')
641         return path;
642       else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
643         continue;
644       else
645         break;
646     }
647
648     WebApp app = getRequest().getWebApp();
649
650     String JavaDoc hostPrefix = null;
651     String JavaDoc host = _request.getHeader("Host");
652     String JavaDoc serverName = app.getHostName();
653
654     if (serverName == null || serverName.equals(""))
655       serverName = _request.getServerName();
656
657     int port = _request.getServerPort();
658
659     if (hostPrefix != null && ! hostPrefix.equals("")) {
660     }
661     else if (serverName.startsWith("http:") ||
662              serverName.startsWith("https:"))
663       hostPrefix = serverName;
664     else if (host != null) {
665       hostPrefix = _request.getScheme() + "://" + host;
666     }
667     else {
668       hostPrefix = _request.getScheme() + "://" + serverName;
669       
670       if (serverName.indexOf(':') < 0 &&
671       port != 0 && port != 80 && port != 443)
672         hostPrefix += ":" + port;
673     }
674
675     if (slash == 0)
676       return hostPrefix + path;
677
678     String JavaDoc uri = _request.getRequestURI();
679     String JavaDoc queryString = null;
680
681     int p = path.indexOf('?');
682     if (p > 0) {
683       queryString = path.substring(p + 1);
684       path = path.substring(0, p);
685     }
686     
687     p = uri.lastIndexOf('/');
688
689     if (p >= 0)
690       path = uri.substring(0, p + 1) + path;
691
692     try {
693       if (queryString != null)
694         return hostPrefix + InvocationDecoder.normalizeUri(path) + '?' + queryString;
695       else
696         return hostPrefix + InvocationDecoder.normalizeUri(path);
697     } catch (IOException JavaDoc e) {
698       throw new RuntimeException JavaDoc(e);
699     }
700   }
701
702   /**
703    * Returns true if the response already contains the named header.
704    *
705    * @param name name of the header to test.
706    */

707   public boolean containsHeader(String JavaDoc name)
708   {
709     for (int i = 0; i < _headerKeys.size(); i++) {
710       String JavaDoc oldKey = _headerKeys.get(i);
711  
712       if (oldKey.equalsIgnoreCase(name))
713     return true;
714     }
715
716     if (name.equalsIgnoreCase("content-type"))
717       return _contentType != null;
718     
719     if (name.equalsIgnoreCase("content-length"))
720       return _contentLength >= 0;
721  
722     return false;
723   }
724
725   /**
726    * Returns the value of an already set output header.
727    *
728    * @param name name of the header to get.
729    */

730   public String JavaDoc getHeader(String JavaDoc name)
731   {
732     for (int i = 0; i < _headerKeys.size(); i++) {
733       String JavaDoc oldKey = (String JavaDoc) _headerKeys.get(i);
734  
735       if (oldKey.equalsIgnoreCase(name))
736     return (String JavaDoc) _headerValues.get(i);
737     }
738
739     if (name.equalsIgnoreCase("content-type"))
740       return _contentType;
741  
742     if (name.equalsIgnoreCase("content-length"))
743       return _contentLength >= 0 ? String.valueOf(_contentLength) : null;
744  
745     return null;
746   }
747
748   /**
749    * Sets a header, replacing an already-existing header.
750    *
751    * @param key the header key to set.
752    * @param value the header value to set.
753    */

754   public void setHeader(String JavaDoc key, String JavaDoc value)
755   {
756     if (_disableHeaders)
757       return;
758     else if (value == null)
759       throw new NullPointerException JavaDoc();
760
761     if (setSpecial(key, value))
762       return;
763
764     int i = 0;
765     boolean hasHeader = false;
766
767     for (i = _headerKeys.size() - 1; i >= 0; i--) {
768       String JavaDoc oldKey = _headerKeys.get(i);
769
770       if (oldKey.equalsIgnoreCase(key)) {
771     if (hasHeader) {
772       _headerKeys.remove(i);
773       _headerValues.remove(i);
774     }
775     else {
776       hasHeader = true;
777
778       _headerValues.set(i, value);
779     }
780       }
781     }
782
783     if (! hasHeader) {
784       _headerKeys.add(key);
785       _headerValues.add(value);
786     }
787   }
788
789   /**
790    * Adds a new header. If an old header with that name exists,
791    * both headers are output.
792    *
793    * @param key the header key.
794    * @param value the header value.
795    */

796   public void addHeader(String JavaDoc key, String JavaDoc value)
797   {
798     if (_disableHeaders)
799       return;
800
801     if (setSpecial(key, value))
802       return;
803
804     _headerKeys.add(key);
805     _headerValues.add(value);
806   }
807
808   /**
809    * Special processing for a special value.
810    */

811   protected boolean setSpecial(String JavaDoc key, String JavaDoc value)
812   {
813     int length = key.length();
814     if (256 <= length)
815       return false;
816     
817     key.getChars(0, length, _headerBuffer, 0);
818
819     switch (_headerCodes.get(_headerBuffer, length)) {
820     case HEADER_CACHE_CONTROL:
821       if (value.startsWith("max-age")) {
822       }
823       else if (value.equals("x-anonymous")) {
824       }
825       else
826     _hasCacheControl = true;
827       return false;
828     
829     case HEADER_CONTENT_TYPE:
830       setContentType(value);
831       return true;
832     
833     case HEADER_CONTENT_LENGTH:
834       _contentLength = Long.parseLong(value);
835       return true;
836     
837     case HEADER_DATE:
838       return true;
839     
840     case HEADER_SERVER:
841       return false;
842
843     default:
844       return false;
845     }
846   }
847   
848   public void removeHeader(String JavaDoc key)
849   {
850     if (_disableHeaders)
851       return;
852     
853     for (int i = _headerKeys.size() - 1; i >= 0; i--) {
854       String JavaDoc oldKey = (String JavaDoc) _headerKeys.get(i);
855
856       if (oldKey.equalsIgnoreCase(key)) {
857         _headerKeys.remove(i);
858         _headerValues.remove(i);
859         return;
860       }
861     }
862   }
863
864   /**
865    * Convenience for setting an integer header. An old header with the
866    * same name will be replaced.
867    *
868    * @param name the header name.
869    * @param value an integer to be converted to a string for the header.
870    */

871   public void setIntHeader(String JavaDoc name, int value)
872   {
873     _cb.clear();
874     _cb.append(value);
875     setHeader(name, _cb.toString());
876   }
877
878   /**
879    * Convenience for adding an integer header. If an old header already
880    * exists, both will be sent to the browser.
881    *
882    * @param key the header name.
883    * @param value an integer to be converted to a string for the header.
884    */

885   public void addIntHeader(String JavaDoc key, int value)
886   {
887     _cb.clear();
888     _cb.append(value);
889     addHeader(key, _cb.toString());
890   }
891
892   /**
893    * Convenience for setting a date header. An old header with the
894    * same name will be replaced.
895    *
896    * @param name the header name.
897    * @param value an time in milliseconds to be converted to a date string.
898    */

899   public void setDateHeader(String JavaDoc name, long value)
900   {
901     _calendar.setGMTTime(value);
902
903     setHeader(name, _calendar.printDate());
904   }
905
906
907   /**
908    * Convenience for adding a date header. If an old header with the
909    * same name exists, both will be displayed.
910    *
911    * @param key the header name.
912    * @param value an time in milliseconds to be converted to a date string.
913    */

914   public void addDateHeader(String JavaDoc key, long value)
915   {
916     _calendar.setGMTTime(value);
917
918     addHeader(key, _calendar.printDate());
919   }
920
921   /**
922    * Sets the content length of the result. In general, Resin will handle
923    * the content length, but for things like long downloads adding the
924    * length will give a valuable hint to the browser.
925    *
926    * @param length the length of the content.
927    */

928   public void setContentLength(int length)
929   {
930     _contentLength = length;
931   }
932
933   /**
934    * Returns the value of the content-length header.
935    */

936   public long getContentLengthHeader()
937   {
938     return _contentLength;
939   }
940
941   /**
942    * Sets the browser content type. If the value contains a charset,
943    * the output encoding will be changed to match.
944    *
945    * <p>For example, to set the output encoding to use UTF-8 instead of
946    * the default ISO-8859-1 (Latin-1), use the following:
947    * <code><pre>
948    * setContentType("text/html; charset=UTF-8");
949    * </pre></code>
950    */

951   public void setContentType(String JavaDoc value)
952   {
953     if (isCommitted())
954       return;
955     if (_disableHeaders || value == null) {
956       _contentType = null;
957       return;
958     }
959     else if (value == "text/html" || value.equals("text/html")) {
960       _contentType = "text/html";
961       return;
962     }
963     
964     _contentType = value;
965     
966     int length = value.length();
967     int i;
968     int ch;
969
970     for (i = 0;
971      i < length && value.charAt(i) != ';' &&
972        ! Character.isWhitespace(value.charAt(i));
973      i++) {
974     }
975
976     if (i < length)
977       _contentPrefix = _contentType.substring(0, i);
978     else
979       _contentPrefix = _contentType;
980
981     while ((i = value.indexOf(';', i)) > 0) {
982       int semicolon = i;
983       for (i++; i < length && XmlChar.isWhitespace(value.charAt(i)); i++) {
984       }
985
986       int j;
987       for (j = i + 1;
988            j < length && ! XmlChar.isWhitespace((ch = value.charAt(j))) &&
989              ch != '=';
990            j++) {
991       }
992       
993       if (length <= j)
994     break;
995       else if ((ch = value.charAt(i)) != 'c' && ch != 'C') {
996       }
997       else if (value.substring(i, j).equalsIgnoreCase("charset")) {
998     for (; j < length && XmlChar.isWhitespace(value.charAt(j)); j++) {
999     }
1000
1001        if (length <= j || value.charAt(j) != '=')
1002          continue;
1003        
1004    for (j++; j < length && XmlChar.isWhitespace(value.charAt(j)); j++) {
1005    }
1006
1007        String JavaDoc encoding;
1008
1009        if (j < length && value.charAt(j) == '"') {
1010          int k = ++j;
1011          
1012          for (; j < length && value.charAt(j) != '"'; j++) {
1013          }
1014
1015          encoding = value.substring(k, j);
1016        }
1017        else {
1018          int k = j;
1019          for (k = j;
1020               j < length && ! XmlChar.isWhitespace(ch = value.charAt(j)) && ch != ';';
1021               j++) {
1022          }
1023
1024          encoding = value.substring(k, j);
1025        }
1026
1027    int tail = value.indexOf(';', semicolon + 1);
1028
1029    StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
1030    sb.append(value, 0, semicolon);
1031    if (tail > 0)
1032      sb.append(value, tail, value.length());
1033    
1034    _contentType = sb.toString();
1035
1036    if (! _hasWriter) {
1037      _hasCharEncoding = true;
1038      _charEncoding = encoding;
1039    }
1040    break;
1041      }
1042      else
1043    i = j;
1044    }
1045
1046    // XXX: conflict with servlet exception throwing order?
1047
try {
1048      _responseStream.setEncoding(_charEncoding);
1049    } catch (Exception JavaDoc e) {
1050      log.log(Level.WARNING, e.toString(), e);
1051    }
1052  }
1053
1054  /**
1055   * Gets the content type.
1056   */

1057  public String JavaDoc getContentType()
1058  {
1059    if (_contentType == null)
1060      return null;
1061    else
1062      return _contentType + "; charset=" + getCharacterEncoding();
1063  }
1064
1065  /**
1066   * Gets the character encoding.
1067   */

1068  public String JavaDoc getCharacterEncoding()
1069  {
1070    return _charEncoding == null ? "ISO-8859-1" : _charEncoding;
1071  }
1072
1073  /**
1074   * Sets the character encoding.
1075   */

1076  public void setCharacterEncoding(String JavaDoc encoding)
1077  {
1078    if (isCommitted())
1079      return;
1080    if (_hasWriter)
1081      return;
1082
1083    _hasCharEncoding = true;
1084    
1085    if (encoding == null ||
1086    encoding.equals("ISO-8859-1") ||
1087    encoding.equals("")) {
1088      encoding = null;
1089      _charEncoding = "iso-8859-1";
1090    }
1091    else
1092      _charEncoding = encoding;
1093
1094    try {
1095      _responseStream.setEncoding(encoding);
1096    } catch (Exception JavaDoc e) {
1097      log.log(Level.WARNING, e.toString(), e);
1098    }
1099  }
1100
1101  String JavaDoc getRealCharacterEncoding()
1102  {
1103    return _charEncoding;
1104  }
1105
1106  /**
1107   * Adds a cookie to the response.
1108   *
1109   * @param cookie the response cookie
1110   */

1111  public void addCookie(Cookie JavaDoc cookie)
1112  {
1113    _request.setHasCookie();
1114
1115    if (_disableHeaders)
1116      return;
1117
1118    if (cookie == null)
1119      return;
1120
1121    _cookiesOut.add(cookie);
1122  }
1123
1124  public Cookie JavaDoc getCookie(String JavaDoc name)
1125  {
1126    if (_cookiesOut == null)
1127      return null;
1128
1129    for (int i = _cookiesOut.size() - 1; i >= 0; i--) {
1130      Cookie JavaDoc cookie = (Cookie JavaDoc) _cookiesOut.get(i);
1131
1132      if (cookie.getName().equals(name))
1133        return cookie;
1134    }
1135
1136    return null;
1137  }
1138
1139  public ArrayList JavaDoc getCookies()
1140  {
1141    return _cookiesOut;
1142  }
1143
1144  public void setSessionId(String JavaDoc id)
1145  {
1146    _sessionId = id;
1147
1148    // XXX: server/1315 vs server/0506 vs server/170k
1149
// could also set the nocache=JSESSIONID
1150
setPrivateOrResinCache(true);
1151  }
1152
1153  /**
1154   * Sets the ResponseStream
1155   */

1156  public void setResponseStream(AbstractResponseStream responseStream)
1157  {
1158    _responseStream = responseStream;
1159
1160    _responseOutputStream.init(responseStream);
1161    _responsePrintWriter.init(responseStream);
1162  }
1163
1164  /**
1165   * Gets the response stream.
1166   */

1167  public AbstractResponseStream getResponseStream()
1168  {
1169    return _responseStream;
1170  }
1171
1172  /**
1173   * Gets the response stream.
1174   */

1175  public AbstractResponseStream getOriginalStream()
1176  {
1177    return _originalResponseStream;
1178  }
1179
1180  /**
1181   * Returns true for a Caucho response stream.
1182   */

1183  public boolean isCauchoResponseStream()
1184  {
1185    return _responseStream.isCauchoResponseStream();
1186  }
1187  
1188  /**
1189   * Returns the ServletOutputStream for the response.
1190   */

1191  public ServletOutputStream JavaDoc getOutputStream() throws IOException JavaDoc
1192  {
1193    /*
1194    if (_hasWriter)
1195      throw new IllegalStateException(L.l("getOutputStream() can't be called after getWriter()."));
1196    */

1197
1198    _hasOutputStream = true;
1199    
1200    return _responseOutputStream;
1201  }
1202
1203  /**
1204   * Sets the flush buffer
1205   */

1206  public void setFlushBuffer(FlushBuffer flushBuffer)
1207  {
1208    _flushBuffer = flushBuffer;
1209  }
1210
1211  /**
1212   * Gets the flush buffer
1213   */

1214  public FlushBuffer getFlushBuffer()
1215  {
1216    return _flushBuffer;
1217  }
1218
1219  /**
1220   * Returns a PrintWriter for the response.
1221   */

1222  public PrintWriter JavaDoc getWriter() throws IOException JavaDoc
1223  {
1224    /*
1225    if (_hasOutputStream)
1226      throw new IllegalStateException(L.l("getWriter() can't be called after getOutputStream()."));
1227    */

1228
1229    if (! _hasWriter) {
1230      _hasWriter = true;
1231
1232      if (_charEncoding != null)
1233    _responseStream.setEncoding(_charEncoding);
1234    }
1235    
1236    return _responsePrintWriter;
1237  }
1238
1239  /**
1240   * Returns the parent writer.
1241   */

1242  public PrintWriter JavaDoc getNextWriter()
1243  {
1244    return null;
1245  }
1246
1247  /**
1248   * Encode the URL with the session jd.
1249   *
1250   * @param string the url to be encoded
1251   *
1252   * @return the encoded url
1253   */

1254  public String JavaDoc encodeURL(String JavaDoc string)
1255  {
1256    CauchoRequest request = getRequest();
1257    
1258    WebApp app = request.getWebApp();
1259
1260    if (app == null)
1261      return string;
1262    
1263    if (request.isRequestedSessionIdFromCookie())
1264      return string;
1265
1266    HttpSession JavaDoc session = request.getSession(false);
1267    if (session == null)
1268      return string;
1269
1270    SessionManager sessionManager = app.getSessionManager();
1271    if (! sessionManager.enableSessionUrls())
1272      return string;
1273
1274    CharBuffer cb = _cb;
1275    cb.clear();
1276
1277    String JavaDoc altPrefix = sessionManager.getAlternateSessionPrefix();
1278
1279    if (altPrefix == null) {
1280      // standard url rewriting
1281
int p = string.indexOf('?');
1282
1283      if (p == 0) {
1284    cb.append(string);
1285      }
1286      else if (p > 0) {
1287        cb.append(string, 0, p);
1288        cb.append(sessionManager.getSessionPrefix());
1289        cb.append(session.getId());
1290        cb.append(string, p, string.length() - p);
1291      }
1292      else if ((p = string.indexOf('#')) >= 0) {
1293        cb.append(string, 0, p);
1294        cb.append(sessionManager.getSessionPrefix());
1295        cb.append(session.getId());
1296        cb.append(string, p, string.length() - p);
1297      }
1298      else {
1299        cb.append(string);
1300        cb.append(sessionManager.getSessionPrefix());
1301        cb.append(session.getId());
1302      }
1303    }
1304    else {
1305      int p = string.indexOf("://");
1306      
1307      if (p < 0) {
1308    cb.append(altPrefix);
1309    cb.append(session.getId());
1310    
1311    if (! string.startsWith("/")) {
1312      cb.append(_request.getContextPath());
1313      cb.append('/');
1314    }
1315        cb.append(string);
1316      }
1317      else {
1318    int q = string.indexOf('/', p + 3);
1319
1320    if (q < 0) {
1321      cb.append(string);
1322      cb.append(altPrefix);
1323      cb.append(session.getId());
1324    }
1325    else {
1326      cb.append(string.substring(0, q));
1327      cb.append(altPrefix);
1328      cb.append(session.getId());
1329      cb.append(string.substring(q));
1330    }
1331      }
1332    }
1333
1334    return cb.toString();
1335  }
1336
1337  public String JavaDoc encodeRedirectURL(String JavaDoc string)
1338  {
1339    return encodeURL(string);
1340  }
1341
1342    /**
1343     * @deprecated
1344     */

1345  public String JavaDoc encodeRedirectUrl(String JavaDoc string)
1346  {
1347    return encodeRedirectURL(string);
1348  }
1349
1350    /**
1351     * @deprecated
1352     */

1353  public String JavaDoc encodeUrl(String JavaDoc string)
1354  {
1355    return encodeURL(string);
1356  }
1357
1358  /*
1359   * jsdk 2.2
1360   */

1361
1362  public void setBufferSize(int size)
1363  {
1364    _responseStream.setBufferSize(size);
1365  }
1366
1367  public int getBufferSize()
1368  {
1369    return _responseStream.getBufferSize();
1370  }
1371
1372  public void flushBuffer()
1373    throws IOException JavaDoc
1374  {
1375    // server/10sn
1376
_responseStream.flush();
1377  }
1378
1379  public void flushHeader()
1380    throws IOException JavaDoc
1381  {
1382    _responseStream.flushBuffer();
1383  }
1384
1385  public void setDisableAutoFlush(boolean disable)
1386  {
1387    // XXX: _responseStream.setDisableAutoFlush(disable);
1388
}
1389
1390  /**
1391   * Returns true if some data has been sent to the browser.
1392   */

1393  public boolean isCommitted()
1394  {
1395    return _originalResponseStream.isCommitted();
1396  }
1397
1398  public void reset()
1399  {
1400    reset(false);
1401  }
1402
1403  public void resetBuffer()
1404  {
1405    _responseStream.clearBuffer();
1406    /*
1407    if (_currentWriter instanceof JspPrintWriter)
1408      ((JspPrintWriter) _currentWriter).clear();
1409    */

1410  }
1411
1412  /**
1413   * Clears the response for a forward()
1414   *
1415   * @param force if not true and the response stream has committed,
1416   * throw the IllegalStateException.
1417   */

1418  void reset(boolean force)
1419  {
1420    if (! force && _originalResponseStream.isCommitted())
1421      throw new IllegalStateException JavaDoc(L.l("response cannot be reset() after committed"));
1422    
1423    _responseStream.clearBuffer();
1424    /*
1425    if (_currentWriter instanceof JspPrintWriter)
1426      ((JspPrintWriter) _currentWriter).clear();
1427    */

1428    _statusCode = 200;
1429    _statusMessage = "OK";
1430
1431    _headerKeys.clear();
1432    _headerValues.clear();
1433
1434    // cookiesOut.clear();
1435

1436    _contentLength = -1;
1437    //_isNoCache = false;
1438
//_isPrivateCache = false;
1439

1440    _charEncoding = null;
1441    _locale = null;
1442
1443    _hasOutputStream = false;
1444    _hasWriter = false;
1445    try {
1446      _responseStream.setLocale(null);
1447      _responseStream.setEncoding(null);
1448    } catch (Exception JavaDoc e) {
1449    }
1450  }
1451
1452  // XXX: hack to deal with forwarding
1453
public void clearBuffer()
1454  {
1455    _responseStream.clearBuffer();
1456  }
1457
1458  public void setLocale(Locale JavaDoc locale)
1459  {
1460    _locale = locale;
1461
1462    if (! _hasCharEncoding && ! isCommitted()) {
1463      _charEncoding = getRequest().getWebApp().getLocaleEncoding(locale);
1464
1465      try {
1466        if (_charEncoding != null) {
1467          // _originalStream.setEncoding(_charEncoding);
1468
_responseStream.setEncoding(_charEncoding);
1469    }
1470      } catch (IOException JavaDoc e) {
1471      }
1472    }
1473
1474    CharBuffer cb = _cb;
1475    cb.clear();
1476    cb.append(locale.getLanguage());
1477    if (locale.getCountry() != null && ! "".equals(locale.getCountry())) {
1478      cb.append("-");
1479      cb.append(locale.getCountry());
1480      if (locale.getVariant() != null && ! "".equals(locale.getVariant())) {
1481        cb.append("-");
1482        cb.append(locale.getVariant());
1483      }
1484    }
1485    
1486    setHeader("Content-Language", cb.toString());
1487  }
1488
1489  public Locale JavaDoc getLocale()
1490  {
1491    if (_locale != null)
1492      return _locale;
1493    else
1494      return Locale.getDefault();
1495  }
1496
1497  // needed to support JSP
1498

1499  public int getRemaining()
1500  {
1501    return _responseStream.getRemaining();
1502  }
1503
1504  /**
1505   * Returns the number of bytes sent to the output.
1506   */

1507  public int getContentLength()
1508  {
1509    return _originalResponseStream.getContentLength();
1510  }
1511
1512  public boolean disableHeaders(boolean disable)
1513  {
1514    boolean old = _disableHeaders;
1515    _disableHeaders = disable;
1516    return old;
1517  }
1518
1519  public boolean disableCaching(boolean disable)
1520  {
1521    boolean old = _disableCaching;
1522    _disableCaching = disable;
1523    return old;
1524  }
1525
1526  /**
1527   * Returns true if the headers have been written.
1528   */

1529  final public boolean isHeaderWritten()
1530  {
1531    return _isHeaderWritten;
1532  }
1533
1534  /**
1535   * Returns true if the headers have been written.
1536   */

1537  final public void setHeaderWritten(boolean isWritten)
1538  {
1539    _isHeaderWritten = isWritten;
1540  }
1541  
1542  /**
1543   * Writes the continue
1544   */

1545  final void writeContinue()
1546    throws IOException JavaDoc
1547  {
1548    if (! _isHeaderWritten) {
1549      writeContinueInt(_rawWrite);
1550      _rawWrite.flush();
1551    }
1552  }
1553  
1554  /**
1555   * Writes the continue
1556   */

1557  protected void writeContinueInt(WriteStream os)
1558    throws IOException JavaDoc
1559  {
1560  }
1561
1562  /**
1563   * Writes the headers to the stream. Called prior to the first flush
1564   * of data.
1565   *
1566   * @param os browser stream.
1567   * @param length length of the response if known, or -1 is unknown.
1568   * @return true if the content is chunked.
1569   */

1570  protected boolean writeHeaders(WriteStream os, int length)
1571    throws IOException JavaDoc
1572  {
1573    if (_isHeaderWritten)
1574      return _isChunked;
1575
1576    // server/1373 for getBufferSize()
1577
boolean canCache = startCaching(true);
1578    _isHeaderWritten = true;
1579    
1580    /*
1581    if (_statusCode == SC_OK && ! _disableCaching) // && getBufferSize() > 0)
1582      canCache = startCaching(_headerKeys, _headerValues,
1583                              _contentType, _charEncoding);
1584    else if (_statusCode != SC_OK && _statusCode != SC_NOT_MODIFIED &&
1585             _statusCode != SC_MOVED_TEMPORARILY && length < 512 &&
1586             (_contentType == null || _contentType.startsWith("text/html"))) {
1587    }
1588
1589    if (canCache) {
1590    }
1591    else if (_statusCode == SC_OK && _request.getMethod().equals("HEAD")) {
1592      // length = 0;
1593      _originalResponseStream.setHead();
1594    }
1595    */

1596    
1597    if (_request.getMethod().equals("HEAD")) {
1598      _originalResponseStream.setHead();
1599    }
1600
1601    SessionImpl session = (SessionImpl) _originalRequest.getSession(false);
1602    if (session != null)
1603      session.saveBeforeHeaders();
1604
1605    if (_sessionId != null && ! _hasSessionCookie) {
1606      _hasSessionCookie = true;
1607      
1608      SessionManager manager = _request.getWebApp().getSessionManager();
1609
1610      String JavaDoc cookieName;
1611
1612      if (_request.isSecure())
1613    cookieName = manager.getSSLCookieName();
1614      else
1615    cookieName = manager.getCookieName();
1616      
1617      CookieImpl cookie = new CookieImpl(cookieName, _sessionId);
1618      cookie.setVersion(manager.getCookieVersion());
1619      String JavaDoc domain = manager.getCookieDomain();
1620      if (domain != null)
1621        cookie.setDomain(domain);
1622      long maxAge = manager.getCookieMaxAge();
1623      if (maxAge > 0)
1624        cookie.setMaxAge((int) (maxAge / 1000));
1625      cookie.setPath("/");
1626      
1627      cookie.setPort(manager.getCookiePort());
1628      if (manager.getCookieSecure()) {
1629    cookie.setSecure(_request.isSecure());
1630    /*
1631    else if (manager.getCookiePort() == null)
1632      cookie.setPort(String.valueOf(_request.getServerPort()));
1633    */

1634      }
1635
1636      addCookie(cookie);
1637    }
1638
1639    _isChunked = writeHeadersInt(os, length);
1640
1641    return _isChunked;
1642  }
1643
1644  /**
1645   * Called to start caching.
1646   */

1647  protected boolean startCaching(boolean isByte)
1648  {
1649    if (_isHeaderWritten)
1650      return false;
1651    _isHeaderWritten = true;
1652
1653    if (_statusCode == SC_OK && ! _disableCaching) // && getBufferSize() > 0)
1654
return startCaching(_headerKeys, _headerValues,
1655              _contentType, _charEncoding, isByte);
1656    else
1657      return false;
1658  }
1659
1660  /**
1661   * Tests to see if the response is cacheable.
1662   *
1663   * @param keys the header keys of the response
1664   * @param values the header values of the response
1665   * @param contentType the contentType of the response
1666   * @param charEncoding the character encoding of the response
1667   *
1668   * @return true if caching has started
1669   */

1670  boolean startCaching(ArrayList JavaDoc<String JavaDoc> keys, ArrayList JavaDoc<String JavaDoc> values,
1671                       String JavaDoc contentType, String JavaDoc charEncoding,
1672               boolean isByte)
1673  {
1674    if (_cacheInvocation == null)
1675      return false;
1676    /*
1677      // jsp/17ah
1678    else if (_responseStream != _originalResponseStream) {
1679      return false;
1680    }
1681    */

1682    else if (! isCauchoResponseStream()) {
1683      return false;
1684    }
1685    else if (! (_originalRequest instanceof CauchoRequest)) {
1686      return false;
1687    }
1688    else if (! _allowCache) {
1689      return false;
1690    }
1691    else if (isByte) {
1692      CauchoRequest request = (CauchoRequest) _originalRequest;
1693      
1694      _cacheStream = _cacheInvocation.startByteCaching(request,
1695                               this, keys, values,
1696                               contentType,
1697                               charEncoding,
1698                               _contentLength);
1699
1700      if (_cacheStream != null)
1701        _originalResponseStream.setByteCacheStream(_cacheStream);
1702      
1703      return _cacheStream != null;
1704    }
1705    else {
1706      CauchoRequest request = (CauchoRequest) _originalRequest;
1707      
1708      _cacheWriter = _cacheInvocation.startCharCaching(request,
1709                               this, keys, values,
1710                               contentType,
1711                               charEncoding,
1712                               _contentLength);
1713
1714      if (_cacheWriter != null)
1715        _originalResponseStream.setCharCacheStream(_cacheWriter);
1716      
1717      return _cacheWriter != null;
1718    }
1719  }
1720  
1721
1722  /**
1723   * Handle a SC_NOT_MODIFIED response. If we've got a cache, fill the
1724   * results from the cache.
1725   *
1726   * @param isTop if true, this is the top-level request.
1727   *
1728   * @return true if we filled from the cache
1729   */

1730  private boolean handleNotModified(boolean isTop)
1731    throws IOException JavaDoc
1732  {
1733    if (_statusCode != SC_NOT_MODIFIED) {
1734      return false;
1735    }
1736    else if (_cacheEntry != null) {
1737      if (_originalResponseStream.isCommitted())
1738        return false;
1739
1740      // need to unclose because the not modified might be detected only
1741
// when flushing the data
1742
_originalResponseStream.clearClosed();
1743      _isClosed = false;
1744
1745      /* XXX: complications with filters */
1746      if (_cacheInvocation != null &&
1747          _cacheInvocation.fillFromCache((CauchoRequest) _originalRequest,
1748                                         this, _cacheEntry, isTop)) {
1749        _cacheEntry.updateExpiresDate();
1750        _cacheInvocation = null;
1751        _cacheEntry = null;
1752
1753        finish(); // Don't force a flush to avoid extra TCP packet
1754

1755        return true;
1756      }
1757    }
1758    // server/13dh
1759
else if (_cacheInvocation != null) {
1760      CauchoRequest req = (CauchoRequest) _originalRequest;
1761      WebApp app = req.getWebApp();
1762      
1763      long maxAge = app.getMaxAge(req.getRequestURI());
1764
1765      if (maxAge > 0 && ! containsHeader("Expires")) {
1766    setDateHeader("Expires", maxAge + Alarm.getCurrentTime());
1767      }
1768    }
1769
1770    return false;
1771  }
1772
1773  abstract protected boolean writeHeadersInt(WriteStream os, int length)
1774    throws IOException JavaDoc;
1775
1776  /**
1777   * Sets true if the cache is only for the browser, but not
1778   * Resin's cache or proxies.
1779   *
1780   * <p>Since proxy caching also caches headers, cached pages with
1781   * session ids can't be cached in the browser.
1782   *
1783   * XXX: but doesn't this just mean that Resin shouldn't
1784   * send the session information back if the page is cached?
1785   * Because a second request where everything is identical
1786   * would see the same response except for the cookies.
1787   */

1788  public void setPrivateCache(boolean isPrivate)
1789  {
1790    // XXX: let the webApp override this?
1791
_isPrivateCache = isPrivate;
1792
1793    // server/12dm
1794
_allowCache = false;
1795  }
1796
1797  /**
1798   * Sets true if the cache is only for the browser and
1799   * Resin's cache but not proxies.
1800   */

1801  public void setPrivateOrResinCache(boolean isPrivate)
1802  {
1803    // XXX: let the webApp override this?
1804

1805    _isPrivateCache = isPrivate;
1806  }
1807  
1808  /**
1809   * Returns the value of the private cache.
1810   */

1811  public boolean getPrivateCache()
1812  {
1813    return _isPrivateCache;
1814  }
1815
1816  /**
1817   * Returns true if the response should contain a Cache-Control: private
1818   */

1819  protected boolean isPrivateCache()
1820  {
1821    return ! _hasCacheControl && _isPrivateCache;
1822  }
1823
1824  /**
1825   * Set if the page is non-cacheable.
1826   */

1827  public void setNoCache(boolean isNoCache)
1828  {
1829    _isNoCache = isNoCache;
1830  }
1831
1832  /**
1833   * Returns true if the page is non-cacheable
1834   */

1835  public boolean isNoCache()
1836  {
1837    return _isNoCache;
1838  }
1839
1840  /**
1841   * Set if the page is non-cacheable.
1842   */

1843  public void killCache()
1844  {
1845    _allowCache = false;
1846
1847    // server/1b15
1848
// setNoCache(true);
1849
}
1850
1851  /**
1852   * Fills the response for a cookie
1853   *
1854   * @param cb result buffer to contain the generated string
1855   * @param cookie the cookie
1856   * @param date the current date
1857   * @param version the cookies version
1858   */

1859  public boolean fillCookie(CharBuffer cb, Cookie JavaDoc cookie,
1860                            long date, int version,
1861                boolean isCookie2)
1862  {
1863    // How to deal with quoted values? Old browsers can't deal with
1864
// the quotes.
1865

1866    cb.clear();
1867    cb.append(cookie.getName());
1868    if (isCookie2) {
1869      cb.append("=\"");
1870      cb.append(cookie.getValue());
1871      cb.append("\"");
1872    }
1873    else {
1874      cb.append("=");
1875      cb.append(cookie.getValue());
1876    }
1877
1878    String JavaDoc domain = cookie.getDomain();
1879    if (domain != null && ! domain.equals("")) {
1880      if (isCookie2) {
1881        cb.append("; Domain=");
1882      
1883        cb.append('"');
1884        cb.append(domain);
1885        cb.append('"');
1886      }
1887      else {
1888        cb.append("; domain=");
1889        cb.append(domain);
1890      }
1891    }
1892
1893    String JavaDoc path = cookie.getPath();
1894    if (path != null && ! path.equals("")) {
1895      if (isCookie2) {
1896        cb.append("; Path=");
1897      
1898        cb.append('"');
1899        cb.append(path);
1900        cb.append('"');
1901      }
1902      else {
1903    // Caps from TCK test
1904
if (version > 0)
1905      cb.append("; Path=");
1906    else
1907      cb.append("; path=");
1908        cb.append(path);
1909      }
1910    }
1911    
1912    if (cookie.getSecure()) {
1913      if (version > 0)
1914        cb.append("; Secure");
1915      else
1916        cb.append("; secure");
1917    }
1918
1919    int maxAge = cookie.getMaxAge();
1920    if (version > 0) {
1921      if (maxAge >= 0) {
1922        cb.append("; Max-Age=");
1923        cb.append(maxAge);
1924      }
1925      
1926      cb.append("; Version=");
1927      cb.append(version);
1928      
1929      if (cookie.getComment() != null) {
1930        cb.append("; Comment=\"");
1931        cb.append(cookie.getComment());
1932        cb.append("\"");
1933      }
1934
1935      if (cookie instanceof CookieImpl) {
1936    CookieImpl extCookie = (CookieImpl) cookie;
1937    String JavaDoc port = extCookie.getPort();
1938
1939    if (port != null && isCookie2) {
1940      cb.append("; Port=\"");
1941      cb.append(port);
1942      cb.append("\"");
1943    }
1944      }
1945    }
1946
1947    if (isCookie2) {
1948    }
1949    else if (maxAge == 0) {
1950      cb.append("; expires=Thu, 01-Dec-1994 16:00:00 GMT");
1951    }
1952    else if (maxAge >= 0) {
1953      _calendar.setGMTTime(date + 1000L * (long) maxAge);
1954      cb.append("; expires=");
1955      cb.append(_calendar.format("%a, %d-%b-%Y %H:%M:%S GMT"));
1956    }
1957
1958    WebApp app = _request.getWebApp();
1959    if (app.getCookieHttpOnly()) {
1960      cb.append("; HttpOnly");
1961    }
1962
1963    return true;
1964  }
1965
1966  /**
1967   * Complete the request. Flushes the streams, completes caching
1968   * and writes the appropriate logs.
1969   */

1970  public void finish() throws IOException JavaDoc
1971  {
1972    finish(false);
1973  }
1974
1975  /**
1976   * Complete the request. Flushes the streams, completes caching
1977   * and writes the appropriate logs.
1978   *
1979   * @param isClose true if the response should be flushed.
1980   */

1981  private void finish(boolean isClose) throws IOException JavaDoc
1982  {
1983    if (_isClosed)
1984      return;
1985
1986    try {
1987      if (_originalRequest instanceof AbstractHttpRequest) {
1988    AbstractHttpRequest request = (AbstractHttpRequest) _originalRequest;
1989
1990    try {
1991      request.skip();
1992    } catch (BadRequestException e) {
1993      log.warning(e.toString());
1994      log.log(Level.FINE, e.toString(), e);
1995    } catch (Exception JavaDoc e) {
1996      log.log(Level.WARNING, e.toString(), e);
1997    }
1998      }
1999
2000      if (_statusCode == SC_NOT_MODIFIED) {
2001    handleNotModified(_isTopCache);
2002      }
2003    
2004      // include() files finish too, but shouldn't force a flush, hence
2005
// flush is false
2006
// Never send flush?
2007
if (isClose)
2008    _responseStream.close();
2009      else
2010    _responseStream.finish();
2011    
2012      if (_responseStream != _originalResponseStream) {
2013    if (isClose)
2014      _originalResponseStream.close();
2015    else
2016      _originalResponseStream.finish();
2017      }
2018
2019      _isClosed = true;
2020
2021      if (_rawWrite == null) {
2022      }
2023      // server/0550
2024
// else if (isClose)
2025
// _rawWrite.close();
2026
else
2027    _rawWrite.flushBuffer();
2028
2029      if (_cacheInvocation == null) {
2030      }
2031      else if (_cacheStream != null || _cacheWriter != null) {
2032    _cacheStream = null;
2033    _cacheWriter = null;
2034    AbstractCacheFilterChain cache = _cacheInvocation;
2035    _cacheInvocation = null;
2036
2037    cache.finishCaching(_statusCode == 200 && _allowCache);
2038      }
2039    } catch (ClientDisconnectException e) {
2040      _request.killKeepalive();
2041
2042      if (isIgnoreClientDisconnect())
2043    log.fine(e.toString());
2044      else
2045    throw e;
2046    } catch (IOException JavaDoc e) {
2047      _request.killKeepalive();
2048      
2049      throw e;
2050    } finally {
2051      _isClosed = true;
2052      _cacheStream = null;
2053      _cacheWriter = null;
2054      _cacheInvocation = null;
2055      _cacheEntry = null;
2056    }
2057  }
2058
2059  TempBuffer getBuffer()
2060  {
2061    return _tempBuffer;
2062  }
2063
2064  protected final QDate getCalendar()
2065  {
2066    return _calendar;
2067  }
2068
2069  protected void free()
2070  {
2071    _request = null;
2072    _originalRequest = null;
2073    _cacheInvocation = null;
2074    _cacheEntry = null;
2075    _cacheStream = null;
2076    _cacheWriter = null;
2077  }
2078
2079  static {
2080    _errors = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
2081    _errors.put("100", "Continue");
2082    _errors.put("101", "Switching Protocols");
2083    _errors.put("200", "OK");
2084    _errors.put("201", "Created");
2085    _errors.put("202", "Accepted");
2086    _errors.put("203", "Non-Authoritative Information");
2087    _errors.put("204", "No Content");
2088    _errors.put("205", "Reset Content");
2089    _errors.put("206", "Partial Content");
2090    _errors.put("300", "Multiple Choices");
2091    _errors.put("301", "Moved Permanently");
2092    _errors.put("302", "Found");
2093    _errors.put("303", "See Other");
2094    _errors.put("304", "Not Modified");
2095    _errors.put("305", "Use Proxy");
2096    _errors.put("307", "Temporary Redirect");
2097    _errors.put("400", "Bad Request");
2098    _errors.put("401", "Unauthorized");
2099    _errors.put("402", "Payment Required");
2100    _errors.put("403", "Forbidden");
2101    _errors.put("404", "Not Found");
2102    _errors.put("405", "Method Not Allowed");
2103    _errors.put("406", "Not Acceptable");
2104    _errors.put("407", "Proxy Authentication Required");
2105    _errors.put("408", "Request Timeout");
2106    _errors.put("409", "Conflict");
2107    _errors.put("410", "Gone");
2108    _errors.put("411", "Length Required");
2109    _errors.put("412", "Precondition Failed");
2110    _errors.put("413", "Request Entity Too Large");
2111    _errors.put("414", "Request-URI Too Long");
2112    _errors.put("415", "Unsupported Media Type");
2113    _errors.put("416", "Requested Range Not Satisfiable");
2114    _errors.put("417", "Expectation Failed");
2115    _errors.put("500", "Internal Server Error");
2116    _errors.put("501", "Not Implemented");
2117    _errors.put("502", "Bad Gateway");
2118    _errors.put("503", "Service Temporarily Unavailable");
2119    _errors.put("504", "Gateway Timeout");
2120    _errors.put("505", "Http Version Not Supported");
2121
2122    _headerCodes = new CaseInsensitiveIntMap();
2123    _headerCodes.put("cache-control", HEADER_CACHE_CONTROL);
2124    _headerCodes.put("content-type", HEADER_CONTENT_TYPE);
2125    _headerCodes.put("content-length", HEADER_CONTENT_LENGTH);
2126    _headerCodes.put("date", HEADER_DATE);
2127    _headerCodes.put("server", HEADER_SERVER);
2128  }
2129}
2130
Popular Tags