From c9ea73786b69597cede74c1762214efec09f67cf Mon Sep 17 00:00:00 2001 From: Klavs Madsen Date: Sat, 4 Feb 2017 17:48:49 +0100 Subject: [PATCH 1/2] Http request support Added support for accessing basic http request information --- environments/dotnet/ExecutorModule.cs | 4 +- environments/dotnet/FissionContext.cs | 62 ++++++++++++++++++++++++--- environments/dotnet/README.md | 54 +++++++++++++++++++++++ 3 files changed, 111 insertions(+), 9 deletions(-) diff --git a/environments/dotnet/ExecutorModule.cs b/environments/dotnet/ExecutorModule.cs index ef1d7fd2..311140fb 100644 --- a/environments/dotnet/ExecutorModule.cs +++ b/environments/dotnet/ExecutorModule.cs @@ -64,10 +64,10 @@ namespace Fission.DotNetCore response.StatusCode = HttpStatusCode.InternalServerError; return response; } - var args = ((DynamicDictionary)Request.Query).ToDictionary(); + try { - return _userFunc.Invoke(new FissionContext(args, new Logger())); + return _userFunc.Invoke(FissionContext.Build(Request, new Logger())); } catch (Exception e) { diff --git a/environments/dotnet/FissionContext.cs b/environments/dotnet/FissionContext.cs index 977444c7..a2dbeee0 100644 --- a/environments/dotnet/FissionContext.cs +++ b/environments/dotnet/FissionContext.cs @@ -1,46 +1,66 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using Nancy; namespace Fission.DotNetCore.Api { public class FissionContext { - public FissionContext(Dictionary args, Logger logger) + public FissionContext(Dictionary args, Logger logger, FissionHttpRequest request) { if (args == null) throw new ArgumentNullException(nameof(args)); if (logger == null) throw new ArgumentNullException(nameof(logger)); + if (request == null) throw new ArgumentNullException(nameof(request)); Arguments = args; Logger = logger; + Request = request; } public Dictionary Arguments { get; private set; } + + public FissionHttpRequest Request { get; private set; } + public Logger Logger { get; private set; } + + public static FissionContext Build(Request request, Logger logger) + { + return new FissionContext(((DynamicDictionary)request.Query).ToDictionary(), + logger, + new FissionHttpRequest(request)); + } } public class Logger { public void Write(Severity severity, string format, params object[] args) { - Console.WriteLine($"{DateTime.Now.ToString("MM/dd/yy H:mm:ss zzz")} {severity}: " + format, args); + Console.WriteLine($"{DateTime.Now.ToString("MM/dd/yy H:mm:ss zzz")} {severity}: " + format, args); } - public void WriteInfo(string format, params object[] args){ + public void WriteInfo(string format, params object[] args) + { Write(Severity.Info, format, args); } - public void WriteWarning(string format, params object[] args){ + public void WriteWarning(string format, params object[] args) + { Write(Severity.Warning, format, args); } - public void WriteError(string format, params object[] args){ + public void WriteError(string format, params object[] args) + { Write(Severity.Error, format, args); } - public void WriteCritical(string format, params object[] args){ + public void WriteCritical(string format, params object[] args) + { Write(Severity.Critical, format, args); } - public void WriteVerbose(string format, params object[] args){ + public void WriteVerbose(string format, params object[] args) + { Write(Severity.Verbose, format, args); } } @@ -53,4 +73,32 @@ namespace Fission.DotNetCore.Api Critical, Verbose } + + public class FissionHttpRequest + { + private readonly Request _request; + internal FissionHttpRequest(Request request) + { + if (request == null) throw new ArgumentNullException(nameof(request)); + _request = request; + } + + public Stream Body { get { return _request.Body; } } + public Dictionary> Headers + { + get + { + var headers = new Dictionary>(); + foreach (var kv in _request.Headers) + { + headers.Add(kv.Key, kv.Value); + } + return headers; + } + } + + public X509Certificate Certificate { get { return _request.ClientCertificate; } } + public string Url { get { return _request.Url.ToString(); } } + public string Method { get { return _request.Method; } } + } } \ No newline at end of file diff --git a/environments/dotnet/README.md b/environments/dotnet/README.md index 9f012558..f3869862 100644 --- a/environments/dotnet/README.md +++ b/environments/dotnet/README.md @@ -163,6 +163,60 @@ $ curl "http://$FISSION_ROUTER/add?x=30&y=12" 42 ``` +## Accessing Http request information example + +### Setup fission environment +First you need to setup the fission according to your cluster setup as +specified here: https://github.com/fission/fission + + +### Create the class to run + +Secondly you need to create a file /tmp/func.cs containing the following code: + +``` +using System; +using Fission.DotNetCore.Api; + +public class FissionFunction +{ + public string Execute(FissionContext context){ + var buffer = new System.Text.StringBuilder(); + foreach(var header in context.Request.Headers){ + buffer.AppendLine(header.Key); + foreach(var item in header.Value){ + buffer.AppendLine($"\t{item}"); + } + } + buffer.AppendLine($"Url: {context.Request.Url}, method: {context.Request.Method}"); + return buffer.ToString(); + } +} + +``` +### Run the example + +Lastly to run the example: + +``` +$ fission env create --name dotnet --image fission/dotnet-env + +$ fission function create --name httpinfo --env dotnet --code /tmp/func.cs + +$ fission route create --method GET --url /http_info --function httpinfo + +$ curl "http://$FISSION_ROUTER/http_info" +Accept + */*;q=1 +Host + fissionserver:8888 +User-Agent + curl/7.47.0 +Url: http://fissionserver:8888, method: GET + +``` + + ## Developing/debugging the enviroment locally The easiest way to debug the environment is to open the directory in From a1d255f03855c33375f87e799bead332fa7d5116 Mon Sep 17 00:00:00 2001 From: Klavs Madsen Date: Sun, 5 Feb 2017 12:57:09 +0100 Subject: [PATCH 2/2] Added usage examples * Cleaned up project.json * Updated README with example on how to deserialize json request body * Added reference to System.Runtime.Serialization.Json as this would seems a common usage * Added C# example files to the /Examples directory --- environments/dotnet/FissionCompiler.cs | 3 +- environments/dotnet/FissionContext.cs | 10 +++++ environments/dotnet/README.md | 56 +++++++++++++++++++++++++- environments/dotnet/project.json | 40 +++++++++--------- examples/dotnet/arguments.cs | 11 +++++ examples/dotnet/helloworld.cs | 9 +++++ examples/dotnet/requestbody.cs | 24 +++++++++++ examples/dotnet/requestheaders.cs | 17 ++++++++ 8 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 examples/dotnet/arguments.cs create mode 100644 examples/dotnet/helloworld.cs create mode 100644 examples/dotnet/requestbody.cs create mode 100644 examples/dotnet/requestheaders.cs diff --git a/environments/dotnet/FissionCompiler.cs b/environments/dotnet/FissionCompiler.cs index 99f236cd..f4dc4ee6 100644 --- a/environments/dotnet/FissionCompiler.cs +++ b/environments/dotnet/FissionCompiler.cs @@ -26,7 +26,8 @@ namespace Fission.DotNetCore.Compiler { MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "mscorlib.dll"), MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location), - MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location) + MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location), + MetadataReference.CreateFromFile(typeof(System.Runtime.Serialization.Json.DataContractJsonSerializer).GetTypeInfo().Assembly.Location) }; foreach (var referencedAssembly in Assembly.GetEntryAssembly().GetReferencedAssemblies()) diff --git a/environments/dotnet/FissionContext.cs b/environments/dotnet/FissionContext.cs index a2dbeee0..b4117531 100644 --- a/environments/dotnet/FissionContext.cs +++ b/environments/dotnet/FissionContext.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography.X509Certificates; +using System.Text; using Nancy; namespace Fission.DotNetCore.Api @@ -84,6 +85,15 @@ namespace Fission.DotNetCore.Api } public Stream Body { get { return _request.Body; } } + + public string BodyAsString() + { + int length = (int)_request.Body.Length; + byte[] data = new byte[length]; + _request.Body.Read(data, 0, length); + return Encoding.UTF8.GetString(data); + } + public Dictionary> Headers { get diff --git a/environments/dotnet/README.md b/environments/dotnet/README.md index f3869862..3e9829d6 100644 --- a/environments/dotnet/README.md +++ b/environments/dotnet/README.md @@ -163,7 +163,7 @@ $ curl "http://$FISSION_ROUTER/add?x=30&y=12" 42 ``` -## Accessing Http request information example +## Accessing http request information example ### Setup fission environment First you need to setup the fission according to your cluster setup as @@ -216,6 +216,60 @@ Url: http://fissionserver:8888, method: GET ``` +## Accessing http request body example + +### Setup fission environment +First you need to setup the fission according to your cluster setup as +specified here: https://github.com/fission/fission + + +### Create the class to run + +Secondly you need to create a file /tmp/func.cs containing the following code: + +``` +using System.IO; +using System.Runtime.Serialization.Json; +using Fission.DotNetCore.Api; + +public class FissionFunction +{ + public string Execute(FissionContext context) + { + var person = Person.Deserialize(context.Request.Body); + return $"Hello, my name is {person.Name} and I am {person.Age} years old."; + } +} + +public class Person +{ + public string Name { get; set; } + public int Age { get; set; } + + public static Person Deserialize(Stream json) + { + var serializer = new DataContractJsonSerializer(typeof(Person)); + return (Person)serializer.ReadObject(json); + } +} + +``` +### Run the example + +Lastly to run the example: + +``` +$ fission env create --name dotnet --image fission/dotnet-env + +$ fission function create --name httpbody --env dotnet --code /tmp/func.cs + +$ fission route create --method GET --url /http_body --function httpbody + +$ curl -XPOST "http://$FISSION_ROUTER/http_body" -d '{ "Name":"Arthur", "Age":42}' +Hello, my name is Arthur and I am 42 years old. + +``` + ## Developing/debugging the enviroment locally diff --git a/environments/dotnet/project.json b/environments/dotnet/project.json index 2c1ea729..d4b6cb83 100755 --- a/environments/dotnet/project.json +++ b/environments/dotnet/project.json @@ -1,24 +1,24 @@ { - "version": "1.0.0-*", - "buildOptions": { - "emitEntryPoint": true, - "outputName": "fission-dotnet" - }, - - "dependencies": { - "Microsoft.NETCore.App": { - "version": "1.1.0", - "type": "platform" - }, - "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", - "Microsoft.AspNetCore.Owin": "1.0.0", - "Nancy": "2.0.0-barneyrubble", - "Microsoft.CodeAnalysis.CSharp": "1.3.0-*", - "System.Runtime.Loader": "4.0.0-*", + "version": "1.0.0-*", + "buildOptions": { + "emitEntryPoint": true, + "outputName": "fission-dotnet" }, - "frameworks": { - "netcoreapp1.0": { - "imports": "dnxcore50" + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.1.0", + "type": "platform" + }, + "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", + "Microsoft.AspNetCore.Owin": "1.0.0", + "Nancy": "2.0.0-barneyrubble", + "Microsoft.CodeAnalysis.CSharp": "1.3.0-*", + "System.Runtime.Loader": "4.0.0-*", + "System.Runtime.Serialization.Json": "4.0.2" + }, + "imports": "netstandard1.3" + } } - } } \ No newline at end of file diff --git a/examples/dotnet/arguments.cs b/examples/dotnet/arguments.cs new file mode 100644 index 00000000..a39e0421 --- /dev/null +++ b/examples/dotnet/arguments.cs @@ -0,0 +1,11 @@ +using System; +using Fission.DotNetCore.Api; + +public class FissionFunction +{ + public string Execute(FissionContext context){ + var x = Convert.ToInt32(context.Arguments["x"]); + var y = Convert.ToInt32(context.Arguments["y"]); + return (x+y).ToString(); + } + diff --git a/examples/dotnet/helloworld.cs b/examples/dotnet/helloworld.cs new file mode 100644 index 00000000..cae5a7e9 --- /dev/null +++ b/examples/dotnet/helloworld.cs @@ -0,0 +1,9 @@ +using Fission.DotNetCore.Api; + +public class FissionFunction +{ + public string Execute(FissionContext context) + { + return "Hello World!"; + } +} diff --git a/examples/dotnet/requestbody.cs b/examples/dotnet/requestbody.cs new file mode 100644 index 00000000..c4be5f44 --- /dev/null +++ b/examples/dotnet/requestbody.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Runtime.Serialization.Json; +using Fission.DotNetCore.Api; + +public class FissionFunction +{ + public string Execute(FissionContext context) + { + var person = Person.Deserialize(context.Request.Body); + return $"Hello, my name is {person.Name} and I am {person.Age} years old."; + } +} + +public class Person +{ + public string Name { get; set; } + public int Age { get; set; } + + public static Person Deserialize(Stream json) + { + var serializer = new DataContractJsonSerializer(typeof(Person)); + return (Person)serializer.ReadObject(json); + } +} diff --git a/examples/dotnet/requestheaders.cs b/examples/dotnet/requestheaders.cs new file mode 100644 index 00000000..8e44f2f7 --- /dev/null +++ b/examples/dotnet/requestheaders.cs @@ -0,0 +1,17 @@ +using System; +using Fission.DotNetCore.Api; + +public class FissionFunction +{ + public string Execute(FissionContext context){ + var buffer = new System.Text.StringBuilder(); + foreach(var header in context.Request.Headers){ + buffer.AppendLine(header.Key); + foreach(var item in header.Value){ + buffer.AppendLine($"\t{item}"); + } + } + buffer.AppendLine($"Url: {context.Request.Url}, method: {context.Request.Method}"); + return buffer.ToString(); + } +}