Skip to content
This repository has been archived by the owner on Feb 14, 2022. It is now read-only.

Commit

Permalink
Changed version to 0.8.0.0; bugfixes; Translate header 'f' is defined…
Browse files Browse the repository at this point in the history
… as default request header (IIS issue); unit tests
  • Loading branch information
DecaTec committed Apr 13, 2017
1 parent 6666108 commit d3b35f2
Show file tree
Hide file tree
Showing 19 changed files with 894 additions and 199 deletions.
10 changes: 6 additions & 4 deletions DecaTec.WebDav/AbsoluteUri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class AbsoluteUri
/// Constructs an <see cref="AbsoluteUri"/>.
/// </summary>
/// <param name="absoluteUrl">The URL to use.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="absoluteUri"/> is null.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="absoluteUrl"/> is null.</exception>
public AbsoluteUri(string absoluteUrl)
{
if (!Uri.TryCreate(absoluteUrl, UriKind.Absolute, out Uri absoluteUri))
Expand All @@ -38,9 +38,11 @@ public AbsoluteUri(Uri absoluteUri)
this.absoluteUri = absoluteUri;
}

/// <inheritdoc />
public override string ToString() =>
absoluteUri.ToString();
/// <summary>
/// Gets the string representation of this AbsoluteUri.
/// </summary>
/// <returns>The string representation of this AbsoluteUri.</returns>
public override string ToString() => absoluteUri.ToString();

/// <summary>
/// Tries to parse the given <paramref name="rawAbsoluteUri"/> into an <see cref="AbsoluteUri"/>.
Expand Down
5 changes: 4 additions & 1 deletion DecaTec.WebDav/CodedUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ public CodedUrl(AbsoluteUri absoluteUri)
AbsoluteUri = absoluteUri ?? throw new ArgumentNullException(nameof(absoluteUri));
}

/// <inheritdoc />
/// <summary>
/// Gets the string representation of this CodedUrl.
/// </summary>
/// <returns>The string representation of this CodedUrl.</returns>
public override string ToString() => $"{CodedUrlPrefix}{AbsoluteUri}{CodedUrlPostfix}";

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions DecaTec.WebDav/DecaTec.WebDav.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<PackageId>PortableWebDavLibrary</PackageId>
<Version>0.7.1.0-beta3</Version>
<Version>0.8.0.0-beta1</Version>
<Authors>DecaTec</Authors>
<Company>DecaTec</Company>
<Product>PortableWebDavLibrary</Product>
Expand All @@ -15,7 +15,7 @@
<RepositoryUrl>https://github.com/DecaTec/Portable-WebDAV-Library</RepositoryUrl>
<PackageTags>WebDAV, portable, NETStandard, NETCore, UWP, dotnet, portable-webdav-library, Xamarin, Mono, multiplatform</PackageTags>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageReleaseNotes>Full documentation is available at https://decatec.de/ext/PortableWebDAVLibrary/Doc</PackageReleaseNotes>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
5 changes: 5 additions & 0 deletions DecaTec.WebDav/HttpHeaderNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,10 @@ public static class HttpHeaderNames
/// Header name for 'Content-Length'.
/// </summary>
public const string ContentLength = "Content-Length";

/// <summary>
/// Header name for 'Translate'.
/// </summary>
public const string Translate = "Translate";
}
}
14 changes: 14 additions & 0 deletions DecaTec.WebDav/HttpTranslateHeaderValues.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

