From 4385e10c9fbbb3b65a5833762d23afa25ff4b030 Mon Sep 17 00:00:00 2001 From: Elisabeth Unger Date: Fri, 14 Oct 2022 11:52:39 +0200 Subject: [PATCH] Download langtags using etag instead of if-modified-since header Implement #987. +semver:minor --- CHANGELOG.md | 1 + SIL.WritingSystems/Sldr.cs | 91 +++++++++++++++++++++++++++----------- 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a153eab..3b2afa891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -174,6 +174,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [SIL.Core] ConsoleErrorReporter logs exception if available - [SIL.Core, SIL.Windows.Forms] If WinFormsErrorReporter is set as the ErrorReporter, and ErrorReporter.NotifyUserOfProblem(IRepeatNoticePolicy, Exception, String, params object[]) is passed null for the exception, the "Details" button will no longer appear, making this consistent with the no-Exception overload of this method - [SIL.WritingSystems] Changed behavior of IetfLanguageTag to better handle zh-TW. +- [SIL.WritingSystems] Download of langtag.json handled with etag instead of IF-MODIFIED-SINCE Header(#987) ### Fixed diff --git a/SIL.WritingSystems/Sldr.cs b/SIL.WritingSystems/Sldr.cs index bc2e3b878..ec155f237 100644 --- a/SIL.WritingSystems/Sldr.cs +++ b/SIL.WritingSystems/Sldr.cs @@ -451,9 +451,10 @@ public static IReadOnlyKeyedCollection LanguageTags } } - public static void InitializeLanguageTags() + public static void InitializeLanguageTags(bool downloadLangTags = true) { LoadLanguageTagsIfNecessary(); + if (downloadLangTags) LoadLanguageTags(); } /// @@ -470,35 +471,75 @@ private static void LoadLanguageTagsIfNecessary() CreateSldrCacheDirectory(); cachedAllTagsPath = Path.Combine(SldrCachePath, "langtags.json"); + string etagPath; + etagPath = Path.Combine(SldrCachePath, "langtags.json.etag"); var sinceTime = _embeddedAllTagsTime.ToUniversalTime(); if (File.Exists(cachedAllTagsPath)) { var fileTime = File.GetLastWriteTimeUtc(cachedAllTagsPath); if (sinceTime > fileTime) + { // delete the old langtags.json file if a newer embedded one is available. // this can happen if the application is upgraded to use a newer version of SIL.WritingSystems // that has an updated embedded langtags.json file. File.Delete(cachedAllTagsPath); + File.Delete(etagPath); + } else sinceTime = fileTime; } sinceTime += TimeSpan.FromSeconds(1); - try - { - if (_offlineMode) - throw new WebException("Test mode: SLDR offline so accessing cache", WebExceptionStatus.ConnectFailure); + } + _languageTags = new ReadOnlyKeyedCollection(ParseAllTagsJson(cachedAllTagsPath)); + } - // get SLDR langtags.json from the SLDR api compressed - // it will throw WebException or have status HttpStatusCode.NotModified if file is unchanged or not get it - var langtagsUrl = - $"{SldrRepository}index.html?query=langtags&ext=json{StagingParameter}"; - var webRequest = (HttpWebRequest) WebRequest.Create(Uri.EscapeUriString(langtagsUrl)); - webRequest.UserAgent = UserAgent; - webRequest.IfModifiedSince = sinceTime; - webRequest.Timeout = 10000; - webRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using var webResponse = (HttpWebResponse) webRequest.GetResponse(); + public static void LoadLanguageTags() + { + CreateSldrCacheDirectory(); + string cachedAllTagsPath; + cachedAllTagsPath = Path.Combine(SldrCachePath, "langtags.json"); + string etagPath; + etagPath = Path.Combine(SldrCachePath, "langtags.json.etag"); + string etag; + string currentEtag; + try + { + if (_offlineMode) + throw new WebException("Test mode: SLDR offline so accessing cache", WebExceptionStatus.ConnectFailure); + // get SLDR langtags.json from the SLDR api compressed + // it will throw WebException or have status HttpStatusCode.NotModified if file is unchanged or not get it + var langtagsUrl = + $"{SldrRepository}index.html?query=langtags&ext=json{StagingParameter}"; + var webRequest = (HttpWebRequest)WebRequest.Create(Uri.EscapeUriString(langtagsUrl)); + webRequest.UserAgent = UserAgent; + webRequest.Timeout = 10000; + webRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + using var webResponse = (HttpWebResponse)webRequest.GetResponse(); + if (File.Exists(etagPath)) + { + etag = File.ReadAllText(etagPath); + currentEtag = webResponse.Headers.Get("Etag"); + if (etag == "") + { + File.WriteAllText(etagPath, currentEtag); + } + else if (!etag.Equals(currentEtag)) + { + File.WriteAllText(etagPath, etag); + webRequest.Headers.Set(etag, "If-None-Match"); + if (webResponse.StatusCode != HttpStatusCode.NotModified) + { + using Stream output = File.OpenWrite(cachedAllTagsPath); + using var input = webResponse.GetResponseStream(); + input.CopyTo(output); + } + } + } + else + { + currentEtag = webResponse.Headers.Get("Etag"); + File.WriteAllText(etagPath, currentEtag); if (webResponse.StatusCode != HttpStatusCode.NotModified) { using Stream output = File.OpenWrite(cachedAllTagsPath); @@ -506,19 +547,19 @@ private static void LoadLanguageTagsIfNecessary() input.CopyTo(output); } } - catch (WebException) - { - } - catch (UnauthorizedAccessException) - { - } - catch (IOException) - { - } } - _languageTags = new ReadOnlyKeyedCollection(ParseAllTagsJson(cachedAllTagsPath)); + catch (WebException) + { + } + catch (UnauthorizedAccessException) + { + } + catch (IOException) + { + } } + private static IKeyedCollection ParseAllTagsJson(string cachedAllTagsPath) { // read in the json file