Skip to content

Commit 388c4d2

Browse files
authored
sending an empty rpchttp to worker when proxying (#9415)
1 parent 6b79fa2 commit 388c4d2

File tree

3 files changed

+108
-12
lines changed

3 files changed

+108
-12
lines changed

release_notes.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@
1616
- Microsoft.IdentityModel.JsonWebTokens
1717
- Microsoft.IdentityModel.Logging
1818
- Updated Grpc.AspNetCore package to 2.55.0 (https://github.com/Azure/azure-functions-host/pull/9373)
19-
- Update protobuf file to v1.10.0 (https://github.com/Azure/azure-functions-host/pull/9405)
19+
- Update protobuf file to v1.10.0 (https://github.com/Azure/azure-functions-host/pull/9405)
20+
- Send an empty RpcHttp payload if proxying the http request to the worker (https://github.com/Azure/azure-functions-host/pull/9415)

src/WebJobs.Script.Grpc/MessageExtensions/GrpcMessageConversionExtensions.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@
77
using System.Text;
88
using System.Threading.Tasks;
99
using Google.Protobuf;
10-
using Google.Protobuf.WellKnownTypes;
1110
using Microsoft.AspNetCore.Http;
1211
using Microsoft.Azure.WebJobs.Extensions.Http;
13-
using Microsoft.Azure.WebJobs.Host.Bindings;
1412
using Microsoft.Azure.WebJobs.Script.Description;
1513
using Microsoft.Azure.WebJobs.Script.Extensions;
1614
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
@@ -25,19 +23,20 @@ namespace Microsoft.Azure.WebJobs.Script.Grpc
2523
internal static class GrpcMessageConversionExtensions
2624
{
2725
private static readonly JsonSerializerSettings _datetimeSerializerSettings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None };
26+
private static readonly TypedData EmptyRpcHttp = new() { Http = new() };
2827

2928
public static object ToObject(this TypedData typedData) =>
3029
typedData.DataCase switch
3130
{
32-
RpcDataType.None => null,
33-
RpcDataType.String => typedData.String,
34-
RpcDataType.Json => JsonConvert.DeserializeObject(typedData.Json, _datetimeSerializerSettings),
35-
RpcDataType.Bytes or RpcDataType.Stream => typedData.Bytes.ToByteArray(),
36-
RpcDataType.Http => GrpcMessageExtensionUtilities.ConvertFromHttpMessageToExpando(typedData.Http),
37-
RpcDataType.Int => typedData.Int,
38-
RpcDataType.Double => typedData.Double,
39-
// TODO better exception
40-
_ => throw new InvalidOperationException($"Unknown RpcDataType: {typedData.DataCase}")
31+
RpcDataType.None => null,
32+
RpcDataType.String => typedData.String,
33+
RpcDataType.Json => JsonConvert.DeserializeObject(typedData.Json, _datetimeSerializerSettings),
34+
RpcDataType.Bytes or RpcDataType.Stream => typedData.Bytes.ToByteArray(),
35+
RpcDataType.Http => GrpcMessageExtensionUtilities.ConvertFromHttpMessageToExpando(typedData.Http),
36+
RpcDataType.Int => typedData.Int,
37+
RpcDataType.Double => typedData.Double,
38+
// TODO better exception
39+
_ => throw new InvalidOperationException($"Unknown RpcDataType: {typedData.DataCase}")
4140
};
4241

4342
public static ValueTask<TypedData> ToRpc(this object value, ILogger logger, GrpcCapabilities capabilities)
@@ -102,6 +101,13 @@ internal static TypedData ToModelBindingDataArray(this ParameterBindingData[] da
102101

103102
internal static async Task<TypedData> ToRpcHttp(this HttpRequest request, ILogger logger, GrpcCapabilities capabilities)
104103
{
104+
// If proxying the http request to the worker, keep the grpc message minimal
105+
bool skipHttpInputs = !string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.HttpUri));
106+
if (skipHttpInputs)
107+
{
108+
return EmptyRpcHttp;
109+
}
110+
105111
var http = new RpcHttp()
106112
{
107113
Url = $"{(request.IsHttps ? "https" : "http")}://{request.Host}{request.Path}{request.QueryString}",

test/WebJobs.Script.Tests/Workers/ScriptInvocationContextExtensionsTests.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using Microsoft.Azure.WebJobs.Script.Workers.SharedMemoryDataTransfer;
1919
using Microsoft.Extensions.Logging;
2020
using Microsoft.Extensions.Logging.Abstractions;
21+
using Microsoft.WebJobs.Script.Tests;
2122
using Newtonsoft.Json.Linq;
2223
using Xunit;
2324

@@ -316,6 +317,94 @@ public async Task ToRpcInvocationRequest_MultipleInputBindings()
316317
Assert.True(result.TriggerMetadata.ContainsKey("query"));
317318
}
318319

320+
[Fact]
321+
public async Task ToRpc_Http()
322+
{
323+
var rpcHttp = await CreateTestRpcHttp();
324+
325+
Assert.Equal("http://local/test?a=b", rpcHttp.Url);
326+
Assert.Equal("test value", rpcHttp.Headers["test-header"]);
327+
Assert.Equal("b", rpcHttp.Query["a"]);
328+
Assert.Equal("test body", rpcHttp.Body.String);
329+
}
330+
331+
[Fact]
332+
public async Task ToRpc_Http_WithProxy()
333+
{
334+
// Specify that we're using proxies.
335+
var rpcHttp = await CreateTestRpcHttp(new Dictionary<string, string>() { { "HttpUri", "something" } });
336+
337+
// everything should come back empty
338+
Assert.Empty(rpcHttp.Url);
339+
Assert.Empty(rpcHttp.Headers);
340+
Assert.Empty(rpcHttp.Query);
341+
Assert.Null(rpcHttp.Body);
342+
}
343+
344+
private async Task<RpcHttp> CreateTestRpcHttp(IDictionary<string, string> capabilities = null)
345+
{
346+
var logger = new TestLogger("test");
347+
GrpcCapabilities grpcCapabilities = new GrpcCapabilities(logger);
348+
if (capabilities is not null)
349+
{
350+
grpcCapabilities.UpdateCapabilities(capabilities);
351+
}
352+
353+
var headers = new HeaderDictionary();
354+
headers.Add("test-header", "test value");
355+
var request = HttpTestHelpers.CreateHttpRequest("POST", "http://local/test?a=b", headers: headers, body: "test body");
356+
357+
var bindingData = new Dictionary<string, object>
358+
{
359+
{ "req", request },
360+
};
361+
362+
var inputs = new List<(string Name, DataType Type, object Val)>
363+
{
364+
("req", DataType.String, request),
365+
};
366+
367+
var invocationContext = new ScriptInvocationContext()
368+
{
369+
ExecutionContext = new ExecutionContext()
370+
{
371+
InvocationId = Guid.NewGuid(),
372+
FunctionName = "Test",
373+
},
374+
BindingData = bindingData,
375+
Inputs = inputs,
376+
ResultSource = new TaskCompletionSource<ScriptInvocationResult>(),
377+
Logger = logger,
378+
AsyncExecutionContext = System.Threading.ExecutionContext.Capture()
379+
};
380+
381+
var functionMetadata = new FunctionMetadata
382+
{
383+
Name = "Test"
384+
};
385+
386+
var httpTriggerBinding = new BindingMetadata
387+
{
388+
Name = "req",
389+
Type = "httpTrigger",
390+
Direction = BindingDirection.In,
391+
Raw = new JObject()
392+
};
393+
394+
functionMetadata.Bindings.Add(httpTriggerBinding);
395+
invocationContext.FunctionMetadata = functionMetadata;
396+
397+
var result = await invocationContext.ToRpcInvocationRequest(logger, grpcCapabilities, isSharedMemoryDataTransferEnabled: false, _sharedMemoryManager);
398+
var resultHttp = result.InputData[0].Data.Http;
399+
Assert.Equal(1, result.TriggerMetadata.Count);
400+
Assert.Same(resultHttp, result.TriggerMetadata["req"].Http);
401+
402+
Assert.Equal(1, result.InputData.Count);
403+
Assert.Equal("req", result.InputData[0].Name);
404+
405+
return resultHttp;
406+
}
407+
319408
[Fact]
320409
public void TestSetRetryContext_NoRetry()
321410
{

0 commit comments

Comments
 (0)