namespace DecaTec.WebDav
{
/// <summary>
/// Class defining the values for the HTTP Translate header.
/// </summary>
public static class HttpTranslateHeaderValues
{
/// <summary>
/// Constant for Translate header when the server should return a file uninterpreted ('f').
/// </summary>
public const string TranslateF = "f";
}
}
6 changes: 3 additions & 3 deletions DecaTec.WebDav/LockToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ namespace DecaTec.WebDav
public class LockToken
{
/// <summary>
/// Constructs a <see cref="LockToken"/> based on the <paramref name="absoluteUri"/>.
/// Constructs a <see cref="LockToken"/> based on the <paramref name="lockToken"/>.
/// </summary>
/// <param name="absoluteUri">The lock token in absolute-URI format as defined in https://tools.ietf.org/html/rfc3986#section-4.3. </param>
/// <param name="lockToken">The lock token as string.</param>
/// <remarks>Use the strong-typed constructors to create a new <see cref="LockToken"/>.</remarks>
public LockToken(string lockToken)
{
Expand All @@ -37,7 +37,7 @@ public LockToken(string lockToken)
/// <summary>
/// Constructs a <see cref="LockToken"/> based on the <paramref name="absoluteUri"/>.
/// </summary>
/// <param name="absoluteUri">The lock token in absolute-URI format as defined in https://tools.ietf.org/html/rfc3986#section-4.3. </param>
/// <param name="absoluteUri">The lock token in absolute-URI format as defined in https://tools.ietf.org/html/rfc3986#section-4.3.</param>
/// <remarks>Use the strong-typed constructors to create a new <see cref="LockToken"/>.</remarks>
/// <exception cref="WebDavException">Thrown when <paramref name="absoluteUri"/> is null.</exception>
public LockToken(AbsoluteUri absoluteUri)
Expand Down
5 changes: 4 additions & 1 deletion DecaTec.WebDav/NoTagList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ public NoTagList(CodedUrl codedUrl)
CodedUrl = codedUrl ?? throw new ArgumentNullException(nameof(codedUrl));
}

/// <inheritdoc />
/// <summary>
/// Gets the string representation of this NoTagList.
/// </summary>
/// <returns>The string representation of this NoTagList.</returns>
public override string ToString() => $"{NoTagListPrefix}{CodedUrl}{NoTagListPostfix}";

/// <summary>
Expand Down
14 changes: 12 additions & 2 deletions DecaTec.WebDav/WebDavClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public class WebDavClient : HttpClient
public WebDavClient()
: base()
{

SetDefaultRequestHeaders();
}

/// <summary>
Expand All @@ -128,7 +128,7 @@ public WebDavClient()
public WebDavClient(HttpMessageHandler httpMessageHandler)
: base(httpMessageHandler)
{

SetDefaultRequestHeaders();
}

/// <summary>
Expand All @@ -139,7 +139,17 @@ public WebDavClient(HttpMessageHandler httpMessageHandler)
public WebDavClient(HttpMessageHandler httpMessageHandler, bool disposeHandler)
: base(httpMessageHandler, disposeHandler)
{
SetDefaultRequestHeaders();
}

private void SetDefaultRequestHeaders()
{
// This is a workaround when the lib is used for WebDAV/IIS:
// When there is a GET request for a file with unmapped extension on IIS (e.g. 'file.01'), the IIS returns 404 ('not found').
// This is due to the server trying to interpret any requested files (which makes sense for ASP, etc.).
// In order that the IIS serves files with unmapped extensions, the "Translate" header has to be set to 'f'.
// See: https://msdn.microsoft.com/en-us/library/cc250063.aspx
this.DefaultRequestHeaders.Add(HttpHeaderNames.Translate, HttpTranslateHeaderValues.TranslateF);
}

#endregion Constructor
Expand Down
57 changes: 37 additions & 20 deletions DecaTec.WebDav/WebDavSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,21 @@ public Uri BaseUri
set;
}

/// <summary>
/// Gets or sets the BaseUri of this WebDavSession by a URL string.
/// </summary>
public string BaseUrl
{
get
{
return this.BaseUri == null ? string.Empty : this.BaseUri.ToString();
}
set
{
this.BaseUri = new Uri(value);
}
}

