//-----------------------------------------------------------------------------
//
// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// A copy of the License is located at
//
// http://aws.amazon.com/apache2.0
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
namespace Amazon.XRay.Recorder.UnitTests.Tools
{
public class MockWebResponse
{
#if NETFRAMEWORK
public static HttpWebResponse CreateFromResource(string resourceName)
{
var rawResponse = Utils.GetResourceText(resourceName);
var response = ParseRawReponse(rawResponse);
var statusCode = ParseStatusCode(response.StatusLine);
return Create(statusCode, response.Headers, response.Body);
}
public static HttpWebResponse Create(HttpStatusCode statusCode, IDictionary headers, string body = null)
{
var type = typeof(HttpWebResponse);
var assembly = Assembly.GetAssembly(type);
var obj = assembly.CreateInstance("System.Net.HttpWebResponse");
var webHeaders = new WebHeaderCollection();
if (headers != null)
{
foreach (var header in headers)
{
webHeaders.Add(header.Key, header.Value);
}
}
body = body ?? string.Empty;
Stream responseBodyStream = Utils.CreateStreamFromString(body);
var statusFieldInfo = type.GetField(
"m_StatusCode",
BindingFlags.NonPublic | BindingFlags.Instance);
var headersFieldInfo = type.GetField(
"m_HttpResponseHeaders",
BindingFlags.NonPublic | BindingFlags.Instance);
var streamFieldInfo = type.GetField(
"m_ConnectStream",
BindingFlags.NonPublic | BindingFlags.Instance);
var contentLengthFieldInfo = type.GetField(
"m_ContentLength",
BindingFlags.NonPublic | BindingFlags.Instance);
statusFieldInfo.SetValue(obj, statusCode);
headersFieldInfo.SetValue(obj, webHeaders);
streamFieldInfo.SetValue(obj, responseBodyStream);
contentLengthFieldInfo.SetValue(obj, responseBodyStream.Length);
return obj as HttpWebResponse;
}
#else
public static HttpResponseMessage CreateFromResource(string resourceName)
{
var rawResponse = Utils.GetResourceText(resourceName);
var response = ParseRawReponse(rawResponse);
var statusCode = ParseStatusCode(response.StatusLine);
return Create(statusCode, response.Headers, response.Body);
}
public static HttpResponseMessage Create(HttpStatusCode statusCode, IDictionary headers, string body = null)
{
var type = typeof(HttpResponseMessage);
var assembly = Assembly.GetAssembly(type);
var obj = assembly.CreateInstance("System.Net.Http.HttpResponseMessage");
HttpResponseMessage httpResponseMessage = obj as HttpResponseMessage;
var webHeaders = new WebHeaderCollection();
if (headers != null)
{
foreach (var header in headers)
{
webHeaders.Add(header.Key, header.Value);
httpResponseMessage.Headers.Add(header.Key, header.Value);
}
}
body = body ?? string.Empty;
_ = Utils.CreateStreamFromString(body);
httpResponseMessage.StatusCode = statusCode;
StreamReader streamReader = new StreamReader($"JSONs{Path.DirectorySeparatorChar}FakeResponse.json");
string json = streamReader.ReadToEnd();
httpResponseMessage.Content = new StringContent(json); // Content should be in Json format else we get exception from downstream unmarshalling
return httpResponseMessage;
}
#endif
public static HttpResponse ParseRawReponse(string rawResponse)
{
var response = new HttpResponse();
response.StatusLine = rawResponse;
var responseLines = rawResponse.Split('\n');
if (responseLines.Length == 0)
{
throw new ArgumentException(
"The resource does not contain a valid HTTP response.",
nameof(rawResponse));
}
response.StatusLine = responseLines[0];
var currentLine = responseLines[0];
/* Unmerged change from project 'AWSXRayRecorder.UnitTests (netcoreapp2.0)'
Before:
var statusCode = ParseStatusCode(currentLine);
After:
HttpStatusCode statusCode;
_ = ParseStatusCode(currentLine);
*/
/* Unmerged change from project 'AWSXRayRecorder.UnitTests (netcoreapp3.1)'
Before:
var statusCode = ParseStatusCode(currentLine);
After:
HttpStatusCode statusCode;
_ = ParseStatusCode(currentLine);
*/
_ = ParseStatusCode(currentLine);
if (responseLines.Length > 1)
{
int lineIndex;
for (lineIndex = 1; lineIndex < responseLines.Length; lineIndex++)
{
currentLine = responseLines[lineIndex];
if (currentLine.Trim() == string.Empty)
{
currentLine = responseLines[lineIndex - 1];
break;
}
var index = currentLine.IndexOf(":");
if (index != -1)
{
var headerKey = currentLine.Substring(0, index);
var headerValue = currentLine.Substring(index + 1);
response.Headers.Add(headerKey.Trim(), headerValue.Trim());
}
}
}
var startOfBody = rawResponse.IndexOf(currentLine) + currentLine.Length;
response.Body = rawResponse.Substring(startOfBody).Trim();
return response;
}
private static HttpStatusCode ParseStatusCode(string statusLine)
{
try
{
string statusCode = statusLine.Split(' ')[1];
return (HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), statusCode);
}
catch (Exception exception)
{
throw new ArgumentException("Invalid HTTP status line.", exception);
}
}
public class HttpResponse
{
public HttpResponse()
{
this.Headers = new Dictionary();
}
public string StatusLine { get; set; }
public IDictionary Headers { get; private set; }
public string Body { get; set; }
}
}
}