private static RequestFileCacheMetadata AddFileCacheEntry(string requestPath, string es5FilePath, string sourceChecksum) { RequestFileCacheMetadata rfc = new RequestFileCacheMetadata(); rfc.Path = requestPath; rfc.ES5Path = es5FilePath; rfc.SourceETag = sourceChecksum; rfc.ETag = GetETagForES5File(sourceChecksum); rfc.ContentLength = new FileInfo(es5FilePath).Length.ToString(); if (!_cachedFiles.ContainsKey(requestPath)) { //If unable to add, that mean an other thread added the file. Returning the file from the other thread if (!_cachedFiles.TryAdd(requestPath, rfc)) { rfc = _cachedFiles.GetOrAdd(requestPath, rfc); } } else { rfc = _cachedFiles.AddOrUpdate(requestPath, rfc, (requestKeyPath, existingRFC) => { return(rfc); }); } return(rfc); }
/// <summary> /// Please call <see cref="HasES5FileInCacheByPath"/> and <see cref="HasES5FileInCacheWithChecksum"/> before calling this method, as /// theses method may populate the cache the first time. /// </summary> /// <param name="path"></param> /// <returns></returns> public static RequestFileCacheMetadata GetES5FileFromCache(string path) { if (_cachedFiles.ContainsKey(path)) { RequestFileCacheMetadata resultValue = null; _cachedFiles.TryGetValue(path, out resultValue); //As we don't remove data from dictionary, this should be OK. Assuring we don't have any concurrency by using the previous method return(resultValue); } return(null); }
private async Task ManageES5Conversion(HttpContext context) { //If this return true here, this mean this is a subsequent call to a file //and that the file has already been cached in the internal dictionary //Otherwise we must try to seek the file hash of the current request and see //if it has been converted at least once in the app lifetime if (ES5CacheHelper.HasES5FileInCacheByPath(context.Request.Path)) { var fileCache = ES5CacheHelper.GetES5FileFromCache(context.Request.Path); //Manage request specificity await HttpRequestManager.ManageRequest(context, fileCache.ES5Path, fileCache.ETag, fileCache.ContentLength); return; } //Here we are tricking the StaticFile handler by modifying the Body Stream type before it try to write in it. //Using a MemoryStream in order to be able to catch and modify the content afterward var originalResponseStream = context.Response.Body; using (var fakeResponseStream = new MemoryStream()) { context.Response.Body = fakeResponseStream; //Awaiting StaticFile to fill the content await _next(context); //If the requested file return 304 status code, we should not try to cache / resolve this file //as StaticFileMiddleware does not return the data in this case //Same as if there is any other behavior that is not a success (not 200) we must retrieve and return the initial stream modified by the other context //without any alteration if (context.Response.StatusCode != 200) { //Restoring the original response WriteOnly Stream context.Response.Body = await CopyStreamAsync(fakeResponseStream, originalResponseStream); return; } var response = await ExtractResponseAsync(context.Request, context.Response); RequestFileCacheMetadata _cacheMetadata = null; //From here we must see if we have a corresponding hash for this fetched resource in the cache store. Subsequent call will short-circuit at the top of this method because //the cache entry dictionary will be populated. If the data is not present we will have //to cache the file in disk if (ES5CacheHelper.HasES5FileInCacheWithChecksum(context.Request.Path, response.SourceCheckSum)) { _cacheMetadata = ES5CacheHelper.GetES5FileFromCache(context.Request.Path); } else { //Convert to ES5 the current response var fileContent = ES5Convert(response); if (fileContent == null) { //If here that mean that conversion failed. //We will take the original object but will add //current content as ES5 even if not fileContent = response; fileContent.ES5Content = fileContent.SourceContent; } _cacheMetadata = ES5CacheHelper.SetES5FileInCache(fileContent); } //As we have interverted the original Http Stream in our 'using' above, we //must restore it in order to make the thing work normally context.Response.Body = originalResponseStream; //Manage request specificity await HttpRequestManager.ManageRequest(context, _cacheMetadata.ES5Path, _cacheMetadata.ETag, _cacheMetadata.ContentLength); } }