Skip to content

Commit

Permalink
Add raw body bytes capability (#4555)
Browse files Browse the repository at this point in the history
  • Loading branch information
maiqbal11 authored Jun 12, 2019
1 parent f7f9d4f commit e132a60
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 18 deletions.
4 changes: 2 additions & 2 deletions src/WebJobs.Script/Rpc/LanguageWorkerChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -455,15 +455,15 @@ internal void SendInvocationRequest(ScriptInvocationContext context)
{
if (pair.Value != null)
{
invocationRequest.TriggerMetadata.Add(pair.Key, pair.Value.ToRpc(_workerChannelLogger));
invocationRequest.TriggerMetadata.Add(pair.Key, pair.Value.ToRpc(_workerChannelLogger, _workerCapabilities));
}
}
foreach (var input in context.Inputs)
{
invocationRequest.InputData.Add(new ParameterBinding()
{
Name = input.name,
Data = input.val.ToRpc(_workerChannelLogger)
Data = input.val.ToRpc(_workerChannelLogger, _workerCapabilities)
});
}

Expand Down
3 changes: 3 additions & 0 deletions src/WebJobs.Script/Rpc/LanguageWorkerConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,8 @@ public static class LanguageWorkerConstants
public const string RpcHttpCookies = "cookies";
public const string RpcHttpStatusCode = "statusCode";
public const string RpcHttpStatus = "status";

// Capabilites
public const string RawHttpBodyBytes = "RawHttpBodyBytes";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static object ToObject(this TypedData typedData)
}
}

public static TypedData ToRpc(this object value, ILogger logger)
public static TypedData ToRpc(this object value, ILogger logger, Capabilities capabilities)
{
TypedData typedData = new TypedData();

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

MediaTypeHeaderValue mediaType = null;
if (MediaTypeHeaderValue.TryParse(request.ContentType, out mediaType))
{
if (string.Equals(mediaType.MediaType, "application/json", StringComparison.OrdinalIgnoreCase))
{
var jsonReader = new StreamReader(request.Body, Encoding.UTF8);
rawBody = jsonReader.ReadToEnd();
rawBodyString = jsonReader.ReadToEnd();
try
{
body = JsonConvert.DeserializeObject(rawBody);
body = JsonConvert.DeserializeObject(rawBodyString);
}
catch (JsonException)
{
body = rawBody;
body = rawBodyString;
}
}
else if (string.Equals(mediaType.MediaType, "application/octet-stream", StringComparison.OrdinalIgnoreCase) ||
mediaType.MediaType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0)
{
var length = Convert.ToInt32(request.ContentLength);
var bytes = new byte[length];
request.Body.Read(bytes, 0, length);
body = bytes;
rawBody = Encoding.UTF8.GetString(bytes);
if (!IsRawBodyBytesRequested(capabilities))
{
rawBodyString = Encoding.UTF8.GetString(bytes);
}
}
}
// default if content-tye not found or recognized
if (body == null && rawBody == null)
if (body == null && rawBodyString == null)
{
var reader = new StreamReader(request.Body, Encoding.UTF8);
body = rawBody = reader.ReadToEnd();
body = rawBodyString = reader.ReadToEnd();
}

request.Body.Position = 0;
http.Body = body.ToRpc(logger);
http.RawBody = rawBody.ToRpc(logger);
http.Body = body.ToRpc(logger, capabilities);
if (IsRawBodyBytesRequested(capabilities))
{
http.RawBody = bytes.ToRpc(logger, capabilities);
}
else
{
http.RawBody = rawBodyString.ToRpc(logger, capabilities);
}
}
}
else
Expand All @@ -196,6 +204,20 @@ public static TypedData ToRpc(this object value, ILogger logger)
return typedData;
}

private static bool IsRawBodyBytesRequested(Capabilities capabilities)
{
return capabilities.GetCapabilityState(LanguageWorkerConstants.RawHttpBodyBytes) != null;
}

internal static byte[] RequestBodyToBytes(HttpRequest request)
{
var length = Convert.ToInt32(request.ContentLength);
var bytes = new byte[length];
request.Body.Read(bytes, 0, length);
request.Body.Position = 0;
return bytes;
}

