Skip to content

Commit

Permalink
[CustomHandler]Include HttpResponseMessage if fails to deserialize to…
Browse files Browse the repository at this point in the history
… HttpInvocationResult (#6616)
  • Loading branch information
pragnagopa committed Sep 11, 2020
1 parent d732227 commit b0eccda
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 7 deletions.
32 changes: 26 additions & 6 deletions src/WebJobs.Script/Workers/Http/DefaultHttpWorkerService.cs
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -114,30 +113,33 @@ private async Task<HttpResponseMessage> SendInvocationRequestAsync(ScriptInvocat
if (_enableRequestTracing)
{
scriptInvocationContext.Logger.LogTrace($"Invocation Request:{httpRequestMessage}");
await TraceHttpContent(httpRequestMessage.Content, scriptInvocationContext.Logger);
await GetHttpContentAsString(httpRequestMessage.Content, scriptInvocationContext.Logger);
}
_logger.CustomHandlerSendingInvocation(scriptInvocationContext.FunctionMetadata.Name, scriptInvocationContext.ExecutionContext.InvocationId);
HttpResponseMessage invocationResponse = await _httpClient.SendAsync(httpRequestMessage);
if (_enableRequestTracing)
{
scriptInvocationContext.Logger.LogTrace($"Invocation Response:{invocationResponse}");
await TraceHttpContent(invocationResponse.Content, scriptInvocationContext.Logger);
await GetHttpContentAsString(invocationResponse.Content, scriptInvocationContext.Logger);
}
_logger.CustomHandlerReceivedInvocationResponse(scriptInvocationContext.FunctionMetadata.Name, scriptInvocationContext.ExecutionContext.InvocationId);
return invocationResponse;
}

private static async Task TraceHttpContent(HttpContent content, ILogger logger)
private static async Task<string> GetHttpContentAsString(HttpContent content, ILogger logger = null)
{
if (content != null)
{
bool isMediaTypeOctetOrMultipart = content.Headers != null && Utility.IsMediaTypeOctetOrMultipart(content.Headers.ContentType);
// do not log binary data as string
if (!isMediaTypeOctetOrMultipart)
{
logger.LogTrace(await content.ReadAsStringAsync());
string stringCotent = await content.ReadAsStringAsync();
logger?.LogTrace(stringCotent);
return stringCotent;
}
}
return null;
}

internal void AddHeaders(HttpRequestMessage httpRequest, string invocationId)
Expand Down Expand Up @@ -175,7 +177,7 @@ internal async Task ProcessDefaultInvocationRequest(ScriptInvocationContext scri
// Only process output bindings if response is succeess code
invocationResponse.EnsureSuccessStatusCode();

HttpScriptInvocationResult httpScriptInvocationResult = await invocationResponse.Content.ReadAsAsync<HttpScriptInvocationResult>();
HttpScriptInvocationResult httpScriptInvocationResult = await GetHttpScriptInvocationResult(invocationResponse);

if (httpScriptInvocationResult != null)
{
Expand All @@ -201,6 +203,24 @@ internal async Task ProcessDefaultInvocationRequest(ScriptInvocationContext scri
}
}

internal async Task<HttpScriptInvocationResult> GetHttpScriptInvocationResult(HttpResponseMessage httpResponseMessage)
{
try
{
return await httpResponseMessage.Content.ReadAsAsync<HttpScriptInvocationResult>();
}
catch (Exception ex)
{
var exMessage = $"Invalid HttpResponseMessage:\n{httpResponseMessage}";
string httpContent = await GetHttpContentAsString(httpResponseMessage.Content);
if (!string.IsNullOrEmpty(httpContent))
{
exMessage = $"{exMessage}\n {httpContent}";
}
throw new InvalidOperationException(exMessage, ex);
}
}

internal void ProcessLogsFromHttpResponse(ScriptInvocationContext scriptInvocationContext, HttpScriptInvocationResult invocationResult)
{
if (scriptInvocationContext == null)
Expand Down
Expand Up @@ -368,6 +368,26 @@ public async Task ProcessDefaultInvocationRequest_JsonResponse_Succeeds()
Assert.Null(invocationResult.Return);
}

[Fact]
public async Task ProcessDefaultInvocationRequest_OkResponse_InvalidBody_Throws()
{
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);

handlerMock.Protected().Setup<Task<HttpResponseMessage>>("SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.Callback<HttpRequestMessage, CancellationToken>((request, token) => RequestHandler(request))
.ReturnsAsync(HttpWorkerTestUtilities.GetValidHttpResponseMessage_JsonType_InvalidContent());

_httpClient = new HttpClient(handlerMock.Object);
_defaultHttpWorkerService = new DefaultHttpWorkerService(_httpClient, new OptionsWrapper<HttpWorkerOptions>(_httpWorkerOptions), _testLogger, _testEnvironment);
var testScriptInvocationContext = HttpWorkerTestUtilities.GetScriptInvocationContext(TestFunctionName, _testInvocationId, _functionLogger);
await _defaultHttpWorkerService.ProcessDefaultInvocationRequest(testScriptInvocationContext);
InvalidOperationException recodedEx = await Assert.ThrowsAsync<InvalidOperationException>(async () => await testScriptInvocationContext.ResultSource.Task);
Assert.Contains("Hello World", recodedEx.Message);
Assert.Contains("StatusCode: 200", recodedEx.Message);
}

[Fact]
public async Task ProcessDefaultInvocationRequest_InvalidMediaType_Throws()
{
Expand All @@ -383,7 +403,8 @@ public async Task ProcessDefaultInvocationRequest_InvalidMediaType_Throws()
_defaultHttpWorkerService = new DefaultHttpWorkerService(_httpClient, new OptionsWrapper<HttpWorkerOptions>(_httpWorkerOptions), _testLogger, _testEnvironment, new OptionsWrapper<ScriptJobHostOptions>(_scriptJobHostOptions));
var testScriptInvocationContext = HttpWorkerTestUtilities.GetScriptInvocationContext(TestFunctionName, _testInvocationId, _testLogger);
await _defaultHttpWorkerService.ProcessDefaultInvocationRequest(testScriptInvocationContext);
await Assert.ThrowsAsync<UnsupportedMediaTypeException>(async () => await testScriptInvocationContext.ResultSource.Task);
InvalidOperationException recodedEx = await Assert.ThrowsAsync<InvalidOperationException>(async () => await testScriptInvocationContext.ResultSource.Task);
Assert.True(recodedEx.InnerException is UnsupportedMediaTypeException);
}

[Fact]
Expand Down
Expand Up @@ -177,6 +177,14 @@ public static HttpResponseMessage GetHttpResponseMessageWithStringContent()
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("TestMessage") };
}

public static HttpResponseMessage GetValidHttpResponseMessage_JsonType_InvalidContent()
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Hello World", Encoding.UTF8, "application/json")
};
}

public static List<(string name, DataType type, object val)> GetScriptInvocationInputs()
{
List<(string name, DataType type, object val)> inputs = new List<(string name, DataType type, object val)>();
Expand Down

0 comments on commit b0eccda

Please sign in to comment.