Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Azure.Sdk.Tools.TestProxy.Common;
using Xunit;

namespace Azure.Sdk.Tools.TestProxy.Tests
Expand Down Expand Up @@ -217,5 +218,35 @@ public async Task TestPlaybackSetsRetryAfterToZero()
await testRecordingHandler.HandlePlaybackRequest(recordingId, request, response);
Assert.False(response.Headers.ContainsKey("Retry-After"));
}

[Fact]
public async Task TestPlaybackWithGZippedContentPlayback()
{
RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
var httpContext = new DefaultHttpContext();
var body = "{\"x-recording-file\":\"Test.RecordEntries/request_response_with_gzipped_content.json\"}";
httpContext.Request.Body = TestHelpers.GenerateStreamRequestBody(body);
httpContext.Request.ContentLength = body.Length;

var controller = new Playback(testRecordingHandler, new NullLoggerFactory())
{
ControllerContext = new ControllerContext()
{
HttpContext = httpContext
}
};
await controller.Start();

var recordingId = httpContext.Response.Headers["x-recording-id"].ToString();
Assert.NotNull(recordingId);
Assert.True(testRecordingHandler.PlaybackSessions.ContainsKey(recordingId));
var entry = testRecordingHandler.PlaybackSessions[recordingId].Session.Entries[0];
HttpRequest request = TestHelpers.CreateRequestFromEntry(entry);

// compress the body to simulate what the request coming from the library will look like
request.Body = new MemoryStream(GZipUtilities.CompressBody(BinaryData.FromStream(request.Body).ToArray(), request.Headers));
HttpResponse response = new DefaultHttpContext().Response;
await testRecordingHandler.HandlePlaybackRequest(recordingId, request, response);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading;
Expand Down Expand Up @@ -632,7 +633,7 @@ public void TestCreateUpstreamRequestIncludesExpectedHeaders(params string[] inc
var recordingHandler = new RecordingHandler(Directory.GetCurrentDirectory());
var upstreamRequestContext = GenerateHttpRequestContext(incomingHeaders);

var output = recordingHandler.CreateUpstreamRequest(upstreamRequestContext.Request, new byte[] { });
var output = recordingHandler.CreateUpstreamRequest(upstreamRequestContext.Request);

// iterate across the set we know about and confirm that GenerateUpstreamRequest worked properly!
var setOfHeaders = GenerateHeaderValuesTuples(incomingHeaders);
Expand Down Expand Up @@ -684,7 +685,7 @@ public async Task TestRecordMaintainsUpstreamOverrideHostHeader(string upstreamH

httpContext.Request.Method = "GET";

var upstreamRequest = testRecordingHandler.CreateUpstreamRequest(httpContext.Request, new byte[] { });
var upstreamRequest = testRecordingHandler.CreateUpstreamRequest(httpContext.Request);

if (!String.IsNullOrWhiteSpace(upstreamHostHeaderValue))
{
Expand All @@ -696,6 +697,52 @@ public async Task TestRecordMaintainsUpstreamOverrideHostHeader(string upstreamH
}
}

[Fact]
public async Task TestRecordWithGZippedContent()
{
var httpContext = new DefaultHttpContext();
var bodyBytes = Encoding.UTF8.GetBytes("{\"hello\":\"world\"}");
var mockClient = new HttpClient(new MockHttpHandler(bodyBytes, "application/json", "gzip"));
var path = Directory.GetCurrentDirectory();
var recordingHandler = new RecordingHandler(path)
{
RedirectableClient = mockClient,
RedirectlessClient = mockClient
};

var relativePath = "recordings/gzip";
var fullPathToRecording = Path.Combine(path, relativePath) + ".json";

await recordingHandler.StartRecordingAsync(relativePath, httpContext.Response);

var recordingId = httpContext.Response.Headers["x-recording-id"].ToString();

httpContext.Request.ContentType = "application/json";
httpContext.Request.Headers["Content-Encoding"] = "gzip";
httpContext.Request.ContentLength = 0;
httpContext.Request.Headers["x-recording-id"] = recordingId;
httpContext.Request.Headers["x-recording-upstream-base-uri"] = "http://example.org";
httpContext.Request.Method = "GET";
httpContext.Request.Body = new MemoryStream(GZipUtilities.CompressBody(bodyBytes, httpContext.Request.Headers));

await recordingHandler.HandleRecordRequestAsync(recordingId, httpContext.Request, httpContext.Response);
recordingHandler.StopRecording(recordingId);

try
{
using var fileStream = File.Open(fullPathToRecording, FileMode.Open);
using var doc = JsonDocument.Parse(fileStream);
var record = RecordSession.Deserialize(doc.RootElement);
var entry = record.Entries.First();
Assert.Equal("{\"hello\":\"world\"}", Encoding.UTF8.GetString(entry.Request.Body));
Assert.Equal("{\"hello\":\"world\"}", Encoding.UTF8.GetString(entry.Response.Body));
}
finally
{
File.Delete(fullPathToRecording);
}
}

#region SetRecordingOptions
[Theory]
[InlineData("{ \"HandleRedirects\": \"true\"}", true)]
Expand Down Expand Up @@ -979,17 +1026,38 @@ public IgnoreOnLinuxFact()
internal class MockHttpHandler : HttpMessageHandler
{
public const string DefaultResponse = "default response";
private readonly byte[] _responseContent;
private readonly string _contentType;
private readonly string _contentEncoding;

public MockHttpHandler()
public MockHttpHandler(byte[] responseContent = default, string contentType = default, string contentEncoding = default)
{
_responseContent = responseContent ?? Encoding.UTF8.GetBytes(DefaultResponse);
_contentType = contentType ?? "application/json";
_contentEncoding = contentEncoding;
}

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);

// we need to set the content before the content headers as otherwise they will be cleared out after setting content.
if (_contentEncoding == "gzip")
{
response.Content = new ByteArrayContent(GZipUtilities.CompressBodyCore(_responseContent));
}
else
{
response.Content = new ByteArrayContent(_responseContent);
}

return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
response.Content.Headers.ContentType = new MediaTypeHeaderValue(_contentType);
if (_contentEncoding != null)
{
Content = new StringContent(DefaultResponse)
});
response.Content.Headers.ContentEncoding.Add(_contentEncoding);
}

return Task.FromResult(response);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"Entries": [
{
"RequestUri": "https://fakeazsdktestaccount.table.core.windows.net/Tables",
"RequestMethod": "POST",
"RequestHeaders": {
"Accept": "application/json;odata=minimalmetadata",
"Accept-Encoding": "gzip, deflate",
"Authorization": "Sanitized",
"Connection": "keep-alive",
"Content-Length": "34",
"Content-Type": "application/json",
"Content-Encoding": "gzip",
"DataServiceVersion": "3.0",
"Date": "Tue, 18 May 2021 23:27:42 GMT",
"User-Agent": "azsdk-python-data-tables/12.0.0b7 Python/3.8.6 (Windows-10-10.0.19041-SP0)",
"x-ms-client-request-id": "a4c24b7a-b830-11eb-a05e-10e7c6392c5a",
"x-ms-date": "Tue, 18 May 2021 23:27:42 GMT",
"x-ms-version": "2019-02-02"
},
"RequestBody": "{\u0022TableName\u0022: \u0022listtable09bf2a3d\u0022}",
"StatusCode": 201,
"ResponseHeaders": {
"Cache-Control": "no-cache",
"Content-Type": "application/json",
"Content-Encoding": "gzip",
"Date": "Tue, 18 May 2021 23:27:43 GMT",
"Retry-After": "10",
"Location": "https://fakeazsdktestaccount.table.core.windows.net/Tables(\u0027listtable09bf2a3d\u0027)",
"Server": [
"Windows-Azure-Table/1.0",
"Microsoft-HTTPAPI/2.0"
],
"Transfer-Encoding": "chunked",
"X-Content-Type-Options": "nosniff",
"x-ms-client-request-id": "a4c24b7a-b830-11eb-a05e-10e7c6392c5a",
"x-ms-request-id": "d2270777-c002-0072-313d-4ce19f000000",
"x-ms-version": "2019-02-02"
},
"ResponseBody": {
"odata.metadata": "https://fakeazsdktestaccount.table.core.windows.net/$metadata#Tables/@Element",
"TableName": "listtable09bf2a3d",
"connectionString": null
}
}
],
"Variables": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Http;

namespace Azure.Sdk.Tools.TestProxy.Common
{
/// <summary>
/// Utility methods to compress and decompress content to/from GZip.
/// </summary>
public static class GZipUtilities
{
private const string Gzip = "gzip";
private const string ContentEncoding = "Content-Encoding";

public static byte[] CompressBody(byte[] incomingBody, IDictionary<string, string[]> headers)
{
if (headers.TryGetValue(ContentEncoding, out var values) && values.Contains(Gzip))
{
return CompressBodyCore(incomingBody);
}

return incomingBody;
}

public static byte[] CompressBody(byte[] incomingBody, IHeaderDictionary headers)
{
if (headers.TryGetValue(ContentEncoding, out var values) && values.Contains(Gzip))
{
return CompressBodyCore(incomingBody);
}

return incomingBody;
}

public static byte[] CompressBodyCore(byte[] body)
{
using (var uncompressedStream = new MemoryStream(body))
using (var resultStream = new MemoryStream())
{
using (var compressedStream = new GZipStream(resultStream, CompressionMode.Compress))
{
uncompressedStream.CopyTo(compressedStream);
}

return resultStream.ToArray();
}
}

public static byte[] DecompressBody(MemoryStream incomingBody, HttpContentHeaders headers)
{
if (headers.TryGetValues(ContentEncoding, out var values) && values.Contains(Gzip))
{
return DecompressBodyCore(incomingBody);
}

return incomingBody.ToArray();
}

public static byte[] DecompressBody(byte[] incomingBody, IHeaderDictionary headers)
{
if (headers.TryGetValue(ContentEncoding, out var values) && values.Contains(Gzip))
{
return DecompressBodyCore(new MemoryStream(incomingBody));
}

return incomingBody;
}

private static byte[] DecompressBodyCore(MemoryStream stream)
{
using var uncompressedStream = new GZipStream(stream, CompressionMode.Decompress);
using var resultStream = new MemoryStream();
uncompressedStream.CopyTo(resultStream);
return resultStream.ToArray();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"profiles": {
"Azure.Sdk.Tools.TestProxy": {
"commandName": "Project",
"commandLineArgs": "--help",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"Logging__LogLevel__Microsoft": "Information"
Expand Down
Loading