public static BindingInfo ToBindingInfo(this BindingMetadata bindingMetadata)
{
BindingInfo bindingInfo = new BindingInfo
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Text;
using Google.Protobuf.Collections;
using Google.Protobuf.WellKnownTypes;
using Microsoft.AspNetCore.Http;
Expand All @@ -18,17 +21,20 @@ namespace Microsoft.Azure.WebJobs.Script.Tests.Rpc
{
public class RpcMessageConversionExtensionsTests
{
private static readonly string TestImageLocation = "Rpc\\Resources\\functions.png";

[Theory]
[InlineData("application/x-www-form-urlencoded’", "say=Hi&to=Mom")]
public void HttpObjects_StringBody(string expectedContentType, object body)
{
var logger = MockNullLoggerFactory.CreateLogger();
var capabilities = new Capabilities(logger);

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

var rpcRequestObject = request.ToRpc(logger);
var rpcRequestObject = request.ToRpc(logger, capabilities);
Assert.Equal(body.ToString(), rpcRequestObject.Http.Body.String);

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

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

var rpcRequestObject = request.ToRpc(logger);
var rpcRequestObject = request.ToRpc(logger, capabilities);
// Same number of expected key value pairs
Assert.Equal(rpcRequestObject.Http.Query.Count, expectedKeys.Length);
Assert.Equal(rpcRequestObject.Http.Query.Count, expectedValues.Length);
Expand Down Expand Up @@ -188,6 +195,7 @@ public void SetCookie_ReturnsExpectedResult(string name, string value, RpcHttpCo
public void HttpObjects_ClaimsPrincipal()
{
var logger = MockNullLoggerFactory.CreateLogger();
var capabilities = new Capabilities(logger);

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

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

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

var rpcRequestObject = request.ToRpc(logger);
var rpcRequestObject = request.ToRpc(logger, capabilities);

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

return identity;
}

[Theory]
[InlineData("application/octet-stream", "true")]
[InlineData("image/png", "true")]
[InlineData("application/octet-stream", null)]
[InlineData("image/png", null)]
public void HttpObjects_RawBodyBytes_Image_Length(string contentType, string rawBytesEnabled)
{
var logger = MockNullLoggerFactory.CreateLogger();
var capabilities = new Capabilities(logger);
if (!string.Equals(rawBytesEnabled, null))
{
capabilities.UpdateCapabilities(new MapField<string, string>
{
{ LanguageWorkerConstants.RawHttpBodyBytes, rawBytesEnabled.ToString() }
});
}

FileStream image = new FileStream(TestImageLocation, FileMode.Open, FileAccess.Read);
byte[] imageBytes = FileStreamToBytes(image);
string imageString = Encoding.UTF8.GetString(imageBytes);

long imageBytesLength = imageBytes.Length;
long imageStringLength = imageString.Length;

var headers = new HeaderDictionary();
headers.Add("content-type", contentType);
HttpRequest request = HttpTestHelpers.CreateHttpRequest("GET", "http://localhost/api/httptrigger-scenarios", headers, imageBytes);

var rpcRequestObject = request.ToRpc(logger, capabilities);
if (string.IsNullOrEmpty(rawBytesEnabled))
{
Assert.Empty(rpcRequestObject.Http.RawBody.Bytes);
Assert.Equal(imageStringLength, rpcRequestObject.Http.RawBody.String.Length);
}
else
{
Assert.Empty(rpcRequestObject.Http.RawBody.String);
Assert.Equal(imageBytesLength, rpcRequestObject.Http.RawBody.Bytes.ToByteArray().Length);
}
}

private byte[] FileStreamToBytes(FileStream file)
{
using (var memoryStream = new MemoryStream())
{
file.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
}
}
3 changes: 3 additions & 0 deletions test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
<None Update="Description\DotNet\TestFiles\PackageReferences\ProjectWithMismatchedLock\MismatchedProjectDependencies\project.assets.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Rpc\Resources\functions.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down

0 comments on commit e132a60

Please # to comment.