namespace Microsoft.Rest.ClientRuntime { using System.Net.Http; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Collections; using System.Linq; /// /// The interface for sending an HTTP request across the wire. /// public interface ISendAsync { Task SendAsync(HttpRequestMessage request, IEventListener callback); } public class SendAsyncTerminalFactory : ISendAsyncTerminalFactory, ISendAsync { SendAsync implementation; public SendAsyncTerminalFactory(SendAsync implementation) => this.implementation = implementation; public SendAsyncTerminalFactory(ISendAsync implementation) => this.implementation = implementation.SendAsync; public ISendAsync Create() => this; public Task SendAsync(HttpRequestMessage request, IEventListener callback) => implementation(request, callback); } public partial class SendAsyncFactory : ISendAsyncFactory { public class Sender : ISendAsync { internal ISendAsync next; internal SendAsyncStep implementation; public Task SendAsync(HttpRequestMessage request, IEventListener callback) => implementation(request, callback, next); } SendAsyncStep implementation; public SendAsyncFactory(SendAsyncStep implementation) => this.implementation = implementation; public ISendAsync Create(ISendAsync next) => new Sender { next = next, implementation = implementation }; } public class HttpClientFactory : ISendAsyncTerminalFactory, ISendAsync { HttpClient client; public HttpClientFactory() : this(new HttpClient()) { } public HttpClientFactory(HttpClient client) => this.client = client; public ISendAsync Create() => this; public Task SendAsync(HttpRequestMessage request, IEventListener callback) => client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, callback.Token); } public interface ISendAsyncFactory { ISendAsync Create(ISendAsync next); } public interface ISendAsyncTerminalFactory { ISendAsync Create(); } public partial class HttpPipeline : ISendAsync { private ISendAsync pipeline; private ISendAsyncTerminalFactory terminal; private List steps = new List(); public HttpPipeline() : this(new HttpClientFactory()) { } public HttpPipeline(ISendAsyncTerminalFactory terminalStep) { if (terminalStep == null) { throw new System.ArgumentNullException(nameof(terminalStep), "Terminal Step Factory in HttpPipeline may not be null"); } TerminalFactory = terminalStep; } /// /// Returns an HttpPipeline with the current state of this pipeline. /// public HttpPipeline Clone() => new HttpPipeline(terminal) { steps = this.steps.ToList(), pipeline = this.pipeline }; public ISendAsyncTerminalFactory TerminalFactory { get => terminal; set { if (value == null) { throw new System.ArgumentNullException("TerminalFactory in HttpPipeline may not be null"); } terminal = value; } } public ISendAsync Pipeline { get { // if the pipeline has been created and not invalidated, return it. if (this.pipeline != null) { return this.pipeline; } // create the pipeline from scratch. var next = terminal.Create(); foreach (var factory in steps) { // skip factories that return null. next = factory.Create(next) ?? next; } return this.pipeline = next; } } public int Count => steps.Count; public HttpPipeline Prepend(ISendAsyncFactory item) { if (item != null) { steps.Add(item); pipeline = null; } return this; } public HttpPipeline Append(SendAsyncStep item) { if (item != null) { Append(new SendAsyncFactory(item)); } return this; } public HttpPipeline Prepend(SendAsyncStep item) { if (item != null) { Prepend(new SendAsyncFactory(item)); } return this; } public HttpPipeline Append(IEnumerable items) { if (items != null) { foreach (var item in items) { Append(new SendAsyncFactory(item)); } } return this; } public HttpPipeline Prepend(IEnumerable items) { if (items != null) { foreach (var item in items) { Prepend(new SendAsyncFactory(item)); } } return this; } public HttpPipeline Append(ISendAsyncFactory item) { if (item != null) { steps.Insert(0, item); pipeline = null; } return this; } public HttpPipeline Prepend(IEnumerable items) { if (items != null) { foreach (var item in items) { Prepend(item); } } return this; } public HttpPipeline Append(IEnumerable items) { if (items != null) { foreach (var item in items) { Append(item); } } return this; } // you can use this as the ISendAsync Implementation public Task SendAsync(HttpRequestMessage request, IEventListener callback) => Pipeline.SendAsync(request, callback); } public static class HttpRequestMessageExtensions { public static HttpRequestMessage CloneAndDispose(this HttpRequestMessage original,System.Uri requestUri = null, System.Net.Http.HttpMethod method = null ) { using( original ) { return original.Clone(requestUri,method); } } public static Task CloneWithContentAndDispose(this HttpRequestMessage original,System.Uri requestUri = null, System.Net.Http.HttpMethod method = null ) { using( original ) { return original.CloneWithContent(requestUri,method); } } /// /// Clones an HttpRequestMessage (without the content) /// /// Original HttpRequestMessage (Will be diposed before returning) /// A clone of the HttpRequestMessage public static HttpRequestMessage Clone(this HttpRequestMessage original, System.Uri requestUri = null, System.Net.Http.HttpMethod method = null ) { var clone = new HttpRequestMessage { Method = method ?? original.Method, RequestUri = requestUri ?? original.RequestUri, Version = original.Version, }; foreach (KeyValuePair prop in original.Properties) { clone.Properties.Add(prop); } foreach (KeyValuePair> header in original.Headers) { clone.Headers.TryAddWithoutValidation(header.Key, header.Value); } return clone; } /// /// Clones an HttpRequestMessage (including the content stream and content headers) /// /// Original HttpRequestMessage (Will be diposed before returning) /// A clone of the HttpRequestMessage public static async Task CloneWithContent(this HttpRequestMessage original, System.Uri requestUri = null, System.Net.Http.HttpMethod method = null) { var clone = original.Clone(requestUri, method); var stream = new System.IO.MemoryStream(); if (original.Content != null) { await original.Content.CopyToAsync(stream).ConfigureAwait(false); stream.Position = 0; clone.Content = new StreamContent(stream); if (original.Content.Headers != null) { foreach (var h in original.Content.Headers) { clone.Content.Headers.Add(h.Key, h.Value); } } } return clone; } } }