The Labs \ Source Viewer \ SSCLI \ System.Net.Cache \ SingleItemRequestCache

  1. //------------------------------------------------------------------------------
  2. // <copyright file="_SingleItemRequestCache.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. /*++   
  16. Abstract:
  17.     Request Caching subsystem capable of caching one file at a time.
  18.     Used by, for example, auto-proxy script downloading.
  19. Author:
  20.     Justin Brown - Aug 2, 2004
  21. Revision History:
  22. --*/
  23. namespace System.Net.Cache
  24. {
  25.     using System;
  26.     using System.Net;
  27.     using System.Diagnostics;
  28.     using System.Text;
  29.     using System.IO;
  30.     using System.Collections.Specialized;
  31.     using System.Threading;
  32.     using System.Collections;
  33.    
  34.     internal class SingleItemRequestCache : RequestCache
  35.     {
  36.         bool _UseWinInet;
  37.         FrozenCacheEntry _Entry;
  38.        
  39.         private sealed class FrozenCacheEntry : RequestCacheEntry
  40.         {
  41.             byte[] _StreamBytes;
  42.             string _Key;
  43.            
  44.             public FrozenCacheEntry(string key, RequestCacheEntry entry, Stream stream) : this(key, entry, GetBytes(stream))
  45.             {
  46.             }
  47.             public FrozenCacheEntry(string key, RequestCacheEntry entry, byte[] streamBytes) : base()
  48.             {
  49.                 _Key = key;
  50.                 _StreamBytes = streamBytes;
  51.                 IsPrivateEntry = entry.IsPrivateEntry;
  52.                 StreamSize = entry.StreamSize;
  53.                 ExpiresUtc = entry.ExpiresUtc;
  54.                 HitCount = entry.HitCount;
  55.                 LastAccessedUtc = entry.LastAccessedUtc;
  56.                 entry.LastModifiedUtc = entry.LastModifiedUtc;
  57.                 LastSynchronizedUtc = entry.LastSynchronizedUtc;
  58.                 MaxStale = entry.MaxStale;
  59.                 UsageCount = entry.UsageCount;
  60.                 IsPartialEntry = entry.IsPartialEntry;
  61.                 EntryMetadata = entry.EntryMetadata;
  62.                 SystemMetadata = entry.SystemMetadata;
  63.             }
  64.            
  65.             static byte[] GetBytes(Stream stream)
  66.             {
  67.                 byte[] bytes;
  68.                 bool resize = false;
  69.                 if (stream.CanSeek)
  70.                     bytes = new byte[stream.Length];
  71.                 else {
  72.                     resize = true;
  73.                     bytes = new byte[4096 * 2];
  74.                 }
  75.                
  76.                 int offset = 0;
  77.                 while (true) {
  78.                     int read = stream.Read(bytes, offset, bytes.Length - offset);
  79.                     if (read == 0)
  80.                         break;
  81.                     if ((offset += read) == bytes.Length && resize) {
  82.                         byte[] newBytes = new byte[bytes.Length + 4096 * 2];
  83.                         Buffer.BlockCopy(bytes, 0, newBytes, 0, offset);
  84.                         bytes = newBytes;
  85.                     }
  86.                 }
  87.                 if (resize) {
  88.                     byte[] newBytes = new byte[offset];
  89.                     Buffer.BlockCopy(bytes, 0, newBytes, 0, offset);
  90.                     bytes = newBytes;
  91.                 }
  92.                 return bytes;
  93.             }
  94.            
  95.             public static FrozenCacheEntry Create(FrozenCacheEntry clonedObject)
  96.             {
  97.                 return (object)clonedObject == (object)null ? null : (FrozenCacheEntry)clonedObject.MemberwiseClone();
  98.             }
  99.            
  100.             public byte[] StreamBytes {
  101.                 get { return _StreamBytes; }
  102.             }
  103.             public string Key {
  104.                 get { return _Key; }
  105.             }
  106.         }
  107.        
  108.        
  109.         internal SingleItemRequestCache(bool useWinInet) : base(true, true)
  110.         {
  111.             _UseWinInet = useWinInet;
  112.         }
  113.        
  114.         // Returns a read data stream and metadata associated with a cached entry.
  115.         // Returns Stream.Null if there is no entry found.
  116.         // <remarks> An opened cache entry be preserved until the stream is closed. </remarks>
  117.         //
  118.         internal override Stream Retrieve(string key, out RequestCacheEntry cacheEntry)
  119.         {
  120.             Stream result;
  121.             if (!TryRetrieve(key, out cacheEntry, out result)) {
  122.                 FileNotFoundException fileNotFoundException = new FileNotFoundException(null, key);
  123.                 throw new IOException(SR.GetString(SR.net_cache_retrieve_failure, fileNotFoundException.Message), fileNotFoundException);
  124.             }
  125.            
  126.             return result;
  127.         }
  128.        
  129.         // Returns a write cache stream associated with the string Key.
  130.         // Passed parameters allow cache to update an entry metadata accordingly.
  131.         // <remarks> The commit operation should happen on the stream closure. </remarks>
  132.         //
  133.         internal override Stream Store(string key, long contentLength, DateTime expiresUtc, DateTime lastModifiedUtc, TimeSpan maxStale, StringCollection entryMetadata, StringCollection systemMetadata)
  134.         {
  135.             Stream result;
  136.             if (!TryStore(key, contentLength, expiresUtc, lastModifiedUtc, maxStale, entryMetadata, systemMetadata, out result)) {
  137.                 FileNotFoundException fileNotFoundException = new FileNotFoundException(null, key);
  138.                 throw new IOException(SR.GetString(SR.net_cache_retrieve_failure, fileNotFoundException.Message), fileNotFoundException);
  139.             }
  140.            
  141.             return result;
  142.         }
  143.        
  144.         //
  145.         // Removes an entry from the cache.
  146.         //
  147.         internal override void Remove(string key)
  148.         {
  149.             if (!TryRemove(key)) {
  150.                 FileNotFoundException fileNotFoundException = new FileNotFoundException(null, key);
  151.                 throw new IOException(SR.GetString(SR.net_cache_retrieve_failure, fileNotFoundException.Message), fileNotFoundException);
  152.             }
  153.         }
  154.        
  155.         //
  156.         // Updates only metadata associated with a cached entry.
  157.         //
  158.         internal override void Update(string key, DateTime expiresUtc, DateTime lastModifiedUtc, DateTime lastSynchronizedUtc, TimeSpan maxStale, StringCollection entryMetadata, StringCollection systemMetadata)
  159.         {
  160.             if (!TryUpdate(key, expiresUtc, lastModifiedUtc, lastSynchronizedUtc, maxStale, entryMetadata, systemMetadata)) {
  161.                 FileNotFoundException fileNotFoundException = new FileNotFoundException(null, key);
  162.                 throw new IOException(SR.GetString(SR.net_cache_retrieve_failure, fileNotFoundException.Message), fileNotFoundException);
  163.             }
  164.         }
  165.        
  166.         internal override bool TryRetrieve(string key, out RequestCacheEntry cacheEntry, out Stream readStream)
  167.         {
  168.             if (key == null)
  169.                 throw new ArgumentNullException("key");
  170.            
  171.             FrozenCacheEntry chkEntry = _Entry;
  172.             cacheEntry = null;
  173.             readStream = null;
  174.            
  175.             if (chkEntry == null || chkEntry.Key != key) {
  176.                 return false;
  177.             }
  178.             cacheEntry = FrozenCacheEntry.Create(chkEntry);
  179.             readStream = new ReadOnlyStream(chkEntry.StreamBytes);
  180.             return true;
  181.         }
  182.        
  183.         internal override bool TryStore(string key, long contentLength, DateTime expiresUtc, DateTime lastModifiedUtc, TimeSpan maxStale, StringCollection entryMetadata, StringCollection systemMetadata, out Stream writeStream)
  184.         {
  185.             if (key == null)
  186.                 throw new ArgumentNullException("key");
  187.            
  188.             RequestCacheEntry requestCacheEntry = new RequestCacheEntry();
  189.             requestCacheEntry.IsPrivateEntry = this.IsPrivateCache;
  190.             requestCacheEntry.StreamSize = contentLength;
  191.             requestCacheEntry.ExpiresUtc = expiresUtc;
  192.             requestCacheEntry.LastModifiedUtc = lastModifiedUtc;
  193.             requestCacheEntry.LastAccessedUtc = DateTime.UtcNow;
  194.             requestCacheEntry.LastSynchronizedUtc = DateTime.UtcNow;
  195.             requestCacheEntry.MaxStale = maxStale;
  196.             requestCacheEntry.HitCount = 0;
  197.             requestCacheEntry.UsageCount = 0;
  198.             requestCacheEntry.IsPartialEntry = false;
  199.             requestCacheEntry.EntryMetadata = entryMetadata;
  200.             requestCacheEntry.SystemMetadata = systemMetadata;
  201.            
  202.             writeStream = null;
  203.             Stream realWriteStream = null;
  204.            
  205.            
  206.             writeStream = new WriteOnlyStream(key, this, requestCacheEntry, realWriteStream);
  207.             return true;
  208.         }
  209.        
  210.         private void Commit(string key, RequestCacheEntry tempEntry, byte[] allBytes)
  211.         {
  212.             FrozenCacheEntry chkEntry = new FrozenCacheEntry(key, tempEntry, allBytes);
  213.             _Entry = chkEntry;
  214.         }
  215.        
  216.         internal override bool TryRemove(string key)
  217.         {
  218.             if (key == null)
  219.                 throw new ArgumentNullException("key");
  220.            
  221.            
  222.             FrozenCacheEntry chkEntry = _Entry;
  223.            
  224.             if (chkEntry != null && chkEntry.Key == key)
  225.                 _Entry = null;
  226.            
  227.             return true;
  228.         }
  229.        
  230.         internal override bool TryUpdate(string key, DateTime expiresUtc, DateTime lastModifiedUtc, DateTime lastSynchronizedUtc, TimeSpan maxStale, StringCollection entryMetadata, StringCollection systemMetadata)
  231.         {
  232.             if (key == null)
  233.                 throw new ArgumentNullException("key");
  234.            
  235.             FrozenCacheEntry chkEntry = FrozenCacheEntry.Create(_Entry);
  236.            
  237.             //
  238.             // This class does not forward metadata updates to WinInet to simplify the design and avoid interlocked ops
  239.             //
  240.            
  241.             if (chkEntry == null || chkEntry.Key != key)
  242.                 return true;
  243.            
  244.             chkEntry.ExpiresUtc = expiresUtc;
  245.             chkEntry.LastModifiedUtc = lastModifiedUtc;
  246.             chkEntry.LastSynchronizedUtc = lastSynchronizedUtc;
  247.             chkEntry.MaxStale = maxStale;
  248.             chkEntry.EntryMetadata = entryMetadata;
  249.             chkEntry.SystemMetadata = systemMetadata;
  250.             _Entry = chkEntry;
  251.             return true;
  252.         }
  253.         //
  254.         // We've chosen to no forward to WinInet metadata-only updates
  255.         // Hence our entries are never locked and this method does nothing
  256.         //
  257.         internal override void UnlockEntry(Stream stream)
  258.         {
  259.         }
  260.        
  261.         //
  262.         //
  263.         //
  264.         internal class ReadOnlyStream : Stream
  265.         {
  266.             private byte[] _Bytes;
  267.             private int _Offset;
  268.             private bool _Disposed;
  269.             private int _ReadTimeout;
  270.             private int _WriteTimeout;
  271.            
  272.             internal ReadOnlyStream(byte[] bytes) : base()
  273.             {
  274.                 _Bytes = bytes;
  275.                 _Offset = 0;
  276.                 _Disposed = false;
  277.                 _ReadTimeout = _WriteTimeout = -1;
  278.             }
  279.            
  280.             public override bool CanRead {
  281.                 get { return true; }
  282.             }
  283.             public override bool CanSeek {
  284.                 get { return true; }
  285.             }
  286.             public override bool CanTimeout {
  287.                 get { return true; }
  288.             }
  289.             public override bool CanWrite {
  290.                 get { return false; }
  291.             }
  292.             public override long Length {
  293.                 get { return _Bytes.Length; }
  294.             }
  295.             public override long Position {
  296.                 get { return _Offset; }
  297.                 set {
  298.                     if (value < 0 || value > (long)_Bytes.Length)
  299.                         throw new ArgumentOutOfRangeException("value");
  300.                     _Offset = (int)value;
  301.                 }
  302.             }
  303.            
  304.             public override int ReadTimeout {
  305.                 get { return _ReadTimeout; }
  306.                 set {
  307.                     if (value <= 0 && value != System.Threading.Timeout.Infinite)
  308.                         throw new ArgumentOutOfRangeException(SR.GetString(SR.net_io_timeout_use_gt_zero));
  309.                     _ReadTimeout = value;
  310.                 }
  311.             }
  312.             public override int WriteTimeout {
  313.                 get { return _WriteTimeout; }
  314.                 set {
  315.                     if (value <= 0 && value != System.Threading.Timeout.Infinite)
  316.                         throw new ArgumentOutOfRangeException(SR.GetString(SR.net_io_timeout_use_gt_zero));
  317.                     _WriteTimeout = value;
  318.                 }
  319.             }
  320.            
  321.             public override void Flush()
  322.             {
  323.             }
  324.            
  325.             public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  326.             {
  327.                 int result = Read(buffer, offset, count);
  328.                
  329.                 LazyAsyncResult ar = new LazyAsyncResult(null, state, callback);
  330.                 ar.InvokeCallback(result);
  331.                 return ar;
  332.             }
  333.             public override int EndRead(IAsyncResult asyncResult)
  334.             {
  335.                 if (asyncResult == null)
  336.                     throw new ArgumentNullException("asyncResult");
  337.                 LazyAsyncResult ar = (LazyAsyncResult)asyncResult;
  338.                 if (ar.EndCalled)
  339.                     throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndRead"));
  340.                 ar.EndCalled = true;
  341.                 return (int)ar.InternalWaitForCompletion();
  342.             }
  343.            
  344.             public override int Read(byte[] buffer, int offset, int count)
  345.             {
  346.                 if (_Disposed)
  347.                     throw new ObjectDisposedException(GetType().Name);
  348.                 if (buffer == null)
  349.                     throw new ArgumentNullException("buffer");
  350.                 if (offset < 0 || offset > buffer.Length)
  351.                     throw new ArgumentOutOfRangeException("offset");
  352.                 if (count < 0 || count > buffer.Length - offset)
  353.                     throw new ArgumentOutOfRangeException("count");
  354.                 if (_Offset == _Bytes.Length)
  355.                     return 0;
  356.                
  357.                 int chkOffset = (int)_Offset;
  358.                 count = Math.Min(count, _Bytes.Length - chkOffset);
  359.                 System.Buffer.BlockCopy(_Bytes, chkOffset, buffer, offset, count);
  360.                 chkOffset += count;
  361.                 _Offset = chkOffset;
  362.                 return count;
  363.             }
  364.             public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
  365.             {
  366.                 throw new NotSupportedException(SR.GetString(SR.net_readonlystream));
  367.             }
  368.             public override void EndWrite(IAsyncResult asyncResult)
  369.             {
  370.                 throw new NotSupportedException(SR.GetString(SR.net_readonlystream));
  371.             }
  372.             public override void Write(byte[] buffer, int offset, int count)
  373.             {
  374.                 throw new NotSupportedException(SR.GetString(SR.net_readonlystream));
  375.             }
  376.            
  377.            
  378.             public override long Seek(long offset, SeekOrigin origin)
  379.             {
  380.                 switch (origin) {
  381.                     case SeekOrigin.Begin:
  382.                         return Position = offset;
  383.                     case SeekOrigin.Current:
  384.                         return Position += offset;
  385.                     case SeekOrigin.End:
  386.                         /// <include file='doc\SeekOrigin.uex' path='docs/doc[@for="SeekOrigin.End"]/*' />
  387.                         return Position = _Bytes.Length - offset;
  388.                     default:
  389.                         throw new ArgumentException(SR.GetString(SR.net_invalid_enum, "SeekOrigin"), "origin");
  390.                         break;
  391.                 }
  392.             }
  393.            
  394.             public override void SetLength(long length)
  395.             {
  396.                 throw new NotSupportedException(SR.GetString(SR.net_readonlystream));
  397.             }
  398.            
  399.            
  400.             protected override void Dispose(bool disposing)
  401.             {
  402.                 try {
  403.                     _Disposed = true;
  404.                 }
  405.                 finally {
  406.                     base.Dispose(disposing);
  407.                 }
  408.             }
  409.            
  410.             internal byte[] Buffer {
  411.                 get { return _Bytes; }
  412.             }
  413.         }
  414.        
  415.         //
  416.         //
  417.         //
  418.         private class WriteOnlyStream : Stream
  419.         {
  420.             private string _Key;
  421.             private SingleItemRequestCache _Cache;
  422.             private RequestCacheEntry _TempEntry;
  423.             private Stream _RealStream;
  424.             private long _TotalSize;
  425.             private ArrayList _Buffers;
  426.            
  427.             private bool _Disposed;
  428.             private int _ReadTimeout;
  429.             private int _WriteTimeout;
  430.            
  431.             public WriteOnlyStream(string key, SingleItemRequestCache cache, RequestCacheEntry cacheEntry, Stream realWriteStream)
  432.             {
  433.                 _Key = key;
  434.                 _Cache = cache;
  435.                 _TempEntry = cacheEntry;
  436.                 _RealStream = realWriteStream;
  437.                 _Buffers = new ArrayList();
  438.             }
  439.            
  440.             public override bool CanRead {
  441.                 get { return false; }
  442.             }
  443.             public override bool CanSeek {
  444.                 get { return false; }
  445.             }
  446.            
  447.             public override bool CanTimeout {
  448.                 get { return true; }
  449.             }
  450.             public override bool CanWrite {
  451.                 get { return true; }
  452.             }
  453.            
  454.             public override long Length {
  455.                 get {
  456.                     throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
  457.                 }
  458.             }
  459.            
  460.             public override long Position {
  461.                 get {
  462.                     throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
  463.                 }
  464.                 set {
  465.                     throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
  466.                 }
  467.             }
  468.            
  469.             public override int ReadTimeout {
  470.                 get { return _ReadTimeout; }
  471.                 set {
  472.                     if (value <= 0 && value != System.Threading.Timeout.Infinite)
  473.                         throw new ArgumentOutOfRangeException(SR.GetString(SR.net_io_timeout_use_gt_zero));
  474.                     _ReadTimeout = value;
  475.                 }
  476.             }
  477.             public override int WriteTimeout {
  478.                 get { return _WriteTimeout; }
  479.                 set {
  480.                     if (value <= 0 && value != System.Threading.Timeout.Infinite)
  481.                         throw new ArgumentOutOfRangeException(SR.GetString(SR.net_io_timeout_use_gt_zero));
  482.                     _WriteTimeout = value;
  483.                 }
  484.             }
  485.            
  486.             public override void Flush()
  487.             {
  488.             }
  489.            
  490.             public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  491.             {
  492.                 throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
  493.             }
  494.            
  495.             public override int EndRead(IAsyncResult asyncResult)
  496.             {
  497.                 throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
  498.             }
  499.            
  500.             public override int Read(byte[] buffer, int offset, int count)
  501.             {
  502.                 throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
  503.             }
  504.            
  505.             public override long Seek(long offset, SeekOrigin origin)
  506.             {
  507.                 throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
  508.             }
  509.             public override void SetLength(long length)
  510.             {
  511.                 throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
  512.             }
  513.            
  514.            
  515.             public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  516.             {
  517.                 Write(buffer, offset, count);
  518.                 LazyAsyncResult ar = new LazyAsyncResult(null, state, callback);
  519.                 ar.InvokeCallback(null);
  520.                 return ar;
  521.             }
  522.             public override void EndWrite(IAsyncResult asyncResult)
  523.             {
  524.                 if (asyncResult == null)
  525.                     throw new ArgumentNullException("asyncResult");
  526.                 LazyAsyncResult ar = (LazyAsyncResult)asyncResult;
  527.                 if (ar.EndCalled)
  528.                     throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndWrite"));
  529.                 ar.EndCalled = true;
  530.                 ar.InternalWaitForCompletion();
  531.             }
  532.             public override void Write(byte[] buffer, int offset, int count)
  533.             {
  534.                 if (_Disposed)
  535.                     throw new ObjectDisposedException(GetType().Name);
  536.                 if (buffer == null)
  537.                     throw new ArgumentNullException("buffer");
  538.                 if (offset < 0 || offset > buffer.Length)
  539.                     throw new ArgumentOutOfRangeException("offset");
  540.                 if (count < 0 || count > buffer.Length - offset)
  541.                     throw new ArgumentOutOfRangeException("count");
  542.                
  543.                 if (_RealStream != null)
  544.                     try {
  545.                         _RealStream.Write(buffer, offset, count);
  546.                     }
  547.                     catch {
  548.                         _RealStream.Close();
  549.                         _RealStream = null;
  550.                     }
  551.                
  552.                 byte[] chunk = new byte[count];
  553.                 System.Buffer.BlockCopy(buffer, offset, chunk, 0, count);
  554.                 _Buffers.Add(chunk);
  555.                 _TotalSize += count;
  556.             }
  557.            
  558.             protected override void Dispose(bool disposing)
  559.             {
  560.                 _Disposed = true;
  561.                 base.Dispose(disposing);
  562.                 // Do we mean to do this here????
  563.                 if (disposing) {
  564.                     if (_RealStream != null)
  565.                         try {
  566.                             _RealStream.Close();
  567.                         }
  568.                         catch {
  569.                         }
  570.                    
  571.                     byte[] allBytes = new byte[_TotalSize];
  572.                     int offset = 0;
  573.                     for (int i = 0; i < _Buffers.Count; ++i) {
  574.                         byte[] buffer = (byte[])_Buffers[i];
  575.                         Buffer.BlockCopy(buffer, 0, allBytes, offset, buffer.Length);
  576.                         offset += buffer.Length;
  577.                     }
  578.                    
  579.                     _Cache.Commit(_Key, _TempEntry, allBytes);
  580.                 }
  581.             }
  582.            
  583.         }
  584.     }
  585. }

Developer Fusion