public FamilyDateTimeClass DecodeDate(HttpDate inDate) { if (inDate != null) { FamilyDateTimeClass date = new FamilyDateTimeClass(inDate.gedcom); } return(null); }
/// <summary> /// Sets the necessary headers to disable caching of a response on the client side. /// </summary> /// <param name="this">The <see cref="IHttpResponse"/> interface on which this method is called.</param> /// <exception cref="NullReferenceException"><paramref name="this"/> is <see langword="null"/>.</exception> public static void DisableCaching(this IHttpResponse @this) { var headers = @this.Headers; headers.Set(HttpHeaderNames.Expires, "Sat, 26 Jul 1997 05:00:00 GMT"); headers.Set(HttpHeaderNames.LastModified, HttpDate.Format(DateTime.UtcNow)); headers.Set(HttpHeaderNames.CacheControl, "no-store, no-cache, must-revalidate"); headers.Add(HttpHeaderNames.Pragma, "no-cache"); }
private static bool IsOutDated(string ifRangeHeader, DateTime lastModified) { try { DateTime utcLastModified = lastModified.ToUniversalTime(); DateTime utc = HttpDate.UtcParse(ifRangeHeader); return(utc < utcLastModified); } catch { return(true); } }
public async Task ListDirectoryAsync( MappedResourceInfo info, string absolutePath, IEnumerable <MappedResourceInfo> entries, Stream stream, CancellationToken cancellationToken) { const int MaxEntryLength = 50; const int SizeIndent = -20; // Negative for right alignment if (!info.IsDirectory) { throw SelfCheck.Failure($"{nameof(HtmlDirectoryLister)}.{nameof(ListDirectoryAsync)} invoked with a file, not a directory."); } var encodedPath = WebUtility.HtmlEncode(absolutePath); using var text = new StreamWriter(stream, Encoding.UTF8); text.Write("<html><head><title>Index of "); text.Write(encodedPath); text.Write("</title></head><body><h1>Index of "); text.Write(encodedPath); text.Write("</h1><hr/><pre>"); if (encodedPath.Length > 1) { text.Write("<a href='../'>../</a>\n"); } entries = entries.ToArray(); foreach (var directory in entries.Where(m => m.IsDirectory).OrderBy(e => e.Name)) { text.Write($"<a href=\"{Uri.EscapeDataString(directory.Name)}{Path.DirectorySeparatorChar}\">{WebUtility.HtmlEncode(directory.Name)}</a>"); text.Write(new string(' ', Math.Max(1, MaxEntryLength - directory.Name.Length + 1))); text.Write(HttpDate.Format(directory.LastModifiedUtc)); text.Write('\n'); await Task.Yield(); } foreach (var file in entries.Where(m => m.IsFile).OrderBy(e => e.Name)) { text.Write($"<a href=\"{Uri.EscapeDataString(file.Name)}{Path.DirectorySeparatorChar}\">{WebUtility.HtmlEncode(file.Name)}</a>"); text.Write(new string(' ', Math.Max(1, MaxEntryLength - file.Name.Length + 1))); text.Write(HttpDate.Format(file.LastModifiedUtc)); text.Write($" {file.Length.ToString("#,###", CultureInfo.InvariantCulture),SizeIndent}\n"); await Task.Yield(); } text.Write("</pre><hr/></body></html>"); }
// Check whether the If-Modified-Since request header exists // and specifies a date and time more recent than or equal to // the date and time of last modification of the requested resource. // RFC7232, Section 3.3 /// <summary> /// <para>Checks whether an <c>If-Modified-Since</c> header exists in a request /// and, if so, whether its value is a date and time more recent or equal to /// a given <see cref="DateTime"/>.</para> /// <para>See <see href="https://tools.ietf.org/html/rfc7232#section-3.3">RFC7232, Section 3.3</see> /// for a normative reference.</para> /// </summary> /// <param name="this">The <see cref="IHttpRequest"/> on which this method is called.</param> /// <param name="lastModifiedUtc">A date and time value, in Coordinated Universal Time, /// expressing the last time a resource was modified.</param> /// <param name="headerExists">When this method returns, a value that indicates whether an /// <c>If-Modified-Since</c> header is present in <paramref name="this"/>, regardless of the method's /// return value. This parameter is passed uninitialized.</param> /// <returns><see langword="true"/> if an <c>If-Modified-Since</c> header is present in /// <paramref name="this"/> and its value is a date and time more recent or equal to <paramref name="lastModifiedUtc"/>; /// <see langword="false"/> otherwise.</returns> public static bool CheckIfModifiedSince(this IHttpRequest @this, DateTime lastModifiedUtc, out bool headerExists) { var value = @this.Headers.Get(HttpHeaderNames.IfModifiedSince); if (value == null) { headerExists = false; return(false); } headerExists = true; return(HttpDate.TryParse(value, out var dateTime) && dateTime.UtcDateTime >= lastModifiedUtc); }
public async Task ListDirectoryAsync( MappedResourceInfo info, string absoluteUrlPath, IEnumerable <MappedResourceInfo> entries, Stream stream, CancellationToken cancellationToken) { if (info?.IsDirectory != true) { throw new ArgumentException("HtmlDirectoryLister.ListDirectoryAsync invoked with a file, not a directory."); } string str = WebUtility.HtmlEncode(absoluteUrlPath); await using StreamWriter text = new StreamWriter(stream, Encoding.UTF8); text.Write("<html><head><title>Index of "); text.Write(str); text.Write("</title></head><body><h1>Index of "); text.Write(str); text.Write("</h1><hr/><pre>"); if (str?.Length > 1) { text.Write("<a href='../'>../</a>\n"); } entries = entries.ToArray(); foreach (MappedResourceInfo mappedResourceInfo in entries.Where(m => m.IsDirectory).OrderBy(e => e.Name)) { text.Write( $"<a href=\"{(!string.IsNullOrEmpty(str)? str + (str.EndsWith(Path.DirectorySeparatorChar) ? "" : Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture)) : "")}{Uri.EscapeDataString(mappedResourceInfo.Name)}{Path.DirectorySeparatorChar}\">{WebUtility.HtmlEncode(mappedResourceInfo.Name)}</a>"); text.Write(new string(' ', Math.Max(1, 50 - mappedResourceInfo.Name.Length + 1))); text.Write(HttpDate.Format(mappedResourceInfo.LastModifiedUtc)); text.Write('\n'); await Task.Yield(); } foreach (MappedResourceInfo mappedResourceInfo in entries.Where(m => m.IsFile).OrderBy(e => e.Name)) { text.Write( $"<a href=\"{(!string.IsNullOrEmpty(str) ? str + (str.EndsWith(Path.DirectorySeparatorChar) ? "" : Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture)) : "")}{Uri.EscapeDataString(mappedResourceInfo.Name)}\">{WebUtility.HtmlEncode(mappedResourceInfo.Name)}</a>"); text.Write(new string(' ', Math.Max(1, 50 - mappedResourceInfo.Name.Length + 1))); text.Write(HttpDate.Format(mappedResourceInfo.LastModifiedUtc)); text.Write(" {0,-20}\n", mappedResourceInfo.Length.ToString("#,###", CultureInfo.InvariantCulture)); await Task.Yield(); } text.Write("</pre><hr/></body></html>"); }
public IHttpAction Handle(Request request) { if (request.Object.Method == "GET" || request.Object.Method == "HEAD") { var includeBody = request.Object.Method != "HEAD"; var httpHeaders = new HttpHeaders(); var fileSystem = new FileSystem(Path.Combine(DirectoryRoot, "www", request.VirtualHost.Directory, request.Object.Uri.Url), request.VirtualHost.DefaultIndex); var fileDescription = fileSystem.GetFile(); if (fileDescription == null) { return(new NotFound(includeBody)); } var dateChange = new HttpDate(fileDescription.GetLastModified()); httpHeaders.Add("Content-Type", HttpMimeType.GetByExtension(fileDescription.GetExtension()) + "; charset=" + fileDescription.GetEncoding()); httpHeaders.Add("Last-Modified", dateChange.ToString()); if (request.Object.HttpRange != null) { try { request.Object.HttpRange.Normalize(fileDescription.GetFileSize() - 1); var bytesArray = fileDescription.GetRangeBytes(request.Object.HttpRange); if (bytesArray.Length > 0) { httpHeaders.Add("Content-Range", request.Object.HttpRange.ToString()); return(new PartialContent(httpHeaders, bytesArray, includeBody)); } else { return(new NotSatisfiable(includeBody)); } } catch (Exception) { request.Object.HttpRange = null; } } if (request.Object["If-Modified-Since"] != null) { try { var requestDate = DateTime.Parse(request.Object["If-Modified-Since"].Trim()); if (requestDate >= dateChange.DateTime) { return(new NotModified(httpHeaders, includeBody)); } } catch { // ignored } } return(new Ok(httpHeaders, fileDescription.GetAllBytes(), includeBody)); } return(NextHandler != null?NextHandler.Handle(request) : new NotImplemented()); }
/* * Try to find this request in the cache. If so, return it. Otherwise, * store the cache key for use on Leave. */ /// <include file='doc\OutputCacheModule.uex' path='docs/doc[@for="OutputCacheModule.OnEnter"]/*' /> /// <devdoc> /// <para>Raises the <see langword='Enter'/> /// event, which searches the output cache for an item to satisfy the HTTP request. </para> /// </devdoc> internal /*public*/ void OnEnter(Object source, EventArgs eventArgs) { HttpApplication app; HttpContext context; string key; HttpRequest request; HttpResponse response; Object item; CachedRawResponse cachedRawResponse; HttpCachePolicySettings settings; int i, n; bool sendBody; HttpValidationStatus validationStatus, validationStatusFinal; ValidationCallbackInfo callbackInfo; string ifModifiedSinceHeader; DateTime utcIfModifiedSince; string etag; string[] etags; int send304; string cacheControl; string[] cacheDirectives = null; string pragma; string[] pragmaDirectives = null; string directive; int maxage; int minfresh; int age; int fresh; bool hasValidationPolicy; CachedVary cachedVary; CacheInternal cacheInternal; Debug.Trace("OutputCacheModuleEnter", "Beginning OutputCacheModule::Enter"); app = (HttpApplication)source; context = app.Context; request = context.Request; response = context.Response; _key = null; _recordedCacheMiss = false; _method = CacheRequestMethod.Invalid; /* * Check if the request can be resolved for this method. */ switch (request.HttpMethod) { case "HEAD": _method = CacheRequestMethod.Head; break; case "GET": _method = CacheRequestMethod.Get; break; case "POST": _method = CacheRequestMethod.Post; break; default: Debug.Trace("OutputCacheModuleEnter", "Output cache miss, Http method not GET, POST, or HEAD" + "\nReturning from OutputCacheModule::Enter"); return; } /* * Create a lookup key. Remember the key for use inside Leave() */ _key = key = CreateOutputCachedItemKey(context, null); Debug.Assert(_key != null, "_key != null"); /* * Lookup the cached item. */ cacheInternal = HttpRuntime.CacheInternal; item = cacheInternal.Get(key); if (item == null) { Debug.Trace("OutputCacheModuleEnter", "Output cache miss, item not found.\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); return; } cachedVary = item as CachedVary; if (cachedVary != null) { /* * This cached output has a Vary policy. Create a new key based * on the vary headers in cachedRawResponse and try again. */ Debug.Trace("OutputCacheModuleEnter", "Output cache found CachedVary, \n\tkey=" + key); key = CreateOutputCachedItemKey(context, cachedVary); if (key == null) { Debug.Trace("OutputCacheModuleEnter", "Output cache miss, key could not be created for vary-by item." + "\nReturning from OutputCacheModule::Enter"); return; } item = cacheInternal.Get(key); if (item == null) { Debug.Trace("OutputCacheModuleEnter", "Output cache miss, item not found.\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); return; } } Debug.Assert(item.GetType() == typeof(CachedRawResponse), "item.GetType() == typeof(CachedRawResponse)"); cachedRawResponse = (CachedRawResponse)item; settings = cachedRawResponse._settings; if (cachedVary == null && !settings.IgnoreParams) { /* * This cached output has no vary policy, so make sure it doesn't have a query string or form post. */ if (_method == CacheRequestMethod.Post) { Debug.Trace("OutputCacheModuleEnter", "Output cache item found but method is POST and no VaryByParam specified." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } string queryStringText = request.QueryStringText; if (queryStringText != null && queryStringText.Length > 0) { Debug.Trace("OutputCacheModuleEnter", "Output cache item found but contains a querystring and no VaryByParam specified." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } } hasValidationPolicy = settings.HasValidationPolicy(); /* * Determine whether the client can accept a cached copy, and * get values of other cache control directives. * * We do this after lookup so we don't have to crack the headers * if the item is not found. Cracking the headers is expensive. */ if (!hasValidationPolicy) { cacheControl = request.Headers["Cache-Control"]; if (cacheControl != null) { cacheDirectives = cacheControl.Split(s_fieldSeparators); for (i = 0; i < cacheDirectives.Length; i++) { directive = cacheDirectives[i]; if (directive == "no-cache") { Debug.Trace("OutputCacheModuleEnter", "Skipping lookup because of Cache-Control: no-cache directive." + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } if (directive.StartsWith("max-age=")) { try { maxage = Convert.ToInt32(directive.Substring(8)); } catch { maxage = -1; } if (maxage >= 0) { age = (int)((context.UtcTimestamp.Ticks - settings.UtcTimestampCreated.Ticks) / TimeSpan.TicksPerSecond); if (age >= maxage) { Debug.Trace("OutputCacheModuleEnter", "Not returning found item due to Cache-Control: max-age directive." + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } } } else if (directive.StartsWith("min-fresh=")) { try { minfresh = Convert.ToInt32(directive.Substring(10)); } catch { minfresh = -1; } if (minfresh >= 0 && settings.IsExpiresSet && !settings.SlidingExpiration) { fresh = (int)((settings.UtcExpires.Ticks - context.UtcTimestamp.Ticks) / TimeSpan.TicksPerSecond); if (fresh < minfresh) { Debug.Trace("OutputCacheModuleEnter", "Not returning found item due to Cache-Control: min-fresh directive." + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } } } } } pragma = request.Headers["Pragma"]; if (pragma != null) { pragmaDirectives = pragma.Split(s_fieldSeparators); for (i = 0; i < pragmaDirectives.Length; i++) { if (pragmaDirectives[i] == "no-cache") { Debug.Trace("OutputCacheModuleEnter", "Skipping lookup because of Pragma: no-cache directive." + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } } } } else if (settings.ValidationCallbackInfo != null) { /* * Check if the item is still valid. */ validationStatus = HttpValidationStatus.Valid; validationStatusFinal = validationStatus; for (i = 0, n = settings.ValidationCallbackInfo.Length; i < n; i++) { callbackInfo = settings.ValidationCallbackInfo[i]; try { callbackInfo.handler(context, callbackInfo.data, ref validationStatus); } catch (Exception e) { validationStatus = HttpValidationStatus.Invalid; HttpApplicationFactory.RaiseError(e); } switch (validationStatus) { case HttpValidationStatus.Invalid: Debug.Trace("OutputCacheModuleEnter", "Output cache item found but callback invalidated it." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); cacheInternal.Remove(key); RecordCacheMiss(); return; case HttpValidationStatus.IgnoreThisRequest: validationStatusFinal = HttpValidationStatus.IgnoreThisRequest; break; case HttpValidationStatus.Valid: break; default: Debug.Trace("OutputCacheModuleEnter", "Invalid validation status, ignoring it, status=" + validationStatus + "\n\tkey=" + key); validationStatus = validationStatusFinal; break; } } if (validationStatusFinal == HttpValidationStatus.IgnoreThisRequest) { Debug.Trace("OutputCacheModuleEnter", "Output cache item found but callback status is IgnoreThisRequest." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); RecordCacheMiss(); return; } Debug.Assert(validationStatusFinal == HttpValidationStatus.Valid, "validationStatusFinal == HttpValidationStatus.Valid"); } /* * Try to satisfy a conditional request. The cached response * must satisfy all conditions that are present. * * We can only satisfy a conditional request if the response * is buffered and has no substitution blocks. * * N.B. RFC 2616 says conditional requests only occur * with the GET method, but we try to satisfy other * verbs (HEAD, POST) as well. */ send304 = -1; if (response.IsBuffered() && !cachedRawResponse._rawResponse.HasSubstBlocks) { /* Check "If-Modified-Since" header */ ifModifiedSinceHeader = request.IfModifiedSince; if (ifModifiedSinceHeader != null) { send304 = 0; try { utcIfModifiedSince = HttpDate.UtcParse(ifModifiedSinceHeader); if (settings.IsLastModifiedSet && settings.UtcLastModified <= utcIfModifiedSince && utcIfModifiedSince <= context.UtcTimestamp) { send304 = 1; } } catch { Debug.Trace("OutputCacheModuleEnter", "Ignore If-Modified-Since header, invalid format: " + ifModifiedSinceHeader); } } /* Check "If-None-Match" header */ if (send304 != 0) { etag = request.IfNoneMatch; if (etag != null) { send304 = 0; etags = etag.Split(s_fieldSeparators); for (i = 0, n = etags.Length; i < n; i++) { if (i == 0 && etags[i].Equals("*")) { send304 = 1; break; } if (etags[i].Equals(settings.ETag)) { send304 = 1; break; } } } } } if (send304 == 1) { /* * Send 304 Not Modified */ Debug.Trace("OutputCacheModuleEnter", "Output cache hit & conditional request satisfied, status=304." + "\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); response.ClearAll(); response.StatusCode = 304; } else { /* * Send the full response. */ #if DBG if (send304 == -1) { Debug.Trace("OutputCacheModuleEnter", "Output cache hit.\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); } else { Debug.Trace("OutputCacheModuleEnter", "Output cache hit but conditional request not satisfied.\n\tkey=" + key + "\nReturning from OutputCacheModule::Enter"); } #endif sendBody = (_method != CacheRequestMethod.Head); // UseSnapshot calls ClearAll response.UseSnapshot(cachedRawResponse._rawResponse, sendBody); } response.Cache.ResetFromHttpCachePolicySettings(settings, context.UtcTimestamp); PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_RATIO_BASE); PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_HITS); _key = null; _recordedCacheMiss = false; _method = CacheRequestMethod.Invalid; app.CompleteRequest(); }
/// <summary>Creates a <see cref="CookieList"/> by parsing /// the value of one or more <c>Cookie</c> or <c>Set-Cookie</c> headers.</summary> /// <param name="headerValue">The value, or comma-separated list of values, /// of the header or headers.</param> /// <returns>A newly-created instance of <see cref="CookieList"/>.</returns> public static CookieList Parse(string headerValue) { var cookies = new CookieList(); Cookie cookie = null; var pairs = SplitCookieHeaderValue(headerValue); for (var i = 0; i < pairs.Length; i++) { var pair = pairs[i].Trim(); if (pair.Length == 0) { continue; } if (pair.StartsWith("version", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.Version = int.Parse(GetValue(pair, true), CultureInfo.InvariantCulture); } else if (pair.StartsWith("expires", StringComparison.OrdinalIgnoreCase) && cookie != null) { var buff = new StringBuilder(GetValue(pair), 32); if (i < pairs.Length - 1) { buff.AppendFormat(CultureInfo.InvariantCulture, ", {0}", pairs[++i].Trim()); } if (!HttpDate.TryParse(buff.ToString(), out var expires)) { expires = DateTimeOffset.Now; } if (cookie.Expires == DateTime.MinValue) { cookie.Expires = expires.LocalDateTime; } } else if (pair.StartsWith("max-age", StringComparison.OrdinalIgnoreCase) && cookie != null) { var max = int.Parse(GetValue(pair, true), CultureInfo.InvariantCulture); cookie.Expires = DateTime.Now.AddSeconds(max); } else if (pair.StartsWith("path", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.Path = GetValue(pair); } else if (pair.StartsWith("domain", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.Domain = GetValue(pair); } else if (pair.StartsWith("port", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.Port = pair.Equals("port", StringComparison.OrdinalIgnoreCase) ? "\"\"" : GetValue(pair); } else if (pair.StartsWith("comment", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.Comment = WebUtility.UrlDecode(GetValue(pair)); } else if (pair.StartsWith("commenturl", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.CommentUri = UriUtility.StringToUri(GetValue(pair, true)); } else if (pair.StartsWith("discard", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.Discard = true; } else if (pair.StartsWith("secure", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.Secure = true; } else if (pair.StartsWith("httponly", StringComparison.OrdinalIgnoreCase) && cookie != null) { cookie.HttpOnly = true; } else { if (cookie != null) { cookies.Add(cookie); } cookie = ParseCookie(pair); } } if (cookie != null) { cookies.Add(cookie); } return(cookies); }
// Checks the Range request header to tell whether to send // a "206 Partial Content" response. /// <summary> /// <para>Checks whether a <c>Range</c> header exists in a request /// and, if so, determines whether it is possible to send a <c>206 Partial Content</c> response.</para> /// <para>See <see href="https://tools.ietf.org/html/rfc7233">RFC7233</see> /// for a normative reference; however, see the Remarks section for more information /// about the RFC compliance of this method.</para> /// </summary> /// <param name="this">The <see cref="IHttpRequest"/> on which this method is called.</param> /// <param name="contentLength">The total length, in bytes, of the response entity, i.e. /// what would be sent in a <c>200 OK</c> response.</param> /// <param name="entityTag">An entity tag representing the response entity. This value is checked against /// the <c>If-Range</c> header, if it is present.</param> /// <param name="lastModifiedUtc">The date and time value, in Coordinated Universal Time, /// expressing the last modification time of the resource entity. This value is checked against /// the <c>If-Range</c> header, if it is present.</param> /// <param name="start">When this method returns <see langword="true"/>, the start of the requested byte range. /// This parameter is passed uninitialized.</param> /// <param name="upperBound"> /// <para>When this method returns <see langword="true"/>, the upper bound of the requested byte range. /// This parameter is passed uninitialized.</para> /// <para>Note that the upper bound of a range is NOT the sum of the range's start and length; /// for example, a range expressed as <c>bytes=0-99</c> has a start of 0, an upper bound of 99, /// and a length of 100 bytes.</para> /// </param> /// <returns> /// <para>This method returns <see langword="true"/> if the following conditions are satisfied:</para> /// <list type="bullet"> /// <item><description>>the request's HTTP method is <c>GET</c>;</description></item> /// <item><description>>a <c>Range</c> header is present in the request;</description></item> /// <item><description>>either no <c>If-Range</c> header is present in the request, or it /// specifies an entity tag equal to <paramref name="entityTag"/>, or a UTC date and time /// equal to <paramref name="lastModifiedUtc"/>;</description></item> /// <item><description>>the <c>Range</c> header specifies exactly one range;</description></item> /// <item><description>>the specified range is entirely contained in the range from 0 to <paramref name="contentLength"/> - 1.</description></item> /// </list> /// <para>If the last condition is not satisfied, i.e. the specified range start and/or upper bound /// are out of the range from 0 to <paramref name="contentLength"/> - 1, this method does not return; /// it throws a <see cref="HttpRangeNotSatisfiableException"/> instead.</para> /// <para>If any of the other conditions are not satisfied, this method returns <see langword="false"/>.</para> /// </returns> /// <remarks> /// <para>According to <see href="https://tools.ietf.org/html/rfc7233#section-3.1">RFC7233, Section 3.1</see>, /// there are several conditions under which a server may ignore or reject a range request; therefore, /// clients are (or should be) prepared to receive a <c>200 OK</c> response with the whole response /// entity instead of the requested range(s). For this reason, until the generation of /// <c>multipart/byteranges</c> responses is implemented in EmbedIO, this method will ignore /// range requests specifying more than one range, even if this behavior is not, strictly speaking, /// RFC7233-compliant.</para> /// <para>To make clients aware that range requests are accepted for a resource, every <c>200 OK</c> /// (or <c>304 Not Modified</c>) response for the same resource should include an <c>Accept-Ranges</c> /// header with the string <c>bytes</c> as value.</para> /// </remarks> public static bool IsRangeRequest(this IHttpRequest @this, long contentLength, string entityTag, DateTime lastModifiedUtc, out long start, out long upperBound) { start = 0; upperBound = contentLength - 1; // RFC7233, Section 3.1: // "A server MUST ignore a Range header field received with a request method other than GET." if (@this.HttpVerb != HttpVerbs.Get) { return(false); } // No Range header, no partial content. var rangeHeader = @this.Headers.Get(HttpHeaderNames.Range); if (rangeHeader == null) { return(false); } // Ignore the Range header if there is no If-Range header // or if the If-Range header specifies a non-matching validator. // RFC7233, Section 3.2: "If the validator given in the If-Range header field matches the // current validator for the selected representation of the target // resource, then the server SHOULD process the Range header field as // requested.If the validator does not match, the server MUST ignore // the Range header field.Note that this comparison by exact match, // including when the validator is an HTTP-date, differs from the // "earlier than or equal to" comparison used when evaluating an // If-Unmodified-Since conditional." var ifRange = @this.Headers.Get(HttpHeaderNames.IfRange)?.Trim(); if (ifRange != null && ifRange != entityTag) { if (!HttpDate.TryParse(ifRange, out var rangeDate)) { return(false); } if (rangeDate.UtcDateTime != lastModifiedUtc) { return(false); } } // Ignore the Range request header if it cannot be parsed successfully. if (!RangeHeaderValue.TryParse(rangeHeader, out var range)) { return(false); } // EmbedIO does not support multipart/byteranges responses (yet), // thus ignore range requests that specify one range. if (range.Ranges.Count != 1) { return(false); } var firstRange = range.Ranges.First(); start = firstRange.From ?? 0L; upperBound = firstRange.To ?? contentLength - 1; if (start >= contentLength || upperBound < start || upperBound >= contentLength) { throw HttpException.RangeNotSatisfiable(contentLength); } return(true); }
internal void OnEnter(object source, EventArgs eventArgs) { this._key = null; this._recordedCacheMiss = false; if (OutputCache.InUse) { string[] strArray2 = null; string[] strArray3 = null; HttpApplication application = (HttpApplication)source; HttpContext context = application.Context; context.GetFilePathData(); HttpRequest request = context.Request; HttpResponse response = context.Response; switch (request.HttpVerb) { case HttpVerb.GET: case HttpVerb.HEAD: case HttpVerb.POST: { string str; this._key = str = this.CreateOutputCachedItemKey(context, null); object obj2 = OutputCache.Get(str); if (obj2 != null) { int num; int length; CachedVary cachedVary = obj2 as CachedVary; if (cachedVary != null) { str = this.CreateOutputCachedItemKey(context, cachedVary); if (str == null) { return; } if (cachedVary._contentEncodings == null) { obj2 = OutputCache.Get(str); } else { obj2 = null; bool flag3 = true; string knownRequestHeader = context.WorkerRequest.GetKnownRequestHeader(0x16); if (knownRequestHeader != null) { string[] contentEncodings = cachedVary._contentEncodings; int startIndex = 0; bool flag4 = false; while (!flag4) { flag4 = true; int index = GetAcceptableEncoding(contentEncodings, startIndex, knownRequestHeader); if (index > -1) { flag3 = false; obj2 = OutputCache.Get(str + contentEncodings[index]); if (obj2 == null) { startIndex = index + 1; if (startIndex < contentEncodings.Length) { flag4 = false; } } } else if (index == -2) { flag3 = false; } } } if ((obj2 == null) && flag3) { obj2 = OutputCache.Get(str); } } if ((obj2 == null) || (((CachedRawResponse)obj2)._cachedVaryId != cachedVary.CachedVaryId)) { if (obj2 != null) { OutputCache.Remove(str, context); } return; } } CachedRawResponse response2 = (CachedRawResponse)obj2; HttpCachePolicySettings settings = response2._settings; if ((cachedVary == null) && !settings.IgnoreParams) { if (request.HttpVerb == HttpVerb.POST) { this.RecordCacheMiss(); return; } if (request.HasQueryString) { this.RecordCacheMiss(); return; } } if (settings.IgnoreRangeRequests) { string str8 = request.Headers["Range"]; if (StringUtil.StringStartsWithIgnoreCase(str8, "bytes")) { return; } } if (!settings.HasValidationPolicy()) { string str4 = request.Headers["Cache-Control"]; if (str4 != null) { strArray2 = str4.Split(s_fieldSeparators); for (num = 0; num < strArray2.Length; num++) { string str6 = strArray2[num]; switch (str6) { case "no-cache": case "no-store": this.RecordCacheMiss(); return; } if (StringUtil.StringStartsWith(str6, "max-age=")) { int num4; try { num4 = Convert.ToInt32(str6.Substring(8), CultureInfo.InvariantCulture); } catch { num4 = -1; } if (num4 >= 0) { int num6 = (int)((context.UtcTimestamp.Ticks - settings.UtcTimestampCreated.Ticks) / 0x989680L); if (num6 >= num4) { this.RecordCacheMiss(); return; } } } else if (StringUtil.StringStartsWith(str6, "min-fresh=")) { int num5; try { num5 = Convert.ToInt32(str6.Substring(10), CultureInfo.InvariantCulture); } catch { num5 = -1; } if (((num5 >= 0) && settings.IsExpiresSet) && !settings.SlidingExpiration) { int num7 = (int)((settings.UtcExpires.Ticks - context.UtcTimestamp.Ticks) / 0x989680L); if (num7 < num5) { this.RecordCacheMiss(); return; } } } } } string str5 = request.Headers["Pragma"]; if (str5 != null) { strArray3 = str5.Split(s_fieldSeparators); for (num = 0; num < strArray3.Length; num++) { if (strArray3[num] == "no-cache") { this.RecordCacheMiss(); return; } } } } else if (settings.ValidationCallbackInfo != null) { HttpValidationStatus valid = HttpValidationStatus.Valid; HttpValidationStatus ignoreThisRequest = valid; num = 0; length = settings.ValidationCallbackInfo.Length; while (num < length) { ValidationCallbackInfo info = settings.ValidationCallbackInfo[num]; try { info.handler(context, info.data, ref valid); } catch (Exception exception) { valid = HttpValidationStatus.Invalid; HttpApplicationFactory.RaiseError(exception); } switch (valid) { case HttpValidationStatus.Invalid: OutputCache.Remove(str, context); this.RecordCacheMiss(); return; case HttpValidationStatus.IgnoreThisRequest: ignoreThisRequest = HttpValidationStatus.IgnoreThisRequest; break; case HttpValidationStatus.Valid: break; default: valid = ignoreThisRequest; break; } num++; } if (ignoreThisRequest == HttpValidationStatus.IgnoreThisRequest) { this.RecordCacheMiss(); return; } } HttpRawResponse rawResponse = response2._rawResponse; if ((cachedVary == null) || (cachedVary._contentEncodings == null)) { string acceptEncoding = request.Headers["Accept-Encoding"]; string contentEncoding = null; ArrayList headers = rawResponse.Headers; if (headers != null) { foreach (HttpResponseHeader header in headers) { if (header.Name == "Content-Encoding") { contentEncoding = header.Value; break; } } } if (!IsAcceptableEncoding(contentEncoding, acceptEncoding)) { this.RecordCacheMiss(); return; } } int num3 = -1; if (!rawResponse.HasSubstBlocks) { string ifModifiedSince = request.IfModifiedSince; if (ifModifiedSince != null) { num3 = 0; try { DateTime time = HttpDate.UtcParse(ifModifiedSince); if ((settings.IsLastModifiedSet && (settings.UtcLastModified <= time)) && (time <= context.UtcTimestamp)) { num3 = 1; } } catch { } } if (num3 != 0) { string ifNoneMatch = request.IfNoneMatch; if (ifNoneMatch != null) { num3 = 0; string[] strArray = ifNoneMatch.Split(s_fieldSeparators); num = 0; length = strArray.Length; while (num < length) { if ((num == 0) && strArray[num].Equals("*")) { num3 = 1; break; } if (strArray[num].Equals(settings.ETag)) { num3 = 1; break; } num++; } } } } if (num3 == 1) { response.ClearAll(); response.StatusCode = 0x130; } else { bool sendBody = request.HttpVerb != HttpVerb.HEAD; response.UseSnapshot(rawResponse, sendBody); } response.Cache.ResetFromHttpCachePolicySettings(settings, context.UtcTimestamp); string originalCacheUrl = response2._kernelCacheUrl; if (originalCacheUrl != null) { response.SetupKernelCaching(originalCacheUrl); } PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_RATIO_BASE); PerfCounters.IncrementCounter(AppPerfCounter.OUTPUT_CACHE_HITS); this._key = null; this._recordedCacheMiss = false; application.CompleteRequest(); return; } return; } } } }