Skip to content

Commit 71a6acd

Browse files
committed
Add a download progress for the attachment download, use TimeSpan timeout instead of int timeout
1 parent b3bcb2d commit 71a6acd

4 files changed

Lines changed: 44 additions & 28 deletions

File tree

API.Test/AuthentificationTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace API.Test;
1111
[TestFixture]
1212
internal class AuthentificationTests
1313
{
14-
public static WebUntisClient Client { get; set; } = new("WebUntisAPI_TEST", 1000);
14+
public static WebUntisClient Client { get; set; } = new("WebUntisAPI_TEST", TimeSpan.FromSeconds(2));
1515

1616
static AuthentificationTests()
1717
{
@@ -34,7 +34,7 @@ public void Authentification()
3434
{
3535
try
3636
{
37-
using WebUntisClient client = new("WebUntisAPI_TEST");
37+
using WebUntisClient client = new("WebUntisAPI_TEST", TimeSpan.FromSeconds(2));
3838
client.LoginAsync(s_Server, s_LoginName, s_UserName, s_Password).Wait();
3939
client.LogoutAsync().Wait();
4040
}

WebUntisAPI.Client/Models/Messages/Attachment.cs

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using System.Net;
88
using System.Net.Http;
9+
using System.Text;
910
using System.Text.RegularExpressions;
1011
using System.Threading;
1112
using System.Threading.Tasks;
@@ -31,16 +32,17 @@ public struct Attachment
3132
internal readonly string _id;
3233

3334
/// <summary>
34-
/// Get the content of the attachment as stream
35+
/// Get the content of the attachment as stream with a progress report
3536
/// </summary>
3637
/// <param name="client">The client with them you got this instane (It doesn't have to be the same client but the same account)</param>
3738
/// <param name="timeout">The time after that the download of the content will cancelled (In miliseconds)</param>
39+
/// <param name="progress">The progress of the download in percent</param>
3840
/// <param name="ct">Cancellation token</param>
3941
/// <returns>The attachment as stream</returns>
4042
/// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception>
4143
/// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception>
4244
/// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception>
43-
public async Task<Stream> DownloadContentAsStreamAsync(WebUntisClient client, int timeout = 2000, CancellationToken ct = default)
45+
public async Task<Stream> DownloadContentAsStreamAsync(WebUntisClient client, TimeSpan timeout, IProgress<double> progress = null, CancellationToken ct = default)
4446
{
4547
string storageResponseString = await client.MakeAPIGetRequestAsync($"/WebUntis/api/rest/view/v1/messages/{_id}/attachmentstorageurl", ct);
4648

@@ -64,24 +66,44 @@ public async Task<Stream> DownloadContentAsStreamAsync(WebUntisClient client, in
6466

6567
using (HttpClient downloadClient = new HttpClient())
6668
{
67-
downloadClient.Timeout = TimeSpan.FromMilliseconds(timeout);
68-
HttpResponseMessage response = await downloadClient.SendAsync(request, ct);
69+
downloadClient.Timeout = timeout;
70+
HttpResponseMessage response = await downloadClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
6971

70-
// Check cancellation token
71-
if (ct.IsCancellationRequested)
72-
return default;
72+
using (MemoryStream content = new MemoryStream())
73+
{
74+
using (Stream contentStream = await response.Content.ReadAsStreamAsync())
75+
{
76+
int totalBytes = (int?)response.Content.Headers.ContentLength ?? -1;
77+
int totalRecievedBytes = 0;
7378

74-
// Verify response
75-
if (response.StatusCode != HttpStatusCode.OK)
76-
throw new HttpRequestException($"There was an error while the http request (Code: {response.StatusCode}).");
79+
int bytesRead = 0;
80+
byte[] buffer = new byte[8192];
81+
while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
82+
{
83+
if (ct.IsCancellationRequested)
84+
return default;
7785

78-
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
79-
{
80-
string detail = Regex.Match(await response.Content.ReadAsStringAsync(), @"<Message>([a-zA-z0-9\s]+)</Message>").Groups[1].Value; // Get the error message
81-
throw new UnauthorizedAccessException($"Invalid authentication. Detail: {detail}");
82-
}
86+
content.Write(buffer, 0, bytesRead);
87+
totalRecievedBytes += bytesRead;
88+
progress.Report((double)totalRecievedBytes / totalBytes * 100d);
89+
}
90+
}
91+
92+
if (ct.IsCancellationRequested)
93+
return default;
8394

84-
return await response.Content.ReadAsStreamAsync();
95+
// Verify response
96+
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
97+
{
98+
string detail = Regex.Match(Encoding.UTF8.GetString(content.ToArray()), @"<Message>([a-zA-z0-9\s]+)</Message>").Groups[1].Value; // Get the error message
99+
throw new UnauthorizedAccessException($"Invalid authentication. Detail: {detail}");
100+
}
101+
102+
if (response.StatusCode != HttpStatusCode.OK)
103+
throw new HttpRequestException($"There was an error while the http request (Code: {response.StatusCode}).");
104+
105+
return content;
106+
}
85107
}
86108
}
87109
}

WebUntisAPI.Client/WebUntisClient.Messages.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,15 @@ public async Task<MessagePreview[]> GetMessageInboxAsync(CancellationToken ct =
9191
/// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception>
9292
/// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception>
9393
/// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception>
94-
public async Task<MessagePreview> SendMessageAsync(Draft draft, MessagePerson[] recipients, int timeout = 2000, CancellationToken ct = default)
94+
public async Task<MessagePreview> SendMessageAsync(Draft draft, MessagePerson[] recipients, TimeSpan timeout, CancellationToken ct = default)
9595
{
9696
Tuple<string, Stream>[] attachments = new Tuple<string, Stream>[0];
9797
if (draft.Attachments.Count > 0)
9898
{
9999
Dictionary<string, Task<Stream>> attachmentTasks = new Dictionary<string, Task<Stream>>();
100100

101101
foreach (Attachment attachment in draft.Attachments)
102-
attachmentTasks.Add(attachment.Name, attachment.DownloadContentAsStreamAsync(this, timeout, ct));
102+
attachmentTasks.Add(attachment.Name, attachment.DownloadContentAsStreamAsync(this, timeout, ct: ct));
103103

104104
await Task.WhenAll(attachmentTasks.Values);
105105
attachments = attachmentTasks.Select(attachment => new Tuple<string, Stream>(attachment.Key, attachment.Value.Result)).ToArray();

WebUntisAPI.Client/WebUntisClient.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ public partial class WebUntisClient : IDisposable
2626
/// </summary>
2727
public string ClientName { get; }
2828

29-
/// <summary>
30-
/// The time in milliseconds until requests will be timeouted
31-
/// </summary>
32-
public int Timeout { get; }
33-
3429
/// <summary>
3530
/// <see langword="true"/> when the client is currently logged in
3631
/// </summary>
@@ -74,13 +69,12 @@ public partial class WebUntisClient : IDisposable
7469
/// </summary>
7570
/// <param name="clientName">Unique identifier for the client app</param>
7671
/// <param name="timeout">The time in milliseconds until requests will be timeouted</param>
77-
public WebUntisClient(string clientName, int timeout = 1000)
72+
public WebUntisClient(string clientName, TimeSpan timeout)
7873
{
7974
ClientName = clientName;
80-
Timeout = timeout;
8175
_client = new HttpClient()
8276
{
83-
Timeout = TimeSpan.FromMilliseconds(Timeout)
77+
Timeout = timeout
8478
};
8579
}
8680

0 commit comments

Comments
 (0)