using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using ExchangeSharp; namespace BTCPayServer.Services.Rates { internal class HttpClientRequestMaker : IAPIRequestMaker { class InternalHttpWebRequest : IHttpWebRequest { internal readonly HttpWebRequest Request; public Uri RequestUri => Request.RequestUri; public string Method { get { return Request.Method; } set { Request.Method = value; } } public int Timeout { get { return Request.Timeout; } set { Request.Timeout = value; } } public int ReadWriteTimeout { get { return Request.ReadWriteTimeout; } set { Request.ReadWriteTimeout = value; } } public InternalHttpWebRequest(Uri fullUri) { Request = ((WebRequest.Create(fullUri) as HttpWebRequest) ?? throw new NullReferenceException("Failed to create HttpWebRequest")); Request.KeepAlive = false; } public void AddHeader(string header, string value) { switch (header.ToStringLowerInvariant()) { case "content-type": Request.ContentType = value; break; case "content-length": Request.ContentLength = value.ConvertInvariant(0L); break; case "user-agent": Request.UserAgent = value; break; case "accept": Request.Accept = value; break; case "connection": Request.Connection = value; break; default: Request.Headers[header] = value; break; } } public Task WriteAllAsync(byte[] data, int index, int length) { throw new NotImplementedException(); } public HttpRequestMessage ToHttpRequestMessage() { var httpRequest = new HttpRequestMessage(HttpMethod.Get, Request.RequestUri); CopyHeadersFrom(httpRequest, Request); return httpRequest; } internal void CopyHeadersFrom(HttpRequestMessage message, HttpWebRequest request) { foreach (string headerName in request.Headers) { string[] headerValues = request.Headers.GetValues(headerName); if (!message.Headers.TryAddWithoutValidation(headerName, headerValues)) { if (message.Content != null) message.Content.Headers.TryAddWithoutValidation(headerName, headerValues); } } } } class InternalHttpWebResponse : IHttpWebResponse { public InternalHttpWebResponse(HttpResponseMessage httpResponseMessage) { var headers = new Dictionary>(); foreach (var h in httpResponseMessage.Headers) { if (!headers.TryGetValue(h.Key, out var list)) { list = new List(); headers.Add(h.Key, list); } list.AddRange(h.Value); } Headers = new Dictionary>(headers.Count); foreach (var item in headers) { Headers.Add(item.Key, item.Value.AsReadOnly()); } } public Dictionary> Headers { get; } static readonly IReadOnlyList Empty = new List().AsReadOnly(); public IReadOnlyList GetHeader(string name) { Headers.TryGetValue(name, out var list); return list ?? Empty; } } private readonly IAPIRequestHandler api; private readonly HttpClient _httpClient; private readonly CancellationToken _cancellationToken; public HttpClientRequestMaker(IAPIRequestHandler api, HttpClient httpClient, CancellationToken cancellationToken) { if (api == null) throw new ArgumentNullException(nameof(api)); if (httpClient == null) throw new ArgumentNullException(nameof(httpClient)); this.api = api; _httpClient = httpClient; _cancellationToken = cancellationToken; } public Action RequestStateChanged { get; set; } public async Task MakeRequestAsync(string url, string baseUrl = null, Dictionary payload = null, string method = null) { await default(SynchronizationContextRemover); await api.RateLimit.WaitToProceedAsync(); if (url[0] != '/') { url = "/" + url; } string uri2 = (baseUrl ?? api.BaseUrl) + url; if (method == null) { method = api.RequestMethod; } Uri uri = api.ProcessRequestUrl(new UriBuilder(uri2), payload, method); InternalHttpWebRequest request = new InternalHttpWebRequest(uri) { Method = method }; request.AddHeader("content-type", api.RequestContentType); request.AddHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36"); int num3 = request.Timeout = (request.ReadWriteTimeout = (int)api.RequestTimeout.TotalMilliseconds); await api.ProcessRequestAsync(request, payload); try { RequestStateChanged?.Invoke(this, RequestMakerState.Begin, uri.AbsoluteUri); using var webHttpRequest = request.ToHttpRequestMessage(); using var webHttpResponse = await _httpClient.SendAsync(webHttpRequest, _cancellationToken); string text = await webHttpResponse.Content.ReadAsStringAsync(); if (!webHttpResponse.IsSuccessStatusCode) { if (string.IsNullOrWhiteSpace(text)) { throw new APIException($"{webHttpResponse.StatusCode.ConvertInvariant(0)} - {webHttpResponse.StatusCode}"); } throw new APIException(text); } api.ProcessResponse(new InternalHttpWebResponse(webHttpResponse)); RequestStateChanged?.Invoke(this, RequestMakerState.Finished, text); return text; } catch (Exception arg) { RequestStateChanged?.Invoke(this, RequestMakerState.Error, arg); throw; } } } }