From 5b8f50748ece2d0037a4102e248a833e47b19020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 12:07:39 +0200 Subject: [PATCH 01/37] Fix typo --- Tiny.RestClient/TinyRestClient.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tiny.RestClient/TinyRestClient.cs b/Tiny.RestClient/TinyRestClient.cs index aff8f0e..5f62cab 100644 --- a/Tiny.RestClient/TinyRestClient.cs +++ b/Tiny.RestClient/TinyRestClient.cs @@ -426,18 +426,18 @@ private async Task CreateContentAsync(IContent content, Cancellatio { var bytesMultiContent = new ByteArrayContent(currentBytesPart.Data); SetContentType(currentBytesPart.ContentType, bytesMultiContent); - AddMulitPartContent(currentPart, bytesMultiContent, multiPartContent); + AddMultiPartContent(currentPart, bytesMultiContent, multiPartContent); } else if (currentPart is StreamMultipartData currentStreamPart) { var streamContent = new HttpStreamContent(currentStreamPart.Data); SetContentType(currentStreamPart.ContentType, streamContent); - AddMulitPartContent(currentPart, streamContent, multiPartContent); + AddMultiPartContent(currentPart, streamContent, multiPartContent); } else if (currentPart is IToSerializeContent toSerializeMultiContent) { var stringContent = await GetSerializedContentAsync(toSerializeMultiContent, cancellationToken).ConfigureAwait(false); - AddMulitPartContent(currentPart, stringContent, multiPartContent); + AddMultiPartContent(currentPart, stringContent, multiPartContent); } #if !FILEINFO_NOT_SUPPORTED @@ -445,7 +445,7 @@ private async Task CreateContentAsync(IContent content, Cancellatio { var currentStreamContent = new HttpStreamContent(currentFileMultipartData.Data.OpenRead()); SetContentType(currentFileMultipartData.ContentType, currentStreamContent); - AddMulitPartContent(currentPart, currentStreamContent, multiPartContent); + AddMultiPartContent(currentPart, currentStreamContent, multiPartContent); } #endif else @@ -504,7 +504,7 @@ private async Task GetSerializedContentAsync(IToSerializeContent co return stringContent; } - private void AddMulitPartContent(MultipartData currentContent, HttpContent content, MultipartFormDataContent multipartFormDataContent) + private void AddMultiPartContent(MultipartData currentContent, HttpContent content, MultipartFormDataContent multipartFormDataContent) { if (string.IsNullOrWhiteSpace(currentContent.Name) && string.IsNullOrWhiteSpace(currentContent.FileName)) { From 2cdaf3a096edc77550a745261fcf4f5625b905d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 16:22:44 +0200 Subject: [PATCH 02/37] Support of ETag --- .../Controllers/FileController.cs | 2 +- .../Middleware/ETagMiddleware.cs | 77 ++++++++++++ Tests/Tiny.RestClient.ForTest.Api/Startup.cs | 1 + Tests/Tiny.RestClient.Tests/EtagTests.cs | 66 ++++++++++ Tests/Tiny.RestClient.Tests/GetTests.cs | 12 +- .../EtagContainer/EtagFileContainer.cs | 116 ++++++++++++++++++ .../EtagContainer/IEtagContainer.cs | 39 ++++++ Tiny.RestClient/Request/IExecutableRequest.cs | 4 +- Tiny.RestClient/RestClientSettings.cs | 15 ++- Tiny.RestClient/Tiny.RestClient.csproj | 3 +- Tiny.RestClient/TinyRestClient.cs | 42 ++++++- 11 files changed, 362 insertions(+), 15 deletions(-) create mode 100644 Tests/Tiny.RestClient.ForTest.Api/Middleware/ETagMiddleware.cs create mode 100644 Tests/Tiny.RestClient.Tests/EtagTests.cs create mode 100644 Tiny.RestClient/EtagContainer/EtagFileContainer.cs create mode 100644 Tiny.RestClient/EtagContainer/IEtagContainer.cs diff --git a/Tests/Tiny.RestClient.ForTest.Api/Controllers/FileController.cs b/Tests/Tiny.RestClient.ForTest.Api/Controllers/FileController.cs index c93b93a..e754e73 100644 --- a/Tests/Tiny.RestClient.ForTest.Api/Controllers/FileController.cs +++ b/Tests/Tiny.RestClient.ForTest.Api/Controllers/FileController.cs @@ -21,7 +21,7 @@ public async Task One() } [HttpGet("GetPdf")] - public IActionResult Download() + public IActionResult GetPdf() { return File("pdf-sample.pdf", "application/pdf", "pdf-sample"); } diff --git a/Tests/Tiny.RestClient.ForTest.Api/Middleware/ETagMiddleware.cs b/Tests/Tiny.RestClient.ForTest.Api/Middleware/ETagMiddleware.cs new file mode 100644 index 0000000..0b25e3c --- /dev/null +++ b/Tests/Tiny.RestClient.ForTest.Api/Middleware/ETagMiddleware.cs @@ -0,0 +1,77 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Net.Http.Headers; +using System.IO; +using System.Security.Cryptography; +using System.Threading.Tasks; + +namespace Tiny.RestClient.ForTest.Api.Middleware +{ + public class ETagMiddleware + { + private readonly RequestDelegate _next; + + public ETagMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context) + { + var response = context.Response; + using (var ms = new MemoryStream()) + { + if (IsEtagSupported(response)) + { + var originalStream = response.Body; + string checksum = CalculateChecksum(ms); + + response.Headers[HeaderNames.ETag] = checksum; + + if (context.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var etag) && checksum == etag) + { + response.StatusCode = StatusCodes.Status304NotModified; + response.Body = ms; + await _next(context); + return; + } + + await _next(context); + } + else + { + await _next(context); + } + } + } + + private static bool IsEtagSupported(HttpResponse response) + { + if (response.StatusCode != StatusCodes.Status200OK) + { + return false; + } + + if (response.Headers.ContainsKey(HeaderNames.ETag)) + { + return false; + } + + return true; + } + + private static string CalculateChecksum(MemoryStream ms) + { + string checksum = string.Empty; + + using (var algo = SHA1.Create()) + { + ms.Position = 0; + byte[] bytes = algo.ComputeHash(ms); + checksum = $"\"{WebEncoders.Base64UrlEncode(bytes)}\""; + } + + return checksum; + } + } +} diff --git a/Tests/Tiny.RestClient.ForTest.Api/Startup.cs b/Tests/Tiny.RestClient.ForTest.Api/Startup.cs index 9852c40..58c0294 100644 --- a/Tests/Tiny.RestClient.ForTest.Api/Startup.cs +++ b/Tests/Tiny.RestClient.ForTest.Api/Startup.cs @@ -49,6 +49,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) } app.UseMiddleware(); + app.UseMiddleware(); app.UseResponseCompression(); app.UseMvc(); } diff --git a/Tests/Tiny.RestClient.Tests/EtagTests.cs b/Tests/Tiny.RestClient.Tests/EtagTests.cs new file mode 100644 index 0000000..b4b8ee8 --- /dev/null +++ b/Tests/Tiny.RestClient.Tests/EtagTests.cs @@ -0,0 +1,66 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Tiny.RestClient.Tests +{ + [TestClass] + public class EtagTests : BaseTest + { + private string _directoryPath; + + public TestContext TestContext { get; set; } + + [TestInitialize] + public void TestInitialize() + { + var tempPath = System.IO.Path.GetTempPath(); + _directoryPath = Path.Combine(tempPath, TestContext.TestName); + + if (!Directory.Exists(_directoryPath)) + { + Directory.CreateDirectory(_directoryPath); + } + else + { + var files = Directory.GetFiles(_directoryPath); + foreach (var file in files) + { + File.Delete(file); + } + } + } + + [TestCleanup] + public void TestCleanup() + { + if (Directory.Exists(_directoryPath)) + { + Directory.Delete(_directoryPath, true); + } + } + + [TestMethod] + public async Task GetComplexData() + { + var client = GetNewClient(); + client.Settings.EtagContainer = new EtagFileContainer(_directoryPath); + var data = await client.GetRequest("GetTest/complex"). + AddHeader("xxx-TestEtag", "test"). + ExecuteAsync(); + Assert.AreEqual(data.Length, 2); + Assert.AreEqual(data[0], "value1"); + Assert.AreEqual(data[1], "value2"); + + data = await client.GetRequest("GetTest/complex"). + AddHeader("xxx-TestEtag", "test"). + ExecuteAsync(); + Assert.AreEqual(data.Length, 2); + Assert.AreEqual(data[0], "value1"); + Assert.AreEqual(data[1], "value2"); + } + } +} diff --git a/Tests/Tiny.RestClient.Tests/GetTests.cs b/Tests/Tiny.RestClient.Tests/GetTests.cs index ebdf67e..fc11c12 100644 --- a/Tests/Tiny.RestClient.Tests/GetTests.cs +++ b/Tests/Tiny.RestClient.Tests/GetTests.cs @@ -41,13 +41,15 @@ await client. FillResponseHeaders(out Headers headersOfResponse). ExecuteAsync(); - Assert.IsTrue(headersOfResponse.Count() == 3); + // 3 custom headers + Etag + var headerFiltered = headersOfResponse.Where(h => h.Key != "ETag"); - for (int i = 0; i < headersOfResponse.Count(); i++) - { - var current = headersOfResponse.ElementAt(i); + Assert.IsTrue(headerFiltered.Count() == 3, "3 headers expected"); - Assert.IsTrue(current.Key == $"custom{i + 1}"); + for (int i = 0; i < headerFiltered.Count() - 1; i++) + { + var current = headerFiltered.ElementAt(i); + Assert.IsTrue(current.Key == $"custom{i + 1}", $"custom{i + 1} expected"); } } diff --git a/Tiny.RestClient/EtagContainer/EtagFileContainer.cs b/Tiny.RestClient/EtagContainer/EtagFileContainer.cs new file mode 100644 index 0000000..49dbb27 --- /dev/null +++ b/Tiny.RestClient/EtagContainer/EtagFileContainer.cs @@ -0,0 +1,116 @@ +#if !FILEINFO_NOT_SUPPORTED +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Tiny.RestClient +{ + /// + /// Class which store data of entity in a directory + /// + public class EtagFileContainer : IEtagContainer + { + private const int BufferSize = 81920; + private readonly string _pathOfDirectoryContainer; + + /// + /// Initializes a new instance of the class. + /// + /// the path of the directory which will store the data + public EtagFileContainer(string pathOfDirectoryContainer) + { + _pathOfDirectoryContainer = pathOfDirectoryContainer ?? throw new ArgumentNullException(nameof(pathOfDirectoryContainer)); + + if (!Directory.Exists(_pathOfDirectoryContainer)) + { + throw new DirectoryNotFoundException($"Directory '{_pathOfDirectoryContainer}' not found"); + } + } + + /// + public Task GetExistingEtagAsync(Uri uri, CancellationToken cancellationToken) + { + var url = uri.AbsoluteUri; + var key = CalculateMD5Hash(url); + var hashPath = GetEtagPath(key); + if (File.Exists(hashPath)) + { + return Task.FromResult(File.ReadAllText(hashPath)); + } + + return Task.FromResult(null); + } + + /// + public Task GetDataAsync(Uri uri, CancellationToken cancellationToken) + { + var url = uri.AbsoluteUri; + var key = CalculateMD5Hash(url); + var dataPath = GetDataPath(key); + return Task.FromResult((Stream)File.OpenRead(dataPath)); + } + + /// + public async Task SaveDataAsync(Uri uri, string etag, Stream stream, CancellationToken cancellationToken) + { + var url = uri.AbsoluteUri; + var key = CalculateMD5Hash(url); + + var hashPath = GetEtagPath(key); + var dataPath = GetDataPath(key); + + if (File.Exists(hashPath)) + { + File.Delete(hashPath); + } + + if (File.Exists(dataPath)) + { + File.Delete(dataPath); + } + + using (var fileStream = File.Create(dataPath)) + { + stream.Seek(0, SeekOrigin.Begin); + await stream.CopyToAsync(fileStream, BufferSize, cancellationToken).ConfigureAwait(false); + } + + var buffer = Encoding.ASCII.GetBytes(etag); + using (var fs = new FileStream(hashPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, buffer.Length, true)) + { + await fs.WriteAsync(buffer, 0, buffer.Length, cancellationToken); + } + } + + private string GetEtagPath(string key) + { + return Path.Combine(_pathOfDirectoryContainer, $"{key}.etag"); + } + + private string GetDataPath(string key) + { + return Path.Combine(_pathOfDirectoryContainer, key); + } + + private string CalculateMD5Hash(string input) + { + using (MD5 md5 = System.Security.Cryptography.MD5.Create()) + { + var inputBytes = System.Text.Encoding.UTF8.GetBytes(input); + var hash = md5.ComputeHash(inputBytes); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < hash.Length; i++) + { + sb.Append(hash[i].ToString("X2")); + } + + return sb.ToString(); + } + } + } +} +#endif \ No newline at end of file diff --git a/Tiny.RestClient/EtagContainer/IEtagContainer.cs b/Tiny.RestClient/EtagContainer/IEtagContainer.cs new file mode 100644 index 0000000..173a9a1 --- /dev/null +++ b/Tiny.RestClient/EtagContainer/IEtagContainer.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Tiny.RestClient +{ + /// + /// Entity Tag container + /// + public interface IEtagContainer + { + /// + /// Get the existing Etag. + /// + /// the uri + /// The cancellation token. + /// return the etag if found. If not return null. + Task GetExistingEtagAsync(Uri uri, CancellationToken cancellationToken); + + /// + /// Get data of specific uri + /// + /// the uri + /// The cancellation token. + /// return the of data + Task GetDataAsync(Uri uri, CancellationToken cancellationToken); + + /// + /// S + /// + /// the uri + /// the etag of data + /// of data to store + /// The cancellation token. + /// + Task SaveDataAsync(Uri uri, string etag, Stream stream, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/Tiny.RestClient/Request/IExecutableRequest.cs b/Tiny.RestClient/Request/IExecutableRequest.cs index a20fb4a..2d1aec9 100644 --- a/Tiny.RestClient/Request/IExecutableRequest.cs +++ b/Tiny.RestClient/Request/IExecutableRequest.cs @@ -13,7 +13,7 @@ public interface IExecutableRequest /// /// Executes the request. /// - /// The type of the t result. + /// The type of the TResult. /// The cancellation token. /// Task ExecuteAsync(CancellationToken cancellationToken = default); @@ -21,7 +21,7 @@ public interface IExecutableRequest /// /// Executes the request. /// - /// The type of the t result. + /// The type of the TResult. /// Allow to override the formatter use for the deserialization. /// The cancellation token. /// Task of TResukt diff --git a/Tiny.RestClient/RestClientSettings.cs b/Tiny.RestClient/RestClientSettings.cs index 602ac91..62eda2d 100644 --- a/Tiny.RestClient/RestClientSettings.cs +++ b/Tiny.RestClient/RestClientSettings.cs @@ -24,6 +24,16 @@ internal RestClientSettings() DefaultTimeout = TimeSpan.FromSeconds(100); } + /// + /// Add to all request the AcceptLanguage based on CurrentCulture of the Thread + /// + public bool AddAcceptLanguageBasedOnCurrentCulture { get; set; } + + /// + /// Get or set the EtagContainer + /// + public IEtagContainer EtagContainer { get; set; } + /// /// Get or set the default timeout of each request /// @@ -60,11 +70,6 @@ public Headers DefaultHeaders /// public Listeners Listeners { get; private set; } - /// - /// Add to all request the AcceptLanguage based on CurrentCulture of the Thread - /// - public bool AddAcceptLanguageBasedOnCurrentCulture { get; set; } - /// /// Gets the list of formatter used to serialize and deserialize data /// diff --git a/Tiny.RestClient/Tiny.RestClient.csproj b/Tiny.RestClient/Tiny.RestClient.csproj index 5406860..4f4d37b 100644 --- a/Tiny.RestClient/Tiny.RestClient.csproj +++ b/Tiny.RestClient/Tiny.RestClient.csproj @@ -50,7 +50,8 @@ - + + diff --git a/Tiny.RestClient/TinyRestClient.cs b/Tiny.RestClient/TinyRestClient.cs index 5f62cab..6fe6c06 100644 --- a/Tiny.RestClient/TinyRestClient.cs +++ b/Tiny.RestClient/TinyRestClient.cs @@ -598,6 +598,21 @@ private async Task SendRequestAsync(HttpMethod httpMethod, } } + var etagContainer = Settings.EtagContainer; + + if (etagContainer != null) + { + if (!request.Headers.IfNoneMatch.Any()) + { + var etag = await etagContainer.GetExistingEtagAsync(uri, cancellationToken).ConfigureAwait(false); + + if (etag != null) + { + request.Headers.IfNoneMatch.Add(new EntityTagHeaderValue(etag)); + } + } + } + if (content != null) { request.Content = content; @@ -680,7 +695,27 @@ private CancellationTokenSource GetCancellationTokenSourceForTimeout( private async Task ReadResponseAsync(HttpResponseMessage response, Headers headersToFill, CancellationToken cancellationToken) { await HandleResponseAsync(response, headersToFill, cancellationToken).ConfigureAwait(false); - var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + + var etagContainer = Settings.EtagContainer; + Stream stream = null; + if (etagContainer != null && response.StatusCode == HttpStatusCode.NotModified) + { + stream = await etagContainer.GetDataAsync(response.RequestMessage.RequestUri, cancellationToken).ConfigureAwait(false); + } + else + { + stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + + if (etagContainer != null) + { + var tag = response.Headers.ETag.Tag; + if (tag != null) + { + await etagContainer.SaveDataAsync(response.RequestMessage.RequestUri, tag, stream, cancellationToken).ConfigureAwait(false); + } + } + } + cancellationToken.ThrowIfCancellationRequested(); return await DecompressAsync(response, stream, cancellationToken).ConfigureAwait(false); } @@ -719,6 +754,11 @@ private async Task HandleResponseAsync(HttpResponseMessage response, Headers hea try { + if (Settings.EtagContainer != null && response.StatusCode == HttpStatusCode.NotModified) + { + return; + } + if (!response.IsSuccessStatusCode) { content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); From 036892cd4cd06e2aa93623c0d6f3670e8789f784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 16:53:17 +0200 Subject: [PATCH 03/37] Add tests for Etag. Add indexer property for header --- Tests/Tiny.RestClient.Tests/BaseTest.cs | 8 ++++++ Tests/Tiny.RestClient.Tests/EtagTests.cs | 30 ++++++++++++++++++---- Tiny.RestClient/Request/Headers/Headers.cs | 17 ++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/Tests/Tiny.RestClient.Tests/BaseTest.cs b/Tests/Tiny.RestClient.Tests/BaseTest.cs index f6185a8..e394527 100644 --- a/Tests/Tiny.RestClient.Tests/BaseTest.cs +++ b/Tests/Tiny.RestClient.Tests/BaseTest.cs @@ -28,6 +28,14 @@ public static TinyRestClient GetClient() return _client; } + public static string ServerUrl + { + get + { + return _serverUrl; + } + } + public static TinyRestClient GetNewClient() { return GetNewClient(_serverUrl); diff --git a/Tests/Tiny.RestClient.Tests/EtagTests.cs b/Tests/Tiny.RestClient.Tests/EtagTests.cs index b4b8ee8..46ca035 100644 --- a/Tests/Tiny.RestClient.Tests/EtagTests.cs +++ b/Tests/Tiny.RestClient.Tests/EtagTests.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace Tiny.RestClient.Tests @@ -18,7 +20,7 @@ public class EtagTests : BaseTest public void TestInitialize() { var tempPath = System.IO.Path.GetTempPath(); - _directoryPath = Path.Combine(tempPath, TestContext.TestName); + _directoryPath = Path.Combine(tempPath, $"{nameof(EtagTests)}_{TestContext.TestName}"); if (!Directory.Exists(_directoryPath)) { @@ -44,23 +46,41 @@ public void TestCleanup() } [TestMethod] - public async Task GetComplexData() + public async Task CheckIfETagIsUsed() { var client = GetNewClient(); - client.Settings.EtagContainer = new EtagFileContainer(_directoryPath); + + var etagContainer = new EtagFileContainer(_directoryPath); + client.Settings.EtagContainer = etagContainer; var data = await client.GetRequest("GetTest/complex"). - AddHeader("xxx-TestEtag", "test"). + FillResponseHeaders(out Headers headers). ExecuteAsync(); Assert.AreEqual(data.Length, 2); Assert.AreEqual(data[0], "value1"); Assert.AreEqual(data[1], "value2"); + var actionUri = new Uri($"{ServerUrl}GetTest/complex"); + var etag = headers["ETag"].FirstOrDefault(); + var etagStored = await etagContainer.GetExistingEtagAsync(actionUri, CancellationToken.None); + Assert.AreEqual(etagStored, etag); + data = await client.GetRequest("GetTest/complex"). - AddHeader("xxx-TestEtag", "test"). ExecuteAsync(); Assert.AreEqual(data.Length, 2); Assert.AreEqual(data[0], "value1"); Assert.AreEqual(data[1], "value2"); + + await etagContainer.SaveDataAsync(actionUri, "\"TEST\"", new MemoryStream(), CancellationToken.None); + + data = await client.GetRequest("GetTest/complex"). + ExecuteAsync(); + + etagStored = await etagContainer.GetExistingEtagAsync(actionUri, CancellationToken.None); + Assert.AreEqual(etagStored, etag); + + Assert.AreEqual(data.Length, 2); + Assert.AreEqual(data[0], "value1"); + Assert.AreEqual(data[1], "value2"); } } } diff --git a/Tiny.RestClient/Request/Headers/Headers.cs b/Tiny.RestClient/Request/Headers/Headers.cs index ebe9871..eb4c3f0 100644 --- a/Tiny.RestClient/Request/Headers/Headers.cs +++ b/Tiny.RestClient/Request/Headers/Headers.cs @@ -90,6 +90,23 @@ internal void AddRange(IEnumerable>> ra } } + /// + /// Gets or sets header + /// + /// header name + /// return header's value + public IEnumerable this[string name] + { + get + { + return _headers[name]; + } + set + { + Add(name, value); + } + } + /// public IEnumerator>> GetEnumerator() { From 440970b68bc59b56cb9813ebd8fc12d8ef6012ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 16:58:41 +0200 Subject: [PATCH 04/37] Add some tests --- Tests/Tiny.RestClient.Tests/EtagTests.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Tests/Tiny.RestClient.Tests/EtagTests.cs b/Tests/Tiny.RestClient.Tests/EtagTests.cs index 46ca035..997e9b9 100644 --- a/Tests/Tiny.RestClient.Tests/EtagTests.cs +++ b/Tests/Tiny.RestClient.Tests/EtagTests.cs @@ -64,11 +64,16 @@ public async Task CheckIfETagIsUsed() var etagStored = await etagContainer.GetExistingEtagAsync(actionUri, CancellationToken.None); Assert.AreEqual(etagStored, etag); + var fakeData = new List() { "test1", "test2" }; + + var json = client.Settings.Formatters.FirstOrDefault().Serialize>(fakeData, client.Settings.Encoding); + await etagContainer.SaveDataAsync(actionUri, etagStored, new MemoryStream(Encoding.UTF8.GetBytes(json)), CancellationToken.None); + data = await client.GetRequest("GetTest/complex"). ExecuteAsync(); Assert.AreEqual(data.Length, 2); - Assert.AreEqual(data[0], "value1"); - Assert.AreEqual(data[1], "value2"); + Assert.AreEqual(data[0], "test1"); + Assert.AreEqual(data[1], "test2"); await etagContainer.SaveDataAsync(actionUri, "\"TEST\"", new MemoryStream(), CancellationToken.None); From 7f1132bb6e0a7eb65f4b658a7e6aa17d0e98b322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 20:17:45 +0200 Subject: [PATCH 05/37] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 3ff33a2..163fec4 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,4 +1,7 @@ # Release notes +## 1.6.0 +* Add support of Etag + ## 1.5.5 * Fix a bug on cURL listener (when body was null) the cURL request wasn't displayed From 5f3625a75112a1c719023cb1bc3881956a8a7d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 20:20:30 +0200 Subject: [PATCH 06/37] Update README.md --- README.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ec68ab5..2493c74 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,9 @@ It hides all the complexity of communication, deserialisation ... ## Platform Support -|Platform|Supported|Version|Dependencies|Feature not supported| -| ------------------- | :-----------: | :------------------: | :------------------: |:------------------: | -|.NET Standard|Yes|1.1 && 1.2|Use System.Xml.XmlSerializer and Newtonsoft.Json|Manipulate files| -|.NET Standard|Yes|1.3|Use System.Xml.XmlSerializer and Newtonsoft.Json|-| -|.NET Standard|Yes|2.0|Use Newtonsoft.Json|-| -|.NET Framework|Yes|4.5+|Use Newtonsoft.Json|-| -|.NET Framework|Yes|4.6+|Use Newtonsoft.Json|-| -|.NET Framework|Yes|4.7+|Use Newtonsoft.Json|-| - -The support of .NET Standard 1.1 to 2.0 allow you to use it in : +The lib support **.NET Standard 1.1 to 2.0**, and **.NET Framework 4.5 to 4.7.** + +The support of **.NET Standard 1.1 to 2.0** allow you to use it in : - .Net Framework 4.6+ - Xamarin iOS et Android - .Net Core From 592aead72c25f137c0b9c3ca31175a729c50f6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 20:21:05 +0200 Subject: [PATCH 07/37] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2493c74..b567957 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ The support of **.NET Standard 1.1 to 2.0** allow you to use it in : * Support of verbs : GET, POST , PUT, DELETE, PATCH * Support of custom http verbs * Support of cancellation token on each requests +* Support of ETag * Automatic XML and JSON serialization / deserialization * Support of custom serialisation / deserialisation * Support of camelCase, snakeCase kebabCase for json serialization From 55349444de5df534721874e00c1a8e1686b69d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 20:23:04 +0200 Subject: [PATCH 08/37] Update Tiny.RestClient.csproj --- Tiny.RestClient/Tiny.RestClient.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tiny.RestClient/Tiny.RestClient.csproj b/Tiny.RestClient/Tiny.RestClient.csproj index 4f4d37b..23aadcb 100644 --- a/Tiny.RestClient/Tiny.RestClient.csproj +++ b/Tiny.RestClient/Tiny.RestClient.csproj @@ -1,7 +1,7 @@  netstandard1.1;netstandard1.2;netstandard1.3;netstandard2.0;net45;net46;net47; - 1.5.5 + 1.6.0-alpha001 Copyright © Jérôme Giacomini 2018 en Tiny.RestClient @@ -14,6 +14,7 @@ * Support of verbs : GET, POST , PUT, DELETE, PATCH * Support of custom http verbs * Support of cancellation token on each requests + * Support of ETag * Automatic XML and JSON serialization / deserialization * Support of custom serialisation / deserialisation * Support of camelCase, snakeCase kebabCase for json serialization @@ -37,7 +38,7 @@ https://github.com/jgiacomini/Tiny.RestClient.git git 3.0.3 - See release notes at https://github.com/jgiacomini/Tiny.RestClient/blob/1.5.5/RELEASE-NOTES.md + See release notes at https://github.com/jgiacomini/Tiny.RestClient/blob/1.6.0/RELEASE-NOTES.md true true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb From 125128af2edd0094eb89e3046673bd38aa34f464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 20:24:09 +0200 Subject: [PATCH 09/37] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b567957..acf71ff 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ string response = await client. // GET http://MyAPI.com/api/City/All with from url encoded content ``` -## multi-part form data +## Multi-part form data ```cs // With 2 json content From 6cd787e34d46397c4bba0349043fff15bcc36cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 20:31:40 +0200 Subject: [PATCH 10/37] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index acf71ff..05f1798 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,15 @@ catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Intern throw new ServerErrorException($"{ex.Message} {ex.ReasonPhrase}"); } ``` +## ETag +The lib support the Entity tag but it's not enabled by default. + +An implementation of IETagContainer is provided. It store all data in multiples files. + +To enable it : +```cs +client.Settings.ETagContainer = new ETagFileContainer(@"C:\ETagFolder"); +``` ## Formatters From 4830e747f84e5302d9ddf15a6d87246c4f53ae6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 20:32:44 +0200 Subject: [PATCH 11/37] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05f1798..591e6aa 100644 --- a/README.md +++ b/README.md @@ -324,7 +324,7 @@ catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Intern ## ETag The lib support the Entity tag but it's not enabled by default. -An implementation of IETagContainer is provided. It store all data in multiples files. +An implementation of IETagContainer is provided. It stores all data in multiples files. To enable it : ```cs From 18570107e8f73e8f1fc4cef48a3af34c2a4a5fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 20:33:51 +0200 Subject: [PATCH 12/37] rename Etag to ETag --- .../Middleware/ETagMiddleware.cs | 4 ++-- Tests/Tiny.RestClient.Tests/EtagTests.cs | 12 ++++++------ Tests/Tiny.RestClient.Tests/GetTests.cs | 2 +- .../ETagFileContainer.cs} | 16 ++++++++-------- .../IEtagContainer.cs => ETag/IETagContainer.cs} | 6 +++--- Tiny.RestClient/RestClientSettings.cs | 4 ++-- Tiny.RestClient/TinyRestClient.cs | 8 ++++---- 7 files changed, 26 insertions(+), 26 deletions(-) rename Tiny.RestClient/{EtagContainer/EtagFileContainer.cs => ETag/ETagFileContainer.cs} (88%) rename Tiny.RestClient/{EtagContainer/IEtagContainer.cs => ETag/IETagContainer.cs} (90%) diff --git a/Tests/Tiny.RestClient.ForTest.Api/Middleware/ETagMiddleware.cs b/Tests/Tiny.RestClient.ForTest.Api/Middleware/ETagMiddleware.cs index 0b25e3c..06d28e6 100644 --- a/Tests/Tiny.RestClient.ForTest.Api/Middleware/ETagMiddleware.cs +++ b/Tests/Tiny.RestClient.ForTest.Api/Middleware/ETagMiddleware.cs @@ -21,7 +21,7 @@ public async Task InvokeAsync(HttpContext context) var response = context.Response; using (var ms = new MemoryStream()) { - if (IsEtagSupported(response)) + if (IsETagSupported(response)) { var originalStream = response.Body; string checksum = CalculateChecksum(ms); @@ -45,7 +45,7 @@ public async Task InvokeAsync(HttpContext context) } } - private static bool IsEtagSupported(HttpResponse response) + private static bool IsETagSupported(HttpResponse response) { if (response.StatusCode != StatusCodes.Status200OK) { diff --git a/Tests/Tiny.RestClient.Tests/EtagTests.cs b/Tests/Tiny.RestClient.Tests/EtagTests.cs index 997e9b9..0d46d23 100644 --- a/Tests/Tiny.RestClient.Tests/EtagTests.cs +++ b/Tests/Tiny.RestClient.Tests/EtagTests.cs @@ -10,7 +10,7 @@ namespace Tiny.RestClient.Tests { [TestClass] - public class EtagTests : BaseTest + public class ETagTests : BaseTest { private string _directoryPath; @@ -20,7 +20,7 @@ public class EtagTests : BaseTest public void TestInitialize() { var tempPath = System.IO.Path.GetTempPath(); - _directoryPath = Path.Combine(tempPath, $"{nameof(EtagTests)}_{TestContext.TestName}"); + _directoryPath = Path.Combine(tempPath, $"{nameof(ETagTests)}_{TestContext.TestName}"); if (!Directory.Exists(_directoryPath)) { @@ -50,8 +50,8 @@ public async Task CheckIfETagIsUsed() { var client = GetNewClient(); - var etagContainer = new EtagFileContainer(_directoryPath); - client.Settings.EtagContainer = etagContainer; + var etagContainer = new ETagFileContainer(_directoryPath); + client.Settings.ETagContainer = etagContainer; var data = await client.GetRequest("GetTest/complex"). FillResponseHeaders(out Headers headers). ExecuteAsync(); @@ -61,7 +61,7 @@ public async Task CheckIfETagIsUsed() var actionUri = new Uri($"{ServerUrl}GetTest/complex"); var etag = headers["ETag"].FirstOrDefault(); - var etagStored = await etagContainer.GetExistingEtagAsync(actionUri, CancellationToken.None); + var etagStored = await etagContainer.GetExistingETagAsync(actionUri, CancellationToken.None); Assert.AreEqual(etagStored, etag); var fakeData = new List() { "test1", "test2" }; @@ -80,7 +80,7 @@ public async Task CheckIfETagIsUsed() data = await client.GetRequest("GetTest/complex"). ExecuteAsync(); - etagStored = await etagContainer.GetExistingEtagAsync(actionUri, CancellationToken.None); + etagStored = await etagContainer.GetExistingETagAsync(actionUri, CancellationToken.None); Assert.AreEqual(etagStored, etag); Assert.AreEqual(data.Length, 2); diff --git a/Tests/Tiny.RestClient.Tests/GetTests.cs b/Tests/Tiny.RestClient.Tests/GetTests.cs index fc11c12..e66f287 100644 --- a/Tests/Tiny.RestClient.Tests/GetTests.cs +++ b/Tests/Tiny.RestClient.Tests/GetTests.cs @@ -41,7 +41,7 @@ await client. FillResponseHeaders(out Headers headersOfResponse). ExecuteAsync(); - // 3 custom headers + Etag + // 3 custom headers + ETag var headerFiltered = headersOfResponse.Where(h => h.Key != "ETag"); Assert.IsTrue(headerFiltered.Count() == 3, "3 headers expected"); diff --git a/Tiny.RestClient/EtagContainer/EtagFileContainer.cs b/Tiny.RestClient/ETag/ETagFileContainer.cs similarity index 88% rename from Tiny.RestClient/EtagContainer/EtagFileContainer.cs rename to Tiny.RestClient/ETag/ETagFileContainer.cs index 49dbb27..eaf3aa8 100644 --- a/Tiny.RestClient/EtagContainer/EtagFileContainer.cs +++ b/Tiny.RestClient/ETag/ETagFileContainer.cs @@ -9,18 +9,18 @@ namespace Tiny.RestClient { /// - /// Class which store data of entity in a directory + /// Class which store data of entity in a directory /// - public class EtagFileContainer : IEtagContainer + public class ETagFileContainer : IETagContainer { private const int BufferSize = 81920; private readonly string _pathOfDirectoryContainer; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// the path of the directory which will store the data - public EtagFileContainer(string pathOfDirectoryContainer) + public ETagFileContainer(string pathOfDirectoryContainer) { _pathOfDirectoryContainer = pathOfDirectoryContainer ?? throw new ArgumentNullException(nameof(pathOfDirectoryContainer)); @@ -31,11 +31,11 @@ public EtagFileContainer(string pathOfDirectoryContainer) } /// - public Task GetExistingEtagAsync(Uri uri, CancellationToken cancellationToken) + public Task GetExistingETagAsync(Uri uri, CancellationToken cancellationToken) { var url = uri.AbsoluteUri; var key = CalculateMD5Hash(url); - var hashPath = GetEtagPath(key); + var hashPath = GetETagPath(key); if (File.Exists(hashPath)) { return Task.FromResult(File.ReadAllText(hashPath)); @@ -59,7 +59,7 @@ public async Task SaveDataAsync(Uri uri, string etag, Stream stream, Cancellatio var url = uri.AbsoluteUri; var key = CalculateMD5Hash(url); - var hashPath = GetEtagPath(key); + var hashPath = GetETagPath(key); var dataPath = GetDataPath(key); if (File.Exists(hashPath)) @@ -85,7 +85,7 @@ public async Task SaveDataAsync(Uri uri, string etag, Stream stream, Cancellatio } } - private string GetEtagPath(string key) + private string GetETagPath(string key) { return Path.Combine(_pathOfDirectoryContainer, $"{key}.etag"); } diff --git a/Tiny.RestClient/EtagContainer/IEtagContainer.cs b/Tiny.RestClient/ETag/IETagContainer.cs similarity index 90% rename from Tiny.RestClient/EtagContainer/IEtagContainer.cs rename to Tiny.RestClient/ETag/IETagContainer.cs index 173a9a1..bdfcb9f 100644 --- a/Tiny.RestClient/EtagContainer/IEtagContainer.cs +++ b/Tiny.RestClient/ETag/IETagContainer.cs @@ -8,15 +8,15 @@ namespace Tiny.RestClient /// /// Entity Tag container /// - public interface IEtagContainer + public interface IETagContainer { /// - /// Get the existing Etag. + /// Get the existing ETag. /// /// the uri /// The cancellation token. /// return the etag if found. If not return null. - Task GetExistingEtagAsync(Uri uri, CancellationToken cancellationToken); + Task GetExistingETagAsync(Uri uri, CancellationToken cancellationToken); /// /// Get data of specific uri diff --git a/Tiny.RestClient/RestClientSettings.cs b/Tiny.RestClient/RestClientSettings.cs index 62eda2d..c64ca2c 100644 --- a/Tiny.RestClient/RestClientSettings.cs +++ b/Tiny.RestClient/RestClientSettings.cs @@ -30,9 +30,9 @@ internal RestClientSettings() public bool AddAcceptLanguageBasedOnCurrentCulture { get; set; } /// - /// Get or set the EtagContainer + /// Get or set the ETagContainer /// - public IEtagContainer EtagContainer { get; set; } + public IETagContainer ETagContainer { get; set; } /// /// Get or set the default timeout of each request diff --git a/Tiny.RestClient/TinyRestClient.cs b/Tiny.RestClient/TinyRestClient.cs index 6fe6c06..693dd9a 100644 --- a/Tiny.RestClient/TinyRestClient.cs +++ b/Tiny.RestClient/TinyRestClient.cs @@ -598,13 +598,13 @@ private async Task SendRequestAsync(HttpMethod httpMethod, } } - var etagContainer = Settings.EtagContainer; + var etagContainer = Settings.ETagContainer; if (etagContainer != null) { if (!request.Headers.IfNoneMatch.Any()) { - var etag = await etagContainer.GetExistingEtagAsync(uri, cancellationToken).ConfigureAwait(false); + var etag = await etagContainer.GetExistingETagAsync(uri, cancellationToken).ConfigureAwait(false); if (etag != null) { @@ -696,7 +696,7 @@ private async Task ReadResponseAsync(HttpResponseMessage response, Heade { await HandleResponseAsync(response, headersToFill, cancellationToken).ConfigureAwait(false); - var etagContainer = Settings.EtagContainer; + var etagContainer = Settings.ETagContainer; Stream stream = null; if (etagContainer != null && response.StatusCode == HttpStatusCode.NotModified) { @@ -754,7 +754,7 @@ private async Task HandleResponseAsync(HttpResponseMessage response, Headers hea try { - if (Settings.EtagContainer != null && response.StatusCode == HttpStatusCode.NotModified) + if (Settings.ETagContainer != null && response.StatusCode == HttpStatusCode.NotModified) { return; } From d85a91dbd12862bd7b4c79b4fc01268b5a5952f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 21:45:44 +0200 Subject: [PATCH 13/37] Add local ETagContainer on request --- Tests/Tiny.RestClient.Tests/EtagTests.cs | 47 +++++++++++++- Tiny.RestClient/Request/IRequest.cs | 7 ++ Tiny.RestClient/Request/Request.cs | 23 +++++-- Tiny.RestClient/TinyRestClient.cs | 83 +++++++++++++++--------- 4 files changed, 122 insertions(+), 38 deletions(-) diff --git a/Tests/Tiny.RestClient.Tests/EtagTests.cs b/Tests/Tiny.RestClient.Tests/EtagTests.cs index 0d46d23..a2da7ae 100644 --- a/Tests/Tiny.RestClient.Tests/EtagTests.cs +++ b/Tests/Tiny.RestClient.Tests/EtagTests.cs @@ -46,7 +46,7 @@ public void TestCleanup() } [TestMethod] - public async Task CheckIfETagIsUsed() + public async Task ETagContainerOnClient() { var client = GetNewClient(); @@ -87,5 +87,50 @@ public async Task CheckIfETagIsUsed() Assert.AreEqual(data[0], "value1"); Assert.AreEqual(data[1], "value2"); } + + [TestMethod] + public async Task ETagContainerOnRequest() + { + var client = GetNewClient(); + + var etagContainer = new ETagFileContainer(_directoryPath); + var data = await client.GetRequest("GetTest/complex"). + WithETagContainer(etagContainer). + FillResponseHeaders(out Headers headers). + ExecuteAsync(); + Assert.AreEqual(data.Length, 2); + Assert.AreEqual(data[0], "value1"); + Assert.AreEqual(data[1], "value2"); + + var actionUri = new Uri($"{ServerUrl}GetTest/complex"); + var etag = headers["ETag"].FirstOrDefault(); + var etagStored = await etagContainer.GetExistingETagAsync(actionUri, CancellationToken.None); + Assert.AreEqual(etagStored, etag); + + var fakeData = new List() { "test1", "test2" }; + + var json = client.Settings.Formatters.FirstOrDefault().Serialize>(fakeData, client.Settings.Encoding); + await etagContainer.SaveDataAsync(actionUri, etagStored, new MemoryStream(Encoding.UTF8.GetBytes(json)), CancellationToken.None); + + data = await client.GetRequest("GetTest/complex"). + WithETagContainer(etagContainer). + ExecuteAsync(); + Assert.AreEqual(data.Length, 2); + Assert.AreEqual(data[0], "test1"); + Assert.AreEqual(data[1], "test2"); + + await etagContainer.SaveDataAsync(actionUri, "\"TEST\"", new MemoryStream(), CancellationToken.None); + + data = await client.GetRequest("GetTest/complex"). + WithETagContainer(etagContainer). + ExecuteAsync(); + + etagStored = await etagContainer.GetExistingETagAsync(actionUri, CancellationToken.None); + Assert.AreEqual(etagStored, etag); + + Assert.AreEqual(data.Length, 2); + Assert.AreEqual(data[0], "value1"); + Assert.AreEqual(data[1], "value2"); + } } } diff --git a/Tiny.RestClient/Request/IRequest.cs b/Tiny.RestClient/Request/IRequest.cs index 063ec07..01566c3 100644 --- a/Tiny.RestClient/Request/IRequest.cs +++ b/Tiny.RestClient/Request/IRequest.cs @@ -32,6 +32,13 @@ public interface IRequest : IExecutableRequest, IFormRequest /// The current request IRequest WithTimeout(TimeSpan timeout); + /// + /// With a specific etag container + /// + /// the eTag container + /// + IRequest WithETagContainer(IETagContainer eTagContainer); + /// /// Adds the content. /// diff --git a/Tiny.RestClient/Request/Request.cs b/Tiny.RestClient/Request/Request.cs index 8ea6822..cecab22 100644 --- a/Tiny.RestClient/Request/Request.cs +++ b/Tiny.RestClient/Request/Request.cs @@ -26,14 +26,16 @@ internal class Request : private IContent _content; private List> _formParameters; private MultipartContent _multiPartFormData; - private Headers _reponseHeaders; + private Headers _responseHeaders; private TimeSpan? _timeout; + private IETagContainer _eTagContainer; internal HttpMethod HttpMethod { get => _httpMethod; } internal Dictionary QueryParameters { get => _queryParameters; } internal string Route { get => _route; } internal IContent Content { get => _content; } - internal Headers ReponseHeaders { get => _reponseHeaders; } + internal IETagContainer ETagContainer { get => _eTagContainer; } + internal Headers ResponseHeaders { get => _responseHeaders; } internal Headers Headers { get => _headers; } internal TimeSpan? Timeout { get => _timeout; } @@ -123,7 +125,7 @@ public IFormRequest AddFormParameters(IEnumerable> public IParameterRequest FillResponseHeaders(out Headers headers) { headers = new Headers(); - _reponseHeaders = headers; + _responseHeaders = headers; return this; } @@ -314,6 +316,13 @@ public IParameterRequest AddQueryParameter(string key, float? value) } #endregion + /// + public IRequest WithETagContainer(IETagContainer eTagContainer) + { + _eTagContainer = eTagContainer; + return this; + } + /// public IRequest WithTimeout(TimeSpan timeout) { @@ -364,7 +373,7 @@ public Task ExecuteAsHttpResponseMessageAsync(CancellationT return _client.ExecuteAsHttpResponseMessageResultAsync(this, cancellationToken); } - #if !FILEINFO_NOT_SUPPORTED +#if !FILEINFO_NOT_SUPPORTED /// public async Task DownloadFileAsync(string fileName, CancellationToken cancellationToken) { @@ -392,7 +401,7 @@ public async Task DownloadFileAsync(string fileName, CancellationToken return new FileInfo(fileName); } - #endif +#endif #region MultiPart @@ -442,7 +451,7 @@ IMultiPartFromDataExecutableRequest IMultipartFromDataRequest.AddContent ExecuteAsync( using (var content = await CreateContentAsync(tinyRequest.Content, cancellationToken).ConfigureAwait(false)) { var requestUri = BuildRequestUri(tinyRequest.Route, tinyRequest.QueryParameters); + var eTagContainer = GetETagContainer(tinyRequest); cancellationToken.ThrowIfCancellationRequested(); - using (HttpResponseMessage response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, formatter, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false)) + using (HttpResponseMessage response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, eTagContainer, formatter, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false)) { - using (var stream = await ReadResponseAsync(response, tinyRequest.ReponseHeaders, cancellationToken).ConfigureAwait(false)) + using (var stream = await ReadResponseAsync(response, tinyRequest.ResponseHeaders, eTagContainer, cancellationToken).ConfigureAwait(false)) { if (stream == null || stream.CanRead == false) { @@ -277,9 +278,9 @@ internal async Task ExecuteAsync( using (var content = await CreateContentAsync(tinyRequest.Content, cancellationToken).ConfigureAwait(false)) { var requestUri = BuildRequestUri(tinyRequest.Route, tinyRequest.QueryParameters); - using (HttpResponseMessage response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false)) + using (HttpResponseMessage response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, null, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false)) { - await HandleResponseAsync(response, tinyRequest.ReponseHeaders, cancellationToken).ConfigureAwait(false); + await HandleResponseAsync(response, tinyRequest.ResponseHeaders, null, cancellationToken).ConfigureAwait(false); } } } @@ -291,9 +292,11 @@ internal async Task ExecuteAsByteArrayResultAsync( using (var content = await CreateContentAsync(tinyRequest.Content, cancellationToken).ConfigureAwait(false)) { var requestUri = BuildRequestUri(tinyRequest.Route, tinyRequest.QueryParameters); - using (HttpResponseMessage response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false)) + var eTagContainer = GetETagContainer(tinyRequest); + + using (HttpResponseMessage response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, eTagContainer, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false)) { - using (var stream = await ReadResponseAsync(response, tinyRequest.ReponseHeaders, cancellationToken).ConfigureAwait(false)) + using (var stream = await ReadResponseAsync(response, tinyRequest.ResponseHeaders, eTagContainer, cancellationToken).ConfigureAwait(false)) { if (stream == null || !stream.CanRead) { @@ -318,8 +321,9 @@ internal async Task ExecuteAsStreamResultAsync( using (var content = await CreateContentAsync(tinyRequest.Content, cancellationToken).ConfigureAwait(false)) { var requestUri = BuildRequestUri(tinyRequest.Route, tinyRequest.QueryParameters); - var response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false); - var stream = await ReadResponseAsync(response, tinyRequest.ReponseHeaders, cancellationToken).ConfigureAwait(false); + var eTagContainer = GetETagContainer(tinyRequest); + var response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, eTagContainer, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false); + var stream = await ReadResponseAsync(response, tinyRequest.ResponseHeaders, eTagContainer, cancellationToken).ConfigureAwait(false); if (stream == null || !stream.CanRead) { return null; @@ -336,9 +340,10 @@ internal async Task ExecuteAsStringResultAsync( using (var content = await CreateContentAsync(tinyRequest.Content, cancellationToken).ConfigureAwait(false)) { var requestUri = BuildRequestUri(tinyRequest.Route, tinyRequest.QueryParameters); - using (var response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false)) + var eTagContainer = GetETagContainer(tinyRequest); + using (var response = await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, eTagContainer, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false)) { - var stream = await ReadResponseAsync(response, tinyRequest.ReponseHeaders, cancellationToken).ConfigureAwait(false); + var stream = await ReadResponseAsync(response, tinyRequest.ResponseHeaders, eTagContainer, cancellationToken).ConfigureAwait(false); if (stream == null || !stream.CanRead) { return null; @@ -363,7 +368,7 @@ internal async Task ExecuteAsHttpResponseMessageResultAsync using (var content = await CreateContentAsync(tinyRequest.Content, cancellationToken).ConfigureAwait(false)) { var requestUri = BuildRequestUri(tinyRequest.Route, tinyRequest.QueryParameters); - return await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false); + return await SendRequestAsync(tinyRequest.HttpMethod, requestUri, tinyRequest.Headers, content, null, null, tinyRequest.Timeout, cancellationToken).ConfigureAwait(false); } } @@ -553,7 +558,15 @@ private Uri BuildRequestUri(string route, Dictionary queryParame return new Uri(stringBuilder.ToString()); } - private async Task SendRequestAsync(HttpMethod httpMethod, Uri uri, Headers requestHeader, HttpContent content, IFormatter deserializer, TimeSpan? localTimeout, CancellationToken cancellationToken) + private async Task SendRequestAsync( + HttpMethod httpMethod, + Uri uri, + Headers requestHeader, + HttpContent content, + IETagContainer eTagContainer, + IFormatter deserializer, + TimeSpan? localTimeout, + CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Stopwatch stopwatch = null; @@ -598,17 +611,15 @@ private async Task SendRequestAsync(HttpMethod httpMethod, } } - var etagContainer = Settings.ETagContainer; - - if (etagContainer != null) + if (eTagContainer != null) { if (!request.Headers.IfNoneMatch.Any()) { - var etag = await etagContainer.GetExistingETagAsync(uri, cancellationToken).ConfigureAwait(false); + var eTag = await eTagContainer.GetExistingETagAsync(uri, cancellationToken).ConfigureAwait(false); - if (etag != null) + if (eTag != null) { - request.Headers.IfNoneMatch.Add(new EntityTagHeaderValue(etag)); + request.Headers.IfNoneMatch.Add(new EntityTagHeaderValue(eTag)); } } } @@ -691,27 +702,35 @@ private CancellationTokenSource GetCancellationTokenSourceForTimeout( } } + private IETagContainer GetETagContainer(Request request) + { + return request.ETagContainer ?? Settings.ETagContainer; + } + #region Read response - private async Task ReadResponseAsync(HttpResponseMessage response, Headers headersToFill, CancellationToken cancellationToken) + private async Task ReadResponseAsync( + HttpResponseMessage response, + Headers responseHeader, + IETagContainer eTagContainer, + CancellationToken cancellationToken) { - await HandleResponseAsync(response, headersToFill, cancellationToken).ConfigureAwait(false); + await HandleResponseAsync(response, responseHeader, eTagContainer, cancellationToken).ConfigureAwait(false); - var etagContainer = Settings.ETagContainer; Stream stream = null; - if (etagContainer != null && response.StatusCode == HttpStatusCode.NotModified) + if (eTagContainer != null && response.StatusCode == HttpStatusCode.NotModified) { - stream = await etagContainer.GetDataAsync(response.RequestMessage.RequestUri, cancellationToken).ConfigureAwait(false); + stream = await eTagContainer.GetDataAsync(response.RequestMessage.RequestUri, cancellationToken).ConfigureAwait(false); } else { stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - if (etagContainer != null) + if (eTagContainer != null) { var tag = response.Headers.ETag.Tag; if (tag != null) { - await etagContainer.SaveDataAsync(response.RequestMessage.RequestUri, tag, stream, cancellationToken).ConfigureAwait(false); + await eTagContainer.SaveDataAsync(response.RequestMessage.RequestUri, tag, stream, cancellationToken).ConfigureAwait(false); } } } @@ -739,22 +758,26 @@ private async Task DecompressAsync(HttpResponseMessage response, Stream return stream; } - private async Task HandleResponseAsync(HttpResponseMessage response, Headers headersToFill, CancellationToken cancellationToken) + private async Task HandleResponseAsync( + HttpResponseMessage response, + Headers responseHeaders, + IETagContainer eTagContainer, + CancellationToken cancellationToken) { string content = null; - if (headersToFill != null) + if (responseHeaders != null) { - headersToFill.AddRange(response.Headers); + responseHeaders.AddRange(response.Headers); if (response.Content != null && response.Content.Headers != null) { - headersToFill.AddRange(response.Content.Headers); + responseHeaders.AddRange(response.Content.Headers); } } try { - if (Settings.ETagContainer != null && response.StatusCode == HttpStatusCode.NotModified) + if (eTagContainer != null && response.StatusCode == HttpStatusCode.NotModified) { return; } From 771f46eb5a59a88ca91d27a443f412c2ac6466b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 21:51:22 +0200 Subject: [PATCH 14/37] Update README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 591e6aa..28fa3fd 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,7 @@ catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Intern ## ETag The lib support the Entity tag but it's not enabled by default. +### Define an ETagContainer globally An implementation of IETagContainer is provided. It stores all data in multiples files. To enable it : @@ -331,6 +332,12 @@ To enable it : client.Settings.ETagContainer = new ETagFileContainer(@"C:\ETagFolder"); ``` +### Define an ETagContainer for one request +You can also define the ETagContainer only on specifice request. +```cs +request.WithETagContainer(eTagContainer); +``` + ## Formatters By default : From 88005fd60824c30be54e23eeff7219c043faa2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 22:07:17 +0200 Subject: [PATCH 15/37] Add test --- Tests/Tiny.RestClient.Tests/EtagTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/Tiny.RestClient.Tests/EtagTests.cs b/Tests/Tiny.RestClient.Tests/EtagTests.cs index a2da7ae..5d27b4c 100644 --- a/Tests/Tiny.RestClient.Tests/EtagTests.cs +++ b/Tests/Tiny.RestClient.Tests/EtagTests.cs @@ -132,5 +132,13 @@ public async Task ETagContainerOnRequest() Assert.AreEqual(data[0], "value1"); Assert.AreEqual(data[1], "value2"); } + + [TestMethod] + [ExpectedException(typeof(DirectoryNotFoundException))] + public void ETagFileContainerDirectoryNotFound() + { + new ETagFileContainer(@"C:\notfound"); + Assert.Fail("It must not go here"); + } } } From 763c601cba966023a98808c34a71939876eeb1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 22:27:56 +0200 Subject: [PATCH 16/37] Add strong naming --- Tiny.RestClient/Tiny.RestClient.csproj | 2 ++ Tiny.RestClient/strong_name.pfx | Bin 0 -> 1764 bytes 2 files changed, 2 insertions(+) create mode 100644 Tiny.RestClient/strong_name.pfx diff --git a/Tiny.RestClient/Tiny.RestClient.csproj b/Tiny.RestClient/Tiny.RestClient.csproj index 23aadcb..8901161 100644 --- a/Tiny.RestClient/Tiny.RestClient.csproj +++ b/Tiny.RestClient/Tiny.RestClient.csproj @@ -48,6 +48,8 @@ $(TargetDir)\Tiny.RestClient.xml + true + strong_name.pfx diff --git a/Tiny.RestClient/strong_name.pfx b/Tiny.RestClient/strong_name.pfx new file mode 100644 index 0000000000000000000000000000000000000000..ce225a7fac890e077d88988c799e55423a803d61 GIT binary patch literal 1764 zcmZWodpMM78-L!JF^$~pje(vAzcR%-YJ%8Lk7{O0tAS{gFi}1qg zNmfZ8cR+$r8G=v6A$Tq-b6^D9`admfH4ed6pmHgig?Q1wr^G}cOc{b{g%QkcSPn1p zUm1OzfD@`?&J7UMLK8687)S_Ry%Kw;nY@`du06awWBu0$|ldN??o(mX5WG(!X9>pe;3;j zz0fcI@~+Q7%wEPf_gNjh(T-<{5ASoHImZVk(_Zz7s+0zs51NH&6!DBLw!^B9cbJik z4@A{WaxG=WeAYw*s3RwOzkQV_%%AsH4{&j~1!+!em6xcANhy{uUej_DaAhvM2;Ck2 zb@iDmO5Qt< zz7yIuC*Ho;Y-DC3kqh-yzNcSpO&_Ic`E7$IFI3^76Up0Y+OBx!AaCxQpDDAeeqN&- zFULDs$YIsf?K(Gyho8n}W7i+e&@ugL)64EAX)7O}m(ngSLOK&Rw`<^e$ymc;{x)gg zSuLxdvXrL2mM184QF=&d@Xn-#XJ%un6gRC}4c_^hJ@lMyB6?3ON0F7&CZ)E)kt7k< zPcvghNUsRKD;EdDYMTnqs=^40#bw`_pIb>sPWLu{R#a5FpMCtMP=J>JOSq8C%(UmW!PZ zoQg|Xl=u;N*FS46EJ?xRa1a0hcKKJ7pxEuP1<^nom;o3KLtu^~9iWL;3qS`3DAENK zpofMgFhx72DA)W_DJarKCk@b^E;t1A(dqNB8(x@5CK2MWLeJwGc!zz=0~6th4Gss*Dw40xf~|I3sDV1Bt5i83E>5`>{n9Qfz=f_T)N zcTuSs0|B(u|J#B7->#N7R6dMUoUZo` zopfPOXl0qB`>2OmP~L`d<7PzVJgBg)n3buj{2=|t*TdTX+}5U7s_T7xq=?p093w(F z$nE#B@dQK2q-d_DCGr|kbu3~naS>dav_I9=R840EII9sm^*d7{R)4(jJ0|_hHS78H zO~)wxxYy|#>E6>Oea)g;%`z-WZvx4SgUDp6|HhBBy1dIFesd;d-(Xo*?_;q7>f9S& z{fc1>j&Gl3oLE;_hvXyNp?H47`;))#>+I2%yL{a!FlxM(5To3}*B}hN`>D05f8reB zf<@P%-IX3x-wSZRU7%sjUfw%0BWjXBTQ6JGGhebI7}1pNG??<514YrYS2d;ho$?zA z(oQ7~1%n3{To9Z8Fsit5Q??QBdwGmOd)QrKCWgMXsk}oDQ4u34@mkL^KV4yuh>xih z|3W7gkS@yxCDT={lYGccSSZr-YckFXlgtelXx7&@Gv0@|RijFkMY z2~&rH@&1KA#C+=eLhMX)V zG+Nejc-z~$nUu*HcC&2ltAbXtTKrjxKj+EKY|p3vP#J%yKq16xNkCkUOkBFqfn+G| zU$o;S1Y>}cQbf^9m%?M?5AL+e%|1JwrPI#6o(+a#cUAK;rlwZeV&^1-8cw*i56Cwd zzqiPf|lKE5|s#tJn!2fJv|*UVu!% zVu(12@)GT;z?;OiQ?mZmo_SC9=Sz`2a1zZ!=Cu}xnNs%}t=`mQatm`SwdC^x{{`F} B=>z}( literal 0 HcmV?d00001 From d538173458a52bb9e48556c785ba4ecd015277c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 22:39:48 +0200 Subject: [PATCH 17/37] add strong_name.snk --- Tiny.RestClient/Tiny.RestClient.csproj | 2 +- Tiny.RestClient/strong_name.pfx | Bin 1764 -> 0 bytes Tiny.RestClient/strong_name.snk | Bin 0 -> 596 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 Tiny.RestClient/strong_name.pfx create mode 100644 Tiny.RestClient/strong_name.snk diff --git a/Tiny.RestClient/Tiny.RestClient.csproj b/Tiny.RestClient/Tiny.RestClient.csproj index 8901161..af0b3db 100644 --- a/Tiny.RestClient/Tiny.RestClient.csproj +++ b/Tiny.RestClient/Tiny.RestClient.csproj @@ -49,7 +49,7 @@ $(TargetDir)\Tiny.RestClient.xml true - strong_name.pfx + strong_name.snk diff --git a/Tiny.RestClient/strong_name.pfx b/Tiny.RestClient/strong_name.pfx deleted file mode 100644 index ce225a7fac890e077d88988c799e55423a803d61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1764 zcmZWodpMM78-L!JF^$~pje(vAzcR%-YJ%8Lk7{O0tAS{gFi}1qg zNmfZ8cR+$r8G=v6A$Tq-b6^D9`admfH4ed6pmHgig?Q1wr^G}cOc{b{g%QkcSPn1p zUm1OzfD@`?&J7UMLK8687)S_Ry%Kw;nY@`du06awWBu0$|ldN??o(mX5WG(!X9>pe;3;j zz0fcI@~+Q7%wEPf_gNjh(T-<{5ASoHImZVk(_Zz7s+0zs51NH&6!DBLw!^B9cbJik z4@A{WaxG=WeAYw*s3RwOzkQV_%%AsH4{&j~1!+!em6xcANhy{uUej_DaAhvM2;Ck2 zb@iDmO5Qt< zz7yIuC*Ho;Y-DC3kqh-yzNcSpO&_Ic`E7$IFI3^76Up0Y+OBx!AaCxQpDDAeeqN&- zFULDs$YIsf?K(Gyho8n}W7i+e&@ugL)64EAX)7O}m(ngSLOK&Rw`<^e$ymc;{x)gg zSuLxdvXrL2mM184QF=&d@Xn-#XJ%un6gRC}4c_^hJ@lMyB6?3ON0F7&CZ)E)kt7k< zPcvghNUsRKD;EdDYMTnqs=^40#bw`_pIb>sPWLu{R#a5FpMCtMP=J>JOSq8C%(UmW!PZ zoQg|Xl=u;N*FS46EJ?xRa1a0hcKKJ7pxEuP1<^nom;o3KLtu^~9iWL;3qS`3DAENK zpofMgFhx72DA)W_DJarKCk@b^E;t1A(dqNB8(x@5CK2MWLeJwGc!zz=0~6th4Gss*Dw40xf~|I3sDV1Bt5i83E>5`>{n9Qfz=f_T)N zcTuSs0|B(u|J#B7->#N7R6dMUoUZo` zopfPOXl0qB`>2OmP~L`d<7PzVJgBg)n3buj{2=|t*TdTX+}5U7s_T7xq=?p093w(F z$nE#B@dQK2q-d_DCGr|kbu3~naS>dav_I9=R840EII9sm^*d7{R)4(jJ0|_hHS78H zO~)wxxYy|#>E6>Oea)g;%`z-WZvx4SgUDp6|HhBBy1dIFesd;d-(Xo*?_;q7>f9S& z{fc1>j&Gl3oLE;_hvXyNp?H47`;))#>+I2%yL{a!FlxM(5To3}*B}hN`>D05f8reB zf<@P%-IX3x-wSZRU7%sjUfw%0BWjXBTQ6JGGhebI7}1pNG??<514YrYS2d;ho$?zA z(oQ7~1%n3{To9Z8Fsit5Q??QBdwGmOd)QrKCWgMXsk}oDQ4u34@mkL^KV4yuh>xih z|3W7gkS@yxCDT={lYGccSSZr-YckFXlgtelXx7&@Gv0@|RijFkMY z2~&rH@&1KA#C+=eLhMX)V zG+Nejc-z~$nUu*HcC&2ltAbXtTKrjxKj+EKY|p3vP#J%yKq16xNkCkUOkBFqfn+G| zU$o;S1Y>}cQbf^9m%?M?5AL+e%|1JwrPI#6o(+a#cUAK;rlwZeV&^1-8cw*i56Cwd zzqiPf|lKE5|s#tJn!2fJv|*UVu!% zVu(12@)GT;z?;OiQ?mZmo_SC9=Sz`2a1zZ!=Cu}xnNs%}t=`mQatm`SwdC^x{{`F} B=>z}( diff --git a/Tiny.RestClient/strong_name.snk b/Tiny.RestClient/strong_name.snk new file mode 100644 index 0000000000000000000000000000000000000000..4c17718b8bc68d03f9a555b32e951cb37ea8c5e2 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096M`0G&eD%U7-Wc>sXEG2HWW*_+_^%Y|n z)v959fSyWg6WOn!?c2IrIpOT~Hf>vm6xWT;pqX+>WpB~NQ69A+xg1N|!#gL<2_$-Z zuXdT(PC^vhdL6kZ+g`6zo-?1AFs0>Qf2?_k%bbpSjhKx7FF@0c|(*b*gLeVN^8TzNCJ=l$z1m<$cUWR z(1+mBB|#v5O!f8O90TQe0-AJGv}kwc3#QcxD>deDg6Z^T#qngZ-0}({?3|&81R|wo z`4{BfDX#aC7X7*xq87OL;5D&Rr0e_-Yet4Rk=s%r{BfHtBfKPBF~iW)XwSO@T^ZY6 z-p>PmU*nw=Kb6H{Or$lsEM$WPcK=8T31jw1oLJH-R!QndFt`TS7NY^gx?B2Ig%A-C zHTW6)C&IRh$a+twyoQ8{N1u%din<~k%dgquYyni&nxk-4uxHYYIc8oF3w)`La>6mI zZuSS75C{&~>=%;XD?fdA2akQL+sRJun~izqOc{}hB`*Hhk?nR2cp@A&5+?wN##>NBo=)H^>_2kZyJ;+z==c(v=EE z=HYdJ&aOF;uxlSMWqU{rz$@x`W^4V79zrZ9ld3g|-m>I#n?QVaEJUQVzrv=cZeMD1 iBc-I%BJ8S%j^S5rU~akd)|i~yI1+M}`L~2jvONlE&n4#o literal 0 HcmV?d00001 From b3f2bbc883de41bc6f0f91f02f0dd7f12f6137ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sun, 21 Oct 2018 22:42:22 +0200 Subject: [PATCH 18/37] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 163fec4..9c0d33d 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,6 +1,7 @@ # Release notes ## 1.6.0 -* Add support of Etag +* Add support of ETag +* The assembly is now strong named ## 1.5.5 * Fix a bug on cURL listener (when body was null) the cURL request wasn't displayed From 55be4e41831a0905c48474d77736a69857177866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Mon, 22 Oct 2018 10:22:38 +0200 Subject: [PATCH 19/37] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 9c0d33d..de8632f 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,7 +1,7 @@ # Release notes ## 1.6.0 * Add support of ETag -* The assembly is now strong named +* The assembly is strong named ## 1.5.5 * Fix a bug on cURL listener (when body was null) the cURL request wasn't displayed From e5eb0576d8079a099048ad54e4eea38cba5c1f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Mon, 22 Oct 2018 10:23:10 +0200 Subject: [PATCH 20/37] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index de8632f..03facb2 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,7 +1,7 @@ # Release notes ## 1.6.0 * Add support of ETag -* The assembly is strong named +* Now the assembly is strong named ## 1.5.5 * Fix a bug on cURL listener (when body was null) the cURL request wasn't displayed From f2e6335e65e24793f01e9d707fdfb0b74b116973 Mon Sep 17 00:00:00 2001 From: Thomas Levesque Date: Thu, 25 Oct 2018 13:35:57 +0200 Subject: [PATCH 21/37] Fixed a few typos / phrasings --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ec68ab5..f54afa4 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ The support of .NET Standard 1.1 to 2.0 allow you to use it in : * Support of gzip/deflate (compression and decompression) * Optimized http calls * Typed exceptions which are easier to interpret -* Define timeout globally or by request -* Timeout exception throwed if the request is in timeout (by default HttpClient send OperationCancelledException, so we can't make difference between a user annulation and timeout) +* Define timeout globally or per request +* Timeout exception thrown if the request is in timeout (by default HttpClient sends OperationCancelledException, so we can't distinguish between user cancellation and timeout) * Provide an easy way to log : all sending of request, failed to get response, and the time get response. * Support of export requests to postman collection * Support of display cURL request in debug output From a4eeefa303aebaa50236fd5493452566ad6bdc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Thu, 25 Oct 2018 13:50:21 +0200 Subject: [PATCH 22/37] Add stringContent --- Tiny.RestClient/Request/Content/StringContent.cs | 10 ++++++++++ Tiny.RestClient/TinyRestClient.cs | 14 +++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 Tiny.RestClient/Request/Content/StringContent.cs diff --git a/Tiny.RestClient/Request/Content/StringContent.cs b/Tiny.RestClient/Request/Content/StringContent.cs new file mode 100644 index 0000000..0d63af4 --- /dev/null +++ b/Tiny.RestClient/Request/Content/StringContent.cs @@ -0,0 +1,10 @@ +namespace Tiny.RestClient +{ + internal class StringContent : BaseContent + { + public StringContent(string data, string contentType) + : base(data, contentType) + { + } + } +} \ No newline at end of file diff --git a/Tiny.RestClient/TinyRestClient.cs b/Tiny.RestClient/TinyRestClient.cs index b1b73bd..8af1285 100644 --- a/Tiny.RestClient/TinyRestClient.cs +++ b/Tiny.RestClient/TinyRestClient.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using HttpStreamContent = System.Net.Http.StreamContent; +using HttpStringContent = System.Net.Http.StringContent; namespace Tiny.RestClient { @@ -379,6 +380,13 @@ private async Task CreateContentAsync(IContent content, Cancellatio return null; } + if (content is StringContent stringContent) + { + var contentString = new HttpStringContent(stringContent.Data); + SetContentType(stringContent.ContentType, contentString); + return contentString; + } + if (content is StreamContent currentContent) { var contentStream = new HttpStreamContent(currentContent.Data); @@ -441,8 +449,8 @@ private async Task CreateContentAsync(IContent content, Cancellatio } else if (currentPart is IToSerializeContent toSerializeMultiContent) { - var stringContent = await GetSerializedContentAsync(toSerializeMultiContent, cancellationToken).ConfigureAwait(false); - AddMultiPartContent(currentPart, stringContent, multiPartContent); + var serializedContent = await GetSerializedContentAsync(toSerializeMultiContent, cancellationToken).ConfigureAwait(false); + AddMultiPartContent(currentPart, serializedContent, multiPartContent); } #if !FILEINFO_NOT_SUPPORTED @@ -504,7 +512,7 @@ private async Task GetSerializedContentAsync(IToSerializeContent co } } - var stringContent = new StringContent(serializedString, Settings.Encoding); + var stringContent = new HttpStringContent(serializedString, Settings.Encoding); stringContent.Headers.ContentType = new MediaTypeHeaderValue(serializer.DefaultMediaType); return stringContent; } From 3fba63fec3308b68f3c96f1228a61a4fd4f701e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Thu, 25 Oct 2018 13:52:40 +0200 Subject: [PATCH 23/37] Fix typos --- Tiny.RestClient/Tiny.RestClient.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tiny.RestClient/Tiny.RestClient.csproj b/Tiny.RestClient/Tiny.RestClient.csproj index af0b3db..783b0a9 100644 --- a/Tiny.RestClient/Tiny.RestClient.csproj +++ b/Tiny.RestClient/Tiny.RestClient.csproj @@ -24,8 +24,8 @@ * Support of gzip and deflate (compression and decompression) * Optimized http calls * Typed exceptions which are easier to interpret - * Define timeout globally or by request - * Timeout exception throwed if the request is in timeout (by default HttpClient send OperationCancelledException, so we can't make difference between a user annulation and timeout) + * Define timeout globally or per request + * Timeout exception thrown if the request is in timeout (by default HttpClient sends OperationCancelledException, so we can't distinguish between user cancellation and timeout) * Provide an easy way to log : all sending of request, failed to get response, and the time get response. * Support of export requests to postman collection * Support of display cURL request in debug output From 2302066b6ce1942d74dd856877f3d0c75d5cf0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Fri, 26 Oct 2018 21:28:52 +0200 Subject: [PATCH 24/37] Add String content to all mono part requests. --- Tests/Tiny.RestClient.Tests/PostTests.cs | 23 +++++++++++++++++++++++ Tiny.RestClient/Request/IRequest.cs | 8 ++++++++ Tiny.RestClient/Request/Request.cs | 13 ++++++++++--- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/Tests/Tiny.RestClient.Tests/PostTests.cs b/Tests/Tiny.RestClient.Tests/PostTests.cs index ee65e55..9976df3 100644 --- a/Tests/Tiny.RestClient.Tests/PostTests.cs +++ b/Tests/Tiny.RestClient.Tests/PostTests.cs @@ -113,6 +113,29 @@ public async Task PostByteArrayData() } } + [TestMethod] + public async Task PostStringData() + { + uint size = 2048; + var client = GetClient(); + + var byteArray = GetByteArray(size); + + var data = System.Text.Encoding.Default.GetString(byteArray); + + var response = await client. + PostRequest("PostTest/Stream"). + AddStringContent(data). + ExecuteAsByteArrayAsync(); + Assert.IsNotNull(response); + Assert.AreEqual(size, (uint)response.Length); + + for (int i = 0; i < byteArray.Length; i++) + { + Assert.IsTrue(byteArray[i] == response[i], "byte array response must have same data than byte array sended"); + } + } + [TestMethod] public async Task PostStreamData() { diff --git a/Tiny.RestClient/Request/IRequest.cs b/Tiny.RestClient/Request/IRequest.cs index 01566c3..1d5b9d6 100644 --- a/Tiny.RestClient/Request/IRequest.cs +++ b/Tiny.RestClient/Request/IRequest.cs @@ -64,6 +64,14 @@ public interface IRequest : IExecutableRequest, IFormRequest /// The content type /// The current request IParameterRequest AddStreamContent(Stream stream, string contentType = "application/octet-stream"); + + /// + /// Adds string as content + /// + /// The content. + /// The content type + /// The current request + IParameterRequest AddStringContent(string content, string contentType = "text/plain"); #if !FILEINFO_NOT_SUPPORTED /// /// Adds file as content. diff --git a/Tiny.RestClient/Request/Request.cs b/Tiny.RestClient/Request/Request.cs index cecab22..8629c52 100644 --- a/Tiny.RestClient/Request/Request.cs +++ b/Tiny.RestClient/Request/Request.cs @@ -72,7 +72,14 @@ public IParameterRequest AddStreamContent(Stream stream, string contentType) _content = new StreamContent(stream, contentType); return this; } - #if !FILEINFO_NOT_SUPPORTED + + public IParameterRequest AddStringContent(string content, string contentType) + { + _content = new StringContent(content, contentType); + return this; + } + +#if !FILEINFO_NOT_SUPPORTED public IParameterRequest AddFileContent(FileInfo content, string contentType) { if (content == null) @@ -88,9 +95,9 @@ public IParameterRequest AddFileContent(FileInfo content, string contentType) _content = new FileContent(content, contentType); return this; } - #endif +#endif -#endregion + #endregion #region Forms Parameters From e30acb535284aad8001deb2fdfcdfa436ca18684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Fri, 26 Oct 2018 21:47:15 +0200 Subject: [PATCH 25/37] Add support of string content for multipart. --- Tests/Tiny.RestClient.Tests/MultiPartTests.cs | 9 +++++---- Tiny.RestClient/Request/IRequest.cs | 2 +- .../IMultipartFromDataRequest.cs | 11 +++++++++++ .../MultipartFormContent/StringMultipartData.cs | 13 +++++++++++++ Tiny.RestClient/Request/Request.cs | 13 +++++++++++++ Tiny.RestClient/TinyRestClient.cs | 6 ++++++ 6 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 Tiny.RestClient/Request/MultipartFormContent/StringMultipartData.cs diff --git a/Tests/Tiny.RestClient.Tests/MultiPartTests.cs b/Tests/Tiny.RestClient.Tests/MultiPartTests.cs index 63f3dbb..cd6faa8 100644 --- a/Tests/Tiny.RestClient.Tests/MultiPartTests.cs +++ b/Tests/Tiny.RestClient.Tests/MultiPartTests.cs @@ -22,12 +22,13 @@ public async Task SendMultipleData() var data = await client. PostRequest("MultiPart/Test"). AsMultiPartFromDataRequest(). + AddContent(postRequest, "request", "request.json"). AddByteArray(new byte[42], "bytesArray", "bytesArray.bin"). AddStream(new MemoryStream(new byte[42]), "stream", "stream.bin"). - AddContent(postRequest, "request", "request.json"). + AddString("string", "string", "string.txt"). ExecuteAsync(); - Assert.AreEqual(data, "bytesArray-bytesArray.bin;stream-stream.bin;request-request.json;"); + Assert.AreEqual("request-request.json;bytesArray-bytesArray.bin;stream-stream.bin;string-string.txt;", data); } [ExpectedException(typeof(ArgumentNullException))] @@ -44,7 +45,7 @@ public async Task MultiPartAddStreamNull() var data = await client. PostRequest("MultiPart/Test"). AsMultiPartFromDataRequest(). - AddByteArray(null, "bytesArray", "bytesArray.bin"). + AddStream(null). ExecuteAsync(); } @@ -62,7 +63,7 @@ public async Task MultiPartAddByteArrayNull() var data = await client. PostRequest("MultiPart/Test"). AsMultiPartFromDataRequest(). - AddStream(null). + AddByteArray(null, "bytesArray", "bytesArray.bin"). ExecuteAsync(); } diff --git a/Tiny.RestClient/Request/IRequest.cs b/Tiny.RestClient/Request/IRequest.cs index 1d5b9d6..112c324 100644 --- a/Tiny.RestClient/Request/IRequest.cs +++ b/Tiny.RestClient/Request/IRequest.cs @@ -66,7 +66,7 @@ public interface IRequest : IExecutableRequest, IFormRequest IParameterRequest AddStreamContent(Stream stream, string contentType = "application/octet-stream"); /// - /// Adds string as content + /// Adds string as content (without apply any serialization) /// /// The content. /// The content type diff --git a/Tiny.RestClient/Request/MultipartFormContent/IMultipartFromDataRequest.cs b/Tiny.RestClient/Request/MultipartFormContent/IMultipartFromDataRequest.cs index d15f874..0b6c153 100644 --- a/Tiny.RestClient/Request/MultipartFormContent/IMultipartFromDataRequest.cs +++ b/Tiny.RestClient/Request/MultipartFormContent/IMultipartFromDataRequest.cs @@ -18,6 +18,17 @@ public interface IMultipartFromDataRequest /// thrown when data is null IMultiPartFromDataExecutableRequest AddByteArray(byte[] data, string name = null, string fileName = null, string contentType = "application/octet-stream"); + /// + /// Adds the content. + /// + /// The content. + /// The name of the item + /// The name of the file + /// The content type of the file + /// The current request + /// thrown when data is null + IMultiPartFromDataExecutableRequest AddString(string data, string name = null, string fileName = null, string contentType = "text/plain"); + /// /// Adds the content. /// diff --git a/Tiny.RestClient/Request/MultipartFormContent/StringMultipartData.cs b/Tiny.RestClient/Request/MultipartFormContent/StringMultipartData.cs new file mode 100644 index 0000000..7ddb2d2 --- /dev/null +++ b/Tiny.RestClient/Request/MultipartFormContent/StringMultipartData.cs @@ -0,0 +1,13 @@ +namespace Tiny.RestClient +{ + internal class StringMultipartData : MultipartData, IContent + { + public StringMultipartData(string data, string name, string fileName, string contentType) + : base(name, fileName, contentType) + { + Data = data; + } + + public string Data { get; } + } +} \ No newline at end of file diff --git a/Tiny.RestClient/Request/Request.cs b/Tiny.RestClient/Request/Request.cs index 8629c52..831c9ee 100644 --- a/Tiny.RestClient/Request/Request.cs +++ b/Tiny.RestClient/Request/Request.cs @@ -433,6 +433,19 @@ IMultiPartFromDataExecutableRequest IMultipartFromDataRequest.AddByteArray(byte[ return this; } + /// + IMultiPartFromDataExecutableRequest IMultipartFromDataRequest.AddString(string data, string name, string fileName, string contentType) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + _multiPartFormData.Add(new StringMultipartData(data, name, fileName, contentType)); + + return this; + } + /// IMultiPartFromDataExecutableRequest IMultipartFromDataRequest.AddStream(Stream data, string name, string fileName, string contentType) { diff --git a/Tiny.RestClient/TinyRestClient.cs b/Tiny.RestClient/TinyRestClient.cs index 8af1285..8753c3e 100644 --- a/Tiny.RestClient/TinyRestClient.cs +++ b/Tiny.RestClient/TinyRestClient.cs @@ -447,6 +447,12 @@ private async Task CreateContentAsync(IContent content, Cancellatio SetContentType(currentStreamPart.ContentType, streamContent); AddMultiPartContent(currentPart, streamContent, multiPartContent); } + else if (currentPart is StringMultipartData currentStringPart) + { + var stringMultiContent = new HttpStringContent(currentStringPart.Data); + SetContentType(currentStringPart.ContentType, stringMultiContent); + AddMultiPartContent(currentPart, stringMultiContent, multiPartContent); + } else if (currentPart is IToSerializeContent toSerializeMultiContent) { var serializedContent = await GetSerializedContentAsync(toSerializeMultiContent, cancellationToken).ConfigureAwait(false); From c319bcdba2b71a0490c1ea5dbf7d4a2a2bec309d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Fri, 26 Oct 2018 22:19:54 +0200 Subject: [PATCH 26/37] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e51c16..6ed695d 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,7 @@ client.Settings.ETagContainer = new ETagFileContainer(@"C:\ETagFolder"); ``` ### Define an ETagContainer for one request -You can also define the ETagContainer only on specifice request. +You can also define the ETagContainer only on specific request. ```cs request.WithETagContainer(eTagContainer); ``` From a6ce43eaa84473f775e3c459be23181020a2aefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Fri, 26 Oct 2018 22:21:06 +0200 Subject: [PATCH 27/37] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 03facb2..f63dd6f 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,7 +1,21 @@ # Release notes ## 1.6.0 -* Add support of ETag +* Add support of Entity tag (ETag) + +ETag is not enabled by default to enable it : +```cs +client.Settings.ETagContainer = new ETagFileContainer(@"C:\ETagFolder"); +``` +You can also enable on only on specific request like below : +```cs +request.WithETagContainer(eTagContainer); +``` + * Now the assembly is strong named +* Add support of string content (for mono part and multipart requests) +```cs +request.AddStringContent("myContent").ExecuteAsycnc(); +``` ## 1.5.5 * Fix a bug on cURL listener (when body was null) the cURL request wasn't displayed From 2112c847302f9faa9dc32781df7c7930f04167fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Fri, 26 Oct 2018 22:25:47 +0200 Subject: [PATCH 28/37] Update README.md --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e51c16..99094fc 100644 --- a/README.md +++ b/README.md @@ -262,10 +262,23 @@ await client.PostRequest("MultiPart/Test"). ``` -## Streams and bytes array -You can use as content : streams or byte arrays. +## String, Streams and bytes array +You can use as content : strings, streams or byte arrays. If you use these methods no serializer will be used. +### String +```cs +// Read string response + Stream stream = await client. + GetRequest("text"). + ExecuteAsStringAsync(); + +// Post String as content +await client.PostRequest("poetry/text"). + AddStringContent(stream). + ExecuteAsync(); +``` + ### Streams ```cs // Read stream response From 0290d9784ac5b23e218f7d8eb2462d50b3db49bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Fri, 26 Oct 2018 22:29:34 +0200 Subject: [PATCH 29/37] Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 99094fc..b30fdac 100644 --- a/README.md +++ b/README.md @@ -251,13 +251,23 @@ var response = await client. AddFileContent(fileInfo1, "text/plain"). AddFileContent(fileInfo2, "text/plain"). ExecuteAsync(); + + +// With 2 strings content +var response = await client. + PostRequest("City/Image/Text"). + AsMultiPartFromDataRequest(). + AddString("string1", "text/plain"). + AddString("string2", "text/plain"). + ExecuteAsync(); // With mixed content -await client.PostRequest("MultiPart/Test"). +await client.PostRequest("Files/Add"). AsMultiPartFromDataRequest(). AddContent(city1, "city1", "city1.json"). AddByteArray(byteArray1, "request", "request2.bin"). AddStream(stream2, "request", "request2.bin") + AddString("string1", "text", "request.txt") ExecuteAsync(); ``` From 3e0be302da39fc2184d5570aaa84e3d26ff61f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sat, 27 Oct 2018 11:26:07 +0200 Subject: [PATCH 30/37] Add configure await --- Tiny.RestClient/Compression/DeflateCompression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tiny.RestClient/Compression/DeflateCompression.cs b/Tiny.RestClient/Compression/DeflateCompression.cs index e646975..d42872f 100644 --- a/Tiny.RestClient/Compression/DeflateCompression.cs +++ b/Tiny.RestClient/Compression/DeflateCompression.cs @@ -47,7 +47,7 @@ public async Task DecompressAsync(Stream stream, int bufferSize, Cancell using (var decompressionStream = new DeflateStream(stream, CompressionMode.Decompress)) { await decompressionStream.CopyToAsync(decompressedStream, bufferSize, cancellationToken).ConfigureAwait(false); - await decompressionStream.FlushAsync(); + await decompressionStream.FlushAsync().ConfigureAwait(false); } return decompressedStream; From a2ca0c3cf6139cfc055783d45741c02409226f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sat, 27 Oct 2018 11:39:04 +0200 Subject: [PATCH 31/37] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d022ce7..524a971 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,10 @@ It hides all the complexity of communication, deserialisation ... ## Platform Support -The lib support **.NET Standard 1.1 to 2.0**, and **.NET Framework 4.5 to 4.7.** The support of **.NET Standard 1.1 to 2.0** allow you to use it in : -- .Net Framework 4.6+ -- Xamarin iOS et Android +- .Net Framework 4.5+ +- Xamarin iOS, Xamarin Android - .Net Core - UWP - Windows Phone 8.1 From d8fd239588afdc128008791502ae3cbe3371b0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sat, 27 Oct 2018 11:39:49 +0200 Subject: [PATCH 32/37] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index f63dd6f..466d9e6 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -10,12 +10,11 @@ You can also enable on only on specific request like below : ```cs request.WithETagContainer(eTagContainer); ``` - -* Now the assembly is strong named * Add support of string content (for mono part and multipart requests) ```cs request.AddStringContent("myContent").ExecuteAsycnc(); ``` +* Now the assembly is strong named ## 1.5.5 * Fix a bug on cURL listener (when body was null) the cURL request wasn't displayed From d9763f6ca705b1bfa71e0f77fab71092039e14af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sat, 27 Oct 2018 11:41:11 +0200 Subject: [PATCH 33/37] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 466d9e6..556df90 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,6 +1,6 @@ # Release notes ## 1.6.0 -* Add support of Entity tag (ETag) +* Add support of Entity Tag (ETag) ETag is not enabled by default to enable it : ```cs From b700f3de75890eef2e78914299933e8ec3769a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sat, 27 Oct 2018 13:16:55 +0200 Subject: [PATCH 34/37] Update README.md --- README.md | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 524a971..bdd8844 100644 --- a/README.md +++ b/README.md @@ -21,33 +21,28 @@ The support of **.NET Standard 1.1 to 2.0** allow you to use it in : ## Features * Modern async http client for REST API. -* Support of verbs : GET, POST , PUT, DELETE, PATCH -* Support of custom http verbs -* Support of cancellation token on each requests +* Support of verbs : GET, POST , PUT, DELETE, PATCH and custom http verbs * Support of ETag +* Support of multi-part form data +* Support of cancellation token on each requests +* Support of : download file and Upload file * Automatic XML and JSON serialization / deserialization * Support of custom serialisation / deserialisation * Support of camelCase, snakeCase kebabCase for json serialization -* Support of multi-part form data -* Download file -* Upload file -* Support of gzip/deflate (compression and decompression) -* Optimized http calls +* Support of compression and decompression (gzip and deflate) * Typed exceptions which are easier to interpret * Define timeout globally or per request * Timeout exception thrown if the request is in timeout (by default HttpClient sends OperationCancelledException, so we can't distinguish between user cancellation and timeout) * Provide an easy way to log : all sending of request, failed to get response, and the time get response. * Support of export requests to postman collection -* Support of display cURL request in debug output +* Support of display cURL requests in debug output * Support of Basic Authentification * Support of OAuth2 Authentification - ## Basic usage ### Create the client -Define a global timeout for all client. (By default it's setted to 100 secondes) ```cs using Tiny.RestClient; @@ -164,8 +159,8 @@ var response = await client. ``` ### Define timeout +Define a global timeout for all client. (By default it's setted to 100 secondes) -Define global timeout ```cs client.Settings.DefaultTimeout = TimeSpan.FromSeconds(100); ``` From 9cf6e72bbe55f8505743336c466e394228897014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sat, 27 Oct 2018 13:24:03 +0200 Subject: [PATCH 35/37] Change package description --- Tiny.RestClient/Tiny.RestClient.csproj | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Tiny.RestClient/Tiny.RestClient.csproj b/Tiny.RestClient/Tiny.RestClient.csproj index 783b0a9..a154f49 100644 --- a/Tiny.RestClient/Tiny.RestClient.csproj +++ b/Tiny.RestClient/Tiny.RestClient.csproj @@ -11,26 +11,24 @@ Features : * Modern async http client for REST API. - * Support of verbs : GET, POST , PUT, DELETE, PATCH - * Support of custom http verbs - * Support of cancellation token on each requests + * Support of verbs : GET, POST , PUT, DELETE, PATCH and custom http verbs * Support of ETag + * Support of multi-part form data + * Support of cancellation token on each requests + * Support of : download file and Upload file * Automatic XML and JSON serialization / deserialization * Support of custom serialisation / deserialisation * Support of camelCase, snakeCase kebabCase for json serialization - * Support of multi-part form data - * Download file - * Upload file - * Support of gzip and deflate (compression and decompression) - * Optimized http calls + * Support of compression and decompression (gzip and deflate) * Typed exceptions which are easier to interpret * Define timeout globally or per request * Timeout exception thrown if the request is in timeout (by default HttpClient sends OperationCancelledException, so we can't distinguish between user cancellation and timeout) - * Provide an easy way to log : all sending of request, failed to get response, and the time get response. + * Provide an easy way to log : all sending of request, failed to get response, and the time get response. * Support of export requests to postman collection - * Support of display cURL request in debug output + * Support of display cURL requests in debug output * Support of Basic Authentification - * Support of OAuth2 Authentification + * Support of OAuth2 Authentification + https://github.com/jgiacomini/Tiny.RestClient/blob/master/LICENSE https://github.com/jgiacomini/Tiny.RestClient https://raw.githubusercontent.com/jgiacomini/Tiny.RestClient/master/icon.png From 9abec57e7327df3e0afde06d28b2876928d2a45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sat, 27 Oct 2018 14:11:32 +0200 Subject: [PATCH 36/37] bump 1.6.0 --- Tiny.RestClient/Tiny.RestClient.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tiny.RestClient/Tiny.RestClient.csproj b/Tiny.RestClient/Tiny.RestClient.csproj index a154f49..20eb306 100644 --- a/Tiny.RestClient/Tiny.RestClient.csproj +++ b/Tiny.RestClient/Tiny.RestClient.csproj @@ -1,7 +1,7 @@  netstandard1.1;netstandard1.2;netstandard1.3;netstandard2.0;net45;net46;net47; - 1.6.0-alpha001 + 1.6.0 Copyright © Jérôme Giacomini 2018 en Tiny.RestClient From 342c6795d8888e0fc2f474bfb3291dce32da64aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20GIACOMINI?= Date: Sat, 27 Oct 2018 14:14:28 +0200 Subject: [PATCH 37/37] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bdd8844..71d0555 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/Tiny-RestClient/Lobby) [![StackOverflow](https://img.shields.io/badge/questions-on%20StackOverflow-orange.svg?style=flat)](http://stackoverflow.com/questions/tagged/tiny.restclient) +[Please visit the main site.](https://jgiacomini.github.io/Tiny.RestClient/) + Tiny.RestClient facilitates the dialog between your API and your application. It hides all the complexity of communication, deserialisation ...