using System; using System.IO; using System.Net.Http; using System.Text; using System.Threading.Tasks; using SPT.Common.Utils; namespace SPT.Common.Http { // NOTE: Don't dispose this, keep a reference for the lifetime of the // application. public class Client : IDisposable { protected readonly HttpClient _httpv; protected readonly string _address; protected readonly string _accountId; protected readonly int _retries; public Client(string address, string accountId, int retries = 3) { _address = address; _accountId = accountId; _retries = retries; var handler = new HttpClientHandler { // set cookies in header instead UseCookies = false }; _httpv = new HttpClient(handler); } private HttpRequestMessage GetNewRequest(HttpMethod method, string path) { return new HttpRequestMessage() { Method = method, RequestUri = new Uri(_address + path), Headers = { { "Cookie", $"PHPSESSID={_accountId}" } } }; } protected async Task SendAsync(HttpMethod method, string path, byte[] data, bool zipped = true) { HttpResponseMessage response = null; using (var request = GetNewRequest(method, path)) { if (data != null) { // add payload to request if (zipped) { data = Zlib.Compress(data, ZlibCompression.Maximum); } request.Content = new ByteArrayContent(data); } // send request response = await _httpv.SendAsync(request); } if (!response.IsSuccessStatusCode) { // response error throw new Exception($"Code {response.StatusCode}"); } using (var ms = new MemoryStream()) { using (var stream = await response.Content.ReadAsStreamAsync()) { // grap response payload await stream.CopyToAsync(ms); var body = ms.ToArray(); if (Zlib.IsCompressed(body)) { body = Zlib.Decompress(body); } if (body == null) { // payload doesn't contains data var code = response.StatusCode.ToString(); body = Encoding.UTF8.GetBytes(code); } return body; } } } protected async Task SendWithRetriesAsync(HttpMethod method, string path, byte[] data, bool compress = true) { var error = new Exception("Internal error"); // NOTE: <= is intentional. 0 is send, 1/2/3 is retry for (var i = 0; i <= _retries; ++i) { try { return await SendAsync(method, path, data, compress); } catch (Exception ex) { error = ex; } } throw error; } public async Task GetAsync(string path) { return await SendWithRetriesAsync(HttpMethod.Get, path, null); } public byte[] Get(string path) { return Task.Run(() => GetAsync(path)).Result; } public async Task PostAsync(string path, byte[] data, bool compress = true) { return await SendWithRetriesAsync(HttpMethod.Post, path, data, compress); } public byte[] Post(string path, byte[] data, bool compress = true) { return Task.Run(() => PostAsync(path, data, compress)).Result; } // NOTE: returns status code as bytes public async Task PutAsync(string path, byte[] data, bool compress = true) { return await SendWithRetriesAsync(HttpMethod.Post, path, data, compress); } // NOTE: returns status code as bytes public byte[] Put(string path, byte[] data, bool compress = true) { return Task.Run(() => PutAsync(path, data, compress)).Result; } public void Dispose() { _httpv.Dispose(); } } }