Skip to content

[Under a feature flag] Removing duplicates #4660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 31, 2019
Merged
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 @@ -62,7 +62,7 @@ protected override async Task<object> InvokeCore(object[] parameters, FunctionIn
ScriptInvocationContext invocationContext = new ScriptInvocationContext()
{
FunctionMetadata = Metadata,
BindingData = context.Binder.BindingData,
BindingData = context.Binder.BindingData, // This has duplicates too (of type DefaultHttpRequest). Needs to be removed after verifying this can indeed be constructed by the workers from the rest of the data being passed (https://github.com/Azure/azure-functions-host/issues/4735).
ExecutionContext = context.ExecutionContext,
Inputs = inputs,
ResultSource = new TaskCompletionSource<ScriptInvocationResult>(),
Expand Down
1 change: 1 addition & 0 deletions src/WebJobs.Script/Rpc/LanguageWorkerConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ public static class LanguageWorkerConstants
// Capabilites
public const string RawHttpBodyBytes = "RawHttpBodyBytes";
public const string TypedDataCollection = "TypedDataCollection";
public const string RpcHttpBodyOnly = "RpcHttpBodyOnly";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,56 +183,94 @@ internal static TypedData ToRpcHttp(this HttpRequest request, ILogger logger, Ca
// parse request body as content-type
if (request.Body != null && request.ContentLength > 0)
{
object body = null;
string rawBodyString = null;
byte[] bytes = RequestBodyToBytes(request);
if (IsBodyOnlySupported(capabilities))
{
PopulateBody(request, http, capabilities, logger);
}
else
{
PopulateBodyAndRawBody(request, http, capabilities, logger);
}
}

return typedData;
}

private static void PopulateBody(HttpRequest request, RpcHttp http, Capabilities capabilities, ILogger logger)
{
object body = null;
if ((MediaTypeHeaderValue.TryParse(request.ContentType, out MediaTypeHeaderValue mediaType) && IsMediaTypeOctetOrMultipart(mediaType)) || IsRawBodyBytesRequested(capabilities))
{
body = RequestBodyToBytes(request);
}
else
{
body = GetStringRepresentationOfBody(request);
}
http.Body = body.ToRpc(logger, capabilities);
}

MediaTypeHeaderValue mediaType = null;
if (MediaTypeHeaderValue.TryParse(request.ContentType, out mediaType))
private static string GetStringRepresentationOfBody(HttpRequest request)
{
string result;
using (StreamReader reader = new StreamReader(request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true))
{
result = reader.ReadToEnd();
}
request.Body.Position = 0;
return result;
}

private static void PopulateBodyAndRawBody(HttpRequest request, RpcHttp http, Capabilities capabilities, ILogger logger)
{
object body = null;
string rawBodyString = null;
byte[] bytes = RequestBodyToBytes(request);

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

request.Body.Position = 0;
http.Body = body.ToRpc(logger, capabilities);
if (IsRawBodyBytesRequested(capabilities))
{
http.RawBody = bytes.ToRpc(logger, capabilities);
}
else
{
http.RawBody = rawBodyString.ToRpc(logger, capabilities);
}
http.Body = body.ToRpc(logger, capabilities);
if (IsRawBodyBytesRequested(capabilities))
{
http.RawBody = bytes.ToRpc(logger, capabilities);
}
else
{
http.RawBody = rawBodyString.ToRpc(logger, capabilities);
}
}

return typedData;
private static bool IsMediaTypeOctetOrMultipart(MediaTypeHeaderValue mediaType)
{
return mediaType != null && (string.Equals(mediaType.MediaType, "application/octet-stream", StringComparison.OrdinalIgnoreCase) ||
mediaType.MediaType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0);
}

internal static TypedData ToRpcDefault(this object value)
Expand Down Expand Up @@ -314,6 +352,11 @@ private static bool IsRawBodyBytesRequested(Capabilities capabilities)
return !string.IsNullOrEmpty(capabilities.GetCapabilityState(LanguageWorkerConstants.RawHttpBodyBytes));
}

private static bool IsBodyOnlySupported(Capabilities capabilities)
{
return !string.IsNullOrEmpty(capabilities.GetCapabilityState(LanguageWorkerConstants.RpcHttpBodyOnly));
}

private static bool IsTypedDataCollectionSupported(Capabilities capabilities)
{
return !string.IsNullOrEmpty(capabilities.GetCapabilityState(LanguageWorkerConstants.TypedDataCollection));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Text;
using Google.Protobuf;
Expand All @@ -26,18 +25,112 @@ 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)
[InlineData("application/x-www-form-urlencoded’", "say=Hi&to=Mom", true)]
[InlineData("application/x-www-form-urlencoded’", "say=Hi&to=Mom", false)]
public void HttpObjects_StringBody(string expectedContentType, object body, bool rcpHttpBodyOnly)
{
var logger = MockNullLoggerFactory.CreateLogger();
var capabilities = new Capabilities(logger);
if (rcpHttpBodyOnly)
{
capabilities.UpdateCapabilities(new MapField<string, string>
{
{ LanguageWorkerConstants.RpcHttpBodyOnly, rcpHttpBodyOnly.ToString() }
});
}

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, capabilities);
Assert.Equal(body.ToString(), rpcRequestObject.Http.Body.String);
if (rcpHttpBodyOnly)
{
Assert.Equal(null, rpcRequestObject.Http.RawBody);
Assert.Equal(body.ToString(), rpcRequestObject.Http.Body.String);
}
else
{
Assert.Equal(body.ToString(), rpcRequestObject.Http.RawBody.String);
Assert.Equal(body.ToString(), rpcRequestObject.Http.Body.String);
}

string contentType;
rpcRequestObject.Http.Headers.TryGetValue("content-type", out contentType);
Assert.Equal(expectedContentType, contentType);
}

