diff --git a/dotnet8/Dockerfile b/dotnet8/Dockerfile
new file mode 100644
index 00000000..0704f190
--- /dev/null
+++ b/dotnet8/Dockerfile
@@ -0,0 +1,18 @@
+# Use the ASP.NET Core 8 base image
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
+WORKDIR /app
+
+# Use the .NET 8 SDK image for the build
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+WORKDIR /src
+COPY . .
+
+RUN dotnet restore Fission.DotNet/Fission.DotNet.csproj
+
+RUN dotnet publish Fission.DotNet/Fission.DotNet.csproj -c Release -o /app
+
+# Final image
+FROM base AS final
+WORKDIR /app
+COPY --from=build /app .
+ENTRYPOINT ["dotnet", "Fission.DotNet.dll"]
diff --git a/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj b/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj
new file mode 100644
index 00000000..0ffdcef5
--- /dev/null
+++ b/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+ Fission.DotNet.Common
+ 1.1.0
+ Lorenzo Caldon
+ Fission dotnet environment common package
+ GPL-3.0-or-later
+ https://github.com/lcsoft77/fission-env-dotnet8
+ ./nupkg
+
+
+
+ README.md
+
+
+
+
+
+
diff --git a/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.sln b/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.sln
new file mode 100644
index 00000000..7ea97c8d
--- /dev/null
+++ b/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.sln
@@ -0,0 +1,24 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fission.DotNet.Common", "Fission.DotNet.Common.csproj", "{66CA3524-4798-6315-18DF-47CD0B936395}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {66CA3524-4798-6315-18DF-47CD0B936395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {66CA3524-4798-6315-18DF-47CD0B936395}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {66CA3524-4798-6315-18DF-47CD0B936395}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {66CA3524-4798-6315-18DF-47CD0B936395}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AF6A70F1-84DB-4983-A581-08BC54DF98A5}
+ EndGlobalSection
+EndGlobal
diff --git a/dotnet8/Fission.DotNet.Common/FissionContext.cs b/dotnet8/Fission.DotNet.Common/FissionContext.cs
new file mode 100644
index 00000000..7ffb1512
--- /dev/null
+++ b/dotnet8/Fission.DotNet.Common/FissionContext.cs
@@ -0,0 +1,72 @@
+using System.Text;
+using System.Text.Json;
+
+namespace Fission.DotNet.Common;
+
+public class FissionContext
+{
+ protected Stream _content;
+ protected Dictionary _arguments;
+ protected Dictionary _headers;
+ protected Dictionary _parameters;
+
+ public FissionContext(Stream body, Dictionary arguments, Dictionary headers, Dictionary parameters)
+ {
+ if (body == null) throw new ArgumentNullException(nameof(body));
+ if (arguments == null) throw new ArgumentNullException(nameof(arguments));
+ if (headers == null) throw new ArgumentNullException(nameof(headers));
+ if (parameters == null) throw new ArgumentNullException(nameof(parameters));
+
+ _content = body;
+ _arguments = arguments;
+ _headers = headers;
+ _parameters = parameters;
+ }
+
+ protected string GetHeaderValue(string key, string defaultValue = null)
+ {
+ return _headers.ContainsKey(key) ? _headers[key] : defaultValue;
+ }
+
+ public Dictionary Arguments => _arguments;
+ public Dictionary Parameters => _parameters;
+
+ public string TraceID => GetHeaderValue("traceparent", Guid.NewGuid().ToString());
+ public string FunctionName => GetHeaderValue("X-Fission-Function-Name");
+ public string Namespace => GetHeaderValue("X-Fission-Function-Namespace");
+ public string ResourceVersion => GetHeaderValue("X-Fission-Function-Resourceversion");
+ public string UID => GetHeaderValue("X-Fission-Function-Uid");
+ public string Trigger => GetHeaderValue("Source-Name");
+ public string ContentType => GetHeaderValue("Content-Type");
+ public Int32 ContentLength => GetHeaderValue("Content-Length") != null ? Int32.Parse(GetHeaderValue("Content-Length")) : 0;
+ public Stream Content => _content;
+
+ public async Task ContentAsString()
+ {
+ if (_content == null)
+ {
+ return null;
+ }
+
+ _content.Position = 0;
+ using (StreamReader reader = new StreamReader(_content, Encoding.UTF8, leaveOpen: true))
+ {
+ return await reader.ReadToEndAsync();
+ }
+ }
+
+ public async Task ContentAs(JsonSerializerOptions? options = null)
+ {
+ if (_content == null)
+ {
+ return default;
+ }
+
+ _content.Position = 0;
+ using (StreamReader reader = new StreamReader(_content, Encoding.UTF8, leaveOpen: true))
+ {
+ string content = await reader.ReadToEndAsync();
+ return JsonSerializer.Deserialize(content, options);
+ }
+ }
+}
diff --git a/dotnet8/Fission.DotNet.Common/FissionHttpContext.cs b/dotnet8/Fission.DotNet.Common/FissionHttpContext.cs
new file mode 100644
index 00000000..08ef8ec8
--- /dev/null
+++ b/dotnet8/Fission.DotNet.Common/FissionHttpContext.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Text;
+using System.Text.Json;
+
+namespace Fission.DotNet.Common;
+
+public class FissionHttpContext : FissionContext
+{
+ private string _method;
+
+ public FissionHttpContext(Stream body, string method, Dictionary arguments, Dictionary headers, Dictionary parameters) : base(body, arguments, headers, parameters)
+ {
+ _method = method;
+ }
+
+ public Dictionary Headers => _headers;
+ public string Url
+ {
+ get
+ {
+ var urlHeader = GetHeaderValue("X-Fission-Full-Url");
+
+ if (urlHeader != null)
+ {
+ if (urlHeader.Contains("?"))
+ {
+ urlHeader = urlHeader.Substring(0, urlHeader.IndexOf("?"));
+ }
+
+ return urlHeader;
+ }
+ else
+ {
+ return "/";
+ }
+ }
+ }
+ public string Method => _method;
+ public string Host => GetHeaderValue("X-Forwarded-Host");
+ public int Port => _headers.ContainsKey("X-Forwarded-Port") ? Int32.Parse(GetHeaderValue("X-Forwarded-Port")) : 0;
+ public string UserAgent => GetHeaderValue("User-Agent");
+}
diff --git a/dotnet8/Fission.DotNet.Common/FissionMqContext.cs b/dotnet8/Fission.DotNet.Common/FissionMqContext.cs
new file mode 100644
index 00000000..8ce818a6
--- /dev/null
+++ b/dotnet8/Fission.DotNet.Common/FissionMqContext.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Fission.DotNet.Common;
+
+public class FissionMqContext : FissionContext
+{
+ public FissionMqContext(Stream body, Dictionary arguments, Dictionary headers, Dictionary parameters) : base(body, arguments, headers, parameters)
+ {
+
+ }
+
+ public string Topic => GetHeaderValue("Topic");
+ public string ErrorTopic => GetHeaderValue("Errortopic");
+ public string ResponseTopic => GetHeaderValue("Resptopic");
+}
diff --git a/dotnet8/Fission.DotNet.Common/ICorsPolicy.cs b/dotnet8/Fission.DotNet.Common/ICorsPolicy.cs
new file mode 100644
index 00000000..7f10a326
--- /dev/null
+++ b/dotnet8/Fission.DotNet.Common/ICorsPolicy.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Fission.DotNet.Common
+{
+ public interface ICorsPolicy
+ {
+ void AllowAnyOrigin();
+ void AllowAnyHeader();
+ void AllowAnyMethod();
+ void AllowCredentials();
+
+ void WithOrigin(string[] origin);
+ void WithHeader(string[] header);
+ void WithMethod(string[] method);
+ }
+}
diff --git a/dotnet8/Fission.DotNet.Common/ILogger.cs b/dotnet8/Fission.DotNet.Common/ILogger.cs
new file mode 100644
index 00000000..0b58d4f8
--- /dev/null
+++ b/dotnet8/Fission.DotNet.Common/ILogger.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Fission.DotNet.Common
+{
+ public interface ILogger
+ {
+ void LogInformation(string message);
+ void LogDebug(string message);
+ void LogWarning(string message);
+ void LogError(string message);
+ void LogCritical(string message);
+ void LogError(string message, Exception exception);
+ }
+}
\ No newline at end of file
diff --git a/dotnet8/Fission.DotNet.Common/README.md b/dotnet8/Fission.DotNet.Common/README.md
new file mode 100644
index 00000000..973384fd
--- /dev/null
+++ b/dotnet8/Fission.DotNet.Common/README.md
@@ -0,0 +1,46 @@
+# Fission.DotNet.Common
+
+This project is a common library for the .NET environment of [Fission](https://fission.io/), a serverless plugin for Kubernetes. The library provides common functionalities and utilities to facilitate the development of serverless functions with Fission on .NET.
+
+## Main Features
+
+- **Common Utilities**: Provides a set of common utilities to simplify the development of serverless functions.
+- **.NET 8 Compatibility**: Designed to work with **.NET 8 on Linux**, offering an updated and improved experience.
+- **Multi-Assembly Management**: Supports projects composed of multiple linked assemblies, simplifying the deployment and integration process.
+
+## Inspiration
+
+This project is inspired by the official Fission environment for .NET Core 2.0 but focuses on improvements and updates requested by the community, allowing developers to work with newer versions of the .NET framework and complex projects that include multiple assemblies.
+
+## Usage
+
+1. **Add the library to your project**:
+ Add the NuGet package `Fission.DotNet.Common` to your .NET project.
+
+ ```bash
+ dotnet add package Fission.DotNet.Common
+
+2. **Create the project**:
+- Create a **class library project** in .NET.
+- Add the NuGet package `Fission.DotNet.Common` to your project.
+- Create a class with the following function:
+
+ ```csharp
+ using Fission.DotNet.Common;
+
+ public class MyFunction
+ {
+ public object Execute(FissionContext input)
+ {
+ return "Hello World";
+ }
+ }
+ ```
+3. **Compression**: Compress the assemblies and related files into a ZIP file.
+
+4. **Deploy to Fission**: Use this library to deploy your project to Fission, leveraging the ability to manage multiple linked assemblies. After compressing your project into a ZIP file, you can create the function in Fission with the following command:
+
+ ```bash
+ fission fn create --name --env dotnet8 --code --entrypoint ::
+ ```
+ Replace `` with the name of your function and `` with the path to your ZIP file.
\ No newline at end of file
diff --git a/dotnet8/Fission.DotNet/Adapter/FissionLoggerAdapter.cs b/dotnet8/Fission.DotNet/Adapter/FissionLoggerAdapter.cs
new file mode 100644
index 00000000..cca1072a
--- /dev/null
+++ b/dotnet8/Fission.DotNet/Adapter/FissionLoggerAdapter.cs
@@ -0,0 +1,44 @@
+using System;
+using Fission.DotNet.Common;
+
+namespace Fission.DotNet.Adapter;
+
+public class FissionLoggerAdapter : Common.ILogger
+{
+ private Microsoft.Extensions.Logging.ILogger _logger;
+
+ public FissionLoggerAdapter(Microsoft.Extensions.Logging.ILogger logger)
+ {
+ this._logger = logger;
+ }
+
+ public void LogCritical(string message)
+ {
+ _logger.LogCritical(message);
+ }
+
+ public void LogDebug(string message)
+ {
+ _logger.LogDebug(message);
+ }
+
+ public void LogError(string message, Exception exception)
+ {
+ _logger.LogError(exception, message);
+ }
+
+ public void LogError(string message)
+ {
+ _logger.LogError(message);
+ }
+
+ public void LogInformation(string message)
+ {
+ _logger.LogInformation(message);
+ }
+
+ public void LogWarning(string message)
+ {
+ _logger.LogWarning(message);
+ }
+}
diff --git a/dotnet8/Fission.DotNet/Controllers/FunctionController.cs b/dotnet8/Fission.DotNet/Controllers/FunctionController.cs
new file mode 100644
index 00000000..f282c6c4
--- /dev/null
+++ b/dotnet8/Fission.DotNet/Controllers/FunctionController.cs
@@ -0,0 +1,194 @@
+using System.Text;
+using Fission.DotNet.Common;
+using Microsoft.AspNetCore.Mvc;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Loader;
+using Fission.DotNet.Interfaces;
+using System.Threading.Tasks.Dataflow;
+using Fission.DotNet.Services;
+
+namespace Fission.DotNet.Controllers
+{
+ [ApiController]
+ [Route("/")]
+ public class FunctionController : Controller
+ {
+ private readonly ILogger _logger;
+ private readonly IFunctionService _functionService;
+
+ public FunctionController(ILogger logger, IFunctionService functionService)
+ {
+ this._logger = logger;
+ this._functionService = functionService;
+ }
+ ///
+ /// Handle HTTP GET requests by forwarding to the Fission function.
+ ///
+ /// The function return value.
+ [HttpGet]
+ public async Task Get() => await this.Run(Request);
+
+ ///
+ /// Handle HTTP POST requests by forwarding to the Fission function.
+ ///
+ /// The function return value.
+ [HttpPost]
+ public async Task Post() => await this.Run(Request);
+
+ ///
+ /// Handle HTTP PUT requests by forwarding to the Fission function.
+ ///
+ /// The function return value.
+ [HttpPut]
+ public async Task Put() => await this.Run(Request);
+
+ ///
+ /// Handle HTTP HEAD requests by forwarding to the Fission function.
+ ///
+ /// The function return value.
+ [HttpHead]
+ public async Task Head() => await this.Run(Request);
+
+ ///
+ /// Handle HTTP OPTIONS requests by forwarding to the Fission function.
+ ///
+ /// The function return value.
+ [HttpOptions]
+ public async Task Options() => await this.Run(Request);
+
+ ///
+ /// Handle HTTP DELETE requests by forwarding to the Fission function.
+ ///
+ /// The function return value.
+ [HttpDelete]
+ public async Task Delete() => await this.Run(Request);
+
+ ///
+ /// Invokes the Fission function on behalf of the caller.
+ ///
+ ///
+ /// 200 OK with the Fission function return value; or 400 Bad Request with the exception message if an exception
+ /// occurred in the Fission function; or 500 Internal Server Error if the environment container has not yet been
+ /// specialized.
+ ///
+ private async Task Run(HttpRequest request)
+ {
+ _logger.LogInformation("FunctionController.Run");
+
+ _functionService.Load();
+ try
+ {
+ if ((request.Method == "OPTIONS") && (request.Headers.ContainsKey("X-Forwarded-Proto")))
+ {
+ var corsPolicy = _functionService.GetCorsPolicy();
+ var headers = (corsPolicy as CorsPolicy).GetCorsHeaders();
+ foreach (var header in headers)
+ {
+ Response.Headers.Add(header.Key, header.Value);
+ }
+ /*Response.Headers.Add("Access-Control-Allow-Origin", "*");
+ Response.Headers.Add("Access-Control-Allow-Methods", "*");
+ Response.Headers.Add("Access-Control-Allow-Headers", "*");
+ Response.Headers.Add("Access-Control-Allow-Credentials", "true");
+ Response.Headers.Add("Access-Control-Expose-Headers", "*");*/
+ return Ok();
+ }
+
+ // Copy the body to a MemoryStream so it can be read again
+ request.EnableBuffering();
+ using (var memoryStream = new MemoryStream())
+ {
+ await request.Body.CopyToAsync(memoryStream);
+ memoryStream.Seek(0, SeekOrigin.Begin);
+
+ request.Body = memoryStream;
+
+
+ Fission.DotNet.Common.FissionContext context = null;
+ try
+ {
+ var httpArgs = request.Query.ToDictionary(x => x.Key, x => (object)x.Value);
+ var headers = request.Headers.ToDictionary(x => x.Key, x => (string)x.Value);
+ var parameters = new Dictionary();
+
+ // Extract headers that start with "X-Fission-Params-"
+ foreach (var header in request.Headers)
+ {
+ if (header.Key.StartsWith("X-Fission-Params-"))
+ {
+ var key = header.Key.Substring("X-Fission-Params-".Length);
+ var value = header.Value.ToString();
+ parameters[key] = value;
+ }
+ }
+
+ if (request.Headers.ContainsKey("Topic"))
+ {
+ _logger.LogInformation("FunctionController.Run: FissionMqContext");
+ context = new Fission.DotNet.Common.FissionMqContext(request.Body, httpArgs, headers, parameters);
+ }
+ else
+ {
+ if (request.Headers.ContainsKey("X-Forwarded-Proto"))
+ {
+ _logger.LogInformation("FunctionController.Run: FissionHttpContext");
+ context = new Fission.DotNet.Common.FissionHttpContext(request.Body, request.Method, httpArgs, headers, parameters);
+ }
+ else
+ {
+ _logger.LogInformation("FunctionController.Run: FissionContext");
+ context = new Fission.DotNet.Common.FissionContext(request.Body, httpArgs, headers, parameters);
+ }
+ }
+
+ if (context is FissionHttpContext)
+ {
+ _logger.LogInformation($"Body stream: {context.Content.Position}-{context.Content.Length}");
+ //var body = await (context as FissionHttpContext).ContentAsString();
+
+ _logger.LogInformation($"Request Method: {(context as FissionHttpContext).Method}");
+ //_logger.LogInformation($"Request Body: {body}");
+ _logger.LogInformation($"Request Arguments: {string.Join(", ", (context as FissionHttpContext).Arguments.Select(x => $"{x.Key}={x.Value}"))}");
+ }
+ _logger.LogInformation($"Request Method: {request.Method}");
+ _logger.LogInformation($"Request Headers: {string.Join(", ", request.Headers.Select(x => $"{x.Key}={x.Value}"))}");
+ _logger.LogInformation($"Request Query: {string.Join(", ", request.Query.Select(x => $"{x.Key}={x.Value}"))}");
+
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "FunctionController.Run");
+ return BadRequest(ex.Message);
+ }
+
+ try
+ {
+ var result = await _functionService.Execute(context);
+ if (context is FissionHttpContext)
+ {
+ var corsPolicy = _functionService.GetCorsPolicy();
+ var headers = (corsPolicy as CorsPolicy).GetRequestCorsHeaders();
+ foreach (var header in headers)
+ {
+ Response.Headers.Add(header.Key, header.Value);
+ }
+ }
+ //Response.Headers.Add("Access-Control-Allow-Origin", "*");
+ return Ok(result);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "FunctionController.Run");
+ return BadRequest(ex.Message);
+ }
+ }
+ }
+ finally
+ {
+ _functionService.Unload();
+ }
+ }
+ }
+}
+
diff --git a/dotnet8/Fission.DotNet/Controllers/SpecializeController.cs b/dotnet8/Fission.DotNet/Controllers/SpecializeController.cs
new file mode 100644
index 00000000..967502c3
--- /dev/null
+++ b/dotnet8/Fission.DotNet/Controllers/SpecializeController.cs
@@ -0,0 +1,57 @@
+using System.Text.Json;
+using Microsoft.AspNetCore.Mvc;
+using Fission.DotNet.Interfaces;
+using Fission.DotNet.Model;
+
+namespace Fission.DotNet
+{
+ public class SpecializeController : Controller
+ {
+ private readonly ILogger logger;
+ private readonly ISpecializeService specializeService;
+
+ public SpecializeController(ILogger logger, ISpecializeService specializeService)
+ {
+ this.logger = logger;
+ this.specializeService = specializeService;
+ }
+
+ // GET: SpecializeController
+ [HttpPost, Route("/specialize")]
+ public Task