Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Net / System / Net / Cache / WinInetCache.cs / 1 / WinInetCache.cs
/*++ Copyright (c) Microsoft Corporation Module Name: _WinInetCache.cs Abstract: The class implements low-level object model for communications with the caching part of WinInet DLL Author: Alexei Vopilov 21-Dec-2002 Revision History: --*/ namespace System.Net.Cache { using System.ComponentModel; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Collections; using System.Text; using System.Collections.Specialized; using System.Threading; using System.Globalization; // // WinInet OS Provider implementation (Caching part only) // // Contains methods marked with unsafe keyword // internal class _WinInetCache { private const int c_CharSz = 2; private _WinInetCache() { } // // DATA Definitions // // Cache Entry declarations [Flags] internal enum EntryType { NormalEntry = 0x00000041, // ored with HTTP_1_1_CACHE_ENTRY StickyEntry = 0x00000044, // ored with HTTP_1_1_CACHE_ENTRY Edited = 0x00000008, TrackOffline = 0x00000010, TrackOnline = 0x00000020, Sparse = 0x00010000, Cookie = 0x00100000, UrlHistory = 0x00200000, // FindDefaultFilter = NormalEntry|StickyEntry|Cookie|UrlHistory|TrackOffline|TrackOnline } /* Some More IE private entry types HTTP_1_1_CACHE_ENTRY 0x00000040 STATIC_CACHE_ENTRY 0x00000080 MUST_REVALIDATE_CACHE_ENTRY 0x00000100 COOKIE_ACCEPTED_CACHE_ENTRY 0x00001000 COOKIE_LEASHED_CACHE_ENTRY 0x00002000 COOKIE_DOWNGRADED_CACHE_ENTRY 0x00004000 COOKIE_REJECTED_CACHE_ENTRY 0x00008000 PENDING_DELETE_CACHE_ENTRY 0x00400000 OTHER_USER_CACHE_ENTRY 0x00800000 PRIVACY_IMPACTED_CACHE_ENTRY 0x02000000 POST_RESPONSE_CACHE_ENTRY 0x04000000 INSTALLED_CACHE_ENTRY 0x10000000 POST_ */ // Some supported Entry fields references [Flags] internal enum Entry_FC { None = 0x0, Attribute = 0x00000004, Hitrate = 0x00000010, Modtime = 0x00000040, Exptime = 0x00000080, Acctime = 0x00000100, Synctime = 0x00000200, Headerinfo = 0x00000400, ExemptDelta = 0x00000800 } // Error status codes, some are mapped to native ones internal enum Status { Success = 0, InsufficientBuffer = 122, FileNotFound = 2, NoMoreItems = 259, NotEnoughStorage = 8, SharingViolation = 32, InvalidParameter = 87, // Below are extensions of native errors (no real errors exist) Warnings = 0x1000000, FatalErrors = Warnings + 0x1000, CorruptedHeaders = (int)FatalErrors+1, InternalError = (int)FatalErrors+2 } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] internal struct FILETIME { public uint Low; public uint High; public static readonly FILETIME Zero = new FILETIME(0L); public FILETIME(long time) { unchecked { Low = (uint)time; High = (uint)(time>>32); } } public long ToLong() { return ((long)High<<32) | Low; } public bool IsNull { get {return Low == 0 && High == 0;} } } // // It's an unmanamged layout of WinInet cache Entry Info // The pointer on this guy will represent the entry info for wininet // native calls // [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] internal struct EntryBuffer { public static int MarshalSize = Marshal.SizeOf(typeof(EntryBuffer)); public int StructSize; // version of cache system MUST BE == sizeof(this) // We replace this with an offset from the struct start public IntPtr _OffsetSourceUrlName; // We replace this with an offset from the struct start public IntPtr _OffsetFileName; // embedded pointer to the local file name. public EntryType EntryType; // cache type bit mask. public int UseCount; // current users count of the cache entry. public int HitRate; // num of times the cache entry was retrieved. public int SizeLow; // low DWORD of the file size. public int SizeHigh; // high DWORD of the file size. public FILETIME LastModifiedTime; // last modified time of the file in GMT format. public FILETIME ExpireTime; // expire time of the file in GMT format public FILETIME LastAccessTime; // last accessed time in GMT format public FILETIME LastSyncTime; // last time the URL was synchronized with the source // We replace this with an offset from the struct start public IntPtr _OffsetHeaderInfo; // embedded pointer to the header info. public int HeaderInfoChars; // size of the above header. // We replace this with an offset from the struct start public IntPtr _OffsetExtension; // File extension used to retrive the urldata as a file. [StructLayout(LayoutKind.Explicit)] public struct Rsv { [FieldOffset(0)] public int ExemptDelta; // Exemption delta from last access [FieldOffset(0)] public int Reserved; // To keep the unmanaged layout } public Rsv U; } // // This class holds a manged version of native WinInet buffer // internal class Entry { public const int DefaultBufferSize = 2048; public Status Error; public string Key; public string Filename; // filled by Create() or returned by LookupXXX() public string FileExt; // filled by Create() or returned by LookupXXX() public int OptionalLength; // should be always null public string OriginalUrl; // should be always null public string MetaInfo; // referenced to by Entry_FC.Headerinfo public int MaxBufferBytes; // contains the buffer size in bytes on input and // copied bytes count in the buffer on output // The tail represents the entry info in its unmanaged layout; public EntryBuffer Info; public Entry(string key, int maxHeadersSize) { Key = key; MaxBufferBytes = maxHeadersSize; if (maxHeadersSize != Int32.MaxValue && (Int32.MaxValue - (key.Length + EntryBuffer.MarshalSize + 1024)*2) > maxHeadersSize) { // // The buffer size is restricted mostly by headers, we reserve 1k more CHARS for additional // metadata, otherwise user has to play with the maxHeadersSize parameter // // MaxBufferBytes += (key.Length + EntryBuffer.MarshalSize + 1024)*2; } Info.EntryType = EntryType.NormalEntry; } } // // Method Definitions // // // Looks up an entry based on url string key. // Parses Headers and strings into entry mamabers. // Parses the raw output into entry.Info member. // unsafe internal static Status LookupInfo(Entry entry) { byte[] entryBuffer = new byte[Entry.DefaultBufferSize]; int size = entryBuffer.Length; byte[] buffer = entryBuffer; //We may need to adjust the buffer size (using 64 attempts although I would rather try to death) for (int k = 0; k < 64; ++k) { fixed (byte* entryPtr = buffer) { bool found = UnsafeNclNativeMethods.UnsafeWinInetCache.GetUrlCacheEntryInfoW(entry.Key, entryPtr, ref size); if (found) { entryBuffer = buffer; entry.MaxBufferBytes = size; EntryFixup(entry, (EntryBuffer*) entryPtr, buffer); entry.Error = Status.Success; return entry.Error; } entry.Error = (Status)Marshal.GetLastWin32Error(); if (entry.Error == Status.InsufficientBuffer) { if ((object) buffer == (object)entryBuffer) { // did not reallocate yet. if (size <= entry.MaxBufferBytes) { buffer = new byte[size]; continue; } } } //some error has occured break; } } return entry.Error; } // // Lookups an entry based on url string key. // If exists, locks the entry and hands out a managed handle representing a locked entry. // unsafe internal static SafeUnlockUrlCacheEntryFile LookupFile(Entry entry) { byte[] buffer = new byte[Entry.DefaultBufferSize]; int size = buffer.Length; SafeUnlockUrlCacheEntryFile handle = null; try { while (true) { fixed (byte* entryPtr = buffer) { //We may need to adjust the buffer size entry.Error = SafeUnlockUrlCacheEntryFile.GetAndLockFile(entry.Key, entryPtr, ref size, out handle); if (entry.Error == Status.Success) { entry.MaxBufferBytes = size; EntryFixup(entry, (EntryBuffer*) entryPtr, buffer); //The method is available in TRAVE return handle; } if (entry.Error == Status.InsufficientBuffer) { if (size <= entry.MaxBufferBytes) { buffer = new byte[size]; continue; } } //some error has occured break; } } } catch(Exception e) { if (handle != null) handle.Close(); if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) throw; if (entry.Error == Status.Success) { entry.Error = Status.InternalError; } } catch { if (handle != null) handle.Close(); if (entry.Error == Status.Success) { entry.Error = Status.InternalError; } } return null; } // // Does the fixup of the returned buffer by converting internal pointer to offsets // it also does copying of non-string values from unmanaged buffer to Entry.Buffer members // unsafe private static Status EntryFixup(Entry entry, EntryBuffer* bufferPtr, byte[] buffer) { unchecked { bufferPtr->_OffsetExtension = bufferPtr->_OffsetExtension == IntPtr.Zero? IntPtr.Zero: (IntPtr)((byte*)bufferPtr->_OffsetExtension - (byte*)bufferPtr); bufferPtr->_OffsetFileName = bufferPtr->_OffsetFileName == IntPtr.Zero? IntPtr.Zero: (IntPtr)((byte*)bufferPtr->_OffsetFileName - (byte*)bufferPtr); bufferPtr->_OffsetHeaderInfo = bufferPtr->_OffsetHeaderInfo == IntPtr.Zero? IntPtr.Zero: (IntPtr)((byte*)bufferPtr->_OffsetHeaderInfo - (byte*)bufferPtr); bufferPtr->_OffsetSourceUrlName = bufferPtr->_OffsetSourceUrlName == IntPtr.Zero? IntPtr.Zero: (IntPtr)((byte*)bufferPtr->_OffsetSourceUrlName - (byte*)bufferPtr); // Get a managed EntryBuffer copy out of byte[] entry.Info = *bufferPtr; entry.OriginalUrl = GetEntryBufferString(bufferPtr, (int)(bufferPtr->_OffsetSourceUrlName)); entry.Filename = GetEntryBufferString(bufferPtr, (int)(bufferPtr->_OffsetFileName)); entry.FileExt = GetEntryBufferString(bufferPtr, (int)(bufferPtr->_OffsetExtension)); } return GetEntryHeaders(entry, bufferPtr, buffer); } // // Returns a filename for a cache entry to write to. // internal static Status CreateFileName(Entry entry) { entry.Error = Status.Success; StringBuilder sb = new StringBuilder(UnsafeNclNativeMethods.UnsafeWinInetCache.MAX_PATH); if (UnsafeNclNativeMethods.UnsafeWinInetCache.CreateUrlCacheEntryW(entry.Key, entry.OptionalLength, entry.FileExt, sb, 0)) { entry.Filename = sb.ToString(); return Status.Success; } entry.Error = (Status) Marshal.GetLastWin32Error(); return entry.Error; } // // Associates a file with a cache entry. // internal static Status Commit(Entry entry) { string s = entry.MetaInfo; if (s == null) { s = string.Empty; } if ((s.Length + entry.Key.Length + entry.Filename.Length + (entry.OriginalUrl==null? 0: entry.OriginalUrl.Length)) > entry.MaxBufferBytes/c_CharSz) { entry.Error = Status.InsufficientBuffer; return entry.Error; } entry.Error = Status.Success; unsafe { fixed (char *ptr = s) { byte* realBytesPtr = s.Length == 0? null: (byte*)ptr; if (!UnsafeNclNativeMethods.UnsafeWinInetCache.CommitUrlCacheEntryW( entry.Key, entry.Filename, entry.Info.ExpireTime, entry.Info.LastModifiedTime, entry.Info.EntryType, realBytesPtr, s.Length, null, // FileExt is reserved, must be null entry.OriginalUrl // It's better to not play with redirections // OrigianlUri should be nulled by the caller )) { entry.Error = (Status)Marshal.GetLastWin32Error(); } } } return entry.Error; } // // Updates a Cached Entry metadata according to attibutes flags. // internal static Status Update(Entry newEntry, Entry_FC attributes) { // Currently WinInet does not support headers update, // hence don't need space for them although we'll need recreate a cache entry // if headers update is requested byte[] buffer = new byte[EntryBuffer.MarshalSize]; newEntry.Error = Status.Success; unsafe { fixed (byte *bytePtr = buffer) { EntryBuffer *ePtr = (EntryBuffer*) bytePtr; *ePtr = newEntry.Info; //set the version just in case ePtr->StructSize = EntryBuffer.MarshalSize; if ((attributes & Entry_FC.Headerinfo) == 0) { if (!UnsafeNclNativeMethods.UnsafeWinInetCache.SetUrlCacheEntryInfoW(newEntry.Key, bytePtr, attributes)) { newEntry.Error = (Status)Marshal.GetLastWin32Error(); } } else { // simulating headers update using Edited cache entry feature of WinInet Entry oldEntry = new Entry(newEntry.Key, newEntry.MaxBufferBytes); SafeUnlockUrlCacheEntryFile handle = null; bool wasEdited = false; try { // lock the entry and get the filename out. handle = LookupFile(oldEntry); if (handle == null) { //The same error would happen on update attributes, return it. newEntry.Error = oldEntry.Error; return newEntry.Error; } //Copy strings from old entry that are not present in the method parameters newEntry.Filename = oldEntry.Filename; newEntry.OriginalUrl = oldEntry.OriginalUrl; newEntry.FileExt = oldEntry.FileExt; // We don't need to update this and some other attributes since will replace entire entry attributes &= ~Entry_FC.Headerinfo; //Copy attributes from an old entry that are not present in the method parameters if ((attributes & Entry_FC.Exptime) == 0) { newEntry.Info.ExpireTime = oldEntry.Info.ExpireTime; } if ((attributes & Entry_FC.Modtime) == 0) { newEntry.Info.LastModifiedTime = oldEntry.Info.LastModifiedTime; } if ((attributes & Entry_FC.Attribute) == 0) { newEntry.Info.EntryType = oldEntry.Info.EntryType; newEntry.Info.U.ExemptDelta = oldEntry.Info.U.ExemptDelta; if ((oldEntry.Info.EntryType & EntryType.StickyEntry) == EntryType.StickyEntry) { attributes |= (Entry_FC.Attribute | Entry_FC.ExemptDelta); } } // Those attributes will be taken care of by Commit() attributes &= ~(Entry_FC.Exptime|Entry_FC.Modtime); wasEdited = (oldEntry.Info.EntryType & EntryType.Edited) != 0; if (!wasEdited) { // Prevent the file from being deleted on entry Remove (kinda hack) oldEntry.Info.EntryType |= EntryType.Edited; // Recursion! if (Update(oldEntry, Entry_FC.Attribute) != Status.Success) { newEntry.Error = oldEntry.Error; return newEntry.Error; } } } finally { if (handle != null) { handle.Close(); } } // At this point we try to delete the exisintg item and create a new one with the same // filename and the new headers. //We wish to ignore any errors from Remove since are going to replace the entry. Remove(oldEntry); if (Commit(newEntry) != Status.Success) { if (!wasEdited) { //revert back the original entry type oldEntry.Info.EntryType &= ~EntryType.Edited; Update(oldEntry, Entry_FC.Attribute); // Being already in error mode, cannot do much if Update fails. } return newEntry.Error; } // Now see what's left in attributes change request. if (attributes != Entry_FC.None) { Update(newEntry, attributes); } //At this point newEntry.Error should contain the resulting status //and we replaced the entry in the cache with the same body //but different headers. Some more attributes may have changed as well. } } } return newEntry.Error; } // // Updates a Cached Entry metadata according to attibutes flags. // internal static Status Remove(Entry entry) { entry.Error = Status.Success; if (!UnsafeNclNativeMethods.UnsafeWinInetCache.DeleteUrlCacheEntryW(entry.Key)) { entry.Error = (Status)Marshal.GetLastWin32Error(); } return entry.Error; } // // Gets the managed copy of a null terminated string resided in the buffer // #if DEBUG /* // Consider removing. private static unsafe string GetEntryBufferString(byte[] buffer, int offset) { fixed (void* bufferPtr = buffer) { return GetEntryBufferString(bufferPtr, offset); } } */ #endif private static unsafe string GetEntryBufferString(void* bufferPtr, int offset) { if (offset == 0) { return null; } IntPtr pointer = new IntPtr((byte*)bufferPtr + offset); return Marshal.PtrToStringUni(pointer); } // // Gets the headers and optionally other meta data out of a cached entry // // Whenever an empty line found in the buffer, the resulting array of // collections will grow in size // private static unsafe Status GetEntryHeaders(Entry entry, EntryBuffer* bufferPtr, byte[] buffer) { entry.Error = Status.Success; entry.MetaInfo = null; // if (bufferPtr->_OffsetHeaderInfo == IntPtr.Zero || bufferPtr->HeaderInfoChars == 0 || (bufferPtr->EntryType & EntryType.UrlHistory) != 0) { return Status.Success; } int bufferCharLength = bufferPtr->HeaderInfoChars + ((int)(bufferPtr->_OffsetHeaderInfo))/c_CharSz; if (bufferCharLength*c_CharSz > entry.MaxBufferBytes) { // WinInet bug? They may report offset+HeaderInfoChars as a greater value than MaxBufferBytes as total buffer size. // Actually, the last one seems to be accurate based on the data we have provided for Commit. bufferCharLength = entry.MaxBufferBytes/c_CharSz; } //WinInet may put terminating nulls at the end of the buffer, remove them. while (((char*)bufferPtr)[bufferCharLength-1] == 0) {--bufferCharLength;} entry.MetaInfo = Encoding.Unicode.GetString(buffer, (int)bufferPtr->_OffsetHeaderInfo, (bufferCharLength-(int)bufferPtr->_OffsetHeaderInfo/2)*2); return entry.Error; } /******************** ArrayList result = new ArrayList(); NameValueCollection collection = new NameValueCollection(); int offset = (int)bufferPtr->_OffsetHeaderInfo/c_CharSz; char *charPtr = (char*)bufferPtr; { int i = offset+1; for (; i < entry.MaxBufferBytes/c_CharSz; ++i) { if ((charPtr[i] == ':' || (charPtr[i] == '\n' && charPtr[(i-1)] == '\r'))) { break; } } if (i < entry.MaxBufferBytes/c_CharSz) { //If this looks like a status line if (charPtr[i] == '\n' && i > offset+1) { string s = Encoding.Unicode.GetString(buffer, offset*2, (i-offset-1)*2); offset = i+1; collection[string.Empty] = s; } } } int bufferCharLength = bufferPtr->HeaderInfoChars + ((int)(bufferPtr->_OffsetHeaderInfo))/c_CharSz; if (bufferCharLength*c_CharSz > entry.MaxBufferBytes) { // WinInet bug? They may report offset+HeaderInfoChars as a greater value than total buffer size. // Actually, the last one seems to be accurate based on the data we have provided for Commit. bufferCharLength = entry.MaxBufferBytes/c_CharSz; } while (true) { int totalLength = 0; DataParseStatus status = WebHeaderCollection.ParseHeaders(collection, false, buffer, bufferCharLength, ref offset, ref totalLength, entry.MaxBufferBytes/c_CharSz); if (status != DataParseStatus.Done) { if (status == DataParseStatus.NeedMoreData) { //WinInet puts terminating null at the end of the buffer, accept that as a "normal" case. if ((offset+1 == bufferCharLength) && charPtr[offset] == 0) { // accept as the last metainfo block if (collection.Count != 0) { result.Add(collection); } break; } } entry.Error = Status.CorruptedHeaders; //throw new InvalidOperationException("Cannot convert Cache Entry metadata into a NameValueCollection instance"); break; } result.Add(collection); // do we have more meta data? if (offset >= bufferCharLength) { break; } // continue parsing next collection collection = new NameValueCollection(); } entry.MetaInfo = (result.Count == 0? null: (NameValueCollection[])result.ToArray(typeof(NameValueCollection))); return entry.Error; } *********************/ #if DEBUG /* // Consider removing. // // For debugging will return a readbale representation of a cached entry info // private static string DebugEntryBuffer(byte[] buffer, int entryBufSize) { EntryBuffer Info; if (entryBufSize < EntryBuffer.MarshalSize) { throw new ArgumentOutOfRangeException("size"); } unsafe { fixed(void* vptr = buffer) { IntPtr ptr = new IntPtr(vptr); Info = (EntryBuffer)Marshal.PtrToStructure(ptr,typeof(EntryBuffer)); } } string allHeaders = null; // */ #endif //TRAVE } //END WinInet class }
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlSchemaType.cs
- WindowsContainer.cs
- MarkedHighlightComponent.cs
- XamlRtfConverter.cs
- SymDocumentType.cs
- OdbcConnectionOpen.cs
- SqlProcedureAttribute.cs
- Utils.cs
- MarkupExtensionReturnTypeAttribute.cs
- DefaultHttpHandler.cs
- CodeEntryPointMethod.cs
- VarInfo.cs
- ProfilePropertySettingsCollection.cs
- xmlfixedPageInfo.cs
- HyperLinkColumn.cs
- RijndaelManaged.cs
- CalendarItem.cs
- OdbcParameter.cs
- DataBindingHandlerAttribute.cs
- WinEventQueueItem.cs
- QueryActivatableWorkflowsCommand.cs
- PostBackOptions.cs
- PropertyConverter.cs
- ContractHandle.cs
- LayoutManager.cs
- XmlAutoDetectWriter.cs
- ImageConverter.cs
- bidPrivateBase.cs
- SerializationEventsCache.cs
- _Semaphore.cs
- PrinterResolution.cs
- TreeChangeInfo.cs
- Parser.cs
- StyleHelper.cs
- ProfessionalColors.cs
- PreloadedPackages.cs
- FlatButtonAppearance.cs
- NullableBoolConverter.cs
- ExtensibleClassFactory.cs
- DeploymentExceptionMapper.cs
- HandledMouseEvent.cs
- ImplicitInputBrush.cs
- ReturnValue.cs
- XmlAttributes.cs
- WebPartTransformer.cs
- util.cs
- ViewValidator.cs
- Serializer.cs
- DocumentEventArgs.cs
- IApplicationTrustManager.cs
- WebBrowserHelper.cs
- ListViewGroupConverter.cs
- InternalConfigEventArgs.cs
- WizardForm.cs
- SessionStateModule.cs
- FileSystemEnumerable.cs
- DBSchemaTable.cs
- _LoggingObject.cs
- LinkedResource.cs
- StorageMappingItemCollection.cs
- TableStyle.cs
- selecteditemcollection.cs
- CodeTypeConstructor.cs
- InlineObject.cs
- DependencyPropertyChangedEventArgs.cs
- NotFiniteNumberException.cs
- ProfileModule.cs
- BinaryOperationBinder.cs
- XmlIlVisitor.cs
- ManagementException.cs
- MimeWriter.cs
- PartitionerQueryOperator.cs
- WpfWebRequestHelper.cs
- ValueQuery.cs
- Form.cs
- DocumentViewerBase.cs
- FormattedTextSymbols.cs
- Subtree.cs
- ToolboxItem.cs
- TableLayoutColumnStyleCollection.cs
- MetadataArtifactLoaderXmlReaderWrapper.cs
- StreamUpdate.cs
- ClientApiGenerator.cs
- QuadraticBezierSegment.cs
- GlyphRun.cs
- StructuredProperty.cs
- XmlSchemaInfo.cs
- ModelVisual3D.cs
- RunWorkerCompletedEventArgs.cs
- CodeCommentStatement.cs
- Match.cs
- XmlCollation.cs
- _NegotiateClient.cs
- LogicalExpr.cs
- ListViewUpdatedEventArgs.cs
- SpeakInfo.cs
- Evaluator.cs
- HandleCollector.cs
- CloudCollection.cs
- QilStrConcatenator.cs