[Theory]
[InlineData("application/json", "{\"name\":\"John\"}", true)]
[InlineData("application/json", "{\"name\":\"John\"}", false)]
public void HttpObjects_JsonBody(string expectedContentType, string body, bool rcpHttpBodyOnly)
{
var logger = MockNullLoggerFactory.CreateLogger();
var capabilities = new Capabilities(logger);
if (rcpHttpBodyOnly)
{
capabilities.UpdateCapabilities(new MapField<string, string>
{
{ LanguageWorkerConstants.RpcHttpBodyOnly, rcpHttpBodyOnly.ToString() }
});
}

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, capabilities);
if (rcpHttpBodyOnly)
{
Assert.Equal(null, rpcRequestObject.Http.RawBody);
Assert.Equal(body.ToString(), rpcRequestObject.Http.Body.String);
}
else
{
Assert.Equal(body.ToString(), rpcRequestObject.Http.RawBody.String);
Assert.Equal(JsonConvert.DeserializeObject(body), JsonConvert.DeserializeObject(rpcRequestObject.Http.Body.Json));
}

string contentType;
rpcRequestObject.Http.Headers.TryGetValue("content-type", out contentType);
Assert.Equal(expectedContentType, contentType);
}

[Theory]
[InlineData("application/octet-stream", true)]
[InlineData("application/octet-stream", false)]
[InlineData("multipart/form-data; boundary=----WebKitFormBoundaryTYtz7wze2XXrH26B", true)]
[InlineData("multipart/form-data; boundary=----WebKitFormBoundaryTYtz7wze2XXrH26B", false)]
public void HttpTrigger_Post_ByteArray(string expectedContentType, bool rcpHttpBodyOnly)
{
var logger = MockNullLoggerFactory.CreateLogger();
var capabilities = new Capabilities(logger);
if (rcpHttpBodyOnly)
{
capabilities.UpdateCapabilities(new MapField<string, string>
{
{ LanguageWorkerConstants.RpcHttpBodyOnly, rcpHttpBodyOnly.ToString() }
});
}

var headers = new HeaderDictionary();
headers.Add("content-type", expectedContentType);
byte[] body = new byte[] { 1, 2, 3, 4, 5 };

HttpRequest request = HttpTestHelpers.CreateHttpRequest("POST", "http://localhost/api/httptrigger-scenarios", headers, body);

var rpcRequestObject = request.ToRpc(logger, capabilities);
if (rcpHttpBodyOnly)
{
Assert.Equal(null, rpcRequestObject.Http.RawBody);
Assert.Equal(body, rpcRequestObject.Http.Body.Bytes);
}
else
{
Assert.Equal(body, rpcRequestObject.Http.Body.Bytes);
Assert.Equal(Encoding.UTF8.GetString(body), rpcRequestObject.Http.RawBody.String);
}

string contentType;
rpcRequestObject.Http.Headers.TryGetValue("content-type", out contentType);
Expand Down