Skip to content

Commit

Permalink
Fixed the file system changes watching and cache invalidation
Browse files Browse the repository at this point in the history
  • Loading branch information
tatarincev committed Jul 3, 2018
1 parent 5aa01ae commit 301069e
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 18 deletions.
68 changes: 68 additions & 0 deletions VirtoCommerce.Storefront.Model/Common/PathUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Extensions.Primitives;

namespace VirtoCommerce.Storefront.Model.Common
{
public static class PathUtils
{
private static readonly char[] _invalidFileNameChars = Path.GetInvalidFileNameChars().Where(c => c != Path.DirectorySeparatorChar && c != Path.AltDirectorySeparatorChar).ToArray();

private static readonly char[] _invalidFilterChars = _invalidFileNameChars.Where(c => c != '*' && c != '|' && c != '?').ToArray();

private static readonly char[] _pathSeparators = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };

public static bool HasInvalidPathChars(string path)
{
return path.IndexOfAny(_invalidFileNameChars) != -1;
}

public static bool HasInvalidFilterChars(string path)
{
return path.IndexOfAny(_invalidFilterChars) != -1;
}

public static string EnsureTrailingSlash(string path)
{
if (!string.IsNullOrEmpty(path) &&
path[path.Length - 1] != Path.DirectorySeparatorChar)
{
return path + Path.DirectorySeparatorChar;
}

return path;
}

public static bool PathNavigatesAboveRoot(string path)
{
var tokenizer = new StringTokenizer(path, _pathSeparators);
var depth = 0;

foreach (var segment in tokenizer)
{
if (segment.Equals(".") || segment.Equals(""))
{
continue;
}
else if (segment.Equals(".."))
{
depth--;

if (depth == -1)
{
return true;
}
}
else
{
depth++;
}
}

return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ namespace VirtoCommerce.Storefront.Domain
{
public class FileSystemBlobContentOptions
{
//The root path
public string Path { get; set; }
//Need to be careful with enabling this setting, this can lead to serious performance issues
public bool PollForChanges { get; set; } = false;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
using Microsoft.Extensions.FileProviders.Physical;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using VirtoCommerce.Storefront.Model.StaticContent;
using System.Threading;
using Microsoft.Extensions.Caching.Memory;
using VirtoCommerce.Storefront.Model.Common.Caching;
using Microsoft.Extensions.FileProviders.Physical;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using VirtoCommerce.Storefront.Extensions;
using System.Threading;
using VirtoCommerce.Storefront.Model.Common;
using VirtoCommerce.Storefront.Model.Common.Caching;
using VirtoCommerce.Storefront.Model.StaticContent;

namespace VirtoCommerce.Storefront.Domain
{
Expand All @@ -27,10 +28,11 @@ public FileSystemContentBlobProvider(IOptions<FileSystemBlobContentOptions> opti
//Create fileSystemWatcher instance only when rootFolder exist to prevent whole application crash on initialization phase.
if (Directory.Exists(_options.Path))
{
_fileSystemWatcher = new PhysicalFilesWatcher(_options.Path, new FileSystemWatcher(_options.Path), _options.PollForChanges);
//It is very important to have rootPath with leading slash '\' without this any changes won't reflected
var rootPath = _options.Path.TrimEnd('\\') + '\\';
_fileSystemWatcher = new PhysicalFilesWatcher(rootPath, new FileSystemWatcher(rootPath), false);
}
}

#region IContentBlobProvider Members
/// <summary>
/// Open blob for read
Expand All @@ -40,6 +42,11 @@ public FileSystemContentBlobProvider(IOptions<FileSystemBlobContentOptions> opti
public virtual Stream OpenRead(string path)
{
path = NormalizePath(path);
// traversing above root not permitted.
if (PathUtils.PathNavigatesAboveRoot(path))
{
throw new InvalidOperationException(path);
}
return File.OpenRead(path);
}

Expand All @@ -51,6 +58,12 @@ public virtual Stream OpenRead(string path)
public virtual Stream OpenWrite(string path)
{
path = NormalizePath(path);
// traversing above root not permitted.
if (PathUtils.PathNavigatesAboveRoot(path))
{
throw new InvalidOperationException(path);
}

var folderPath = Path.GetDirectoryName(path);

if (!Directory.Exists(folderPath))
Expand Down Expand Up @@ -108,6 +121,11 @@ public virtual IChangeToken Watch(string path)
//TODO: Test with symbolic links
if (_fileSystemWatcher != null)
{
// Absolute paths not permitted for watcher, need to convert it to relative
if (Path.IsPathRooted(path))
{
path = GetRelativePath(path).TrimStart('/');
}
return _fileSystemWatcher.CreateFileChangeToken(path);
}
else
Expand All @@ -133,6 +151,6 @@ protected virtual string NormalizePath(string path)
return Path.Combine(_options.Path, path.TrimStart(Path.DirectorySeparatorChar));
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ public class StaticContentService : IStaticContentService
private static readonly Regex _headerRegExp = new Regex(@"(?s:^---(.*?)---)");
private static readonly string[] _extensions = { ".md", ".liquid", ".html" };
private readonly IStorefrontUrlBuilder _urlBuilder;
private readonly IStaticContentItemFactory _contentItemFactory;
private readonly IStaticContentItemFactory _contentItemFactory;
private readonly IContentBlobProvider _contentBlobProvider;
private readonly MarkdownPipeline _markdownPipeline;
private readonly IMemoryCache _memoryCache;
private readonly IMemoryCache _memoryCache;
private readonly string _basePath = "Pages";

public StaticContentService(IMemoryCache memoryCache, IWorkContextAccessor workContextAccessor,
Expand All @@ -38,7 +38,7 @@ public StaticContentService(IMemoryCache memoryCache, IWorkContextAccessor workC
_urlBuilder = urlBuilder;
_contentItemFactory = contentItemFactory;
_contentBlobProvider = contentBlobProvider;
_memoryCache = memoryCache;
_memoryCache = memoryCache;
_markdownPipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
}

Expand All @@ -52,7 +52,7 @@ public IEnumerable<ContentItem> LoadStoreStaticContent(Store store)
{
cacheEntry.AddExpirationToken(new CompositeChangeToken(new[] { StaticContentCacheRegion.CreateChangeToken(), _contentBlobProvider.Watch(baseStoreContentPath + "/**/*") }));

var retVal = new List<ContentItem>();
var retVal = new List<ContentItem>();
const string searchPattern = "*.*";
if (_contentBlobProvider.PathExists(baseStoreContentPath))
{
Expand Down Expand Up @@ -114,7 +114,7 @@ private void LoadAndRenderContentItem(string contentPath, ContentItem contentIte
metaHeaders = new Dictionary<string, IEnumerable<string>>();
}

content = RemoveYamlHeader(content);
content = RemoveYamlHeader(content);

//Render markdown content
if (Path.GetExtension(contentItem.StoragePath).EqualsInvariant(".md"))
Expand All @@ -131,7 +131,7 @@ private void LoadAndRenderContentItem(string contentPath, ContentItem contentIte

if (string.IsNullOrEmpty(contentItem.Permalink))
{
contentItem.Permalink = ":folder/:categories/:title";
contentItem.Permalink = ":folder/:categories/:title";
}
//Transform permalink template to url
contentItem.Url = GetContentItemUrl(contentItem, contentItem.Permalink);
Expand Down
1 change: 0 additions & 1 deletion VirtoCommerce.Storefront/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ public void ConfigureServices(IServiceCollection services)
services.AddFileSystemBlobContent(options =>
{
options.Path = HostingEnvironment.MapPath(contentConnectionString.RootPath);
options.PollForChanges = fileSystemBlobOptions.PollForChanges;
});
}

Expand Down

0 comments on commit 301069e

Please sign in to comment.