/// <summary>
/// Gets or sets the <see cref="IWebProxy"/> to use with this WebDavSession.
/// </summary>
Expand Down Expand Up @@ -203,8 +218,8 @@ public async Task<bool> CopyAsync(string sourceUrl, string destinationUrl, bool
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> CopyAsync(Uri sourceUri, Uri destinationUri, bool overwrite)
{
sourceUri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, sourceUri, true, false);
destinationUri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, destinationUri, true, false);
sourceUri = UriHelper.CombineUri(this.BaseUri, sourceUri, true);
destinationUri = UriHelper.CombineUri(this.BaseUri, destinationUri, true);
var lockToken = GetAffectedLockToken(destinationUri);
var response = await this.webDavClient.CopyAsync(sourceUri, destinationUri, overwrite, WebDavDepthHeaderValue.Infinity, lockToken);
return response.IsSuccessStatusCode;
Expand All @@ -231,7 +246,7 @@ public async Task<bool> CreateDirectoryAsync(string url)
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> CreateDirectoryAsync(Uri uri)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);
var lockToken = GetAffectedLockToken(uri);
var response = await this.webDavClient.MkcolAsync(uri, lockToken);
return response.IsSuccessStatusCode;
Expand All @@ -258,7 +273,7 @@ public async Task<bool> DeleteAsync(string url)
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> DeleteAsync(Uri uri)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);
var lockToken = GetAffectedLockToken(uri);
var response = await this.webDavClient.DeleteAsync(uri, lockToken);
return response.IsSuccessStatusCode;
Expand Down Expand Up @@ -311,7 +326,7 @@ public async Task<bool> DownloadFileAsync(string url, Stream localStream, Cancel
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> DownloadFileAsync(Uri uri, Stream localStream, CancellationToken ct)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);
var response = await this.webDavClient.GetAsync(uri, ct);

if (response.Content != null)
Expand Down Expand Up @@ -378,7 +393,7 @@ public async Task<bool> DownloadFileWithProgressAsync(Uri uri, Stream localStrea
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> DownloadFileWithProgressAsync(Uri uri, Stream localStream, IProgress<WebDavProgress> progress, CancellationToken cancellationToken)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);
var response = await this.webDavClient.DownloadFileWithProgressAsync(uri, localStream, cancellationToken, progress);
return response.IsSuccessStatusCode;
}
Expand All @@ -404,7 +419,7 @@ public async Task<bool> ExistsAsync(string url)
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> ExistsAsync(Uri uri)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);
var response = await this.webDavClient.HeadAsync(uri);
return response.IsSuccessStatusCode;
}
Expand Down Expand Up @@ -459,14 +474,14 @@ public async Task<IList<WebDavSessionListItem>> ListAsync(Uri uri, PropFind prop
if (propFind == null)
throw new ArgumentException("Argument propFind must not be null.");

uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);
var response = await this.webDavClient.PropFindAsync(uri, WebDavDepthHeaderValue.One, propFind);

// Remember the original port to include it in the hrefs later.
var port = UriHelper.GetPort(uri);

if (response.StatusCode != WebDavStatusCode.MultiStatus)
throw new WebDavException(string.Format("Error while executing ListAsync (wrong response status code). Expected status code: 207 (MultiStatus); actual status code: {0} ({1})", (int)response.StatusCode, response.StatusCode));
throw new WebDavException($"Error while executing ListAsync (wrong response status code). Expected status code: 207 (MultiStatus); actual status code: {(int)response.StatusCode} ({response.StatusCode})");

var multistatus = await WebDavResponseContentParser.ParseMultistatusResponseContentAsync(response.Content);

Expand Down Expand Up @@ -591,7 +606,7 @@ public async Task<bool> LockAsync(string url)
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> LockAsync(Uri uri)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);

if (this.permanentLocks.ContainsKey(uri))
return true; // Lock already set.
Expand All @@ -607,21 +622,23 @@ public async Task<bool> LockAsync(Uri uri)
if (!response.IsSuccessStatusCode)
return false; // Lock already exists.

// Save the content stream as string as it will be consumed while getting the lock token.
var responseContentString = await response.Content.ReadAsStringAsync();
var lockToken = WebDavHelper.GetLockTokenFromWebDavResponseMessage(response);

var prop = await WebDavResponseContentParser.ParsePropResponseContentAsync(response.Content);
var prop = WebDavResponseContentParser.ParsePropResponseContentString(responseContentString);
var lockDiscovery = prop.LockDiscovery;

if (lockDiscovery == null)
return false;

