Skip to content

Commit

Permalink
WIP: Start porting diagnostics and formatter support from HTTPlease
Browse files Browse the repository at this point in the history
  • Loading branch information
tintoy committed Dec 13, 2024
1 parent efb9933 commit 7f6a846
Show file tree
Hide file tree
Showing 76 changed files with 5,907 additions and 221 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="HTTPlease.Core" Version="1.9.2" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Reactive" Version="6.0.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="HTTPlease.Core" Version="1.9.2" />
<PackageReference Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
</ItemGroup>

Expand Down
41 changes: 41 additions & 0 deletions src/KubeClient/Http/ClientExtensions.Streamed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace KubeClient.Http
{
/// <summary>
/// Invocation-related extension methods for <see cref="HttpClient"/>s that use an <see cref="HttpRequest"/>.
/// </summary>
public static partial class ClientExtensions
{
#region Invoke (streamed)

/// <summary>
/// Asynchronously execute a request as a streamed HTTP GET.
/// </summary>
/// <param name="httpClient">
/// The HTTP client.
/// </param>
/// <param name="request">
/// The <see cref="HttpRequest"/> to execute.
/// </param>
/// <param name="cancellationToken">
/// An optional <see cref="CancellationToken"/> that can be used to cancel the request.
/// </param>
/// <returns>
/// An <see cref="HttpResponseMessage"/> representing the response.
/// </returns>
public static async Task<HttpResponseMessage> GetStreamedAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
{
using (HttpRequestMessage requestMessage = request.BuildRequestMessage(HttpMethod.Get, baseUri: httpClient.BaseAddress))
{
requestMessage.MarkAsStreamed();

return await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead);
}
}

#endregion // Invoke (streamed)
}
}
266 changes: 266 additions & 0 deletions src/KubeClient/Http/ClientExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace KubeClient.Http
{
/// <summary>
/// Invocation-related extension methods for <see cref="HttpClient"/>s that use an <see cref="HttpRequest"/>.
/// </summary>
public static partial class ClientExtensions
{
#region Invoke

/// <summary>
/// Asynchronously execute a request as an HTTP HEAD.
/// </summary>
/// <param name="httpClient">
/// The <see cref="HttpClient"/> used to execute the request.
/// </param>
/// <param name="request">
/// The HTTP request.
/// </param>
/// <param name="cancellationToken">
/// An optional cancellation token that can be used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// An <see cref="HttpResponseMessage"/> representing the response.
/// </returns>
public static async Task<HttpResponseMessage> HeadAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
{
if (httpClient == null)
throw new ArgumentNullException(nameof(httpClient));

if (request == null)
throw new ArgumentNullException(nameof(request));

return await httpClient.SendAsync(request, HttpMethod.Head, cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Asynchronously execute a request as an HTTP GET.
/// </summary>
/// <param name="httpClient">
/// The <see cref="HttpClient"/> used to execute the request.
/// </param>
/// <param name="request">
/// The HTTP request.
/// </param>
/// <param name="cancellationToken">
/// An optional cancellation token that can be used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// An <see cref="HttpResponseMessage"/> representing the response.
/// </returns>
public static async Task<HttpResponseMessage> GetAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
{
if (httpClient == null)
throw new ArgumentNullException(nameof(httpClient));

if (request == null)
throw new ArgumentNullException(nameof(request));

return await httpClient.SendAsync(request, HttpMethod.Get, cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Asynchronously execute a request as an HTTP POST.
/// </summary>
/// <param name="httpClient">
/// The <see cref="HttpClient"/> used to execute the request.
/// </param>
/// <param name="request">
/// The HTTP request.
/// </param>
/// <param name="postBody">
/// Optional <see cref="HttpContent"/> representing the request body.
/// </param>
/// <param name="cancellationToken">
/// An optional cancellation token that can be used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// An <see cref="HttpResponseMessage"/> representing the response.
/// </returns>
public static async Task<HttpResponseMessage> PostAsync(this HttpClient httpClient, HttpRequest request, HttpContent postBody = null, CancellationToken cancellationToken = default)
{
if (httpClient == null)
throw new ArgumentNullException(nameof(httpClient));

if (request == null)
throw new ArgumentNullException(nameof(request));

return await httpClient.SendAsync(request, HttpMethod.Post, postBody, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Asynchronously execute a request as an HTTP PUT.
/// </summary>
/// <param name="httpClient">
/// The <see cref="HttpClient"/> used to execute the request.
/// </param>
/// <param name="request">
/// The HTTP request.
/// </param>
/// <param name="putBody">
/// <see cref="HttpContent"/> representing the request body.
/// </param>
/// <param name="cancellationToken">
/// An optional cancellation token that can be used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// An <see cref="HttpResponseMessage"/> representing the response.
/// </returns>
public static async Task<HttpResponseMessage> PutAsync(this HttpClient httpClient, HttpRequest request, HttpContent putBody, CancellationToken cancellationToken = default)
{
if (httpClient == null)
throw new ArgumentNullException(nameof(httpClient));

if (request == null)
throw new ArgumentNullException(nameof(request));

if (putBody == null)
throw new ArgumentNullException(nameof(putBody));

return await httpClient.SendAsync(request, HttpMethod.Put, putBody, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Asynchronously execute a request as an HTTP PATCH.
/// </summary>
/// <param name="httpClient">
/// The <see cref="HttpClient"/> used to execute the request.
/// </param>
/// <param name="request">
/// The HTTP request.
/// </param>
/// <param name="patchBody">
/// <see cref="HttpContent"/> representing the request body.
/// </param>
/// <param name="cancellationToken">
/// An optional cancellation token that can be used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// An <see cref="HttpResponseMessage"/> representing the response.
/// </returns>
public static async Task<HttpResponseMessage> PatchAsync(this HttpClient httpClient, HttpRequest request, HttpContent patchBody, CancellationToken cancellationToken = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));

if (patchBody == null)
throw new ArgumentNullException(nameof(patchBody));

return await httpClient.SendAsync(request, OtherHttpMethods.Patch, patchBody, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Asynchronously execute a request as an HTTP DELETE.
/// </summary>
/// <param name="httpClient">
/// The <see cref="HttpClient"/> used to execute the request.
/// </param>
/// <param name="request">
/// The HTTP request.
/// </param>
/// <param name="cancellationToken">
/// An optional cancellation token that can be used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// An <see cref="HttpResponseMessage"/> representing the response.
/// </returns>
public static async Task<HttpResponseMessage> DeleteAsync(this HttpClient httpClient, HttpRequest request, CancellationToken cancellationToken = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));

return await httpClient.SendAsync(request, HttpMethod.Delete, cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Asynchronously execute the request using the specified HTTP method.
/// </summary>
/// <param name="httpClient">
/// The <see cref="HttpClient"/> used to execute the request.
/// </param>
/// <param name="request">
/// The HTTP request.
/// </param>
/// <param name="method">
/// An <see cref="HttpMethod"/> representing the method to use.
/// </param>
/// <param name="body">
/// Optional <see cref="HttpContent"/> representing the request body (if any).
/// </param>
/// <param name="cancellationToken">
/// An optional cancellation token that can be used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// An <see cref="HttpResponseMessage"/> representing the response.
/// </returns>
public static async Task<HttpResponseMessage> SendAsync(this HttpClient httpClient, HttpRequest request, HttpMethod method, HttpContent body = null, CancellationToken cancellationToken = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));

using (HttpRequestMessage requestMessage = request.BuildRequestMessage(method, body, httpClient.BaseAddress))
{
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
try
{
request.ExecuteResponseActions(responseMessage);
}
catch
{
using (responseMessage)
{
throw;
}
}

return responseMessage;
}
}

#endregion // Invoke

#region Helpers

/// <summary>
/// Execute the request's configured response actions (if any) against the specified response message.
/// </summary>
/// <param name="request">
/// The <see cref="HttpRequest"/>.
/// </param>
/// <param name="responseMessage">
/// The HTTP response message.
/// </param>
static void ExecuteResponseActions(this HttpRequest request, HttpResponseMessage responseMessage)
{
if (request == null)
throw new ArgumentNullException(nameof(request));

if (responseMessage == null)
throw new ArgumentNullException(nameof(responseMessage));

List<Exception> responseActionExceptions = new List<Exception>();
foreach (ResponseAction<object> responseAction in request.ResponseActions)
{
try
{
responseAction(responseMessage, HttpRequest.DefaultContext);
}
catch (Exception eResponseAction)
{
responseActionExceptions.Add(eResponseAction);
}
}

if (responseActionExceptions.Count > 0)
throw new AggregateException("One or more errors occurred while processing the response message.", responseActionExceptions);
}

#endregion // Helpers
}
}
Loading

0 comments on commit 7f6a846

Please sign in to comment.