Skip to content

Commit e132a60

Browse files
authored
Add raw body bytes capability (#4555)
1 parent f7f9d4f commit e132a60

File tree

6 files changed

+104
-18
lines changed

6 files changed

+104
-18
lines changed

src/WebJobs.Script/Rpc/LanguageWorkerChannel.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -455,15 +455,15 @@ internal void SendInvocationRequest(ScriptInvocationContext context)
455455
{
456456
if (pair.Value != null)
457457
{
458-
invocationRequest.TriggerMetadata.Add(pair.Key, pair.Value.ToRpc(_workerChannelLogger));
458+
invocationRequest.TriggerMetadata.Add(pair.Key, pair.Value.ToRpc(_workerChannelLogger, _workerCapabilities));
459459
}
460460
}
461461
foreach (var input in context.Inputs)
462462
{
463463
invocationRequest.InputData.Add(new ParameterBinding()
464464
{
465465
Name = input.name,
466-
Data = input.val.ToRpc(_workerChannelLogger)
466+
Data = input.val.ToRpc(_workerChannelLogger, _workerCapabilities)
467467
});
468468
}
469469

src/WebJobs.Script/Rpc/LanguageWorkerConstants.cs

+3
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,8 @@ public static class LanguageWorkerConstants
4747
public const string RpcHttpCookies = "cookies";
4848
public const string RpcHttpStatusCode = "statusCode";
4949
public const string RpcHttpStatus = "status";
50+
51+
// Capabilites
52+
public const string RawHttpBodyBytes = "RawHttpBodyBytes";
5053
}
5154
}

src/WebJobs.Script/Rpc/MessageExtensions/RpcMessageConversionExtensions.cs

+35-13
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public static object ToObject(this TypedData typedData)
4747
}
4848
}
4949

50-
public static TypedData ToRpc(this object value, ILogger logger)
50+
public static TypedData ToRpc(this object value, ILogger logger, Capabilities capabilities)
5151
{
5252
TypedData typedData = new TypedData();
5353

@@ -141,44 +141,52 @@ public static TypedData ToRpc(this object value, ILogger logger)
141141
if (request.Body != null && request.ContentLength > 0)
142142
{
143143
object body = null;
144-
string rawBody = null;
144+
string rawBodyString = null;
145+
byte[] bytes = RequestBodyToBytes(request);
145146

146147
MediaTypeHeaderValue mediaType = null;
147148
if (MediaTypeHeaderValue.TryParse(request.ContentType, out mediaType))
148149
{
149150
if (string.Equals(mediaType.MediaType, "application/json", StringComparison.OrdinalIgnoreCase))
150151
{
151152
var jsonReader = new StreamReader(request.Body, Encoding.UTF8);
152-
rawBody = jsonReader.ReadToEnd();
153+
rawBodyString = jsonReader.ReadToEnd();
153154
try
154155
{
155-
body = JsonConvert.DeserializeObject(rawBody);
156+
body = JsonConvert.DeserializeObject(rawBodyString);
156157
}
157158
catch (JsonException)
158159
{
159-
body = rawBody;
160+
body = rawBodyString;
160161
}
161162
}
162163
else if (string.Equals(mediaType.MediaType, "application/octet-stream", StringComparison.OrdinalIgnoreCase) ||
163164
mediaType.MediaType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0)
164165
{
165-
var length = Convert.ToInt32(request.ContentLength);
166-
var bytes = new byte[length];
167-
request.Body.Read(bytes, 0, length);
168166
body = bytes;
169-
rawBody = Encoding.UTF8.GetString(bytes);
167+
if (!IsRawBodyBytesRequested(capabilities))
168+
{
169+
rawBodyString = Encoding.UTF8.GetString(bytes);
170+
}
170171
}
171172
}
172173
// default if content-tye not found or recognized
173-
if (body == null && rawBody == null)
174+
if (body == null && rawBodyString == null)
174175
{
175176
var reader = new StreamReader(request.Body, Encoding.UTF8);
176-
body = rawBody = reader.ReadToEnd();
177+
body = rawBodyString = reader.ReadToEnd();
177178
}
178179

179180
request.Body.Position = 0;
180-
http.Body = body.ToRpc(logger);
181-
http.RawBody = rawBody.ToRpc(logger);
181+
http.Body = body.ToRpc(logger, capabilities);
182+
if (IsRawBodyBytesRequested(capabilities))
183+
{
184+
http.RawBody = bytes.ToRpc(logger, capabilities);
185+
}
186+
else
187+
{
188+
http.RawBody = rawBodyString.ToRpc(logger, capabilities);
189+
}
182190
}
183191
}
184192
else
@@ -196,6 +204,20 @@ public static TypedData ToRpc(this object value, ILogger logger)
196204
return typedData;
197205
}
198206

