using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; namespace dotnetcore.utils { /// /// Various Http helper routines /// public static class HttpHelpers { /// /// Makes a http request to the specified endpoint /// /// /// /// /// public static void InvokeHttpRequest(Uri endpointUri, string httpMethod, IDictionary headers, string requestBody) { try { var request = ConstructWebRequest(endpointUri, httpMethod, headers); if (!string.IsNullOrEmpty(requestBody)) { var buffer = new byte[8192]; // arbitrary buffer size var requestStream = request.GetRequestStream(); using (var inputStream = new MemoryStream(Encoding.UTF8.GetBytes(requestBody))) { var bytesRead = 0; while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0) { requestStream.Write(buffer, 0, bytesRead); } } } GetResponse(request); } catch (WebException ex) { using (var response = ex.Response as HttpWebResponse) { if (response != null) { var errorMsg = ReadResponseBody(response); Logger.LogError($"\n-- HTTP call failed with exception '{errorMsg}', status code '{response.StatusCode}'"); } } } } /// /// Construct a HttpWebRequest onto the specified endpoint and populate /// the headers. /// /// The endpoint to call /// GET, PUT etc /// The set of headers to apply to the request /// Initialized HttpWebRequest instance public static HttpWebRequest ConstructWebRequest(Uri endpointUri, string httpMethod, IDictionary headers) { var request = (HttpWebRequest)WebRequest.Create(endpointUri); request.Method = httpMethod; foreach (var header in headers.Keys) { // not all headers can be set via the dictionary if (header.Equals("host", StringComparison.OrdinalIgnoreCase)) request.Host = headers[header]; else if (header.Equals("content-length", StringComparison.OrdinalIgnoreCase)) request.ContentLength = long.Parse(headers[header]); else if (header.Equals("content-type", StringComparison.OrdinalIgnoreCase)) request.ContentType = headers[header]; else request.Headers.Add(header, headers[header]); } return request; } public static HttpWebResponse GetResponse(HttpWebRequest request) { // Get the response and read any body into a string, then display. using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK) { Logger.LogDebug("\n-- HTTP call succeeded"); var responseBody = ReadResponseBody(response); if (!string.IsNullOrEmpty(responseBody)) { Logger.LogDebug("\n-- Response body:"); Logger.LogDebug(responseBody); } } else Logger.LogDebug($"\n-- HTTP call failed, status code: {response.StatusCode}"); return response; } } /// /// Reads the response data from the service call, if any /// /// /// The response instance obtained from the previous request /// /// The body content of the response public static string ReadResponseBody(HttpWebResponse response) { if (response == null) throw new ArgumentNullException("response", "Value cannot be null"); // Then, open up a reader to the response and read the contents to a string // and return that to the caller. string responseBody = string.Empty; using (var responseStream = response.GetResponseStream()) { if (responseStream != null) { using (var reader = new StreamReader(responseStream)) { responseBody = reader.ReadToEnd(); } } } return responseBody; } /// /// Helper routine to url encode canonicalized header names and values for safe /// inclusion in the presigned url. /// /// The string to encode /// Whether the string is a URL path or not /// The encoded string public static string UrlEncode(string data, bool isPath = false) { // The Set of accepted and valid Url characters per RFC3986. Characters outside of this set will be encoded. const string validUrlCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; var encoded = new StringBuilder(data.Length * 2); string unreservedChars = String.Concat(validUrlCharacters, (isPath ? "/:" : "")); foreach (char symbol in System.Text.Encoding.UTF8.GetBytes(data)) { if (unreservedChars.IndexOf(symbol) != -1) encoded.Append(symbol); else encoded.Append("%").Append(String.Format("{0:X2}", (int)symbol)); } return encoded.ToString(); } } }