//
// Author(s):
// Jackson Harper (jackson@ximian.com)
+// Marek Habersack <mhabersack@novell.com>
//
-// (C) 2003 Novell, Inc (http://www.novell.com)
+// (C) 2003-2009 Novell, Inc (http://www.novell.com)
//
//
using System;
-using System.Text;
using System.Collections;
using System.Collections.Specialized;
+using System.IO;
+using System.Text;
-namespace System.Web.Caching {
+#if NET_2_0
+using System.Collections.Generic;
+#endif
- internal sealed class CachedRawResponse {
- static readonly byte[] emptyBuffer = new byte[0];
+namespace System.Web.Caching
+{
+ sealed class CachedRawResponse
+ {
+ public sealed class DataItem
+ {
+ public readonly byte[] Buffer;
+ public readonly long Length;
+#if NET_2_0
+ public readonly HttpResponseSubstitutionCallback Callback;
+#endif
+
+ public DataItem (byte[] buffer, long length)
+ {
+ Buffer = buffer;
+ Length = length;
+ }
+
+#if NET_2_0
+ public DataItem (HttpResponseSubstitutionCallback callback) : this (null, 0)
+ {
+ Callback = callback;
+ }
+#endif
+ }
+
HttpCachePolicy policy;
CachedVaryBy varyby;
int status_code;
string status_desc;
int content_length;
NameValueCollection headers;
- byte[] buffer;
+#if NET_2_0
+ List <DataItem> data;
+#else
+ ArrayList data;
+#endif
+
+ IList Data {
+ get {
+ if (data == null) {
+#if NET_2_0
+ data = new List <DataItem> ();
+#else
+ data = new ArrayList ();
+#endif
+ }
+
+ return data;
+ }
+ }
- internal CachedRawResponse (HttpCachePolicy policy)
+ public CachedRawResponse (HttpCachePolicy policy)
{
this.policy = policy;
- this.buffer = emptyBuffer;
}
- internal HttpCachePolicy Policy {
+ public HttpCachePolicy Policy {
get { return policy; }
set { policy = value; }
}
- internal CachedVaryBy VaryBy {
+ public CachedVaryBy VaryBy {
get { return varyby; }
set { varyby = value; }
}
- internal int StatusCode {
+ public int StatusCode {
get { return status_code; }
set { status_code = value; }
}
- internal string StatusDescription {
+ public string StatusDescription {
get { return status_desc; }
set { status_desc = value; }
}
- internal int ContentLength {
- get { return content_length; }
- set { content_length = value; }
- }
-
- internal NameValueCollection Headers {
+ public NameValueCollection Headers {
get { return headers; }
}
- internal void SetHeaders (NameValueCollection headers) {
+ public void SetHeaders (NameValueCollection headers)
+ {
this.headers = headers;
}
- internal void SetData (byte[] buffer)
+ public void SetData (MemoryStream ms)
+ {
+ if (ms == null)
+ return;
+
+ Data.Add (new DataItem (ms.GetBuffer (), ms.Length));
+ }
+
+#if NET_2_0
+ public void SetData (HttpResponseSubstitutionCallback callback)
{
- this.buffer = buffer;
+ if (callback == null)
+ return;
+
+ Data.Add (new DataItem (callback));
}
+#endif
- internal byte[] GetData ()
+ public IList GetData ()
{
- return buffer;
+ int count = data != null ? data.Count :0;
+ if (count == 0)
+ return null;
+
+ return data;
}
}
}
+2009-10-20 Marek Habersack <mhabersack@novell.com>
+
+ * OutputCacheModule.cs: keysCache and entriesToInvalidate
+ dictionaries are allocated only when they are
+ required. BuildManager's RemoveEntry envent is subscribed to only
+ if cache actually has some entries.
+ Added support for post-cache substitution.
+
+ * CachedRawResponse.cs: data is stored in a list of DataItem
+ instances instead of just in a single buffer. This enables support
+ for post-cache substitution.
+
2009-10-05 Marek Habersack <mhabersack@novell.com>
* Cache.cs: if item expiration time exceeds the maximum value
//
// Authors:
// Jackson Harper (jackson@ximian.com)
+// Marek Habersack <mhabersack@novell.com>
//
-// (C) 2003 Novell, Inc (http://www.novell.com)
+// (C) 2003-2009 Novell, Inc (http://www.novell.com)
//
//
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+using System.Collections;
using System.IO;
+using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.Util;
-using System.Collections;
using System.Web.Compilation;
#if NET_2_0
{
context.ResolveRequestCache += new EventHandler(OnResolveRequestCache);
context.UpdateRequestCache += new EventHandler(OnUpdateRequestCache);
-#if NET_2_0
- keysCache = new Dictionary <string, string> ();
- entriesToInvalidate = new Dictionary <string, string> ();
- BuildManager.RemoveEntry += new BuildManagerRemoveEntryEventHandler (OnBuildManagerRemoveEntry);
-#endif
response_removed = new CacheItemRemovedCallback (OnRawResponseRemoved);
}
return;
keysCache.Remove (entry);
- if (context == null && !entriesToInvalidate.ContainsKey (entry)) {
- entriesToInvalidate.Add (entry, cacheValue);
- return;
+ if (context == null) {
+ if (entriesToInvalidate == null) {
+ entriesToInvalidate = new Dictionary <string, string> (StringComparer.Ordinal);
+ entriesToInvalidate.Add (entry, cacheValue);
+ return;
+ } else if (!entriesToInvalidate.ContainsKey (entry)) {
+ entriesToInvalidate.Add (entry, cacheValue);
+ return;
+ }
}
}
#if NET_2_0
lock (keysCacheLock) {
string invValue;
- if (entriesToInvalidate.TryGetValue (vary_key, out invValue) && String.Compare (invValue, key, StringComparison.Ordinal) == 0) {
+ if (entriesToInvalidate != null && entriesToInvalidate.TryGetValue (vary_key, out invValue) && String.Compare (invValue, key, StringComparison.Ordinal) == 0) {
context.Cache.Remove (vary_key);
context.InternalCache.Remove (key);
entriesToInvalidate.Remove (vary_key);
return;
}
}
-
- context.Response.ClearContent ();
- context.Response.BinaryWrite (c.GetData (), 0, c.ContentLength);
- context.Response.ClearHeaders ();
- context.Response.SetCachedHeaders (c.Headers);
+ HttpResponse response = context.Response;
+ response.ClearContent ();
+ IList cachedData = c.GetData ();
+ if (cachedData != null) {
+ Encoding outEnc = WebEncoding.ResponseEncoding;
+
+ foreach (CachedRawResponse.DataItem d in cachedData) {
+ if (d.Length > 0) {
+ response.BinaryWrite (d.Buffer, 0, (int)d.Length);
+ continue;
+ }
+
+#if NET_2_0
+ if (d.Callback == null)
+ continue;
+
+ string s = d.Callback (context);
+ if (s == null || s.Length == 0)
+ continue;
- context.Response.StatusCode = c.StatusCode;
- context.Response.StatusDescription = c.StatusDescription;
+ byte[] bytes = outEnc.GetBytes (s);
+ response.BinaryWrite (bytes, 0, bytes.Length);
+#endif
+ }
+ }
+
+ response.ClearHeaders ();
+ response.SetCachedHeaders (c.Headers);
+ response.StatusCode = c.StatusCode;
+ response.StatusDescription = c.StatusDescription;
app.CompleteRequest ();
}
if (context.Response.IsCached && context.Response.StatusCode == 200 &&
!context.Trace.IsEnabled)
DoCacheInsert (context);
-
}
void DoCacheInsert (HttpContext context)
#if NET_2_0
if (cacheKey != null) {
lock (keysCacheLock) {
- if (!keysCache.ContainsKey (cacheKey))
+ if (keysCache == null) {
+ BuildManager.RemoveEntry += new BuildManagerRemoveEntryEventHandler (OnBuildManagerRemoveEntry);
+ keysCache = new Dictionary <string, string> (StringComparer.Ordinal);
+ keysCache.Add (cacheKey, cacheValue);
+ } else if (!keysCache.ContainsKey (cacheKey))
keysCache.Add (cacheKey, cacheValue);
}
}
+2009-10-20 Marek Habersack <mhabersack@novell.com>
+
+ * HttpResponse.cs: implemented the WriteSubstitution method,
+ thus enabling post-cache substitution.
+
2009-10-12 Marek Habersack <mhabersack@novell.com>
* HttpResponse.cs: actually _use_ the url passed to Redirect when
app_instance.TriggerPreSendRequestContent ();
}
- if (IsCached) {
- MemoryStream ms = output_stream.GetData ();
- cached_response.ContentLength = (int) ms.Length;
- cached_response.SetData (ms.GetBuffer ());
- }
+ if (IsCached)
+ cached_response.SetData (output_stream.GetData ());
if (WorkerRequest != null)
output_stream.Flush (WorkerRequest, final_flush);
Flush ();
}
#if NET_2_0
- [MonoTODO ("Not implemented")]
public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
{
- throw new NotImplementedException ();
+ // Emulation of .NET behavior
+ if (callback == null)
+ throw new NullReferenceException ();
+
+ object target = callback.Target;
+ if (target != null && target.GetType () == typeof (Control))
+ throw new ArgumentException ("callback");
+
+ string s = callback (context);
+ if (!IsCached) {
+ Write (s);
+ return;
+ }
+
+ Cache.Cacheability = HttpCacheability.Server;
+ Flush ();
+ if (WorkerRequest == null)
+ Write (s); // better this than nothing
+ else {
+ byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s);
+ WorkerRequest.SendResponseFromMemory (bytes, bytes.Length);
+ }
+
+ cached_response.SetData (callback);
}
#endif
//