207+
private static bool IsRawBodyBytesRequested(Capabilities capabilities)
208+
{
209+
return capabilities.GetCapabilityState(LanguageWorkerConstants.RawHttpBodyBytes) != null;
210+
}
211+
212+
internal static byte[] RequestBodyToBytes(HttpRequest request)
213+
{
214+
var length = Convert.ToInt32(request.ContentLength);
215+
var bytes = new byte[length];
216+
request.Body.Read(bytes, 0, length);
217+
request.Body.Position = 0;
218+
return bytes;
219+
}
220+
199221
public static BindingInfo ToBindingInfo(this BindingMetadata bindingMetadata)
200222
{
201223
BindingInfo bindingInfo = new BindingInfo
Loading

test/WebJobs.Script.Tests/Rpc/RpcMessageConversionExtensionsTests.cs

+61-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.IO;
67
using System.Linq;
8+
using System.Reflection;
79
using System.Security.Claims;
10+
using System.Text;
811
using Google.Protobuf.Collections;
912
using Google.Protobuf.WellKnownTypes;
1013
using Microsoft.AspNetCore.Http;
@@ -18,17 +21,20 @@ namespace Microsoft.Azure.WebJobs.Script.Tests.Rpc
1821
{
1922
public class RpcMessageConversionExtensionsTests
2023
{
24+
private static readonly string TestImageLocation = "Rpc\\Resources\\functions.png";
25+
2126
[Theory]
2227
[InlineData("application/x-www-form-urlencoded’", "say=Hi&to=Mom")]
2328
public void HttpObjects_StringBody(string expectedContentType, object body)
2429
{
2530
var logger = MockNullLoggerFactory.CreateLogger();
31+
var capabilities = new Capabilities(logger);
2632

2733
var headers = new HeaderDictionary();
2834
headers.Add("content-type", expectedContentType);
2935
HttpRequest request = HttpTestHelpers.CreateHttpRequest("GET", "http://localhost/api/httptrigger-scenarios", headers, body);
3036

31-
var rpcRequestObject = request.ToRpc(logger);
37+
var rpcRequestObject = request.ToRpc(logger, capabilities);
3238
Assert.Equal(body.ToString(), rpcRequestObject.Http.Body.String);
3339

3440
string contentType;
@@ -43,10 +49,11 @@ public void HttpObjects_StringBody(string expectedContentType, object body)
4349
public void HttpObjects_Query(string queryString, string[] expectedKeys, string[] expectedValues)
4450
{
4551
var logger = MockNullLoggerFactory.CreateLogger();
52+
var capabilities = new Capabilities(logger);
4653

4754
HttpRequest request = HttpTestHelpers.CreateHttpRequest("GET", $"http://localhost/api/httptrigger-scenarios?{queryString}");
4855

49-
var rpcRequestObject = request.ToRpc(logger);
56+
var rpcRequestObject = request.ToRpc(logger, capabilities);
5057
// Same number of expected key value pairs
5158
Assert.Equal(rpcRequestObject.Http.Query.Count, expectedKeys.Length);
5259
Assert.Equal(rpcRequestObject.Http.Query.Count, expectedValues.Length);
@@ -188,6 +195,7 @@ public void SetCookie_ReturnsExpectedResult(string name, string value, RpcHttpCo
188195
public void HttpObjects_ClaimsPrincipal()
189196
{
190197
var logger = MockNullLoggerFactory.CreateLogger();
198+
var capabilities = new Capabilities(logger);
191199

192200
HttpRequest request = HttpTestHelpers.CreateHttpRequest("GET", $"http://localhost/apihttptrigger-scenarios");
193201

@@ -201,7 +209,7 @@ public void HttpObjects_ClaimsPrincipal()
201209

202210
request.HttpContext.User = new ClaimsPrincipal(claimsIdentities);
203211

204-
var rpcRequestObject = request.ToRpc(logger);
212+
var rpcRequestObject = request.ToRpc(logger, capabilities);
205213

206214
var identities = request.HttpContext.User.Identities.ToList();
207215
var rpcIdentities = rpcRequestObject.Http.Identities.ToList();
@@ -247,5 +255,55 @@ internal static ClaimsIdentity MockEasyAuth(string provider, string name, string
247255

248256
return identity;
249257
}
258+
259+
[Theory]
260+
[InlineData("application/octet-stream", "true")]
261+
[InlineData("image/png", "true")]
262+
[InlineData("application/octet-stream", null)]
263+
[InlineData("image/png", null)]
264+
public void HttpObjects_RawBodyBytes_Image_Length(string contentType, string rawBytesEnabled)
265+
{
266+
var logger = MockNullLoggerFactory.CreateLogger();
267+
var capabilities = new Capabilities(logger);
268+
if (!string.Equals(rawBytesEnabled, null))
269+
{
270+
capabilities.UpdateCapabilities(new MapField<string, string>
271+
{
272+
{ LanguageWorkerConstants.RawHttpBodyBytes, rawBytesEnabled.ToString() }
273+
});
274+
}
275+
276+
FileStream image = new FileStream(TestImageLocation, FileMode.Open, FileAccess.Read);
277+
byte[] imageBytes = FileStreamToBytes(image);
278+
string imageString = Encoding.UTF8.GetString(imageBytes);
279+
280+
long imageBytesLength = imageBytes.Length;
281+
long imageStringLength = imageString.Length;
282+
283+
var headers = new HeaderDictionary();
284+
headers.Add("content-type", contentType);
285+
HttpRequest request = HttpTestHelpers.CreateHttpRequest("GET", "http://localhost/api/httptrigger-scenarios", headers, imageBytes);
286+
287+
var rpcRequestObject = request.ToRpc(logger, capabilities);
288+
if (string.IsNullOrEmpty(rawBytesEnabled))
289+
{
290+
Assert.Empty(rpcRequestObject.Http.RawBody.Bytes);
291+
Assert.Equal(imageStringLength, rpcRequestObject.Http.RawBody.String.Length);
292+
}
293+
else
294+
{
295+
Assert.Empty(rpcRequestObject.Http.RawBody.String);
296+
Assert.Equal(imageBytesLength, rpcRequestObject.Http.RawBody.Bytes.ToByteArray().Length);
297+
}
298+
}
299+
300+
private byte[] FileStreamToBytes(FileStream file)
301+
{
302+
using (var memoryStream = new MemoryStream())
303+
{
304+
file.CopyTo(memoryStream);
305+
return memoryStream.ToArray();
306+
}
307+
}
250308
}
251309
}

test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@
8888
<None Update="Description\DotNet\TestFiles\PackageReferences\ProjectWithMismatchedLock\MismatchedProjectDependencies\project.assets.json">
8989
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
9090
</None>
91+
<None Update="Rpc\Resources\functions.png">
92+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
93+
</None>
9194
<None Update="xunit.runner.json">
9295
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
9396
</None>

0 commit comments

Comments
 (0)