var url = uri.ToString();
var lockGranted = lockDiscovery.ActiveLock.FirstOrDefault(x => url.EndsWith(UriHelper.AddTrailingSlash(x.LockRoot.Href, false), StringComparison.OrdinalIgnoreCase));
var lockGranted = lockDiscovery.ActiveLock.FirstOrDefault(x => UriHelper.AddTrailingSlash(url, false).EndsWith(UriHelper.AddTrailingSlash(x.LockRoot.Href, false), StringComparison.OrdinalIgnoreCase));

if (lockGranted == null)
{
// Try with file expected.
lockGranted = lockDiscovery.ActiveLock.FirstOrDefault(x => url.EndsWith(UriHelper.AddTrailingSlash(x.LockRoot.Href, true), StringComparison.OrdinalIgnoreCase));
lockGranted = lockDiscovery.ActiveLock.FirstOrDefault(x => UriHelper.AddTrailingSlash(url, true).EndsWith(UriHelper.AddTrailingSlash(x.LockRoot.Href, true), StringComparison.OrdinalIgnoreCase));
}

if (lockGranted == null)
Expand Down Expand Up @@ -682,8 +699,8 @@ public async Task<bool> MoveAsync(string sourceUrl, string destinationUrl, bool
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> MoveAsync(Uri sourceUri, Uri destinationUri, bool overwrite)
{
sourceUri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, sourceUri, true, false);
destinationUri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, destinationUri, true, false);
sourceUri = UriHelper.CombineUri(this.BaseUri, sourceUri, true);
destinationUri = UriHelper.CombineUri(this.BaseUri, destinationUri, true);
var lockTokenSource = GetAffectedLockToken(sourceUri);
var lockTokenDestination = GetAffectedLockToken(destinationUri);
var response = await this.webDavClient.MoveAsync(sourceUri, destinationUri, overwrite, lockTokenSource, lockTokenDestination);
Expand Down Expand Up @@ -713,7 +730,7 @@ public async Task<bool> UploadFileAsync(string url, Stream localStream)
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> UploadFileAsync(Uri uri, Stream localStream)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);
var lockToken = GetAffectedLockToken(uri);
var content = new StreamContent(localStream);
var response = await this.webDavClient.PutAsync(uri, content, lockToken);
Expand Down Expand Up @@ -744,7 +761,7 @@ public async Task<bool> UploadFileWithProgressAsync(string url, Stream stream, s
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> UploadFileWithProgressAsync(string url, Stream stream, string contentType, IProgress<WebDavProgress> progress, CancellationToken cancellationToken)
{
return await UploadFileWithProgressAsync(url, stream, contentType, progress, cancellationToken);
return await UploadFileWithProgressAsync(UriHelper.CreateUriFromUrl(url), stream, contentType, progress, cancellationToken);
}

/// <summary>
Expand All @@ -771,7 +788,7 @@ public async Task<bool> UploadFileWithProgressAsync(Uri uri, Stream stream, stri
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> UploadFileWithProgressAsync(Uri uri, Stream stream, string contentType, IProgress<WebDavProgress> progress, CancellationToken cancellationToken)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);
var lockToken = GetAffectedLockToken(uri);
var response = await this.webDavClient.UploadFileWithProgressAsync(uri, stream, contentType, progress, cancellationToken, lockToken);
return response.IsSuccessStatusCode;
Expand All @@ -798,7 +815,7 @@ public async Task<bool> UnlockAsync(string url)
/// <returns>The <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task<bool> UnlockAsync(Uri uri)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);

if (!this.permanentLocks.TryRemove(uri, out PermanentLock permanentLock))
return false;
Expand Down Expand Up @@ -829,7 +846,7 @@ private static WebDavClient CreateWebDavClient(HttpMessageHandler messageHandler

private LockToken GetAffectedLockToken(Uri uri)
{
uri = UriHelper.GetCombinedUriWithTrailingSlash(this.BaseUri, uri, true, false);
uri = UriHelper.CombineUri(this.BaseUri, uri, true);

foreach (var lockItem in this.permanentLocks)
{
Expand Down
Loading

0 comments on commit d3b35f2

Please sign in to comment.