protected MoveTestsBase(RecursiveProcessingMode processingMode, params XName[] propertiesToIgnore) : base(processingMode) { _propsToIgnoreDocument = propertiesToIgnore.Union(new[] { LockDiscoveryProperty.PropertyName, DisplayNameProperty.PropertyName }).ToArray(); _propsToIgnoreCollection = propertiesToIgnore.Union(new[] { LockDiscoveryProperty.PropertyName, DisplayNameProperty.PropertyName, GetETagProperty.PropertyName }).ToArray(); Dispatcher = ServiceProvider.GetRequiredService <IWebDavDispatcher>(); }
private void ConfigureServices(ServerTestsBase container, RecursiveProcessingMode processingMode, IServiceCollection services) { IFileSystemFactory fileSystemFactory = null; IPropertyStoreFactory propertyStoreFactory = null; services .AddOptions() .AddLogging() .Configure <CopyHandlerOptions>( opt => { opt.Mode = processingMode; }) .Configure <MoveHandlerOptions>( opt => { opt.Mode = processingMode; }) .AddScoped <IWebDavContext>(sp => new TestHost(sp, container.Server.BaseAddress, sp.GetRequiredService <IHttpContextAccessor>())) .AddScoped <IHttpMessageHandlerFactory>(sp => new TestHttpMessageHandlerFactory(container.Server)) .AddScoped(sp => fileSystemFactory ?? (fileSystemFactory = ActivatorUtilities.CreateInstance <InMemoryFileSystemFactory>(sp))) .AddScoped(sp => propertyStoreFactory ?? (propertyStoreFactory = ActivatorUtilities.CreateInstance <InMemoryPropertyStoreFactory>(sp))) .AddSingleton <ILockManager, InMemoryLockManager>() .AddMvcCore() .AddApplicationPart(typeof(TestWebDavController).GetTypeInfo().Assembly) .AddWebDav(); }
protected ServerTestsBase(RecursiveProcessingMode processingMode) { var builder = new WebHostBuilder() .ConfigureServices(sc => ConfigureServices(this, processingMode, sc)) .UseStartup <TestStartup>(); Server = new TestServer(builder); FileSystem = Server.Host.Services.GetRequiredService <IFileSystem>(); Client = new WebDavClient(Server.CreateHandler()) { BaseAddress = Server.BaseAddress, }; }
/// <inheritdoc /> protected override ITargetActions <CollectionTarget, DocumentTarget, MissingTarget> CreateLocalTargetActions(RecursiveProcessingMode mode) { if (mode == RecursiveProcessingMode.PreferFastest) { return(new MoveInFileSystemTargetAction(WebDavContext.Dispatcher, Logger)); } return(new MoveBetweenFileSystemsTargetAction(WebDavContext.Dispatcher)); }
/// <summary> /// Executes the COPY or MOVE recursively /// </summary> /// <param name="sourcePath">The source path</param> /// <param name="destination">The destination URI</param> /// <param name="depth">The depth</param> /// <param name="overwrite">Can the destination be overwritten?</param> /// <param name="mode">The COPY mode to use</param> /// <param name="isMove">Is this a move operation?</param> /// <param name="cancellationToken">The cancellcation token</param> /// <returns>The result of the operation</returns> protected async Task <IWebDavResult> ExecuteAsync( string sourcePath, Uri destination, DepthHeader depth, bool overwrite, RecursiveProcessingMode mode, bool isMove, CancellationToken cancellationToken) { var sourceSelectionResult = await _rootFileSystem.SelectAsync(sourcePath, cancellationToken); if (sourceSelectionResult.IsMissing) { if (WebDavContext.RequestHeaders.IfNoneMatch != null) { throw new WebDavException(WebDavStatusCode.PreconditionFailed); } throw new WebDavException(WebDavStatusCode.NotFound); } await WebDavContext.RequestHeaders .ValidateAsync(sourceSelectionResult.TargetEntry, cancellationToken); IWebDavResult result; IImplicitLock sourceTempLock; var lockManager = _rootFileSystem.LockManager; if (isMove) { var sourceLockRequirements = new Lock( sourceSelectionResult.TargetEntry.Path, WebDavContext.PublicRelativeRequestUrl, depth != DepthHeader.Zero, new XElement(WebDavXml.Dav + "owner", WebDavContext.User.Identity.Name), LockAccessType.Write, LockShareMode.Shared, TimeoutHeader.Infinite); sourceTempLock = lockManager == null ? new ImplicitLock(true) : await lockManager.LockImplicitAsync( _rootFileSystem, WebDavContext.RequestHeaders.If?.Lists, sourceLockRequirements, cancellationToken) ; if (!sourceTempLock.IsSuccessful) { return(sourceTempLock.CreateErrorResponse()); } } else { sourceTempLock = new ImplicitLock(true); } try { var sourceUrl = WebDavContext.PublicAbsoluteRequestUrl; var destinationUrl = new Uri(sourceUrl, destination); // Ignore different schemes if (!WebDavContext.PublicControllerUrl.IsBaseOf(destinationUrl) || mode == RecursiveProcessingMode.PreferCrossServer) { if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace("Using cross-server mode"); } if (Logger.IsEnabled(LogLevel.Debug)) { Logger.LogDebug($"{WebDavContext.PublicControllerUrl} is not a base of {destinationUrl}"); } using (var remoteHandler = await CreateRemoteTargetActionsAsync( destinationUrl, cancellationToken) ) { if (remoteHandler == null) { throw new WebDavException( WebDavStatusCode.BadGateway, "No remote handler for given client"); } // For error reporting sourceUrl = WebDavContext.PublicRootUrl.MakeRelativeUri(sourceUrl); var remoteTargetResult = await RemoteExecuteAsync( remoteHandler, sourceUrl, sourceSelectionResult, destinationUrl, depth, overwrite, cancellationToken); result = remoteTargetResult.Evaluate(WebDavContext); } } else { // Copy or move from one known file system to another var destinationPath = WebDavContext.PublicControllerUrl.MakeRelativeUri(destinationUrl).ToString(); // For error reporting sourceUrl = WebDavContext.PublicRootUrl.MakeRelativeUri(sourceUrl); destinationUrl = WebDavContext.PublicRootUrl.MakeRelativeUri(destinationUrl); var destinationSelectionResult = await _rootFileSystem.SelectAsync(destinationPath, cancellationToken); if (destinationSelectionResult.IsMissing && destinationSelectionResult.MissingNames.Count != 1) { Logger.LogDebug( $"{destinationUrl}: The target is missing with the following path parts: {string.Join(", ", destinationSelectionResult.MissingNames)}"); throw new WebDavException(WebDavStatusCode.Conflict); } var destLockRequirements = new Lock( new Uri(destinationPath, UriKind.Relative), destinationUrl, isMove || depth != DepthHeader.Zero, new XElement(WebDavXml.Dav + "owner", WebDavContext.User.Identity.Name), LockAccessType.Write, LockShareMode.Shared, TimeoutHeader.Infinite); var destTempLock = lockManager == null ? new ImplicitLock(true) : await lockManager.LockImplicitAsync( _rootFileSystem, WebDavContext.RequestHeaders.If?.Lists, destLockRequirements, cancellationToken) ; if (!destTempLock.IsSuccessful) { return(destTempLock.CreateErrorResponse()); } try { var isSameFileSystem = ReferenceEquals( sourceSelectionResult.TargetFileSystem, destinationSelectionResult.TargetFileSystem); var localMode = isSameFileSystem && mode == RecursiveProcessingMode.PreferFastest ? RecursiveProcessingMode.PreferFastest : RecursiveProcessingMode.PreferCrossFileSystem; var handler = CreateLocalTargetActions(localMode); var targetInfo = FileSystemTarget.FromSelectionResult( destinationSelectionResult, destinationUrl, handler); var targetResult = await LocalExecuteAsync( handler, sourceUrl, sourceSelectionResult, targetInfo, depth, overwrite, cancellationToken); result = targetResult.Evaluate(WebDavContext); } finally { await destTempLock.DisposeAsync(cancellationToken); } } } finally { await sourceTempLock.DisposeAsync(cancellationToken); } if (isMove && lockManager != null) { var locksToRemove = await lockManager .GetAffectedLocksAsync(sourcePath, true, false, cancellationToken) ; foreach (var activeLock in locksToRemove) { await lockManager.ReleaseAsync( activeLock.Path, new Uri(activeLock.StateToken), cancellationToken) ; } } return(result); }
/// <summary> /// Create the target action implementation for local COPY or MOVE /// </summary> /// <param name="mode">The requested processing mode (in-filesystem or cross-filesystem)</param> /// <returns>The implementation for local actions</returns> protected abstract ITargetActions <CollectionTarget, DocumentTarget, MissingTarget> CreateLocalTargetActions(RecursiveProcessingMode mode);