2020-02-14 22:23:27 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2020-02-26 04:00:28 +00:00
|
|
|
|
using System.IO;
|
2020-02-14 22:23:27 +00:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Http;
|
2020-12-31 06:44:42 +00:00
|
|
|
|
using System.Threading;
|
2020-02-14 22:23:27 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2020-04-08 12:43:29 +00:00
|
|
|
|
using HtmlAgilityPack;
|
2020-06-26 17:08:30 +00:00
|
|
|
|
using Wabbajack.Common;
|
2020-05-21 22:12:51 +00:00
|
|
|
|
using Wabbajack.Common.Exceptions;
|
2020-07-25 18:09:02 +00:00
|
|
|
|
using Wabbajack.Lib.LibCefHelpers;
|
2020-02-14 22:23:27 +00:00
|
|
|
|
|
2020-06-26 17:08:30 +00:00
|
|
|
|
namespace Wabbajack.Lib.Http
|
2020-02-14 22:23:27 +00:00
|
|
|
|
{
|
|
|
|
|
public class Client
|
|
|
|
|
{
|
2020-04-10 01:29:53 +00:00
|
|
|
|
public List<(string, string?)> Headers = new List<(string, string?)>();
|
2020-02-14 22:23:27 +00:00
|
|
|
|
public List<Cookie> Cookies = new List<Cookie>();
|
2020-12-31 06:44:42 +00:00
|
|
|
|
public async Task<HttpResponseMessage> GetAsync(string url, HttpCompletionOption responseHeadersRead = HttpCompletionOption.ResponseHeadersRead, bool errorsAsExceptions = true, bool retry = true, CancellationToken? token = null)
|
2020-02-14 22:23:27 +00:00
|
|
|
|
{
|
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
2020-12-31 06:44:42 +00:00
|
|
|
|
return await SendAsync(request, responseHeadersRead, errorsAsExceptions: errorsAsExceptions, retry: retry, token: token);
|
2020-02-14 22:23:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-31 06:44:42 +00:00
|
|
|
|
public async Task<HttpResponseMessage> GetAsync(Uri url, HttpCompletionOption responseHeadersRead = HttpCompletionOption.ResponseHeadersRead, bool errorsAsExceptions = true, CancellationToken? token = null)
|
2020-05-20 03:25:41 +00:00
|
|
|
|
{
|
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
2020-12-31 06:44:42 +00:00
|
|
|
|
return await SendAsync(request, responseHeadersRead, errorsAsExceptions: errorsAsExceptions, token:token);
|
2020-05-20 03:25:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-26 04:00:28 +00:00
|
|
|
|
|
2020-05-20 03:25:41 +00:00
|
|
|
|
public async Task<HttpResponseMessage> PostAsync(string url, HttpContent content, HttpCompletionOption responseHeadersRead = HttpCompletionOption.ResponseHeadersRead, bool errorsAsExceptions = true)
|
2020-02-26 04:00:28 +00:00
|
|
|
|
{
|
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, url) {Content = content};
|
2020-05-20 03:25:41 +00:00
|
|
|
|
return await SendAsync(request, responseHeadersRead, errorsAsExceptions);
|
2020-02-26 04:00:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<HttpResponseMessage> PutAsync(string url, HttpContent content, HttpCompletionOption responseHeadersRead = HttpCompletionOption.ResponseHeadersRead)
|
|
|
|
|
{
|
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Put, url) {Content = content};
|
|
|
|
|
return await SendAsync(request, responseHeadersRead);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-31 06:44:42 +00:00
|
|
|
|
public async Task<string> GetStringAsync(string url, CancellationToken? token = null)
|
2020-02-14 22:23:27 +00:00
|
|
|
|
{
|
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
2020-12-31 06:44:42 +00:00
|
|
|
|
return await SendStringAsync(request, token: token);
|
2020-02-14 22:23:27 +00:00
|
|
|
|
}
|
2020-03-02 23:16:15 +00:00
|
|
|
|
|
2021-02-17 05:46:05 +00:00
|
|
|
|
public async Task<string> GetStringAsync(Uri url, CancellationToken? token = null)
|
2020-03-22 21:55:31 +00:00
|
|
|
|
{
|
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
2021-02-17 05:46:05 +00:00
|
|
|
|
return await SendStringAsync(request, token: token);
|
2020-03-22 21:55:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 23:16:15 +00:00
|
|
|
|
public async Task<string> DeleteStringAsync(string url)
|
|
|
|
|
{
|
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Delete, url);
|
|
|
|
|
return await SendStringAsync(request);
|
|
|
|
|
}
|
2020-02-14 22:23:27 +00:00
|
|
|
|
|
2020-12-31 06:44:42 +00:00
|
|
|
|
private async Task<string> SendStringAsync(HttpRequestMessage request, CancellationToken? token = null)
|
2020-02-14 22:23:27 +00:00
|
|
|
|
{
|
2020-12-31 06:44:42 +00:00
|
|
|
|
using var result = await SendAsync(request, token: token);
|
2020-03-30 03:47:35 +00:00
|
|
|
|
if (!result.IsSuccessStatusCode)
|
|
|
|
|
{
|
|
|
|
|
Utils.Log("Internal Error");
|
|
|
|
|
Utils.Log(await result.Content.ReadAsStringAsync());
|
|
|
|
|
throw new Exception(
|
|
|
|
|
$"Bad HTTP request {result.StatusCode} {result.ReasonPhrase} - {request.RequestUri}");
|
|
|
|
|
}
|
2020-02-14 22:23:27 +00:00
|
|
|
|
return await result.Content.ReadAsStringAsync();
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-31 06:44:42 +00:00
|
|
|
|
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage msg, HttpCompletionOption responseHeadersRead = HttpCompletionOption.ResponseHeadersRead, bool errorsAsExceptions = true, bool retry = true, CancellationToken? token = null)
|
2020-02-14 22:23:27 +00:00
|
|
|
|
{
|
2020-02-28 02:26:58 +00:00
|
|
|
|
foreach (var (k, v) in Headers)
|
|
|
|
|
msg.Headers.Add(k, v);
|
|
|
|
|
if (Cookies.Count > 0)
|
|
|
|
|
Cookies.ForEach(c => ClientFactory.Cookies.Add(c));
|
2020-02-14 22:23:27 +00:00
|
|
|
|
int retries = 0;
|
2020-05-23 21:03:25 +00:00
|
|
|
|
HttpResponseMessage response;
|
2020-02-14 22:23:27 +00:00
|
|
|
|
TOP:
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-12-31 06:44:42 +00:00
|
|
|
|
response = await ClientFactory.Client.SendAsync(msg, responseHeadersRead, token ?? CancellationToken.None);
|
2020-05-09 22:16:16 +00:00
|
|
|
|
if (response.IsSuccessStatusCode) return response;
|
|
|
|
|
|
2020-05-15 03:52:23 +00:00
|
|
|
|
if (errorsAsExceptions)
|
2020-05-23 21:03:25 +00:00
|
|
|
|
{
|
|
|
|
|
response.Dispose();
|
2020-05-21 22:12:51 +00:00
|
|
|
|
throw new HttpException(response);
|
2020-05-23 21:03:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-10 12:37:55 +00:00
|
|
|
|
return response;
|
2020-02-14 22:23:27 +00:00
|
|
|
|
}
|
2020-05-15 03:52:23 +00:00
|
|
|
|
catch (Exception ex)
|
2020-02-14 22:23:27 +00:00
|
|
|
|
{
|
2020-07-20 03:45:55 +00:00
|
|
|
|
if (!retry) throw;
|
2020-05-21 22:12:51 +00:00
|
|
|
|
if (ex is HttpException http)
|
|
|
|
|
{
|
2020-06-21 22:03:54 +00:00
|
|
|
|
if (http.Code != 503 && http.Code != 521) throw;
|
2020-05-21 22:12:51 +00:00
|
|
|
|
|
2020-05-23 21:03:25 +00:00
|
|
|
|
retries++;
|
2020-05-21 22:12:51 +00:00
|
|
|
|
var ms = Utils.NextRandom(100, 1000);
|
2020-06-21 22:03:54 +00:00
|
|
|
|
Utils.Log($"Got a {http.Code} from {msg.RequestUri} retrying in {ms}ms");
|
2020-05-21 22:12:51 +00:00
|
|
|
|
|
2020-12-31 06:44:42 +00:00
|
|
|
|
await Task.Delay(ms, token ?? CancellationToken.None);
|
2020-05-23 21:03:25 +00:00
|
|
|
|
msg = CloneMessage(msg);
|
2020-05-21 22:12:51 +00:00
|
|
|
|
goto TOP;
|
|
|
|
|
}
|
2020-02-14 22:23:27 +00:00
|
|
|
|
if (retries > Consts.MaxHTTPRetries) throw;
|
|
|
|
|
|
|
|
|
|
retries++;
|
2020-07-16 21:17:37 +00:00
|
|
|
|
Utils.LogStraightToFile(ex.ToString());
|
2020-02-14 22:23:27 +00:00
|
|
|
|
Utils.Log($"Http Connect error to {msg.RequestUri} retry {retries}");
|
2020-12-31 06:44:42 +00:00
|
|
|
|
await Task.Delay(100 * retries, token ?? CancellationToken.None);
|
2020-02-15 23:28:20 +00:00
|
|
|
|
msg = CloneMessage(msg);
|
2020-02-14 22:23:27 +00:00
|
|
|
|
goto TOP;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2020-02-15 23:28:20 +00:00
|
|
|
|
|
|
|
|
|
private HttpRequestMessage CloneMessage(HttpRequestMessage msg)
|
|
|
|
|
{
|
|
|
|
|
var new_message = new HttpRequestMessage(msg.Method, msg.RequestUri);
|
|
|
|
|
foreach (var header in msg.Headers)
|
|
|
|
|
new_message.Headers.Add(header.Key, header.Value);
|
|
|
|
|
new_message.Content = msg.Content;
|
|
|
|
|
return new_message;
|
|
|
|
|
|
|
|
|
|
}
|
2020-04-02 21:16:46 +00:00
|
|
|
|
|
|
|
|
|
public async Task<T> GetJsonAsync<T>(string s)
|
|
|
|
|
{
|
|
|
|
|
var result = await GetStringAsync(s);
|
2020-04-06 20:48:54 +00:00
|
|
|
|
return result.FromJsonString<T>();
|
2020-04-02 21:16:46 +00:00
|
|
|
|
}
|
2020-04-08 12:43:29 +00:00
|
|
|
|
|
2020-12-31 06:44:42 +00:00
|
|
|
|
public async Task<HtmlDocument> GetHtmlAsync(string s, CancellationToken? token = null)
|
2020-04-08 12:43:29 +00:00
|
|
|
|
{
|
2020-12-31 06:44:42 +00:00
|
|
|
|
var body = await GetStringAsync(s, token: token);
|
2020-04-08 12:43:29 +00:00
|
|
|
|
var doc = new HtmlDocument();
|
|
|
|
|
doc.LoadHtml(body);
|
|
|
|
|
return doc;
|
|
|
|
|
}
|
2020-06-16 22:21:01 +00:00
|
|
|
|
|
|
|
|
|
public Client WithHeader((string MetricsKeyHeader, string) header)
|
|
|
|
|
{
|
|
|
|
|
var newHeaders = Headers.Cons(header).ToList();
|
|
|
|
|
var client = new Client {Headers = newHeaders, Cookies = Cookies,};
|
|
|
|
|
return client;
|
|
|
|
|
}
|
2020-07-25 18:09:02 +00:00
|
|
|
|
|
|
|
|
|
public void AddCookies(Helpers.Cookie[] cookies)
|
|
|
|
|
{
|
|
|
|
|
Cookies.AddRange(cookies.Select(c => new Cookie {Domain = c.Domain, Name = c.Name, Value = c.Value, Path = c.Path}));
|
|
|
|
|
}
|
2021-03-11 15:00:26 +00:00
|
|
|
|
|
|
|
|
|
public void UseChromeUserAgent()
|
|
|
|
|
{
|
|
|
|
|
Headers.Add(("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task DownloadAsync(Uri url, AbsolutePath path)
|
|
|
|
|
{
|
|
|
|
|
using var response = await GetAsync(url);
|
|
|
|
|
await using var content = await response.Content.ReadAsStreamAsync();
|
|
|
|
|
path.Parent.CreateDirectory();
|
|
|
|
|
await using var of = await path.Create();
|
|
|
|
|
await content.CopyToAsync(of);
|
|
|
|
|
}
|
2020-02-14 22:23:27 +00:00
|
|
|
|
}
|
|
|
|
|
}
|