/// <summary> /// Checks if the given response can be cached. http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4 /// </summary> /// <returns>Returns true if cacheable, false otherwise.</returns> internal static bool IsCacheble(Uri uri, HTTPMethods method, HTTPResponse response) { if (method != HTTPMethods.Get) return false; if (response == null) return false; // Already cached if (response.StatusCode == 304) return false; if (response.StatusCode < 200 || response.StatusCode >= 400) return false; //http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2 var cacheControls = response.GetHeaderValues("cache-control"); if (cacheControls != null && cacheControls[0].ToLower().Contains("no-store")) return false; var pragmas = response.GetHeaderValues("pragma"); if (pragmas != null && pragmas[0].ToLower().Contains("no-cache")) return false; // Responses with byte ranges not supported yet. var byteRanges = response.GetHeaderValues("content-range"); if (byteRanges != null) return false; return true; }
/// <summary> /// Will set or update all cookies from the response object. /// </summary> internal static void Set(HTTPResponse response) { if (response == null) { return; } lock (Locker) { try { Maintain(); List <Cookie> newCookies = new List <Cookie>(); var setCookieHeaders = response.GetHeaderValues("set-cookie"); // No cookies. :'( if (setCookieHeaders == null) { return; } foreach (var cookieHeader in setCookieHeaders) { try { Cookie cookie = Cookie.Parse(cookieHeader, response.baseRequest.CurrentUri); if (cookie != null) { int idx; var old = Find(cookie, out idx); // if no value for the cookie or already expired then the server asked us to delete the cookie bool expired = string.IsNullOrEmpty(cookie.Value) || !cookie.WillExpireInTheFuture(); if (!expired) { // no old cookie, add it straith to the list if (old == null) { Cookies.Add(cookie); newCookies.Add(cookie); } else { // Update the creation-time of the newly created cookie to match the creation-time of the old-cookie. cookie.Date = old.Date; Cookies[idx] = cookie; newCookies.Add(cookie); } } else if (idx != -1) // delete the cookie { Cookies.RemoveAt(idx); } } } catch { // Ignore cookie on error } } response.Cookies = newCookies; } catch {} } }
/// <summary> /// Checks if the given response can be cached. http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4 /// </summary> /// <returns>Returns true if cacheable, false otherwise.</returns> internal static bool IsCacheble(Uri uri, HTTPMethods method, HTTPResponse response) { if (!IsSupported) { return(false); } if (method != HTTPMethods.Get) { return(false); } if (response == null) { return(false); } // Already cached if (response.StatusCode == 304) { return(false); } if (response.StatusCode < 200 || response.StatusCode >= 400) { return(false); } //http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2 var cacheControls = response.GetHeaderValues("cache-control"); if (cacheControls != null) { if (cacheControls.Exists(headerValue => { string value = headerValue.ToLower(); return(value.Contains("no-store") || value.Contains("no-cache")); })) { return(false); } } var pragmas = response.GetHeaderValues("pragma"); if (pragmas != null) { if (pragmas.Exists(headerValue => { string value = headerValue.ToLower(); return(value.Contains("no-store") || value.Contains("no-cache")); })) { return(false); } } // Responses with byte ranges not supported yet. var byteRanges = response.GetHeaderValues("content-range"); if (byteRanges != null) { return(false); } return(true); }
/// <summary> /// Checks if the given response can be cached. http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4 /// </summary> /// <returns>Returns true if cacheable, false otherwise.</returns> internal static bool IsCacheble(Uri uri, HTTPMethods method, HTTPResponse response) { if (!IsSupported) { return(false); } if (method != HTTPMethods.Get) { return(false); } if (response == null) { return(false); } // https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.12 - Cache Replacement // It MAY insert it into cache storage and MAY, if it meets all other requirements, use it to respond to any future requests that would previously have caused the old response to be returned. //if (response.StatusCode == 304) // return false; if (response.StatusCode < 200 || response.StatusCode >= 400) { return(false); } //http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2 var cacheControls = response.GetHeaderValues("cache-control"); if (cacheControls != null) { if (cacheControls.Exists(headerValue => { string value = headerValue.ToLower(); return(value.Contains("no-store") || value.Contains("no-cache")); })) { return(false); } } var pragmas = response.GetHeaderValues("pragma"); if (pragmas != null) { if (pragmas.Exists(headerValue => { string value = headerValue.ToLower(); return(value.Contains("no-store") || value.Contains("no-cache")); })) { return(false); } } // Responses with byte ranges not supported yet. var byteRanges = response.GetHeaderValues("content-range"); if (byteRanges != null) { return(false); } return(true); }
internal void SetUpCachingValues(HTTPResponse response) { response.CacheFileInfo = this; this.ETag = response.GetFirstHeaderValue("ETag").ToStr(this.ETag ?? string.Empty); this.Expires = response.GetFirstHeaderValue("Expires").ToDateTime(this.Expires); this.LastModified = response.GetFirstHeaderValue("Last-Modified").ToStr(this.LastModified ?? string.Empty); this.Age = response.GetFirstHeaderValue("Age").ToInt64(this.Age); this.Date = response.GetFirstHeaderValue("Date").ToDateTime(this.Date); List <string> cacheControls = response.GetHeaderValues("cache-control"); if (cacheControls != null && cacheControls.Count > 0) { // Merge all Cache-Control header values into one string cacheControl = cacheControls[0]; for (int i = 1; i < cacheControls.Count; ++i) { cacheControl += "," + cacheControls[i]; } if (!string.IsNullOrEmpty(cacheControl)) { string[] options = cacheControl.ToLowerInvariant().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] kvp = options.FindOption("max-age"); if (kvp != null) { // Some cache proxies will return float values double maxAge; if (double.TryParse(kvp[1], out maxAge)) { this.MaxAge = (int)maxAge; } else { this.MaxAge = 0; } } else { this.MaxAge = 0; } kvp = options.FindOption("stale-while-revalidate"); if (kvp != null && kvp.Length == 2 && !string.IsNullOrEmpty(kvp[1])) { this.StaleWhileRevalidate = kvp[1].ToInt64(0); } kvp = options.FindOption("stale-if-error"); if (kvp != null && kvp.Length == 2 && !string.IsNullOrEmpty(kvp[1])) { this.StaleIfError = kvp[1].ToInt64(0); } this.MustRevalidate = cacheControl.Contains("must-revalidate"); this.NoCache = cacheControl.Contains("no-cache"); } } this.Received = DateTime.UtcNow; }
/// <summary> /// Checks if the given response can be cached. http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4 /// </summary> /// <returns>Returns true if cacheable, false otherwise.</returns> internal static bool IsCacheble(Uri uri, HTTPMethods method, HTTPResponse response) { if (!IsSupported) { return(false); } if (method != HTTPMethods.Get) { return(false); } if (response == null) { return(false); } // https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.12 - Cache Replacement // It MAY insert it into cache storage and MAY, if it meets all other requirements, use it to respond to any future requests that would previously have caused the old response to be returned. //if (response.StatusCode == 304) // return false; // Partial response if (response.StatusCode == 206) { return(false); } if (response.StatusCode < 200 || response.StatusCode >= 400) { return(false); } //http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2 var cacheControls = response.GetHeaderValues("cache-control"); if (cacheControls != null) { if (cacheControls.Exists(headerValue => { string value = headerValue.ToLower(); if (value.StartsWith("max-age")) { string[] kvp = headerValue.FindOption("max-age"); if (kvp != null) { // Some cache proxies will return float values double maxAge; if (double.TryParse(kvp[1], out maxAge)) { // A negative max-age value is a no cache if (maxAge <= 0) { return(false); } } } } // https://csswizardry.com/2019/03/cache-control-for-civilians/#no-store return(value.Contains("no-store")); })) { return(false); } } var pragmas = response.GetHeaderValues("pragma"); if (pragmas != null) { if (pragmas.Exists(headerValue => { string value = headerValue.ToLower(); return(value.Contains("no-store") || value.Contains("no-cache")); })) { return(false); } } // Responses with byte ranges not supported yet. var byteRanges = response.GetHeaderValues("content-range"); if (byteRanges != null) { return(false); } // Store only if at least one caching header with proper value present var etag = response.GetFirstHeaderValue("ETag"); if (!string.IsNullOrEmpty(etag)) { return(true); } var expires = response.GetFirstHeaderValue("Expires").ToDateTime(DateTime.FromBinary(0)); if (expires >= DateTime.UtcNow) { return(true); } if (response.GetFirstHeaderValue("Last-Modified") != null) { return(true); } return(false); }
/// <summary> /// Will set or update all cookies from the response object. /// </summary> internal static void Set(HTTPResponse response) { if (response == null) return; lock(Locker) { try { Maintain(); List<Cookie> newCookies = new List<Cookie>(); var setCookieHeaders = response.GetHeaderValues("set-cookie"); // No cookies. :'( if (setCookieHeaders == null) return; foreach (var cookieHeader in setCookieHeaders) { try { Cookie cookie = Cookie.Parse(cookieHeader, response.baseRequest.CurrentUri); if (cookie != null) { int idx; var old = Find(cookie, out idx); // if no value for the cookie or already expired then the server asked us to delete the cookie bool expired = string.IsNullOrEmpty(cookie.Value) || !cookie.WillExpireInTheFuture(); if (!expired) { // no old cookie, add it straith to the list if (old == null) { Cookies.Add(cookie); newCookies.Add(cookie); } else { // Update the creation-time of the newly created cookie to match the creation-time of the old-cookie. cookie.Date = old.Date; Cookies[idx] = cookie; newCookies.Add(cookie); } } else if (idx != -1) // delete the cookie Cookies.RemoveAt(idx); } } catch { // Ignore cookie on error } } response.Cookies = newCookies; } catch {} } }
/// <summary> /// Will set or update all cookies from the response object. /// </summary> internal static bool Set(HTTPResponse response) { if (response == null) { return(false); } List <Cookie> newCookies = new List <Cookie>(); var setCookieHeaders = response.GetHeaderValues("set-cookie"); // No cookies. :'( if (setCookieHeaders == null) { return(false); } foreach (var cookieHeader in setCookieHeaders) { Cookie cookie = Cookie.Parse(cookieHeader, response.baseRequest.CurrentUri, response.baseRequest.Context); if (cookie != null) { rwLock.EnterWriteLock(); try { int idx; var old = Find(cookie, out idx); // if no value for the cookie or already expired then the server asked us to delete the cookie bool expired = string.IsNullOrEmpty(cookie.Value) || !cookie.WillExpireInTheFuture(); if (!expired) { // no old cookie, add it straight to the list if (old == null) { Cookies.Add(cookie); newCookies.Add(cookie); } else { // Update the creation-time of the newly created cookie to match the creation-time of the old-cookie. cookie.Date = old.Date; Cookies[idx] = cookie; newCookies.Add(cookie); } } else if (idx != -1) // delete the cookie { Cookies.RemoveAt(idx); } } catch { // Ignore cookie on error } finally { rwLock.ExitWriteLock(); } } } response.Cookies = newCookies; PluginEventHelper.EnqueuePluginEvent(new PluginEventInfo(PluginEvents.SaveCookieLibrary)); return(true); }