diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index edfb8415f..f822ba417 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -190,14 +190,14 @@ jobs: $PushToAzureArgs += "--skip-duplicate" } - dotnet $PushToAzureArgs + dotnet $PushToAzureArgs || exit 1 $pushedToAzure += 1 if (!$IsPrerelease) { - dotnet $PushToGitHubArgs + dotnet $PushToGitHubArgs || exit 1 $pushedToGitHub += 1 - dotnet $PushToNugetArgs + dotnet $PushToNugetArgs || exit 1 $pushedToNuget += 1 } diff --git a/README.md b/README.md index 04de49d5b..6f4534743 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # GeneXus Standard Classes for .NET and .NET Framework GeneXus Standard Classes for .NET and .NET Framework generators. -## Build status -| Branch | Status -|---|--- -|master|[![](https://github.com/genexuslabs/dotnetclasses/workflows/Build/badge.svg?branch=master)](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Amaster) -|beta|[![](https://github.com/genexuslabs/dotnetclasses/workflows/Build/badge.svg?branch=beta)](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Abeta) +## Repo status +| Branch | Build | Security +|---|---|--- +|master|[![](https://github.com/genexuslabs/dotnetclasses/workflows/Build/badge.svg?branch=master)](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Amaster)|[![CodeQL](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml) +|beta|[![](https://github.com/genexuslabs/dotnetclasses/workflows/Build/badge.svg?branch=beta)](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Abeta)|[![CodeQL](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml/badge.svg?branch=beta)](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml) ## Modules diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props index 03e14d90e..1be77468d 100644 --- a/dotnet/Directory.Build.props +++ b/dotnet/Directory.Build.props @@ -3,7 +3,7 @@ 11.0.0.0 1 $([MSBuild]::Add($(MajorFileVersion), 100)) - 27 + 30 $(COMMIT_NUMBER) 0 $(MajorFileVersion).$(MinorFileVersion).$(PatchFileVersion) @@ -29,6 +29,8 @@ NU5105;CS0618;CS8032;CS0618;SYSLIB0021;SYSLIB0023 true True + + NU1900;NU1901;NU1902;NU1903;NU1904 diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index 16834f162..01e391f0b 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -257,6 +257,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXOtel.Diagnostics", "src\d EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreCmdTest", "test\DotNetCoreCmdTest\DotNetCoreCmdTest.csproj", "{956402BD-AC8C-426E-961B-B77B3F3EDAEB}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{46DAAFD1-FAF5-4904-8EC5-406BE04E5538}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogTest", "test\benchmarks\LogTest\LogTest.csproj", "{A1DBDCE0-4F09-445F-A202-9B260CDD46CF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessTokenController_Test", "test\NativeAccessControllerTest\AccessTokenController_Test.csproj", "{A5589382-DB6F-4450-AE2B-6C6AA1643EF1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -627,7 +633,14 @@ Global {956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Debug|Any CPU.Build.0 = Debug|Any CPU {956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Release|Any CPU.ActiveCfg = Release|Any CPU {956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Release|Any CPU.Build.0 = Release|Any CPU - + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Release|Any CPU.Build.0 = Release|Any CPU + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -751,7 +764,9 @@ Global {0FCFB078-5584-469F-92CC-61B0A6216D0D} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {A42066E8-DDB9-4767-AEFA-E6D13EFF051A} = {BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8} {956402BD-AC8C-426E-961B-B77B3F3EDAEB} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} - + {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {A1DBDCE0-4F09-445F-A202-9B260CDD46CF} = {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} + {A5589382-DB6F-4450-AE2B-6C6AA1643EF1} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj b/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj index dde00ac2a..45d27f4a7 100644 --- a/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj +++ b/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj @@ -18,4 +18,8 @@ + + + + \ No newline at end of file diff --git a/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj b/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj index 58d869618..16e8434b3 100644 --- a/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj +++ b/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj @@ -13,6 +13,7 @@ + diff --git a/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj b/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj index 5f7d6885f..f4518c1e4 100644 --- a/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj +++ b/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj @@ -6,11 +6,7 @@ Encrypt64 Decrypt64 GeneXus.Encrypt - - + + - - - - \ No newline at end of file diff --git a/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj b/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj index 281cac6cd..0c61c7593 100644 --- a/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj +++ b/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj @@ -13,8 +13,9 @@ - + + diff --git a/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj b/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj index 0d2a21dba..d57e3d3ee 100644 --- a/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj +++ b/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj @@ -12,6 +12,7 @@ + diff --git a/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj b/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj index b838905a3..e95fc82c8 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj +++ b/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj @@ -29,8 +29,9 @@ - - + + + @@ -44,15 +45,5 @@ - - - False - ..\libs\net8.0\Jayrock.dll - - - False - ..\libs\Jayrock.dll - - \ No newline at end of file diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs index 8ffe0c2de..bd3ae4a37 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs +++ b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs @@ -58,6 +58,7 @@ public GXRouting(string baseURL) ServicesGroupSetting(); ServicesFunctionsMetadata(); GetAzureDeploy(); + SvcFiles(); } static public List GetRouteController(Dictionary apiPaths, @@ -377,8 +378,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle GxContext gxContext = GxContext.CreateDefaultInstance(); gxContext.HttpContext = context; context.NewSessionCheck(); - string nspace; - Config.GetValueOf("AppMainNamespace", out nspace); + string nspace = Preferences.AppMainNamespace; String tmpController = controller; String addNspace = string.Empty; @@ -397,7 +397,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle bool privateDirExists = Directory.Exists(privateDir); GXLogging.Debug(log, $"PrivateDir:{privateDir} asssemblycontroller:{asssemblycontroller}"); - + string svcFile=null; if (privateDirExists && File.Exists(Path.Combine(privateDir, $"{asssemblycontroller.ToLower()}.grp.json"))) { controller = tmpController; @@ -412,9 +412,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle else { string controllerLower = controller.ToLower(); - string svcFile = SvcFile($"{controller}{SvcExtension}"); - if (svcFile==null) - svcFile = SvcFile($"{controllerLower}{SvcExtension}"); + svcFile = SvcFile($"{controller}{SvcExtension}"); if (File.Exists(svcFile)) { string[] controllerAssemblyQualifiedName = new string(File.ReadLines(svcFile).First().SkipWhile(c => c != '"') @@ -446,22 +444,26 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle GXLogging.Warn(log, $"Controller was not found"); return null; } - string SvcFile(string controller) + + private void SvcFiles() { - if (svcFiles == null) + svcFiles = new HashSet(new CaseInsensitiveStringEqualityComparer()); + foreach (string file in Directory.GetFiles(ContentRootPath, SvcExtensionPattern, SearchOption.AllDirectories)) { - svcFiles = new HashSet(); - foreach (string file in Directory.GetFiles(ContentRootPath, SvcExtensionPattern, SearchOption.AllDirectories)) - { - svcFiles.Add(file); - } + svcFiles.Add(file); } + } + string SvcFile(string controller) + { string fileName; - string controllerFullName = Path.Combine(ContentRootPath, controller); - if (svcFiles.TryGetValue(new FileInfo(controllerFullName).FullName, out fileName)) + string controllerFullName = new FileInfo(Path.Combine(ContentRootPath, controller)).FullName; + if (svcFiles.TryGetValue(controllerFullName, out fileName)) return fileName; else + { + GXLogging.Warn(log, "Service file not found:" + controllerFullName); return null; + } } public void ServicesGroupSetting() @@ -601,7 +603,18 @@ public string GAM } + internal class CaseInsensitiveStringEqualityComparer : IEqualityComparer + { + public bool Equals(string x, string y) + { + return string.Equals(x, y, StringComparison.OrdinalIgnoreCase); + } + public int GetHashCode(string obj) + { + return obj.ToLower().GetHashCode(); + } + } public static class AzureFeature { public const string AzureServerless = "AzureServerless"; diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs index d9eb0b14a..d072c5d27 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs +++ b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs @@ -8,6 +8,7 @@ using GeneXus.Configuration; using GeneXus.Http; using GeneXus.Mime; +using GeneXus.Procedure; using GeneXus.Utils; using Microsoft.AspNetCore.Http; @@ -37,25 +38,6 @@ public class HandlerFactory {"gxoauthuserinfo",typeof(GXOAuthUserInfo)}, {"gxoauthaccesstoken",typeof(GXOAuthAccessToken)}, {"gxmulticall",typeof(GXMultiCall)}}; - static Dictionary _aspxRewrite = new Dictionary(){ - {"oauth/access_token","gxoauthaccesstoken.aspx"}, - {"oauth/logout","gxoauthlogout.aspx"}, - {"oauth/userinfo","gxoauthuserinfo.aspx"}, - {"oauth/gam/signin","agamextauthinput.aspx"}, - {"oauth/gam/callback","agamextauthinput.aspx"}, - {"oauth/gam/access_token","agamoauth20getaccesstoken.aspx"}, - {"oauth/gam/userinfo","agamoauth20getuserinfo.aspx"}, - {"oauth/gam/signout","agamextauthinput.aspx"}, - {"saml/gam/signin","Saml2/SignIn"}, - {"saml/gam/callback","gamexternalauthenticationinputsaml20_ws.aspx"}, - {"saml/gam/signout","Saml2/Logout"}, - {"oauth/requesttokenservice","agamstsauthappgetaccesstoken.aspx"}, - {"oauth/queryaccesstoken","agamstsauthappvalidaccesstoken.aspx"}, - {"oauth/gam/v2.0/access_token","agamoauth20getaccesstoken_v20.aspx"}, - {"oauth/gam/v2.0/userinfo","agamoauth20getuserinfo_v20.aspx"}, - {"oauth/gam/v2.0/requesttokenanduserinfo","aGAMSSORestRequestTokenAndUserInfo_v20.aspx"}}; - private const string QUERYVIEWER_NAMESPACE = "QueryViewer.Services"; - private const string GXFLOW_NSPACE = "GXflow.Programs"; private static List GxNamespaces; public HandlerFactory() @@ -93,8 +75,16 @@ public async Task Invoke(HttpContext context) handler.sendAdditionalHeaders(); return Task.CompletedTask; }); - handler.ProcessRequest(context); - await Task.CompletedTask; + GXWebProcedure gxWebProc = handler as GXWebProcedure; + if (gxWebProc != null && gxWebProc.GetAsyncEnabledInternal()) + { + await gxWebProc.ProcessRequestAsync(context); + } + else + { + handler.ProcessRequest(context); + await Task.CompletedTask; + } handler.ControlOutputWriter?.Flush(); } else @@ -122,9 +112,9 @@ private static string ObjectUrl(string requestPath, string basePath) } lastSegment = CleanUploadUrlSuffix(lastSegment.TrimStart('/').ToLower()); GXLogging.Debug(log, "ObjectUrl:", lastSegment); - if (_aspxRewrite.ContainsKey(lastSegment)) + if (HttpHelper.GAMServices.ContainsKey(lastSegment)) { - return _aspxRewrite[lastSegment]; + return HttpHelper.GAMServices[lastSegment]; } return lastSegment; } @@ -167,11 +157,15 @@ public IHttpHandler GetHandler(HttpContext context, string requestType, string u string className; if (cname.StartsWith("agxpl_", StringComparison.OrdinalIgnoreCase) || cname.Equals("gxqueryviewerforsd", StringComparison.OrdinalIgnoreCase)) { - className = $"{QUERYVIEWER_NAMESPACE}.{cname}"; + className = $"{HttpHelper.QUERYVIEWER_NAMESPACE}.{cname}"; } else if (Preferences.GxpmEnabled && (cname.StartsWith("awf", StringComparison.OrdinalIgnoreCase) || cname.StartsWith("wf", StringComparison.OrdinalIgnoreCase) || cname.StartsWith("apwf", StringComparison.OrdinalIgnoreCase))) { - className = $"{GXFLOW_NSPACE}.{cname}"; + className = $"{HttpHelper.GXFLOW_NSPACE}.{cname}"; + } + else if (HttpHelper.GamServicesInternalName.Contains(cname)) + { + className = $"{HttpHelper.GAM_NSPACE}.{cname}"; } else { diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs b/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs index eea674266..84430c30a 100644 --- a/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs +++ b/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs @@ -1,3 +1,8 @@ +#if NETCORE +using GeneXus.Application; +#else +using Jayrock.Json; +#endif using System; using System.Collections.Generic; using System.Linq; @@ -11,7 +16,6 @@ using GeneXus.Procedure; using GeneXus.Services; using GeneXus.Utils; -using Jayrock.Json; namespace GeneXus.Http.WebSocket { @@ -119,8 +123,7 @@ private bool ExecuteHandler(HandlerType type, Object[] parameters) { try { - string nSpace = string.Empty; - Config.GetValueOf("AppMainNamespace", out nSpace); + string nSpace = Preferences.AppMainNamespace; GXProcedure obj = null; try { diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs new file mode 100644 index 000000000..8d53a77e4 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs @@ -0,0 +1,360 @@ +#region License, Terms and Conditions +// +// Jayrock - A JSON-RPC implementation for the Microsoft .NET Framework +// Written by Atif Aziz (atif.aziz@skybow.com) +// Copyright (c) Atif Aziz. All rights reserved. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 2.1 of the License, or (at your option) +// any later version. +// +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +#endregion + +namespace GeneXus.Application +{ + #region Imports + + using System; + using System.Collections; + using System.Globalization; + using System.Text; + using GeneXus.Utils; + + #endregion + + /// + /// An ordered sequence of values. This class also provides a number of + /// methods that can be found on a JavaScript Array for sake of parity. + /// + /// + /// + /// Public Domain 2002 JSON.org, ported to C# by Are Bjolseth (teleplan.no) + /// and re-adapted by Atif Aziz (www.raboof.com) + /// + + [ Serializable ] + internal class JArray : CollectionBase, IJayrockCompatible + { + public JArray() {} + + public JArray(IEnumerable collection) + { + foreach (object item in collection) + List.Add(item); + } + + public virtual object this[int index] + { + get { return InnerList[index]; } + set { List[index] = value; } + } + + public int Length + { + get { return Count; } + } + + public JArray Put(object value) + { + Add(value); + return this; + } + + public virtual void Add(object value) + { + List.Add(value); + } + public virtual void Add(int index, object value) + { + InnerList.Insert(index, value); + } + + public virtual void Remove(object value) + { + List.Remove(value); + } + + public virtual bool Contains(object value) + { + return List.Contains(value); + } + + public virtual int IndexOf(object value) + { + return List.IndexOf(value); + } + + public virtual bool HasValueAt(int index) + { + return this[index] != null; + } + + public virtual object GetValue(int index) + { + return GetValue(index, null); + } + + public virtual object GetValue(int index, object defaultValue) + { + object value = this[index]; + return value != null ? value : defaultValue; + } + + public virtual bool GetBoolean(int index) + { + return GetBoolean(index, false); + } + + public virtual bool GetBoolean(int index, bool defaultValue) + { + object value = GetValue(index); + if (value == null) return defaultValue; + return Convert.ToBoolean(value, CultureInfo.InvariantCulture); + } + + public virtual double GetDouble(int index) + { + return GetDouble(index, float.NaN); + } + + public virtual double GetDouble(int index, float defaultValue) + { + object value = GetValue(index); + if (value == null) return defaultValue; + return Convert.ToDouble(value, CultureInfo.InvariantCulture); + } + + public virtual int GetInt32(int index) + { + return GetInt32(index, 0); + } + + public virtual int GetInt32(int index, int defaultValue) + { + object value = GetValue(index); + if (value == null) return defaultValue; + return Convert.ToInt32(value, CultureInfo.InvariantCulture); + } + + public virtual string GetString(int index) + { + return GetString(index, string.Empty); + } + + public virtual string GetString(int index, string defaultValue) + { + object value = GetValue(index); + if (value == null) return defaultValue; + return value.ToString(); + } + + public virtual JArray GetArray(int index) + { + return (JArray) GetValue(index); + } + + public virtual JObject GetObject(int index) + { + return (JObject) GetValue(index); + } + + protected override void OnValidate(object value) + { + // + // Null values are allowed in a JSON array so don't delegate + // to the base class (CollectionBase) implementation since that + // disallows null entries by default. + // + } + + /// + /// Make an JSON external form string of this JsonArray. For + /// compactness, no unnecessary whitespace is added. + /// + /// + /// This method assumes that the data structure is acyclical. + /// + + public override string ToString() + { + return TextJsonSerializer.SerializeToJayrockCompatibleJson(this); + } + + + /// + /// Copies the elements to a new object array. + /// + + public virtual object[] ToArray() + { + return (object[]) ToArray(typeof(object)); + } + + /// + /// Copies the elements to a new array of the specified type. + /// + + public virtual Array ToArray(Type elementType) + { + return InnerList.ToArray(elementType); + } + + public virtual void Reverse() + { + InnerList.Reverse(); + } + + // + // Methods that imitate the JavaScript array methods. + // + + /// + /// Appends new elements to an array. + /// + /// + /// The new length of the array. + /// + /// + /// This method appends elements in the order in which they appear. If + /// one of the arguments is an array, it is added as a single element. + /// Use the method to join the elements from two or + /// more arrays. + /// + + public int Push(object value) + { + Add(value); + return Count; + } + + /// + /// Appends new elements to an array. + /// + /// + /// The new length of the array. + /// + /// + /// This method appends elements in the order in which they appear. If + /// one of the arguments is an array, it is added as a single element. + /// Use the method to join the elements from two or + /// more arrays. + /// + + public int Push(params object[] values) + { + if (values != null) + { + foreach (object value in values) + Push(value); + } + + return Count; + } + + /// + /// Removes the last element from an array and returns it. + /// + /// + /// If the array is empty, null is returned. + /// + + public object Pop() + { + if (Count == 0) + return null; + + object lastValue = InnerList[Count - 1]; + RemoveAt(Count - 1); + return lastValue; + } + + /// + /// Returns a new array consisting of a combination of two or more + /// arrays. + /// + + public JArray Concat(params object[] values) + { + JArray newArray = new JArray(this); + + if (values != null) + { + foreach (object value in values) + { + JArray arrayValue = value as JArray; + + if (arrayValue != null) + { + foreach (object arrayValueValue in arrayValue) + newArray.Push(arrayValueValue); + } + else + { + newArray.Push(value); + } + } + } + + return newArray; + } + + /// + /// Removes the first element from an array and returns it. + /// + + public object Shift() + { + if (Count == 0) + return null; + + object firstValue = InnerList[0]; + RemoveAt(0); + return firstValue; + } + + /// + /// Returns an array with specified elements inserted at the beginning. + /// + /// + /// The unshift method inserts elements into the start of an array, so + /// they appear in the same order in which they appear in the argument + /// list. + /// + + public JArray Unshift(object value) + { + List.Insert(0, value); + return this; + } + + /// + /// Returns an array with specified elements inserted at the beginning. + /// + /// + /// The unshift method inserts elements into the start of an array, so + /// they appear in the same order in which they appear in the argument + /// list. + /// + + public JArray Unshift(params object[] values) + { + if (values != null) + { + foreach (object value in values) + Unshift(value); + } + + return this; + } + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs new file mode 100644 index 000000000..434ad643d --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs @@ -0,0 +1,87 @@ +#region License, Terms and Conditions +// +// Jayrock - A JSON-RPC implementation for the Microsoft .NET Framework +// Written by Atif Aziz (atif.aziz@skybow.com) +// Copyright (c) Atif Aziz. All rights reserved. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 2.1 of the License, or (at your option) +// any later version. +// +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +#endregion + +namespace GeneXus.Application +{ + #region Imports + + using System; + //using System.Data.SqlTypes; + #endregion + + /// + /// Represent the one and only representation of the "null" value in JSON. + /// + + internal interface IJayrockCompatible + { + + } + [ Serializable ] + internal sealed class JNull : IJayrockCompatible + { + public static readonly JNull Value = new JNull(); + + public override string ToString() + { + return "null"; + } + + public static bool LogicallyEquals(object o) + { + // + // Equals a null reference. + // + + if (o == null) + return true; + + // + // Equals self, of course. + // + + if (o.Equals(JNull.Value)) + return true; + + // + // Equals the logical null value used in database applications. + // + + //if (Convert.IsDBNull(o)) + // return true; + + // + // Equals any type that supports logical nullability and whose + // current state is logically null. + // + + //INullable nullable = o as INullable; + + // if (nullable == null) + return false; + + //return nullable.IsNull; + } + + private JNull() {} + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs new file mode 100644 index 000000000..e1aadfc30 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs @@ -0,0 +1,123 @@ +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 2.1 of the License, or (at your option) +// any later version. +// +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +using System; +using System.Collections; +using System.Collections.Specialized; +using GeneXus.Utils; +namespace GeneXus.Application +{ + /// the implementation does internally try to remember the order in which + /// the keys were added in order facilitate human-readability as in when + /// an instance is rendered as text. + + [ Serializable ] + internal class JObject : OrderedDictionary, IJayrockCompatible + { + public JObject() {} + + /// + /// Construct a JObject from a IDictionary + /// + + public JObject(IDictionary members) + { + foreach (DictionaryEntry entry in members) + { + if (entry.Key == null) + throw new Exception("InvalidMemberException"); + + base.Add(entry.Key.ToString(), entry.Value); + } + + } + + public virtual bool HasMembers + { + get { return Count > 0; } + } + + + /// + /// Accumulate values under a key. It is similar to the Put method except + /// that if there is already an object stored under the key then a + /// JArray is stored under the key to hold all of the accumulated values. + /// If there is already a JArray, then the new value is appended to it. + /// In contrast, the Put method replaces the previous value. + /// + + public virtual JObject Accumulate(string name, object value) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + object current = base[name]; + + if (current == null) + { + Put(name, value); + } + else + { + IList values = current as IList; + + if (values != null) + { + values.Add(value); + } + else + { + values = new JArray + { + current, + value + }; + Put(name, values); + } + } + + return this; + } + + /// + /// Put a key/value pair in the JObject. If the value is null, + /// then the key will be removed from the JObject if it is present. + /// + + public virtual JObject Put(string name, object value) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + if (value != null) + this[name] = value; + else + Remove(name); + + return this; + } + + public virtual ICollection Names + { + get + { + return base.Keys; + } + } + + /// + /// Overridden to return a JSON formatted object as a string. + /// + + public override string ToString() + { + return TextJsonSerializer.SerializeToJayrockCompatibleJson(this); + } + + + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj index 43b211dc5..be360c140 100644 --- a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj +++ b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj @@ -1,4 +1,4 @@ - + net6.0;net8.0 @@ -151,31 +151,31 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + + + + + - - + - + diff --git a/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs b/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs index f6d093183..d7cdc1ca2 100644 --- a/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs +++ b/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs @@ -1,11 +1,11 @@ -using System; -using System.Collections.Generic; +using System; using System.Text; namespace GxClasses.Helpers { public class BasicAuthenticationHeaderValue { + const char UserNamePasswordSeparator= ':'; public BasicAuthenticationHeaderValue(string authenticationHeaderValue) { if (!string.IsNullOrWhiteSpace(authenticationHeaderValue)) @@ -19,7 +19,7 @@ public BasicAuthenticationHeaderValue(string authenticationHeaderValue) } private readonly string _authenticationHeaderValue; - private string[] _splitDecodedCredentials; + private string _usernamePassword; public bool IsValidBasicAuthenticationHeaderValue { get; private set; } public string UserIdentifier { get; private set; } @@ -32,11 +32,11 @@ private bool TryDecodeHeaderValue() { return false; } - var encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength); + string encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength); try { - var decodedCredentials = Convert.FromBase64String(encodedCredentials); - _splitDecodedCredentials = System.Text.Encoding.ASCII.GetString(decodedCredentials).Split(':'); + byte[] decodedCredentials = Convert.FromBase64String(encodedCredentials); + _usernamePassword = Encoding.ASCII.GetString(decodedCredentials); return true; } catch (FormatException) @@ -47,13 +47,19 @@ private bool TryDecodeHeaderValue() private void ReadAuthenticationHeaderValue() { - IsValidBasicAuthenticationHeaderValue = _splitDecodedCredentials!= null && _splitDecodedCredentials.Length == 2 - && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[0]) - && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[1]); + IsValidBasicAuthenticationHeaderValue = !string.IsNullOrEmpty(_usernamePassword) && _usernamePassword.Contains(UserNamePasswordSeparator); if (IsValidBasicAuthenticationHeaderValue) { - UserIdentifier = _splitDecodedCredentials[0]; - UserPassword = _splitDecodedCredentials[1]; + int separatorIndex = _usernamePassword.IndexOf(UserNamePasswordSeparator); + UserIdentifier = _usernamePassword.Substring(0, separatorIndex); + if (separatorIndex + 1 < _usernamePassword.Length) + { + UserPassword = _usernamePassword.Substring(separatorIndex + 1); + } + else + { + UserPassword = string.Empty; + } } } } diff --git a/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs b/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs index 80d80a9e7..af51f7be6 100644 --- a/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs +++ b/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs @@ -2,7 +2,12 @@ using System.Collections; using System.Globalization; using System.Runtime.Serialization; +using System.Text.Json.Serialization; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using GeographicLib; using NetTopologySuite.Geometries; using NetTopologySuite.IO; @@ -201,6 +206,7 @@ internal static object STRingN(object instance, int i) [KnownType(typeof(System.Double[]))] [KnownType(typeof(System.Collections.ArrayList))] [DataContract] + [JsonConverter(typeof(CustomGeospatialConverter))] public class Geospatial : IGeographicNative { static readonly IGXLogger log = GXLoggerFactory.GetLogger(); @@ -405,7 +411,7 @@ public double Latitude } } - public String JSONPointToWKT(JArray coords) + internal String JSONPointToWKT(JArray coords) { String[] jbuffer = new String[] { "", "" }; jbuffer[0] = ""; diff --git a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs index 1eeded06b..1c8dea312 100644 --- a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs +++ b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs @@ -14,5 +14,7 @@ [assembly: InternalsVisibleTo("GeneXus.Deploy.AzureFunctions.Handlers")] [assembly: InternalsVisibleTo("AzureFunctionsTest")] [assembly: InternalsVisibleTo("GXMessageBroker")] +[assembly: InternalsVisibleTo("GeneXus.SD.Store.StoreManager")] [assembly: InternalsVisibleTo("DotNetCoreChunkedTest")] -[assembly: InternalsVisibleTo("GeneXus.OpenTelemetry.Diagnostics")] \ No newline at end of file +[assembly: InternalsVisibleTo("DotNetCoreChunkedTest")] +[assembly: InternalsVisibleTo("GeneXus.OpenTelemetry.Diagnostics")] diff --git a/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs b/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs index cc455dbdc..a015eca6d 100644 --- a/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs +++ b/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs @@ -1,3 +1,4 @@ +using System; using GeneXus.Configuration; using Microsoft.Extensions.Logging; @@ -7,15 +8,25 @@ public static class GXLogService { private static string AZURE_APPLICATION_INSIGHTS_LOG = "AZUREAPPLICATIONINSIGHTS"; const string OTEL_AZUREMONITOR_EXPORTER = "OTEL_AZUREMONITOR_EXPORTER"; + const string OPENTELEMETRY = "OPENTELEMETRY"; + internal static string OTEL_LOGS_EXPORTER = "OTEL_LOGS_EXPORTER"; const string LOG_OUTPUT = "LOG_OUTPUT"; + public static ILoggerFactory GetLogFactory() { - if (Config.GetValueOf(LOG_OUTPUT, out string logProvider)) - { - if (logProvider == OTEL_AZUREMONITOR_EXPORTER) - return AzureAppInsightsLogProvider.GetAzureMonitorLoggerFactory(); - else if (logProvider == AZURE_APPLICATION_INSIGHTS_LOG) - return AzureAppInsightsLogProvider.GetLoggerFactory(); + string otelLogEnvVar = Environment.GetEnvironmentVariable(OTEL_LOGS_EXPORTER); + if (string.IsNullOrEmpty(otelLogEnvVar) || !otelLogEnvVar.ToLower().Equals("none")) + { + if (Config.GetValueOf(LOG_OUTPUT, out string logProvider)) + { + if (logProvider == OTEL_AZUREMONITOR_EXPORTER) + return OpentelemetryLogProvider.GetAzureMonitorLoggerFactory(); + else if (logProvider == AZURE_APPLICATION_INSIGHTS_LOG) + return OpentelemetryLogProvider.GetAzureAppInsightsLoggerFactory(); + else + if (logProvider == OPENTELEMETRY) + return OpentelemetryLogProvider.GetOpentelemetryLoggerFactory(); + } } return null; } diff --git a/dotnet/src/dotnetcore/GxClasses/Services/LogService/AzureAppInsights/AzureAppInsightsLogProvider.cs b/dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs similarity index 64% rename from dotnet/src/dotnetcore/GxClasses/Services/LogService/AzureAppInsights/AzureAppInsightsLogProvider.cs rename to dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs index 8a1e4318c..20fef179b 100644 --- a/dotnet/src/dotnetcore/GxClasses/Services/LogService/AzureAppInsights/AzureAppInsightsLogProvider.cs +++ b/dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs @@ -1,11 +1,15 @@ using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; using Azure.Monitor.OpenTelemetry.Exporter; using Microsoft.Extensions.Logging; using OpenTelemetry.Logs; +using OpenTelemetry.Resources; +using OpenTelemetry.Exporter; namespace GeneXus.Services.Log { - public class AzureAppInsightsLogProvider : ILoggerFactory + public class OpentelemetryLogProvider : ILoggerFactory { private static string APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING"; private const string LOG_LEVEL_ENVVAR = "GX_LOG_LEVEL"; @@ -25,7 +29,8 @@ public static ILoggerFactory GetAzureMonitorLoggerFactory() builder.AddOpenTelemetry(options => { options.AddAzureMonitorLogExporter(o => o.ConnectionString = appInsightsConnection); - options.AddConsoleExporter(); + if (GenerateOtelLogsToConsole()) + options.AddConsoleExporter(); }).SetMinimumLevel(loglevel); }); } @@ -42,7 +47,7 @@ public static ILoggerFactory GetAzureMonitorLoggerFactory() return loggerFactory; } - public static ILoggerFactory GetLoggerFactory() + public static ILoggerFactory GetAzureAppInsightsLoggerFactory() { string appInsightsConnection = Environment.GetEnvironmentVariable(APPLICATIONINSIGHTS_CONNECTION_STRING); try @@ -71,6 +76,25 @@ public static ILoggerFactory GetLoggerFactory() return loggerFactory; } + + public static ILoggerFactory GetOpentelemetryLoggerFactory() + { + LogLevel loglevel = GetLogLevel(); + loggerFactory = LoggerFactory.Create(builder => builder.AddOpenTelemetry(logging => + { + var resourceBuilder = ResourceBuilder.CreateDefault() + .AddTelemetrySdk(); + + logging.SetResourceBuilder(resourceBuilder) + .AddOtlpExporter(); + + if (GenerateOtelLogsToConsole()) + logging.AddConsoleExporter(); + + }) + .SetMinimumLevel(loglevel)); + return loggerFactory; + } private static LogLevel GetLogLevel() { string loglevelvalue = Environment.GetEnvironmentVariable(LOG_LEVEL_ENVVAR); @@ -79,7 +103,7 @@ private static LogLevel GetLogLevel() { if (!Enum.TryParse(loglevelvalue, out loglevel)) { - CustomLogLevel customLogLevel = CustomLogLevel.Info; + CustomLogLevel customLogLevel = CustomLogLevel.info; if (Enum.TryParse(loglevelvalue, out customLogLevel)) { loglevel = toLogLevel(customLogLevel); @@ -91,6 +115,13 @@ private static LogLevel GetLogLevel() return loglevel; } + private static bool GenerateOtelLogsToConsole() + { + string otelLogsEnvVar = Environment.GetEnvironmentVariable(GXLogService.OTEL_LOGS_EXPORTER); + if (string.IsNullOrEmpty(otelLogsEnvVar)) { return false; } + return otelLogsEnvVar.ToLower().Contains("console") || otelLogsEnvVar.ToLower().Contains("logging"); + } + public void AddProvider(ILoggerProvider provider) { loggerFactory.AddProvider(provider); @@ -105,25 +136,25 @@ public ILogger CreateLogger(string name) } private enum CustomLogLevel { - None, - All, - Debug, - Info, - Warn, - Error, - Fatal + none, + all, + debug, + info, + warn, + error, + fatal } private static LogLevel toLogLevel(CustomLogLevel customLogLevel) { switch (customLogLevel) { - case CustomLogLevel.None: return LogLevel.None; - case CustomLogLevel.All: return LogLevel.Trace; - case CustomLogLevel.Debug: return LogLevel.Debug; - case CustomLogLevel.Info: return LogLevel.Information; - case CustomLogLevel.Warn: return LogLevel.Warning; - case CustomLogLevel.Error: return LogLevel.Error; - case CustomLogLevel.Fatal: return LogLevel.Critical; + case CustomLogLevel.none: return LogLevel.None; + case CustomLogLevel.all: return LogLevel.Trace; + case CustomLogLevel.debug: return LogLevel.Debug; + case CustomLogLevel.info: return LogLevel.Information; + case CustomLogLevel.warn: return LogLevel.Warning; + case CustomLogLevel.error: return LogLevel.Error; + case CustomLogLevel.fatal: return LogLevel.Critical; default: return LogLevel.Information; } } diff --git a/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs b/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs index 3c8cacf39..3dfdf2b00 100644 --- a/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs +++ b/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs @@ -1,4 +1,5 @@ using System; +using System.Text.RegularExpressions; using GxClasses.Helpers; namespace GeneXus.Services.OpenTelemetry @@ -10,11 +11,56 @@ public interface IOpenTelemetryProvider public static class OpenTelemetryService { - private static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(OpenTelemetryService).FullName); - private static string OPENTELEMETRY_SERVICE = "Observability"; - public static string GX_ACTIVITY_SOURCE_NAME = "GeneXus.Tracing"; - + const string OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES"; + const string OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME"; + const string OTEL_SERVICE_VERSION = "OTEL_SERVICE_VERSION"; + const string OPENTELEMETRY_SERVICE = "Observability"; + + public static string GX_ACTIVITY_SOURCE_NAME= GetServiceNameValue(OTEL_RESOURCE_ATTRIBUTES); + public static string GX_ACTIVITY_SOURCE_VERSION= GetServiceVersionValue(OTEL_RESOURCE_ATTRIBUTES); + + private static string GetServiceNameValue(string input) + { + string otelServiceNameEnvVar = Environment.GetEnvironmentVariable(OTEL_SERVICE_NAME); + if (!string.IsNullOrEmpty(otelServiceNameEnvVar)) + return otelServiceNameEnvVar; + + string pattern = @"(?:\b\w+\b=\w+)(?:,(?:\b\w+\b=\w+))*"; + MatchCollection matches = Regex.Matches(input, pattern); + + foreach (Match match in matches) + { + string[] keyValue = match.Value.Split('='); + + if (keyValue[0] == "service.name") + { + return keyValue[1]; + } + } + return "GeneXus.Tracing"; + } + private static string GetServiceVersionValue(string input) + { + string otelServiceNameEnvVar = Environment.GetEnvironmentVariable(OTEL_SERVICE_VERSION); + if (!string.IsNullOrEmpty(otelServiceNameEnvVar)) + return otelServiceNameEnvVar; + + string pattern = @"(?:\b\w+\b=\w+)(?:,(?:\b\w+\b=\w+))*"; + MatchCollection matches = Regex.Matches(input, pattern); + + foreach (Match match in matches) + { + string[] keyValue = match.Value.Split('='); + + if (keyValue[0] == "service.version") + { + return keyValue[1]; + } + } + return string.Empty; + } private static IOpenTelemetryProvider GetOpenTelemetryProvider() { IOpenTelemetryProvider otelProvider = null; @@ -34,7 +80,7 @@ private static IOpenTelemetryProvider GetOpenTelemetryProvider() } catch (Exception e) { - GXLogging.Error(log, "Couldn´t create OpenTelemetry provider.", e.Message, e); + GXLogging.Error(log, "Couldn't create OpenTelemetry provider.", e.Message, e); throw e; } } diff --git a/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj b/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj index 886477ee5..31547472d 100644 --- a/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj +++ b/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj @@ -12,6 +12,10 @@ + + + + diff --git a/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj b/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj index 775944fc7..916d3c26d 100644 --- a/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj +++ b/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj @@ -16,8 +16,9 @@ + - + diff --git a/dotnet/src/dotnetcore/GxMail/GxMail.csproj b/dotnet/src/dotnetcore/GxMail/GxMail.csproj index 61616d6d9..b9210f368 100644 --- a/dotnet/src/dotnetcore/GxMail/GxMail.csproj +++ b/dotnet/src/dotnetcore/GxMail/GxMail.csproj @@ -66,6 +66,7 @@ + diff --git a/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj b/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj index bf243af4a..dc95fa2c3 100644 --- a/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj +++ b/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj @@ -11,17 +11,11 @@ - - - - False - ..\libs\net8.0\Jayrock.dll - - - False - ..\libs\Jayrock.dll - - - + + + - \ No newline at end of file + + + + \ No newline at end of file diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs index 2306f028e..2020e7371 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs @@ -13,13 +13,15 @@ public class ValidateAntiForgeryTokenMiddleware static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private readonly RequestDelegate _next; private readonly IAntiforgery _antiforgery; + private string _restBasePath; private string _basePath; - public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery, String basePath) + public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery, string basePath) { _next = next; _antiforgery = antiforgery; - _basePath = "/" + basePath; + _restBasePath = $"{basePath}{Startup.REST_BASE_URL}"; + _basePath = $"/{basePath}"; } public async Task Invoke(HttpContext context) @@ -42,9 +44,13 @@ public async Task Invoke(HttpContext context) SetAntiForgeryTokens(_antiforgery, context); } } - if (!context.Request.Path.Value.EndsWith(_basePath)) //VerificationToken + if (!IsVerificationTokenServiceRequest(context)) await _next(context); } + private bool IsVerificationTokenServiceRequest(HttpContext context) + { + return context.Request.Path.Value.EndsWith(_restBasePath); + } internal static void SetAntiForgeryTokens(IAntiforgery _antiforgery, HttpContext context) { AntiforgeryTokenSet tokenSet = _antiforgery.GetAndStoreTokens(context); diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj index 9b0bd19a9..172d64c86 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj @@ -10,19 +10,18 @@ - - - + + - + - + - - - - - + + + + + @@ -33,17 +32,6 @@ - - - False - ..\libs\net8.0\Jayrock.dll - - - False - ..\libs\Jayrock.dll - - - diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs index 5cda19ae5..b90fd4050 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Net; +using System.Reflection; using System.Threading.Tasks; using GeneXus.Configuration; using GeneXus.Http; @@ -18,6 +19,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Rewrite; using Microsoft.AspNetCore.Routing; @@ -133,7 +135,7 @@ public class Startup const string RESOURCES_FOLDER = "Resources"; const string TRACE_FOLDER = "logs"; const string TRACE_PATTERN = "trace.axd"; - const string REST_BASE_URL = "rest/"; + internal const string REST_BASE_URL = "rest/"; const string DATA_PROTECTION_KEYS = "DataProtection-Keys"; const string REWRITE_FILE = "rewrite.config"; const string SWAGGER_DEFAULT_YAML = "default.yaml"; @@ -142,6 +144,7 @@ public class Startup const string CORS_POLICY_NAME = "AllowSpecificOriginsPolicy"; const string CORS_ANY_ORIGIN = "*"; const double CORS_MAX_AGE_SECONDS = 86400; + internal const string GX_CONTROLLERS = "gxcontrollers"; public List servicesBase = new List(); @@ -160,11 +163,33 @@ public void ConfigureServices(IServiceCollection services) { OpenTelemetryService.Setup(services); - services.AddMvc(option => option.EnableEndpointRouting = false); + services.AddControllers(); + string controllers = Path.Combine(Startup.LocalPath, "bin", GX_CONTROLLERS); + IMvcBuilder mvcBuilder = services.AddMvc(option => option.EnableEndpointRouting = false); + try + { + if (Directory.Exists(controllers)) + { + foreach (string controller in Directory.GetFiles(controllers)) + { + Console.WriteLine($"Loading controller {controller}"); + mvcBuilder.AddApplicationPart(Assembly.LoadFrom(controller)).AddControllersAsServices(); + } + } + } + catch (Exception ex) + { + Console.Error.WriteLine("Error loading gxcontrollers " + ex.Message); + } services.Configure(options => { options.AllowSynchronousIO = true; options.Limits.MaxRequestBodySize = null; + if (Config.GetValueOrEnvironmentVarOf("MinRequestBodyDataRate", out string MinRequestBodyDataRateStr) && double.TryParse(MinRequestBodyDataRateStr, out double MinRequestBodyDataRate)) + { + GXLogging.Info(log, $"MinRequestBodyDataRate:{MinRequestBodyDataRate}"); + options.Limits.MinRequestBodyDataRate = new MinDataRate(bytesPerSecond: MinRequestBodyDataRate, gracePeriod: TimeSpan.FromSeconds(10)); + } }); services.Configure(options => { @@ -196,6 +221,12 @@ public void ConfigureServices(IServiceCollection services) options.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; options.Cookie.IsEssential = true; + string sessionCookieName = GxWebSession.GetSessionCookieName(VirtualPath); + if (!string.IsNullOrEmpty(sessionCookieName)) + { + options.Cookie.Name=sessionCookieName; + GxWebSession.SessionCookieName = sessionCookieName; + } string sameSite; SameSiteMode sameSiteMode = SameSiteMode.Unspecified; if (Config.GetValueOf("SAMESITE_COOKIE", out sameSite) && Enum.TryParse(sameSite, out sameSiteMode)) @@ -209,7 +240,7 @@ public void ConfigureServices(IServiceCollection services) services.AddAntiforgery(options => { options.HeaderName = HttpHeader.X_CSRF_TOKEN_HEADER; - options.SuppressXFrameOptionsHeader = false; + options.SuppressXFrameOptionsHeader = true; }); } services.AddDirectoryBrowser(); @@ -236,7 +267,6 @@ public void ConfigureServices(IServiceCollection services) }); } DefineCorsPolicy(services); - services.AddMvc(); } private void DefineCorsPolicy(IServiceCollection services) @@ -317,10 +347,12 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos provider.Mappings[".usdz"] = "model/vnd.pixar.usd"; provider.Mappings[".sfb"] = "model/sfb"; provider.Mappings[".gltf"] = "model/gltf+json"; + provider.Mappings[".ini"] = "text/plain"; if (GXUtil.CompressResponse()) { app.UseResponseCompression(); } + app.UseRouting(); app.UseCookiePolicy(); app.UseSession(); app.UseStaticFiles(); @@ -343,6 +375,10 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos app.UseHttpsRedirection(); app.UseHsts(); } + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); if (log.IsDebugEnabled) { try @@ -411,7 +447,7 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos if (RestAPIHelpers.ValidateCsrfToken()) { antiforgery = app.ApplicationServices.GetRequiredService(); - app.UseAntiforgeryTokens(restBasePath); + app.UseAntiforgeryTokens(apiBasePath); } app.UseMvc(routes => { @@ -430,7 +466,7 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos app.UseWebSockets(); string basePath = string.IsNullOrEmpty(VirtualPath) ? string.Empty : $"/{VirtualPath}"; - Config.ScriptPath = basePath; + Config.ScriptPath = string.IsNullOrEmpty(basePath) ? "/" : basePath; app.MapWebSocketManager(basePath); app.MapWhen( @@ -501,7 +537,6 @@ bool IsAspx(HttpContext context, string basePath) } public class CustomExceptionHandlerMiddleware { - const string InvalidCSRFToken = "InvalidCSRFToken"; static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public async Task Invoke(HttpContext httpContext) { @@ -516,9 +551,8 @@ public async Task Invoke(HttpContext httpContext) } else if (ex is AntiforgeryValidationException) { - //"The required antiforgery header value "X-GXCSRF-TOKEN" is not present. httpStatusCode = HttpStatusCode.BadRequest; - httpReasonPhrase = InvalidCSRFToken; + httpReasonPhrase = HttpHelper.InvalidCSRFToken; GXLogging.Error(log, $"Validation of antiforgery failed", ex); } else diff --git a/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj b/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj index 3169f7bcf..094c0b13e 100644 --- a/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj +++ b/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj @@ -36,6 +36,7 @@ + diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs index dc00e11aa..74e1befce 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs @@ -16,5 +16,5 @@ [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.NativeSharpFunctionsMS.getRegistrySubValues(System.String,System.String)~System.Collections.ArrayList")] [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.NativeSharpFunctionsMS.ReadRegKey(System.String)~System.String")] [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.MSPDFFontDescriptor.getTrueTypeFontLocation(System.String)~System.String")] -[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportItextBase.getAcrobatLocation~System.String")] -[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportItextBase.loadSubstituteTable")] +[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportBase.getAcrobatLocation~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportBase.loadSubstituteTable")] diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj b/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj index 01a52310d..95fa355b4 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj @@ -15,8 +15,12 @@ + + + + diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs index bad1d5e68..c927b8281 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs @@ -35,7 +35,7 @@ public class GxReportBuilderPdf8 : GxReportBuilderPdf public GxReportBuilderPdf8() { } public GxReportBuilderPdf8(string appPath, Stream outputStream) { - + _appPath = appPath; _pdfReport = new com.genexus.reports.PDFReportItext8(appPath); if (outputStream != null) { @@ -49,7 +49,7 @@ public GxReportBuilderPdf8(string appPath, Stream outputStream) namespace com.genexus.reports { - public class PDFReportItext8 : PDFReportItextBase + public class PDFReportItext8 : PDFReportBase { static IGXLogger log = GXLoggerFactory.GetLogger(); diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs new file mode 100644 index 000000000..0f1a30120 --- /dev/null +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs @@ -0,0 +1,1044 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using System.IO; +using System.Net.Http; +using System.Text; +using GeneXus; +using UglyToad.PdfPig.Core; +using UglyToad.PdfPig.Fonts.Standard14Fonts; +using UglyToad.PdfPig.Writer; +using static GeneXus.Utils.StringUtil; +using static UglyToad.PdfPig.Writer.PdfDocumentBuilder; +using static UglyToad.PdfPig.Writer.PdfPageBuilder; +using Color = System.Drawing.Color; +using Font = System.Drawing.Font; +using PageSize = UglyToad.PdfPig.Content.PageSize; +using PdfRectangle = UglyToad.PdfPig.Core.PdfRectangle; + +namespace GeneXus.Printer +{ + public class GxReportBuilderPDFPig : GxReportBuilderPdf + { + static IGXLogger log = GXLoggerFactory.GetLogger(); + public GxReportBuilderPDFPig() { } + public GxReportBuilderPDFPig(string appPath, Stream outputStream) + { + _appPath = appPath; + _pdfReport = new com.genexus.reports.PDFReportPDFPig(appPath); + if (outputStream != null) + { + _pdfReport.setOutputStream(outputStream); + GXLogging.Debug(log, "GxReportBuilderPdf outputStream: binaryWriter"); + } + } + } + +} + +namespace com.genexus.reports +{ + public class PDFReportPDFPig : PDFReportBase + { + static IGXLogger log = GXLoggerFactory.GetLogger(); + + private PdfDocumentBuilder documentBuilder; + private PdfPageBuilder pageBuilder; + private ExtendedPageSize pageSize; + + private AddedFont baseFont; + private string baseFontPath; + private string baseFontName; + private Color backColor, foreColor; + + private string barcodeType = null; + + private Dictionary documentImages; + + protected override void init(ref int gxYPage, ref int gxXPage, int pageWidth, int pageLength) + { + try + { + pageSize = ComputePageSize(leftMargin, topMargin, pageWidth, pageLength, props.getBooleanGeneralProperty(Const.MARGINS_INSIDE_BORDER, Const.DEFAULT_MARGINS_INSIDE_BORDER)); + documentBuilder = new PdfDocumentBuilder(); + + pageBuilder = pageSize.IsCustomPageSize() ? documentBuilder.AddPage(pageSize.Width, pageSize.Height) : documentBuilder.AddPage(pageSize.PageSize); + + gxXPage = (int) pageBuilder.PageSize.TopRight.X; + if (props.getBooleanGeneralProperty(Const.FIX_SAC24437, true)) + gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y); + else + gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y_OLD); + + } + catch (Exception e) + { + GXLogging.Error(log, "GxPrintInit failed", e); + } + } + + public PDFReportPDFPig(string appPath) : base(appPath) + { + documentBuilder = null; + documentImages = new Dictionary(); + } + + public override void GxStartPage() + { + try + { + if (pages > 0) + { + pageBuilder = pageSize.IsCustomPageSize() ? documentBuilder.AddPage(pageSize.Width, pageSize.Height) : documentBuilder.AddPage(pageSize.PageSize); + } + pages = pages + 1; + } + catch (Exception de) + { + GXLogging.Error(log, "GxStartPage error", de); + } + } + + internal override bool SetComplainceLevel(PdfConformanceLevel level) + { + return false; + } + + public override void GxDrawRect(int left, int top, int right, int bottom, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue, + int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) + { + float penAux = (float)convertScale(pen); + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + float x1, y1, x2, y2; + x1 = leftAux + leftMargin; + y1 = (float)pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin; + x2 = rightAux + leftMargin; + y2 = (float)pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin; + + GXLogging.Info(log, "Corner styling and radio are not taken into consideration because the PDFPig " + + "API provides no way to render rounded rectangles or style them"); + + if (pen > 0) + pageBuilder.SetStrokeColor( (byte)foreRed, (byte)foreGreen, (byte)foreBlue); + else + pageBuilder.SetStrokeColor( (byte)backRed, (byte)backGreen, (byte)backBlue); + + GXLogging.Info(log, "The PDFPig API provides no way of setting the line cap style"); + + if (backMode != 0) + { + pageBuilder.SetTextAndFillColor((byte)backRed, (byte)backGreen, (byte)backBlue); + pageBuilder.DrawRectangle(new PdfPoint(x1, y1), (decimal)(x2 - x1), (decimal)(y2 - y1), (decimal)penAux, true); + } + else + { + pageBuilder.DrawRectangle(new PdfPoint(x1, y1), (decimal)(x2 - x1), (decimal)(y2 - y1), (decimal) penAux, false); + } + pageBuilder.ResetColor(); + } + + public override void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style) + { + try + { + float widthAux = (float)convertScale(width); + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + GXLogging.Debug(log, "GxDrawLine -> (" + left + "," + top + ") - (" + right + "," + bottom + ") Width: " + width); + + float x1, y1, x2, y2; + + x1 = leftAux + leftMargin; + y1 = (float)pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin; + x2 = rightAux + leftMargin; + y2 = (float)pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin; + + pageBuilder.SetStrokeColor((byte)foreRed, (byte)foreGreen, (byte)foreBlue); + + if (style != 0) + { + GXLogging.Info(log, "The PDFPig API provides no way of creating a dashed line"); + float[] dashPattern = getDashedPattern(style); + } + + pageBuilder.DrawLine(new PdfPoint(x1, y1), new PdfPoint(x2, y2), (decimal)widthAux); + pageBuilder.ResetColor(); + } + catch (Exception e) + { + GXLogging.Error(log, "GxDrawLine error", e); + } + } + + public override void GxDrawBitMap(string bitmap, int left, int top, int right, int bottom, int aspectRatio) + { + try + { + string imageType = Path.GetExtension(bitmap).Substring(1); + + float rightAux = (float)convertScale(right); + float bottomAux = (float)convertScale(bottom); + float leftAux = (float)convertScale(left); + float topAux = (float)convertScale(top); + + float llx = leftAux + leftMargin; + float lly = (float) pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin; + float width; + float height; + if (aspectRatio == 0) + { + width = rightAux - leftAux; + height = bottomAux - topAux; + } + else + { + width = (rightAux - leftAux) * aspectRatio; + height = (bottomAux - topAux) * aspectRatio; + } + + PdfRectangle position = new PdfRectangle(llx, lly, llx + width, lly + height); + + AddedImage image = null; + AddedImage imageRef; + if (documentImages != null && documentImages.TryGetValue(bitmap, out imageRef)) + { + image = imageRef; + } + else + { + try + { + if (!Path.IsPathRooted(bitmap)) + { + + image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(defaultRelativePrepend + bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(defaultRelativePrepend + bitmap), position); + if (image == null) + { + bitmap = webAppDir + bitmap; + image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(bitmap), position); + } + else + { + bitmap = defaultRelativePrepend + bitmap; + } + } + else + { + image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(bitmap), position); + } + } + catch (Exception) + { + image = AddImageFromURL(bitmap, position); + } + if (image == null) + { + image = AddImageFromURL(bitmap, position); + } + + if (documentImages == null) + { + documentImages = new Dictionary(); + } + documentImages[bitmap] = image; + } + GXLogging.Debug(log, "GxDrawBitMap -> '" + bitmap + "' [" + left + "," + top + "] - Size: (" + (right - left) + "," + (bottom - top) + ")"); + } + catch (Exception e) + { + GXLogging.Error(log, "GxDrawBitMap error", e); + } + } + + private AddedImage AddImageFromURL(string url, PdfRectangle position) + { + AddedImage image = null; + using (HttpClient httpClient = new HttpClient()) + { + byte[] imageBytes = httpClient.GetByteArrayAsync(url).Result; + try + { + image = pageBuilder.AddJpeg(imageBytes, position); + } + catch (Exception) + { + pageBuilder.AddPng(imageBytes, position); + } + } + if (image == null) + { + GXLogging.Error(log, "GxDrawBitMap : PDFPig only supports adding jpeg or png images to documents"); + } + return image; + } + + public override void GxAttris(string fontName, int fontSize, bool fontBold, bool fontItalic, bool fontUnderline, bool fontStrikethru, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue) + { + bool isCJK = false; + bool embedFont = IsEmbeddedFont(fontName); + string originalFontName = fontName; + if (!embedFont) + { + fontName = getSubstitute(fontName); + } + + string fontSubstitute = originalFontName != fontName ? $"Original Font: {originalFontName} Substitute" : ""; + + GXLogging.Debug(log, $"GxAttris: "); + GXLogging.Debug(log, $"\\-> Font: {fontName} ({fontSize})" + + (fontBold ? " BOLD" : "") + + (fontItalic ? " ITALIC" : "") + + (fontStrikethru ? " Strike" : "")); + GXLogging.Debug(log, $"\\-> Fore ({foreRed}, {foreGreen}, {foreBlue})"); + GXLogging.Debug(log, $"\\-> Back ({backRed}, {backGreen}, {backBlue})"); + + this.fontUnderline = fontUnderline; + this.fontStrikethru = fontStrikethru; + this.fontSize = fontSize; + foreColor = Color.FromArgb(foreRed, foreGreen, foreBlue); + backColor = Color.FromArgb(backRed, backGreen, backBlue); + + backFill = backMode != 0; + + try + { + string fontNameLower = fontName.ToLower(); + if (PDFFont.isType1(fontName)) + { + foreach (string[] cjkName in Type1FontMetrics.CJKNames) + { + if (cjkName[0].ToLower().Equals(fontNameLower) || cjkName[1].ToLower().Equals(fontNameLower)) + { + string style = fontBold && fontItalic ? "BoldItalic" : fontItalic ? "Italic" : fontBold ? "Bold" : ""; + setAsianFont(fontName, style); + isCJK = true; + break; + } + } + if (!isCJK) + { + int style = (fontBold && fontItalic ? 3 : 0) + (fontItalic && !fontBold ? 2 : 0) + (fontBold && !fontItalic ? 1 : 0); + foreach (string[] base14Font in PDFFont.base14) + { + if (base14Font[0].ToLower().Equals(fontNameLower)) + { + fontName = base14Font[1 + style].Substring(1); + break; + } + } + ProcessBaseFont(fontName); + } + } + else + { + string style = (fontBold && fontItalic ? ",BoldItalic" : "") + (fontItalic && !fontBold ? ",Italic" : "") + (fontBold && !fontItalic ? ",Bold" : ""); + fontName += style; + ProcessBaseFont(fontName); + } + + if (barcode128AsImage && ( + fontName.ToLower().Contains("barcode 128") || fontName.ToLower().Contains("barcode128") + || + (!string.IsNullOrEmpty(baseFontPath) && (baseFontPath.ToLower().Contains("3of9") || baseFontPath.ToLower().Contains("3 of 9"))) + ) + ) + { + barcodeType = "barcode128"; + } + } + catch (Exception e) + { + GXLogging.Debug(log, "GxAttris DocumentException", e); + throw; + } + } + + private void ProcessBaseFont(string fontName) + { + string fontLocation = GetFontLocation(fontName); + if (string.IsNullOrEmpty(fontLocation)) + { + fontLocation = new MSPDFFontDescriptor().getTrueTypeFontLocation(fontName); + } + + if (!string.IsNullOrEmpty(fontLocation)) + { + byte[] fontBytes = File.ReadAllBytes(fontLocation); + baseFont = documentBuilder.AddTrueTypeFont(fontBytes); + } + else + { + baseFont = createType1FontFromName(fontName); + if (baseFont == null) + baseFont = documentBuilder.AddStandard14Font(Standard14Font.TimesRoman); + } + + baseFontName = fontName; + baseFontPath = fontLocation; + } + + private AddedFont createType1FontFromName(string fontName) + { + switch (fontName.ToLower()) + { + case "times-roman": + return documentBuilder.AddStandard14Font(Standard14Font.TimesRoman); + case "times-bold": + return documentBuilder.AddStandard14Font(Standard14Font.TimesBold); + case "times-italic": + return documentBuilder.AddStandard14Font(Standard14Font.TimesItalic); + case "times-bolditalic": + return documentBuilder.AddStandard14Font(Standard14Font.TimesBoldItalic); + case "helvetica": + return documentBuilder.AddStandard14Font(Standard14Font.Helvetica); + case "helvetica-bold": + return documentBuilder.AddStandard14Font(Standard14Font.HelveticaBold); + case "helvetica-oblique": + return documentBuilder.AddStandard14Font(Standard14Font.HelveticaOblique); + case "helvetica-boldoblique": + return documentBuilder.AddStandard14Font(Standard14Font.HelveticaBoldOblique); + case "courier": + return documentBuilder.AddStandard14Font(Standard14Font.Courier); + case "courier-bold": + return documentBuilder.AddStandard14Font(Standard14Font.CourierBold); + case "courier-oblique": + return documentBuilder.AddStandard14Font(Standard14Font.CourierOblique); + case "courier-boldoblique": + return documentBuilder.AddStandard14Font(Standard14Font.CourierBoldOblique); + case "symbol": + return documentBuilder.AddStandard14Font(Standard14Font.Symbol); + case "zapfdingbats": + return documentBuilder.AddStandard14Font(Standard14Font.ZapfDingbats); + default: + return null; + } + } + + public override void setAsianFont(string fontName, string style) + { + try + { + string fontPath = GetFontLocation(fontName); + if (string.IsNullOrEmpty(fontPath)) + { + MSPDFFontDescriptor fontDescriptor = new MSPDFFontDescriptor(); + fontPath = fontDescriptor.getTrueTypeFontLocation(fontName); + } + byte[] fontBytes = File.ReadAllBytes(fontPath); + baseFont = documentBuilder.AddTrueTypeFont(fontBytes); + } + catch (IOException de) + { + GXLogging.Debug(log, "setAsianFont error", de); + } + } + + public override void GxDrawText(string sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign) + { + bool printRectangle = false; + if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true)) + printRectangle = true; + + if (printRectangle && (border == 1 || backFill)) + { + GxDrawRect(left, top, right, bottom, border, foreColor.R, foreColor.G, foreColor.B, backFill ? 1 : 0, backColor.R, backColor.G, backColor.B, 0, 0); + } + + sTxt = RTrim(sTxt); + + AddedFont font = createType1FontFromName(baseFontName); + if (font == null) + { + try + { + byte[] fontBytes = File.ReadAllBytes(baseFontPath); + font = documentBuilder.AddTrueTypeFont(fontBytes); + } + catch + { + font = baseFont; + } + } + + float captionHeight = CalculateFontCaptionHeight(baseFontPath, fontSize); + float rectangleWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize); + float lineHeight = MeasureTextHeight(sTxt, baseFontPath, fontSize); + float textBlockHeight = (float)convertScale(bottom - top); + int linesCount = (int)(textBlockHeight / lineHeight); + int bottomOri = bottom; + int topOri = top; + + if (linesCount >= 2 && !((align & 16) == 16) && htmlformat != 1) + { + if (valign == (int)VerticalAlign.TOP) + bottom = top + (int)reconvertScale(lineHeight); + else if (valign == (int)VerticalAlign.BOTTOM) + top = bottom - (int)reconvertScale(lineHeight); + } + + float bottomAux = (float)convertScale(bottom) - ((float)convertScale(bottom - top)) / 2; + float topAux = (float)convertScale(top) + ((float)convertScale(bottom - top)) / 2; + + float startHeight = bottomAux - topAux - captionHeight; + + float leftAux = (float)convertScale(left); + float rightAux = (float)convertScale(right); + int alignment = align & 3; + bool autoResize = (align & 256) == 256; + + if (htmlformat == 1) + { + GXLogging.Error(log, "GxDrawText: PDFPig report implementation does not support rendering HTML content into PDF reports"); + } + + if (barcodeType != null) + { + PdfRectangle barcodeRectangle; + switch (alignment) + { + case 1: // Center Alignment + barcodeRectangle = new PdfRectangle( + (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin, + (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin + ); + break; + case 2: // Right Alignment + barcodeRectangle = new PdfRectangle( + rightAux + leftMargin - rectangleWidth, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin, + rightAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin + ); + break; + default: // Left Alignment (Corresponds to alignment = 0 but used as default) + barcodeRectangle = new PdfRectangle( + leftAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin, + leftAux + leftMargin + rectangleWidth, + pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin + ); + break; + + } + Image barcodeImage = CreateBarcodeImage((float)barcodeRectangle.Width, (float)barcodeRectangle.Height, baseFontPath, sTxt); + using (MemoryStream ms = new MemoryStream()) + { + barcodeImage.Save(ms, ImageFormat.Png); + pageBuilder.AddPng(ms.ToArray(), barcodeRectangle); + } + return; + } + + if (backFill) + { + PdfPoint rectangleStartingPoint; + switch (alignment) + { + case 1: // Center Alignment + rectangleStartingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + ); + break; + case 2: // Right Alignment + rectangleStartingPoint = new PdfPoint( + rightAux + leftMargin - rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + ); + break; + default: // Left Alignment (Corresponds to alignment = 0 but used as default) + rectangleStartingPoint = new PdfPoint( + leftAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + ); + break; + + } + + decimal width = (decimal)(((leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2) - ((leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2)); + decimal height = (decimal)((pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin) - (pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin)); + + pageBuilder.SetTextAndFillColor(backColor.R, backColor.G, backColor.B); + pageBuilder.DrawRectangle(rectangleStartingPoint, width, height, 1, true); + } + + pageBuilder.SetTextAndFillColor(foreColor.R, foreColor.G, foreColor.B); + + if (fontUnderline) + { + float underlineSeparation = lineHeight / 5; + int underlineHeight = (int)underlineSeparation + (int)(underlineSeparation / 4); + + PdfPoint underlineStartingPoint; + PdfPoint underlineEndingPoint; + switch (alignment) + { + case 1: // Center Alignment + underlineStartingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + underlineEndingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + break; + case 2: // Right Alignment + underlineStartingPoint = new PdfPoint( + rightAux + leftMargin - rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + underlineEndingPoint = new PdfPoint( + rightAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + break; + default: // Left Alignment (Corresponds to alignment = 0 but used as default) + underlineStartingPoint = new PdfPoint( + leftAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + underlineEndingPoint = new PdfPoint( + leftAux + leftMargin + rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + ); + break; + + } + + pageBuilder.DrawLine(underlineStartingPoint, underlineEndingPoint, underlineHeight); + } + + if (fontStrikethru) + { + float strikethruSeparation = (float)(lineHeight / 1.5); + PdfPoint strikethruStartingPoint; + PdfPoint strikethruEndingPoint; + switch (alignment) + { + case 1: // Center Alignment + strikethruStartingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + strikethruEndingPoint = new PdfPoint( + (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + break; + case 2: // Right Alignment + strikethruStartingPoint = new PdfPoint( + rightAux + leftMargin - rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + strikethruEndingPoint = new PdfPoint( + rightAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + break; + default: // Left Alignment (Corresponds to alignment = 0 but used as default) + strikethruStartingPoint = new PdfPoint( + leftAux + leftMargin, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + strikethruEndingPoint = new PdfPoint( + leftAux + leftMargin + rectangleWidth, + pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation + ); + break; + + } + pageBuilder.DrawLine(strikethruStartingPoint, strikethruEndingPoint, (decimal)convertScale(fontSize) / 10); + } + + float textBlockWidth = rightAux - leftAux; + float TxtWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize); + bool justified = (alignment == 3) && textBlockWidth < TxtWidth; + bool wrap = ((align & 16) == 16); + + if (wrap || justified) + { + bottomAux = (float)convertScale(bottomOri); + topAux = (float)convertScale(topOri); + + float llx = leftAux + leftMargin; + float lly = (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin); + float urx = rightAux + leftMargin; + float ury = (float)(pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin); + + ShowWrappedTextAligned(font, alignment, sTxt, llx, lly, urx, ury); + } + else + { + if (!autoResize) + { + string newsTxt = sTxt; + while (TxtWidth > textBlockWidth && (newsTxt.Length - 1 >= 0)) + { + sTxt = newsTxt; + newsTxt = newsTxt.Substring(0, newsTxt.Length - 1); + TxtWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize); + } + } + switch (alignment) + { + case 1: // Center Alignment + ShowTextAligned(font, alignment, sTxt, ((leftAux + rightAux) / 2) + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin)); + break; + case 2: // Right Alignment + ShowTextAligned(font, alignment, sTxt, rightAux + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin)); + break; + case 0: // Left Alignment + case 3: // Justified, only one text line + ShowTextAligned(font, alignment, sTxt, leftAux + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin)); + break; + } + } + } + + public Image CreateBarcodeImage(float width, float height, string fontPath, string text) + { + PrivateFontCollection fontCollection = new PrivateFontCollection(); + fontCollection.AddFontFile(fontPath); + FontFamily fontFamily = fontCollection.Families[0]; + + int bitmapWidth = (int)Math.Ceiling(width); + int bitmapHeight = (int)Math.Ceiling(height); + + float fontSize = Math.Min(width, height); + Font font; + SizeF textSize; + + using (Bitmap tempBitmap = new Bitmap(1, 1)) + { + using (Graphics tempGraphics = Graphics.FromImage(tempBitmap)) + { + do + { + font = new Font(fontFamily, fontSize, GraphicsUnit.Pixel); + textSize = tempGraphics.MeasureString(text, font); + fontSize--; + } while (textSize.Width > width || textSize.Height > height); + } + } + + Bitmap bitmap = new Bitmap(bitmapWidth, bitmapHeight); + bitmap.SetResolution(600, 600); + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + graphics.Clear(Color.White); + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit; + StringFormat format = new StringFormat + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }; + graphics.DrawString(text, font, Brushes.Black, new RectangleF(0, 0, width, height), format); + } + font.Dispose(); + fontCollection.Dispose(); + + return bitmap; + } + + private float MeasureTextWidth(string text, string fontPath, float fontSize) + { + Font font; + if (string.IsNullOrEmpty(fontPath)) + { + font = new Font("Times New Roman", fontSize, GraphicsUnit.Point); + } + else + { + PrivateFontCollection pfc = new PrivateFontCollection(); + pfc.AddFontFile(fontPath); + font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point); + } + + using (font) + { + using (var fakeImage = new Bitmap(1, 1)) + { + using (var graphics = Graphics.FromImage(fakeImage)) + { + graphics.PageUnit = GraphicsUnit.Point; + graphics.TextRenderingHint = TextRenderingHint.AntiAlias; + var sizeF = graphics.MeasureString(text, font); + return sizeF.Width; + } + } + } + } + + private float MeasureTextHeight(string text, string fontPath, float fontSize) + { + Font font; + if (string.IsNullOrEmpty(fontPath)) + { + font = new Font("Times New Roman", fontSize, GraphicsUnit.Point); + } + else + { + PrivateFontCollection pfc = new PrivateFontCollection(); + pfc.AddFontFile(fontPath); + font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point); + } + + using (font) + { + using (var fakeImage = new Bitmap(1, 1)) + { + using (var graphics = Graphics.FromImage(fakeImage)) + { + graphics.PageUnit = GraphicsUnit.Point; + graphics.TextRenderingHint = TextRenderingHint.AntiAlias; + var sizeF = graphics.MeasureString(text, font); + return sizeF.Height; + } + } + } + } + + private float CalculateFontCaptionHeight(string fontPath, float fontSize, FontStyle fontStyle = FontStyle.Regular) + { + Font font; + if (string.IsNullOrEmpty(fontPath)) + { + font = new Font("Times New Roman", fontSize, GraphicsUnit.Point); + } + else + { + PrivateFontCollection pfc = new PrivateFontCollection(); + pfc.AddFontFile(fontPath); + font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point); + } + using (font) + { + FontFamily ff = font.FontFamily; + + float ascent = ff.GetCellAscent(fontStyle); + float descent = ff.GetCellDescent(fontStyle); + float lineSpacing = ff.GetLineSpacing(fontStyle); + + float height = fontSize * (ascent + descent) / ff.GetEmHeight(fontStyle); + + return height; + } + } + + private void ShowTextAligned(AddedFont font, int alignment, string text, float x, float y) + { + try + { + float textWidth = MeasureTextWidth(text, baseFontPath, fontSize); + switch (alignment) + { + case 0: // Left-aligned + case 3: // Justified, only one text line + break; + case 1: // Center-aligned + x = x - textWidth / 2; + break; + case 2: // Right-aligned + x = x - textWidth; + break; + } + y = (float)(y - fontSize * 0.5); + pageBuilder.AddText(text, fontSize, new PdfPoint(x, y), font); + } + catch (IOException ioe) + { + GXLogging.Error(log, "failed to draw aligned text: ", ioe); + } + } + + private void ShowWrappedTextAligned(AddedFont font, int alignment, string text, float llx, float lly, float urx, float ury) + { + try + { + List lines = new List(); + string[] words = text.Split(' '); + StringBuilder currentLine = new StringBuilder(); + foreach (string word in words) + { + float currentLineWidth = MeasureTextWidth(currentLine + " " + word, baseFontPath, fontSize); + if (currentLineWidth < urx - llx) + { + if (currentLine.Length > 0) + { + currentLine.Append(" "); + } + currentLine.Append(word); + } + else + { + lines.Add(currentLine.ToString()); + currentLine.Clear(); + currentLine.Append(word); + } + } + lines.Add(currentLine.ToString()); + + float leading = (float)(lines.Count == 1 ? fontSize : 1.2 * fontSize); + float totalTextHeight = fontSize * lines.Count + leading * (lines.Count - 1); + float startY = lines.Count == 1 ? lly + (ury - lly - totalTextHeight) / 2 : lly + (ury - lly - totalTextHeight) / 2 + (lines.Count - 1) * (fontSize + leading); + + foreach (string line in lines) + { + float lineWidth = MeasureTextWidth(line, baseFontPath, fontSize); + float startX; + + switch (alignment) + { + case 1: // Center-aligned + startX = llx + (urx - llx - lineWidth) / 2; + break; + case 2: // Right-aligned + startX = urx - lineWidth; + break; + default: // Left-aligned & Justified, only one text line + startX = llx; + break; + } + + pageBuilder.AddText(line, fontSize, new PdfPoint(startX, startY), font); + startY -= leading; + } + } + catch (IOException ioe) + { + GXLogging.Error(log, "Failed to draw wrapped text", ioe); + } + } + + private ExtendedPageSize ComputePageSize(float leftMargin, float topMargin, int width, int length, bool marginsInsideBorder) + { + if ((leftMargin == 0 && topMargin == 0) || marginsInsideBorder) + { + if (length == 23818 && width == 16834) + return new ExtendedPageSize(PageSize.A3); + else if (length == 16834 && width == 11909) + return new ExtendedPageSize(PageSize.A4); + else if (length == 11909 && width == 8395) + return new ExtendedPageSize(PageSize.A5); + else if (length == 15120 && width == 10440) + return new ExtendedPageSize(PageSize.Executive); + else if (length == 20160 && width == 12240) + return new ExtendedPageSize(PageSize.Legal); + else if (length == 15840 && width == 12240) + return new ExtendedPageSize(PageSize.Letter); + else + return new ExtendedPageSize((width / PAGE_SCALE_X), (length / PAGE_SCALE_Y)); + } + return new ExtendedPageSize((width / PAGE_SCALE_X) + leftMargin, (length / PAGE_SCALE_Y) + topMargin); + + } + + public override void GxEndDocument() + { + if (pages == 0) + { + GxStartPage(); + } + + GXLogging.Info(log, "The PDFPig API provides no way of setting the number of copies and the duplex value for the viewer preferences"); + + GXLogging.Info(log, "The PDFPig API provides no way of embedding javascript into the document"); + + try + { + byte[] documentBytes = documentBuilder.Build(); + outputStream.Write(documentBytes, 0, documentBytes.Length); + } + catch (IOException e) + { + GXLogging.Debug(log,"GxEndDocument: failed to write document to the output stream", e); + } + + string serverPrinting = props.getGeneralProperty(Const.SERVER_PRINTING); + bool printingScript = (outputType == Const.OUTPUT_PRINTER || outputType == Const.OUTPUT_STREAM_PRINTER) && serverPrinting.Equals("false"); + switch (outputType) + { + case Const.OUTPUT_SCREEN: + try + { + outputStream.Close(); + GXLogging.Debug(log, "GxEndDocument OUTPUT_SCREEN outputstream length" + outputStream.ToString().Length); + } + catch (IOException e) + { + GXLogging.Error(log, "GxEndDocument OUTPUT_SCREEN error", e); + } + try { showReport(docName, modal); } + catch (Exception) { } + break; + + case Const.OUTPUT_PRINTER: + try { outputStream.Close(); } + catch (IOException) { } + try + { + if (!serverPrinting.Equals("false") && !printingScript) + { + printReport(docName, this.printerOutputMode == 0, printerSettings.getProperty(form, Const.PRINTER)); + } + } + catch (Exception) { } + break; + + case Const.OUTPUT_FILE: + try + { + outputStream.Close(); + GXLogging.Debug(log, "GxEndDocument OUTPUT_FILE outputstream length" + outputStream.ToString().Length); + } + catch (IOException e) + { + GXLogging.Error(log, "GxEndDocument OUTPUT_FILE error", e); + } + break; + + case Const.OUTPUT_STREAM: + case Const.OUTPUT_STREAM_PRINTER: + default: break; + } + outputStream = null; + } + + } + + internal class ExtendedPageSize + { + internal double Width; + internal double Height; + internal PageSize PageSize; + internal ExtendedPageSize(double w, double h) + { + Width = w; + Height = h; + PageSize = PageSize.Custom; + } + internal ExtendedPageSize(PageSize pageSize) + { + PageSize = pageSize; + } + internal bool IsCustomPageSize() + { + return PageSize == PageSize.Custom; + } + } +} diff --git a/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj b/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj index e2ef170a8..8588e6c64 100644 --- a/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj +++ b/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj @@ -20,25 +20,13 @@ + - + - - - - False - ..\libs\net8.0\Jayrock.dll - - - False - ..\libs\Jayrock.dll - - - - \ No newline at end of file diff --git a/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj b/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj index 64129d3dd..b4815e353 100644 --- a/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj +++ b/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj @@ -6,7 +6,9 @@ GeneXus.SD.Store StoreManager GeneXus.StoreManager.Core - + NETCORE + + @@ -25,8 +27,9 @@ - + + @@ -36,15 +39,4 @@ - - - False - ..\..\libs\net8.0\Jayrock.dll - - - False - ..\..\libs\Jayrock.dll - - - diff --git a/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj b/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj index f69db1bbf..ecc9fba40 100644 --- a/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj +++ b/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj @@ -15,7 +15,8 @@ - + + diff --git a/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj b/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj index 9d38c6b46..f616027d3 100644 --- a/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj +++ b/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj @@ -18,10 +18,14 @@ - + + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj index 1bacb83af..47d2c6a5e 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj @@ -8,7 +8,7 @@ - + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj index 543b2cd48..e9956e29f 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj @@ -8,7 +8,7 @@ - + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj index 15a2e62da..251919cc3 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj @@ -8,7 +8,7 @@ - + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj index c2c2ccf43..cdf1f3ab0 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj @@ -7,6 +7,10 @@ TRACE;DEBUG;NETCORE + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj index 2cc2f4242..d40bc0234 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj @@ -7,6 +7,11 @@ TRACE;DEBUG;NETCORE + + + + + diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj index 4779d04dc..bce3bd249 100644 --- a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj +++ b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj @@ -5,7 +5,11 @@ TRACE;DEBUG;NETCORE GeneXus.Message.Queue - + + + + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj index 8c6a62190..ef080b861 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs index 4b35201d4..d05c93aa8 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs @@ -1,112 +1,183 @@ using System; using System.Diagnostics; -using GeneXus.Application; +using GeneXus.Attributes; +using OpenTelemetry; using OpenTelemetry.Trace; -using static GeneXus.OpenTelemetry.Diagnostics.OtelTracer; namespace GeneXus.OpenTelemetry.Diagnostics { + [GXApi] public class OtelSpan { private Activity _activity; - public enum SpanStatusCode { Unset, Ok, Error } - public string Id - { get => _activity?.Id; } - public bool IsAllDataRequested + internal OtelSpan(Activity activity) { - get - { - if (_activity != null) - return _activity.IsAllDataRequested; - return false; - } + _activity = activity; } - public bool IsStopped - { get - { - if (_activity != null ) - return _activity.IsStopped; - return false; - } - } + public OtelSpan() + {} - public short Kind - { get => (short)_activity?.Kind; } + public Activity Activity => _activity; - public string ParentId - { get => _activity?.ParentId; } + #region EO Properties + public GXSpanContext SpanContext => _activity == null ? null : new GXSpanContext(_activity.Context); - public string ParentSpanId - { get => _activity?.ParentSpanId.ToString(); } + public GXTraceContext GetContext => _activity == null ? null : new GXTraceContext(_activity.Context); + public string SpanId => _activity?.Id; - public string TraceId - { get => _activity?.TraceId.ToString(); } + public string TraceId => _activity?.TraceId.ToHexString(); - public short Status - { get => (short)_activity?.Status; } - - internal OtelSpan(Activity activity) + #endregion + + #region Methods + public void Stop() { - _activity = activity; + _activity?.Stop(); + } + public void RecordException(string message) + { + _activity?.RecordException(new Exception(message)); } - public OtelSpan() + public void SetStringAttribute(string property, string value) { - _activity = Activity.Current; + _activity?.SetTag(property, value); + } - public void Start() + public void SetLongAttribute(string property, long value) { - _activity?.Start(); + _activity?.SetTag(property, value); + } + public void SetDoubleAttribute(string property, double value) + { + _activity?.SetTag(property, value); - public void Stop() + } + public void SetBooleanAttribute(string property, bool value) { - _activity?.Stop(); + _activity?.SetTag(property, value); + } - public void RecordException(string message) + public GXTraceContext AddBaggage(string property, string value) + { + Baggage.SetBaggage(property, value); + if (_activity != null) + return new GXTraceContext(_activity.Context); + else return null; + } + + public string GetBaggageItem(string property,GXTraceContext gXTraceContext) + { + return Baggage.GetBaggage(property); + } + public void SetStatus(SpanStatusCode spanStatusCode, string message) { - _activity.RecordException(new Exception(message)); + _activity?.SetStatus((ActivityStatusCode)spanStatusCode, message); } + public void SetStatus(SpanStatusCode spanStatusCode) + { + _activity?.SetStatus((ActivityStatusCode)spanStatusCode); + } + + #endregion - public void SetTag(string property, string value) + #region Private Methods + public bool IsRecording => IsSpanRecording(); + + private bool IsSpanRecording() { - _activity.SetTag(property, value); + if (_activity != null) + return _activity.IsAllDataRequested; + else + return false; } - public string GetTagItem(string property) + #endregion + + } + + public class GXTraceContext : GXSpanContext + { + //Dummy class to be compatible with Java. + //.NET does not requiere propagating the context explicitly in most of the cases. + //https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Api/README.md#baggage-api + + public GXTraceContext(ActivityContext activityContext):base(activityContext) { } + public GXTraceContext():base() { } + } + public class GXSpanContext + { + private ActivityContext _activityContext; + public GXSpanContext() { - return _activity.GetTagItem(property).ToString(); + _activityContext = Activity.Current.Context; } - public void AddBaggage(string property, string value) + + public GXSpanContext(ActivityContext activityContext) { - _activity.AddBaggage(property, value); + _activityContext = activityContext; } - public string GetBaggageItem(string property) + public ActivityContext ActivityContext { get { return _activityContext; } } + + public string TraceId => GetTraceId(); + + public string SpanId => GetSpanId(); + + private string GetTraceId() { - return _activity.GetBaggageItem(property).ToString(); + if (_activityContext != null) + { + ActivityTraceId activityTraceId = _activityContext.TraceId; + return activityTraceId.ToHexString(); + } else return null; + } + private string GetSpanId() + { + if (_activityContext != null) + { + ActivitySpanId activitySpanId = _activityContext.SpanId; + return activitySpanId.ToHexString(); + } else { return null; } } - public void SetStatus(SpanStatusCode spanStatusCode, string message) + private GXActivityTraceFlags TraceFlags() { - _activity.SetStatus((ActivityStatusCode)spanStatusCode, message); + if (_activityContext != null) { + ActivityTraceFlags activityTraceFlags = _activityContext.TraceFlags; + return (GXActivityTraceFlags)activityTraceFlags; + } + else { return GXActivityTraceFlags.None;} } - public SpanStatusCode GetStatus() + private string TraceState() { - return (SpanStatusCode)_activity.GetStatus().StatusCode; + if (_activityContext != null) + return _activityContext.TraceState; + else return null; } - //ToDO - //public void AddEvent() - //{ - //} + // + // Summary: + // Specifies flags defined by the W3C standard that are associated with an activity. + internal enum GXActivityTraceFlags + { + // + // Summary: + // The activity has not been marked. + None = 0, + // + // Summary: + // The activity (or more likely its parents) has been marked as useful to record. + Recorded = 1 + } } } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs index 8ce722bef..812b1ae28 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs @@ -1,35 +1,103 @@ +using System; +using System.Collections.Generic; using System.Diagnostics; -using GeneXus.Application; +using System.Linq; using GeneXus.Attributes; +using GeneXus.Services.OpenTelemetry; namespace GeneXus.OpenTelemetry.Diagnostics { [GXApi] public class OtelTracer { + internal static ActivitySource activitySource; + internal static ActivitySource ActivitySource + { + get + { + if (activitySource == null) + { + activitySource = string.IsNullOrEmpty(OpenTelemetryService.GX_ACTIVITY_SOURCE_VERSION) ? new(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME) : new(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME, OpenTelemetryService.GX_ACTIVITY_SOURCE_VERSION); + } + return activitySource; + } + } public enum SpanType { - Client, - Consumer, Internal, + Server, + Client, Producer, - Server + Consumer + } + + public OtelSpan CreateSpan(string name) + { + //Returns null when the are no listeners. + Activity activity = ActivitySource.StartActivity(name); + if (activity != null) + return new OtelSpan(activity); + return null; } public OtelSpan CreateSpan(string name, SpanType kind) { - Activity activity = GXBaseObject.ActivitySource.StartActivity(name, (ActivityKind)kind); + Activity activity = ActivitySource.StartActivity(name, (ActivityKind)kind); + if (activity != null) + return new OtelSpan(activity); + else + return null; + } + + public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType) + { + Activity activity = ActivitySource.StartActivity(name, + kind: (ActivityKind)spanType, + parentContext: gxTraceContext.ActivityContext); + return new OtelSpan(activity); + + } + + public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType, IList gxSpanContexts) + { + //https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs#optional-links + List contexts = new List(); + + foreach (GXSpanContext gxSpanContext in gxSpanContexts) + { + ActivityContext context = gxSpanContext.ActivityContext; + contexts.Add(context); + } + + Activity activity = ActivitySource.StartActivity(name, + kind: (ActivityKind)spanType, + parentContext: gxTraceContext.ActivityContext, + links: contexts.Select(ctx => new ActivityLink(ctx))); return new OtelSpan(activity); } - public OtelSpan GetCurrent() + public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType, IList gxSpanContexts, DateTime dateTime) { - return new OtelSpan(Activity.Current); + //https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs#optional-links + List contexts = new List(); + + foreach (GXSpanContext gxSpanContext in gxSpanContexts) + { + ActivityContext context = gxSpanContext.ActivityContext; + contexts.Add(context); + } + + Activity activity = ActivitySource.StartActivity(name, + kind: (ActivityKind)spanType, + parentContext: gxTraceContext.ActivityContext, + links: contexts.Select(ctx => new ActivityLink(ctx)), + startTime:dateTime); + return new OtelSpan(activity); } - public bool HasListeners() + public static OtelSpan GetCurrent() { - return GXBaseObject.ActivitySource.HasListeners(); + return new OtelSpan(Activity.Current); } } } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs new file mode 100644 index 000000000..0e5630c85 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace GeneXus.OpenTelemetry +{ + public static class GXMeterProviderBuilder + { + public static MeterProviderBuilder AddGxMeterAspNetInstrumentation(this MeterProviderBuilder meter) + { + string envvar = Environment.GetEnvironmentVariable("OTEL_METRICS_EXPORTER"); + meter + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddOtlpExporter(); + if (envvar != null && envvar.Contains("console")) + meter.AddConsoleExporter(); + return meter; + } + + } +} diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj index 6c23dac29..171a608cd 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj @@ -1,21 +1,20 @@ - - net6.0;net8.0 - enable - enable - GeneXus.OpenTelemetry - OpenTelemetry GeneXus DotNet - GeneXus.OpenTelemetry.OpenTelemetry - NU1605 - + + net6.0;net8.0 + GeneXus.OpenTelemetry + OpenTelemetry GeneXus DotNet + GeneXus.OpenTelemetry.OpenTelemetry + NU1605 + - - - - - + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs index 58da3b8d2..124a86da6 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs @@ -1,3 +1,4 @@ +using System; using GeneXus.Services.OpenTelemetry; using OpenTelemetry.Trace; @@ -19,17 +20,15 @@ public static TracerProviderBuilder AddGxAspNetInstrumentation(this TracerProvid { opt.RecordException = true; }) - .AddAspNetCoreInstrumentation(opt => - { - opt.RecordException = true; - }) .AddSqlClientInstrumentation(opt => { opt.RecordException = true; opt.EnableConnectionLevelAttributes = true; opt.SetDbStatementForText = true; - }) - .AddConsoleExporter(); + }); + string envvar = Environment.GetEnvironmentVariable("OTEL_TRACES_EXPORTER"); + if (envvar != null && envvar.Contains("console")) + tracer.AddConsoleExporter(); return tracer; } } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs index 78eaf649e..c7c237dcf 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs @@ -2,23 +2,41 @@ using GeneXus.Services.OpenTelemetry; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Trace; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using System; +using Microsoft.IdentityModel.Tokens; namespace GeneXus.OpenTelemetry.OpenTelemetry { public class OpenTelemetryProvider : IOpenTelemetryProvider { + const string OTEL_METRICS_EXPORTER = "OTEL_METRICS_EXPORTER"; + const string OTEL_TRACES_EXPORTER = "OTEL_TRACES_EXPORTER"; public OpenTelemetryProvider(GXService s) { } public bool InstrumentAspNetCoreApplication(IServiceCollection services) { - services.AddOpenTelemetry().WithTracing(tracerProviderBuilder => - { - tracerProviderBuilder - .AddOtlpExporter() - .AddGxAspNetInstrumentation(); - }); + string envvar = Environment.GetEnvironmentVariable(OTEL_TRACES_EXPORTER); + if (envvar.IsNullOrEmpty() || !envvar.ToLower().Equals("none")) { + services.AddOpenTelemetry().WithTracing(tracerProviderBuilder => + { + tracerProviderBuilder + .AddOtlpExporter() + .AddGxAspNetInstrumentation(); + }); + } + + envvar = Environment.GetEnvironmentVariable(OTEL_METRICS_EXPORTER); + if (envvar.IsNullOrEmpty() || !envvar.ToLower().Equals("none")) { + services.AddOpenTelemetry().WithMetrics(metricsProviderBuilder => + { + metricsProviderBuilder + .AddGxMeterAspNetInstrumentation(); + }); + } return true; } } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs index 2aae5321f..7de8b29f1 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs @@ -1,12 +1,11 @@ using System; +using GeneXus.Services; using GeneXus.Services.OpenTelemetry; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry; -using OpenTelemetry.Trace; using OpenTelemetry.Contrib.Extensions.AWSXRay.Trace; -using GeneXus.Services; -using GeneXus.Services.Common; -using GeneXus.Diagnostics; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; namespace GeneXus.OpenTelemetry.AWS { @@ -33,6 +32,10 @@ public bool InstrumentAspNetCoreApplication(IServiceCollection _) .AddGxAspNetInstrumentation() .Build(); + Sdk.CreateMeterProviderBuilder() + .AddGxMeterAspNetInstrumentation() + .Build(); + Sdk.SetDefaultTextMapPropagator(new AWSXRayPropagator()); return true; diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs index 98d6c7d1c..96c804a64 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs @@ -56,6 +56,7 @@ public bool InstrumentAspNetCoreApplication(IServiceCollection _) GXLogging.Debug(log, "Connect to Azure monitor Opentelemetry Metrics exporter using Default Azure credential"); } }) + .AddGxMeterAspNetInstrumentation() .Build(); return true; } diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj index 12ae1791b..6fcea0822 100644 --- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj @@ -11,13 +11,13 @@ - - + + - + - - + + diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj index 2e1e3ce13..ca162324b 100644 --- a/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj +++ b/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj @@ -13,6 +13,7 @@ + diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj index 099f6efb1..62f614c32 100644 --- a/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj +++ b/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj @@ -15,6 +15,7 @@ + diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj index 0c3bfd68c..1af8b36e6 100644 --- a/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj +++ b/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj @@ -14,6 +14,7 @@ + diff --git a/dotnet/src/dotnetcore/Reor/Reor.csproj b/dotnet/src/dotnetcore/Reor/Reor.csproj index 59e7feedf..7743e4090 100644 --- a/dotnet/src/dotnetcore/Reor/Reor.csproj +++ b/dotnet/src/dotnetcore/Reor/Reor.csproj @@ -16,6 +16,10 @@ + + + + diff --git a/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs b/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs index d30bdc160..6357186e2 100644 --- a/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs +++ b/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs @@ -15,7 +15,7 @@ public class ExternalStorage : GxStorageProvider private GXService providerService; - static readonly IGXLogger logger = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger logger = GXLoggerFactory.GetLogger(); public ExternalStorage() { diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs index 08d94ef70..8ec7b8ee8 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs @@ -21,7 +21,9 @@ namespace GeneXus.Application #endif using GeneXus.Configuration; using GeneXus.Metadata; +#if !NETCORE using Jayrock.Json; +#endif using GeneXus.Http; using System.Collections.Specialized; using System.Collections.Generic; @@ -47,6 +49,8 @@ namespace GeneXus.Application using System.Security.Claims; using System.Security; using Microsoft.Net.Http.Headers; + using System.Threading.Tasks; + using GeneXus.Data.ADO; public interface IGxContext { @@ -320,7 +324,6 @@ internal class GxApplication public class GxContext : IGxContext { private static IGXLogger log = null; - internal static bool configurationLoaded = Config.configLoaded; internal static string GX_SPA_REQUEST_HEADER = "X-SPA-REQUEST"; internal static string GX_SPA_REDIRECT_URL = "X-SPA-REDIRECT-URL"; internal const string GXLanguage = "GXLanguage"; @@ -1406,12 +1409,27 @@ public IGxDataStore GetDataStore(string id) return ds; return null; } +#if NETCORE + internal async Task CloseConnectionsAsync() + { + GxUserInfo.RemoveHandle(this.handle); + foreach (GxDataStore ds in _DataStores) + await ds.CloseConnectionsAsync(); + + CloseConnectionsResources(); + } +#endif public void CloseConnections() { GxUserInfo.RemoveHandle(this.handle); foreach (IGxDataStore ds in _DataStores) ds.CloseConnections(); + CloseConnectionsResources(); + } + + private void CloseConnectionsResources() + { if (_reportHandlerToClose != null) { for (int i = 0; i < _reportHandlerToClose.Count; i++) diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs index db1162f17..7b029ad64 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs @@ -771,6 +771,7 @@ public short dfwnext() return GX_ASCDEL_WRITEERROR; } } + const int MAX_DECIMAL_PRECISION = 29; public short dfwpnum(decimal num, int dec) { if (_writeStatus == FileIOStatus.Closed) @@ -778,7 +779,7 @@ public short dfwpnum(decimal num, int dec) GXLogging.Error(log, "Error ADF0004"); return GX_ASCDEL_INVALIDSEQUENCE; } - appendFld(StringUtil.Str(num, 18, dec).TrimStart(null)); + appendFld(StringUtil.Str(num, MAX_DECIMAL_PRECISION, dec).TrimStart(null)); return GX_ASCDEL_SUCCESS; } public short dfwptxt(string s, int len) diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs index 9f2919a80..21cf5a104 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs @@ -665,18 +665,83 @@ private enum FORMAT_SECTION NEGATIVE_VALUES = -1, ZEROS = 0 } - private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsLiterals, FORMAT_SECTION section) + const char ESCAPE_CHARACTER = '\\'; + const char NUMBER_SIGN = '#'; + const char QUESTION_MARK = '?'; + const char BLANK = ' '; + static bool EscapedSymbol(string picture, int idx) + { + return (idx > 0 && picture[idx - 1] == ESCAPE_CHARACTER); + } + static int LeadingBlanks(string gxpicture) + { + int leadingBlanks = 0; + bool inBlanks = false; + if (gxpicture.Contains(QUESTION_MARK)) { + for (int i = 0; i < gxpicture.Length; i++) + { + if (gxpicture[i] == QUESTION_MARK && !EscapedSymbol(gxpicture, i)) + { + inBlanks = true; + } + else if ((gxpicture[i] == '.') && !EscapedSymbol(gxpicture, i)) + { + inBlanks = false; + break; + } + + if (inBlanks) + leadingBlanks++; + } + } + return leadingBlanks; + } + static int TrailingBlanks(string gxpicture, out bool decimalsAsBlank) + { + int trailingBlanks = 0; + int sep = gxpicture.IndexOf('.'); + decimalsAsBlank = false; + if (sep >= 0) + { + string rightPic = gxpicture.Substring(sep); + if (rightPic.Contains(QUESTION_MARK)) + { + for (int i = gxpicture.Length-1; i >= 0; i--) + { + if (gxpicture[i] == QUESTION_MARK && !EscapedSymbol(gxpicture, i)) + trailingBlanks++; + else if (gxpicture[i] == '.') + { + decimalsAsBlank = true; + break; + } + else if (gxpicture[i] == NUMBER_SIGN || gxpicture[i] == 'Z' || gxpicture[i] == '9') + { + decimalsAsBlank = false; + break; + } + } + } + } + return trailingBlanks; + } + + private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsLiterals, FORMAT_SECTION section, int digits, int decimals) { if (string.IsNullOrEmpty(gxpicture)) return string.Empty; bool inDecimals = false; StringBuilder strPicture = new StringBuilder("{0,"); - strPicture.Append(gxpicture.Length); + strPicture.Append(PictureLength(gxpicture)); strPicture.Append(':'); bool blankwhenzero = true; bool explicitSign = (gxpicture[0] == '+'); bool withoutMinusSign = (gxpicture[0] == '(' && gxpicture[gxpicture.Length - 1] == ')') || gxpicture.EndsWith("DB") || explicitSign; + int totalLeadingBlanks = LeadingBlanks(gxpicture); + int totalRighBlanks = TrailingBlanks(gxpicture, out bool decimalsAsBlank); + int lBlanks = 0; + int rDigits = 0; if (section == FORMAT_SECTION.NEGATIVE_VALUES && withoutMinusSign) //If it has a sign, then use the first section (which by default assigns only a negative sign). @@ -688,9 +753,34 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL strPicture.Append(';');//Section Separator. strPicture.Append(';');//Section Separator. } + if (gxpicture.IndexOf(ESCAPE_CHARACTER) >= 0) + { + StringBuilder gxEscapedPicture = new StringBuilder(); + for (int i = 0; i < gxpicture.Length; i++) + { + + if (gxpicture[i] == ESCAPE_CHARACTER && i + 1 < gxpicture.Length && gxpicture[i + 1] == ESCAPE_CHARACTER) + { + gxEscapedPicture.Append(ESCAPE_CHARACTER); + gxEscapedPicture.Append(ESCAPE_CHARACTER); + gxEscapedPicture.Append(ESCAPE_CHARACTER); + i++; + } + else + { + gxEscapedPicture.Append(gxpicture[i]); + } + } + gxpicture = gxEscapedPicture.ToString(); + } for (int i = 0; i < gxpicture.Length; i++) { - if (gxpicture[i] == 'Z') + bool inLiteral = EscapedSymbol(gxpicture, i); + if (inLiteral || gxpicture[i] == ESCAPE_CHARACTER) + { + strPicture.Append(gxpicture[i]); + } + else if (gxpicture[i] == 'Z') { if (inDecimals) { @@ -701,7 +791,39 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL strPicture.Append('0'); } else - strPicture.Append('#'); + strPicture.Append(NUMBER_SIGN); + } + else if (gxpicture[i] == NUMBER_SIGN) + { + strPicture.Append(NUMBER_SIGN); + } + else if (gxpicture[i] == QUESTION_MARK) + { + if (inDecimals) + { + if (rDigits >= decimals && rDigits < totalRighBlanks) + { + strPicture.Append(BLANK); + } + else + { + strPicture.Append(NUMBER_SIGN); + } + rDigits++; + } + else + { + if (lBlanks < (totalLeadingBlanks - digits)) + { + strPicture.Append(BLANK); + lBlanks++; + } + else + { + strPicture.Append(NUMBER_SIGN); + } + } + } else if (gxpicture[i] == '9') { @@ -711,11 +833,20 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL else if (gxpicture[i] == '.') { inDecimals = true; - if (i > 0 && strPicture[strPicture.Length - 1] == '#') strPicture[strPicture.Length - 1] = '0'; + if (i > 0 && strPicture[strPicture.Length - 1] == NUMBER_SIGN) strPicture[strPicture.Length - 1] = '0'; if (separatorsAsLiterals) strPicture.Append("\".\""); else - strPicture.Append(gxpicture[i]); + { + if (decimalsAsBlank && decimals == 0) + { + strPicture.Append(BLANK); //Replace decimal separator by blank + } + else + { + strPicture.Append(gxpicture[i]); + } + } } else if (gxpicture[i] == ',') { @@ -724,6 +855,10 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL else strPicture.Append(gxpicture[i]); } + else if (gxpicture[i] == BLANK) + { + strPicture.Append(BLANK); + } else { //0,#,.,,,%,E0, E+0, E-0,e0,e+0,e-0, are characters with special meaning in Custom Numeric Format Strings of .net @@ -740,7 +875,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL //Pictures (99.9) => 12.5 -12.5 if (section != FORMAT_SECTION.NEGATIVE_VALUES && withoutMinusSign && (i == 0 || i == gxpicture.Length - 1)) { - strPicture.Append(' '); + strPicture.Append(BLANK); } else { @@ -751,7 +886,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL //Pictures +99.9 => +12.5 -12.5 if (explicitSign && i == 0 && section == FORMAT_SECTION.ZEROS) { - strPicture.Append(' '); + strPicture.Append(BLANK); } else if (explicitSign && i == 0 && section == FORMAT_SECTION.NEGATIVE_VALUES) { @@ -769,7 +904,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL if (section == FORMAT_SECTION.POSITIVE_VALUES) strPicture.Append('C'); else - strPicture.Append(' '); + strPicture.Append(BLANK); } else { @@ -782,7 +917,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL if (section == FORMAT_SECTION.POSITIVE_VALUES) strPicture.Append('R'); else - strPicture.Append(' '); + strPicture.Append(BLANK); } else { @@ -799,14 +934,29 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL } if (blankwhenzero && section == FORMAT_SECTION.ZEROS)//Z,ZZZ,ZZZ.ZZ format 0.00 to "". sac.20145 { - return Replicate(' ', gxpicture.Length); + return Replicate(BLANK, gxpicture.Length); } else { return strPicture.Append('}').ToString(); } } - static bool useLiteralSeparators(string gxpicture) + + private static int PictureLength(string gxpicture) + { + int count = 0; + if (gxpicture.Contains(ESCAPE_CHARACTER)) + { + foreach (char ch in gxpicture) + { + if (ch == ESCAPE_CHARACTER) + count++; + } + } + return gxpicture.Length - count; + } + + static bool UseLiteralSeparators(string gxpicture) { // If it has non-numerical characters, then the separators are used as literals @@ -861,7 +1011,7 @@ public static string GetPictureFromLD(int digits, int decimals, int thousandSepL } else { - str.Append('#'); + str.Append(NUMBER_SIGN); } } } @@ -877,14 +1027,14 @@ public static string GetPictureFromLD(int digits, int decimals, int thousandSepL } public static string Concat(string init, string last) { - char[] trimChars = { ' ' }; + char[] trimChars = { BLANK }; StringBuilder fmtString = new StringBuilder(init.TrimEnd(trimChars)); fmtString.Append(last); return fmtString.ToString(); } public static string Concat(string init, string last, string separator) { - char[] trimChars = { ' ' }; + char[] trimChars = { BLANK }; StringBuilder fmtString = new StringBuilder(init.TrimEnd(trimChars)); fmtString.Append(separator); fmtString.Append(last); @@ -1011,7 +1161,7 @@ static public int Len(string s) static public string Trim(string s) { if (!string.IsNullOrEmpty(s)) - return s.Trim(' '); + return s.Trim(BLANK); else return s; } @@ -1020,8 +1170,8 @@ static public string RTrim(string s) if (!string.IsNullOrEmpty(s)) { int len = s.Length; - if (len > 0 && s[len - 1] == ' ') - return s.TrimEnd(' '); + if (len > 0 && s[len - 1] == BLANK) + return s.TrimEnd(BLANK); else return s; } @@ -1033,8 +1183,8 @@ static public string LTrim(string s) if (!string.IsNullOrEmpty(s)) { int len = s.Length; - if (len > 0 && s[0] == ' ') - return s.TrimStart(' '); + if (len > 0 && s[0] == BLANK) + return s.TrimStart(BLANK); else return s; } @@ -1166,9 +1316,14 @@ public string Format(decimal value, string gxpicture) { section = FORMAT_SECTION.ZEROS; } - bool separatorsAsLiterals = useLiteralSeparators(gxpicture); + bool separatorsAsLiterals = UseLiteralSeparators(gxpicture); + string invariantStrValue = Math.Abs(value).ToString(CultureInfo.InvariantCulture.NumberFormat); + int decSeparatorIdx = invariantStrValue.IndexOf(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator); + + int digits = WholeDigits(value, invariantStrValue, decSeparatorIdx); + int decimals = DecimalDigits(invariantStrValue, decSeparatorIdx); - string picture = GxPictureToNetPicture(gxpicture, separatorsAsLiterals, section); + string picture = GxPictureToNetPicture(gxpicture, separatorsAsLiterals, section, digits, decimals); //It must consider format because it can have other types of characters that are not Z or 9 or. neither ,. string res; if (!string.IsNullOrEmpty(picture)) @@ -1177,7 +1332,7 @@ public string Format(decimal value, string gxpicture) } else { - res = value.ToString(CultureInfo.InvariantCulture.NumberFormat); + res = invariantStrValue; } if (separatorsAsLiterals) { @@ -1191,7 +1346,35 @@ public string Format(decimal value, string gxpicture) { return ReplaceSeparators(res, numFmtInfo.NumberDecimalSeparator, numFmtInfo.NumberGroupSeparator); } + } + int DecimalDigits(string invariantStrValue, int decSeparatorIdx) + { + int decimals; + if (decSeparatorIdx < 0) + decimals = 0; + else + decimals = invariantStrValue.Length - decSeparatorIdx - 1; + return decimals; + } + int WholeDigits(decimal value, string invariantStrValue, int decSeparatorIdx) + { + int digits; + if (value == 0) + { + digits = 1; + } + else if (value < 1 && value >= 0) + digits = 0; + else if (decSeparatorIdx < 0) + { + digits = invariantStrValue.Length; + } + else + { + digits = decSeparatorIdx; + } + return digits; } string FormatNumber(string s, string p) { @@ -1218,7 +1401,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep) pStart = p.Length; else pDec = p.Length - pStart; // decimal count (including point) - StringBuilder result = new StringBuilder(new string(' ', Math.Max(p.Length, s.Length))); + StringBuilder result = new StringBuilder(new string(BLANK, Math.Max(p.Length, s.Length))); // Process the left of the decimal point j = sStart - 1; k = pStart - 1; @@ -1229,7 +1412,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep) case '9': if (j < 0) result[k--] = '0'; - else if (s[j] == ' ') + else if (s[j] == BLANK) result[k--] = '0'; else result[k--] = s[j]; @@ -1237,10 +1420,10 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep) break; case 'Z': if (j < 0) - result[k--] = ' '; + result[k--] = BLANK; else if (leftZ || leftZero(s, j)) { - result[k--] = ' '; + result[k--] = BLANK; leftZ = true; } else @@ -1288,7 +1471,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep) case 'Z': if (rightZ || rightZero(s, j)) { - result[i] = ' '; + result[i] = BLANK; rightZ = true; } else if (j < s.Length) @@ -1392,7 +1575,7 @@ static public bool ToBoolean(string value) } public static string Space(int spaces) { - return new string(' ', spaces); + return new string(BLANK, spaces); } public static string Right(string text, int size) { @@ -1418,7 +1601,7 @@ public static string Left(string text, int size) static public bool Like(string str, string ptrn) { - return Like(str, ptrn, ' '); + return Like(str, ptrn, BLANK); } static public bool Like(string str, string ptrn, char escape) { @@ -1443,10 +1626,10 @@ static public bool Like(string str, string ptrn, char escape) srchPtr = 0; scapeCount = 0; - wildChr = ' '; - srchChr = ' '; + wildChr = BLANK; + srchChr = BLANK; - bool useEscape = escape != ' '; + bool useEscape = escape != BLANK; bool isEscape = false; bool applyEscape = false; @@ -1463,7 +1646,7 @@ static public bool Like(string str, string ptrn, char escape) if (srchPtr <= srchLen) srchChr = str[srchPtr - scapeCount]; else - srchChr = ' '; + srchChr = BLANK; } if (isEscape) @@ -1624,7 +1807,7 @@ public static string JSONEncode(string s) sb.Append("\\r"); else { - if (ch < ' ') + if (ch < BLANK) { sb.Append("\\u"); sb.Append(((int)ch).ToString("x4", CultureInfo.InvariantCulture)); @@ -2656,18 +2839,18 @@ public static DateTime CToD2(string value) } return nullDate; } - internal static DateTime CToDT2(string jsonDate) { + internal static DateTime CToDT2(string jsonDate, IGxContext context) { if (!string.IsNullOrEmpty(jsonDate) && (jsonDate.Contains(ISO_8601_TIME_SEPARATOR) || jsonDate.Contains(ISO_8601_TIME_SEPARATOR_1))) { - return CToT2(jsonDate); + return CToT2(jsonDate, context); } else { return CToD2(jsonDate); } } - public static DateTime CToT2(string value) + public static DateTime CToT2(string value, IGxContext context) { if (isNullJsonDate(value)) return nullDate; @@ -2684,38 +2867,76 @@ public static DateTime CToT2(string value) } } if (Preferences.useTimezoneFix()) - ret = fromUniversalTime(ret); + { + if (context==null) + ret = fromUniversalTime(ret); + else + ret = fromUniversalTime(ret, context); + } return ret; } } + //[Obsolete("CToT2 is deprecated, use CToT2(string, IGxContext) instead", false)] + public static DateTime CToT2(string value) + { + return CToT2(value, null); + } + //[Obsolete("TToC2 is deprecated, use TToC2(DateTime, IGxContext) instead", false)] public static string TToC2(DateTime dt) { return TToC2(dt, true); } + public static string TToC2(DateTime dt, IGxContext context) + { + return TToC2(dt, true, context); + } + //[Obsolete("TToC2 is deprecated, use TToC2(DateTime, bool, IGxContext) instead", false)] public static string TToC2(DateTime dt, bool toUTC) { - return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, toUTC); + return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, null, toUTC); + } + internal static string TToC2(DateTime dt, bool toUTC, IGxContext context) + { + return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, context, toUTC); } + //[Obsolete("TToC3 is deprecated, use TToC3(DateTime, IGxContext) instead", false)] public static string TToC3(DateTime dt) { return TToC3(dt, true); } + public static string TToC3(DateTime dt, IGxContext context) + { + return TToC3(dt, true, context); + } internal const string JsonDateFormatMillis = "yyyy-MM-ddTHH:mm:ss.fff"; internal const string JsonDateFormat = "yyyy-MM-ddTHH:mm:ss"; - + + //[Obsolete("TToC3 is deprecated, use TToC3(DateTime, bool, IGxContext) instead", false)] public static string TToC3(DateTime dt, bool toUTC) { - return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, toUTC); + return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, null, toUTC); + } + internal static string TToC3(DateTime dt, bool toUTC, IGxContext context) + { + return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, context, toUTC); } - static string TToCRest(DateTime dt, String nullString, String formatStr, bool toUTC=true) + static string TToCRest(DateTime dt, String nullString, String formatStr, IGxContext context, bool toUTC=true) { if (dt == nullDate) return FormatEmptyDate(nullString); else { - DateTime ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt) : dt) : dt; + DateTime ret; + if (context != null) + { + ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt, context) : dt) : dt; + } + else + { + ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt) : dt) : dt; + } return ret.ToString(formatStr, CultureInfo.InvariantCulture); } } @@ -3536,7 +3757,7 @@ static private bool isNullDateCompatible(DateTime dt) else return isNullDate(dt); } - + //[Obsolete("fromUniversalTime is deprecated, use fromUniversalTime(DateTime, IGxContext) instead", false)] static public DateTime fromUniversalTime(DateTime dt) { #if NODATIME @@ -3545,7 +3766,15 @@ static public DateTime fromUniversalTime(DateTime dt) return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, GxContext.Current.GetOlsonTimeZone()); #endif } - + static public DateTime fromUniversalTime(DateTime dt, IGxContext context) + { +#if NODATIME + return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, context.GetTimeZone()); +#else + return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, context.GetOlsonTimeZone()); +#endif + } + //[Obsolete("toUniversalTime is deprecated, use toUniversalTime(DateTime, IGxContext) instead", false)] static public DateTime toUniversalTime(DateTime dt) { #if NODATIME @@ -5455,15 +5684,22 @@ public static void ErrorToMessages(string errorId, string errorDescription, GXBa } } public static void ErrorToMessages(string errorId, Exception ex, GXBaseCollection Messages) + { + ErrorToMessages(errorId, ex, Messages, true); + } + internal static void ErrorToMessages(string errorId, Exception ex, GXBaseCollection Messages, bool parseInnerExceptions) { if (Messages != null && ex != null) { StringBuilder str = new StringBuilder(); str.Append(ex.Message); - while (ex.InnerException != null) + if (parseInnerExceptions) { - str.Append(ex.InnerException.Message); - ex = ex.InnerException; + while (ex.InnerException != null) + { + str.Append(ex.InnerException.Message); + ex = ex.InnerException; + } } ErrorToMessages(errorId, str.ToString(), Messages); } @@ -5551,7 +5787,7 @@ public string IniReadValue(string Section, string Key) #endif public static class GXDbFile { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXDbFile).FullName); private static Regex schemeRegex = new Regex("^" + Scheme + ":", RegexOptions.Compiled); @@ -5772,7 +6008,7 @@ public static string PathToUrl(string path, bool absUrl, IGxContext context = nu public static class GxImageUtil { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GxImageUtil).FullName); private static Bitmap BitmapCreateFromStream(string filePathOrUrl) { Uri uri; diff --git a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs index 4eebfe44d..b78405faa 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs @@ -1,7 +1,10 @@ namespace GeneXus.Http { using System; + using System.IO; +#if !NETCORE using Jayrock.Json; +#endif using System.Web; using GeneXus.Application; using GeneXus.Utils; @@ -41,12 +44,6 @@ public interface IHttpAjaxContext void AddStylesHidden(); void ajax_rsp_clear(); - JArray AttValues { get;} - JObject HiddenValues { get;} - JArray PropValues { get; } - JObject WebComponents { get;} - JObject Messages { get;} - JArray Grids { get; } void LoadFormVars(HttpContext localHttpContext); void disableJsOutput(); void enableJsOutput(); @@ -88,7 +85,7 @@ public void ClearParmsMetadata() public bool isInputParm(string key) { - return inParmsMetadataHash.Contains(key); + return (inParmsMetadataHash!=null && inParmsMetadataHash.Contains(key)); } public void Clear() @@ -108,7 +105,7 @@ public bool isParmModified(string fieldName, object value) IGxJSONSerializable jsonValue = value as IGxJSONSerializable; if (jsonValue!=null) { - if (!inParmsHashValue.ContainsKey(fieldName)) + if (inParmsHashValue!=null && !inParmsHashValue.ContainsKey(fieldName)) return true; return GXUtil.GetHash(jsonValue.ToJSonString()) != inParmsHashValue[fieldName]; } @@ -146,24 +143,24 @@ public class HttpAjaxContext : IHttpAjaxContext public string FormCaption { get; set; } - public JObject HiddenValues + internal JObject HiddenValues { get { return _HiddenValues; } } - public JArray AttValues + internal JArray AttValues { get { return _AttValues; } } - public JArray PropValues + internal JArray PropValues { get { return _PropValues; } } - public JObject WebComponents + internal JObject WebComponents { get { return _WebComponents; } } - public Hashtable LoadCommands + internal Hashtable LoadCommands { get { return _LoadCommands; } } - public JObject Messages + internal JObject Messages { get { return _Messages; } } - public JArray Grids + internal JArray Grids { get { return _Grids; } } - public JObject ComponentObjects + internal JObject ComponentObjects { get { return _ComponentObjects; } } public GXAjaxCommandCollection Commands { get { return commands; } } @@ -210,7 +207,7 @@ public void appendAjaxCommand(String cmdType, Object cmdData) commands.AppendCommand(new GXAjaxCommand(cmdType, cmdData)); } - public void appendLoadData(int SId, JObject Data) + internal void appendLoadData(int SId, JObject Data) { LoadCommands[SId] = Data; } @@ -349,7 +346,7 @@ public void ajax_rsp_assign_sdt_attri(String CmpContext, bool IsMasterPage, Stri try { JObject obj = GetGxObject(AttValues, CmpContext, IsMasterPage); - if (obj != null && (DynAjaxEventContext.isParmModified(AttName, SdtObj) || !isUndefinedOutParam( AttName, SdtObj))) + if (obj != null && (!isUndefinedOutParam(AttName, SdtObj) || DynAjaxEventContext.isParmModified(AttName, SdtObj))) { IGxJSONAble SdtObjJson = SdtObj as IGxJSONAble; if (SdtObjJson != null) @@ -667,7 +664,7 @@ internal string getJSONResponse(string cmpContext) return jsonCmdWrapper.ToString(); } - public static JArray GetParmsJArray(Object[] parms) + internal static JArray GetParmsJArray(Object[] parms) { JArray inputs = new JArray(); for (int i = 0; i < parms.Length; i++) @@ -688,29 +685,36 @@ public static JArray GetParmsJArray(Object[] parms) public string GetAjaxEncryptionKey() { - if (context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY) == null) + string ajaxKey = context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY); + if (ajaxKey == null) { - if(!RecoverEncryptionKey()) - context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY,CryptoImpl.GetRijndaelKey()); + string sessionKey; + if (!RecoverEncryptionKey(out sessionKey)) { + ajaxKey = CryptoImpl.GetRijndaelKey(); + context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, ajaxKey); + } + else + { + ajaxKey = sessionKey; + } } - return context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY); + return ajaxKey; } - private bool RecoverEncryptionKey() + private bool RecoverEncryptionKey(out string sessionKey) { - if ( (context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY) == null)) + sessionKey = null; + if (context.HttpContext != null) { - if (context.HttpContext != null) + String clientKey = context.HttpContext.Request.Headers[CryptoImpl.AJAX_SECURITY_TOKEN]; + if (!string.IsNullOrEmpty(clientKey)) { - String clientKey = context.HttpContext.Request.Headers[CryptoImpl.AJAX_SECURITY_TOKEN]; - if (!string.IsNullOrEmpty(clientKey)) + bool correctKey; + clientKey = CryptoImpl.DecryptRijndael(CryptoImpl.GX_AJAX_PRIVATE_IV + clientKey, CryptoImpl.GX_AJAX_PRIVATE_KEY, out correctKey); + if (correctKey) { - bool correctKey = false; - clientKey = CryptoImpl.DecryptRijndael(CryptoImpl.GX_AJAX_PRIVATE_IV + clientKey, CryptoImpl.GX_AJAX_PRIVATE_KEY, out correctKey); - if (correctKey) - { - context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, clientKey); - return true; - } + sessionKey = clientKey; + context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, clientKey); + return true; } } } @@ -744,7 +748,7 @@ public void LoadFormVars(HttpContext localHttpContext) } } - public void ParseGXState(JObject tokenValues) + internal void ParseGXState(JObject tokenValues) { if (tokenValues != null) { @@ -821,7 +825,7 @@ public string Content } } - public class GXJObject : JObject + internal class GXJObject : JObject { private bool base64Encoded; @@ -882,7 +886,7 @@ public object Data } } - public JObject JSONObject + internal JObject JSONObject { get { @@ -1027,7 +1031,7 @@ private GXAjaxCommand GetCommand(GXAjaxCommand cmd) return null; } - public JArray JSONArray + internal JArray JSONArray { get { diff --git a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs index 6a3789eaf..5dadc513c 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs @@ -157,7 +157,25 @@ public static bool GetValueOf(string sId, out string sString) return false; } } - + internal static bool GetValueOrEnvironmentVarOf(string sId, out string sString) + { + try + { + sString = config.Get(sId); + if (String.IsNullOrEmpty(sString)) + { + sString = MappedValue(sId, sString); + if (string.IsNullOrEmpty(sString)) + return false; + } + return true; + } + catch + { + sString = string.Empty; + return false; + } + } public static bool GetValueOf(string sId) { string sString; @@ -533,14 +551,15 @@ static NameValueCollection config { loadedConfigFile = configFileName; _config = loadConfig(configFileName, out logConfigSource); + configLoaded = true; if (!string.IsNullOrEmpty(logConfigSource)) logConfig(logConfigSource, out configuredFilename); else logConfig(configFileName, out configuredFilename); } +#if !NETCORE else { -#if !NETCORE if (GxContext.IsHttpContext && File.Exists(Path.Combine(GxContext.StaticPhysicalPath(), "web.config"))) { @@ -576,10 +595,13 @@ static NameValueCollection config logConfig(logFile, out configuredFilename); loadedConfigFile = Path.GetFullPath(file); _config = loadConfig(file); - } - + configLoaded = true; + } #else + else + { + string basePath = FileUtil.GetBasePath(); string currentDir = Directory.GetCurrentDirectory(); string startupDir = FileUtil.GetStartupDirectory(); @@ -635,8 +657,8 @@ static NameValueCollection config { //"Could not register encoding provider"; } -#endif } +#endif } } log = GXLoggerFactory.GetLogger(); @@ -826,6 +848,15 @@ public class Preferences const string DEFAULT_DS = "Default"; static int httpclient_max_per_route = -1; static int sessionTimeout = -1; + static int singletonHttpClient = -1; + internal static string AppMainNamespace + { + get + { + Config.GetValueOf("AppMainNamespace", out string nameSpace); + return nameSpace; + } + } internal static string DefaultDatastore { get @@ -1413,6 +1444,18 @@ internal static bool WrapSingleApiOutput return DefaultWrapSingleApiOutput; } } + + internal static bool SingletonHttpClient() + { + if (singletonHttpClient == -1) + { + if (Config.GetValueOrEnvironmentVarOf("HTTPCLIENT_SINGLETON", out string sValue) && int.TryParse(sValue, out int value)) + singletonHttpClient = value; + else + singletonHttpClient = 1; + } + return singletonHttpClient==1; + } internal static string CorsAllowedOrigins() { if (Config.GetValueOf("CORS_ALLOW_ORIGIN", out string corsOrigin)) @@ -1454,27 +1497,27 @@ public static string GetDefaultTheme() else return ""; } - - public static int GetHttpClientMaxConnectionPerRoute() + internal const int DEFAULT_HTTPCLIENT_MAX_PER_ROUTE= 1000; + internal static int GetHttpClientMaxConnectionPerRoute() { if (httpclient_max_per_route == -1) { try { string strmax; - if (Config.GetValueOf("HTTPCLIENT_MAX_PER_ROUTE", out strmax)) + if (Config.GetValueOrEnvironmentVarOf("HTTPCLIENT_MAX_PER_ROUTE", out strmax)) { httpclient_max_per_route = Convert.ToInt32(strmax); } else { - httpclient_max_per_route = 1000; + httpclient_max_per_route = DEFAULT_HTTPCLIENT_MAX_PER_ROUTE; } } catch (Exception ex) { GXLogging.Error(log, "HttpClientMaxPerRoute error", ex); - httpclient_max_per_route = 1000; + httpclient_max_per_route = DEFAULT_HTTPCLIENT_MAX_PER_ROUTE; } } return httpclient_max_per_route; diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs index 22938b4ad..7cfe94720 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs @@ -8,6 +8,7 @@ using System.IO; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Configuration; @@ -212,7 +213,22 @@ public void RemoveAllConnections(int handle) throw e; } } +#if NETCORE + internal async Task RemoveConnectionAsync(int handle, string dataSource) + { + ServerUserInformation sui; + if (userConnections.TryGetValue(handle, out sui)) + { + GXLogging.Debug(log, "RemoveConnection handle " + handle + ",datasource:" + dataSource); + GxConnection con = sui[dataSource]; + if (sui.TryRemove(dataSource, out con)) + await con.DisposeAsync(); + ServerUserInformation suiDeleted; + if (sui.Count == 0) userConnections.TryRemove(handle, out suiDeleted); + } + } +#endif public void RemoveConnection(int handle, string dataSource) { @@ -411,7 +427,12 @@ public void Dispose() { Close(); } - +#if NETCORE + internal async Task DisposeAsync() + { + await CloseAsync(); + } +#endif public IGxDataStore DataStore { get{ return dataStore;} @@ -736,7 +757,61 @@ public void Close() wmiconnection.CleanUp(); } } +#if NETCORE + internal async Task CloseAsync() + { + if (connection != null) + { + GXLogging.Debug(log, "GxConnection.Close Id " + " connection State '" + connection.State + "'" + " handle:" + handle + " datastore:" + DataStore.Id); + } + if (connection != null && ((connection.State & ConnectionState.Closed) == 0)) + { + try + { + connectionCache.Clear(); + } + catch (Exception e) + { + GXLogging.Warn(log, "GxConnection.Close can't close all prepared cursors", e); + } + GXLogging.Debug(log, "UncommitedChanges before Close:" + UncommitedChanges); + try + { + if (UncommitedChanges) + { + rollbackTransactionOnly(); + UncommitedChanges = false; + } + } + catch (Exception e) + { + GXLogging.Warn(log, "GxConnection.Close can't rollback transaction", e); + } + try + { + await connection.CloseAsync(); + if (transaction != null) + { + transaction.Dispose(); + transaction = null; + } + + } + catch (Exception e) + { + GXLogging.Warn(log, "GxConnection.Close can't close connection", e); + } + spid = 0; + GXLogging.Debug(log, "GxConnection.Close connection is closed "); + } + m_opened = false; + if (Preferences.Instrumented && wmiconnection != null) + { + wmiconnection.CleanUp(); + } + } +#endif public int OpenHandles { get{return openHandles;} @@ -2719,7 +2794,12 @@ GxDataRecord getDbmsDataRecord(string id, string dbms) switch (dbms) { case "sqlserver": - return new GxSqlServer(); + GxSqlServer gxSqlServer = new GxSqlServer(); + if (Config.GetValueOf("Connection-" + id + "-UseSmallDateTime", out cfgBuf) && cfgBuf == "1") + { + gxSqlServer.UseSmallDateTime(); + } + return gxSqlServer; case "mysql": #if NETCORE return new GxMySqlConnector(id); @@ -2837,6 +2917,12 @@ public void CloseConnections() { GxConnectionManager.Instance.RemoveConnection(handle, id); } +#if NETCORE + internal async Task CloseConnectionsAsync() + { + await ((GxConnectionManager)GxConnectionManager.Instance).RemoveConnectionAsync(handle, id); + } +#endif public void Release() { } diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs index 362374b08..1eb63d2f7 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs @@ -25,6 +25,7 @@ using GeneXus.Metadata; using System.Data.Common; using System.Linq; +using System.Threading.Tasks; namespace GeneXus.Data { @@ -1599,7 +1600,12 @@ public class GxSqlServer : GxDataRecord private const string INTEGRATED_SECURITY_NO = "no"; #endif private bool multipleDatareadersEnabled; + private DateTime SqlServer_NullDateTime= SQLSERVER_NULL_DATETIME; + internal void UseSmallDateTime() + { + SqlServer_NullDateTime = SQLSERVER_NULL_SMALLDATETIME; + } public override int GetCommandTimeout() { return base.GetCommandTimeout(); @@ -2114,14 +2120,14 @@ public override DateTime Dbms2NetDate(IGxDbCommand cmd, IDataRecord DR, int i) public override DateTime Dbms2NetDateTime( DateTime dt, Boolean precision) { //DBMS MinDate => Genexus null Date - if (dt.Equals(SQLSERVER_NULL_DATE)) + if (dt.Equals(SqlServer_NullDateTime)) { return DateTimeUtil.NullDate(); } - if (dt.Year==SQLSERVER_NULL_DATE.Year && - dt.Month==SQLSERVER_NULL_DATE.Month && - dt.Day==SQLSERVER_NULL_DATE.Day) + if (dt.Year== SqlServer_NullDateTime.Year && + dt.Month== SqlServer_NullDateTime.Month && + dt.Day== SqlServer_NullDateTime.Day) { return new DateTime( @@ -2137,16 +2143,16 @@ public override Object Net2DbmsDateTime(IDbDataParameter parm, DateTime dt) //Genexus null => save DBMS MinDate if(dt.Equals(DateTimeUtil.NullDate())) { - return SQLSERVER_NULL_DATE; + return SqlServer_NullDateTime; } //Date < DBMS MinDate => save DBMS MinDate keeping the Time component - if (dt.CompareTo(SQLSERVER_NULL_DATE)<0) + if (dt.CompareTo(SqlServer_NullDateTime) <0) { DateTime aux = new DateTime( - SQLSERVER_NULL_DATE.Year,SQLSERVER_NULL_DATE.Month, - SQLSERVER_NULL_DATE.Day,dt.Hour,dt.Minute,dt.Second,dt.Millisecond); + SqlServer_NullDateTime.Year, SqlServer_NullDateTime.Month, + SqlServer_NullDateTime.Day,dt.Hour,dt.Minute,dt.Second,dt.Millisecond); return aux; } @@ -2161,8 +2167,8 @@ public override string ConcatOp(int pos) { return ConcatOpValues[pos]; } - - static DateTime SQLSERVER_NULL_DATE = new DateTime(1753,1,1) ; + static DateTime SQLSERVER_NULL_DATETIME = new DateTime(1753,1,1) ; + static DateTime SQLSERVER_NULL_SMALLDATETIME= new DateTime(1900, 1, 1); } public class GxDataReader: IDataReader @@ -2174,7 +2180,7 @@ public class GxDataReader: IDataReader protected string stmt; protected GxConnectionCache cache; protected IGxConnection con; - protected GxArrayList block; + protected List block; protected string key; protected int pos; protected bool cached; @@ -2208,7 +2214,7 @@ public GxDataReader( IGxConnectionManager connManager, GxDataRecord dr, IGxConne reader=dr.GetCommand(con,stmt,parameters).ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open=true; - block=new GxArrayList(fetchSize); + block= new List(fetchSize); pos=-1; if (cached) { @@ -2619,7 +2625,6 @@ public void PrepareClose() public GxConnectionCache(IGxConnection gxconn,int maxSize) { preparedCommandsCache = new Dictionary(MAX_SIZE); - InitCommands(); preparedStmtCache=new GxPreparedStatementCache(gxconn,maxSize); conn=gxconn; dataAdapterCacheQueue = new List(); @@ -2629,7 +2634,6 @@ public GxConnectionCache(IGxConnection gxconn) { preparedCommandsCache = new Dictionary(MAX_SIZE); preparedStmtCache = new GxPreparedStatementCache(gxconn, MAX_SIZE); - InitCommands(); conn = gxconn; dataAdapterCacheQueue = new List(); } @@ -3012,118 +3016,120 @@ public void Clear() if (commandsToRemove!=null) commandsToRemove.Clear(); } - private void InitCommands() + internal void InitCommands() { - int commandTimeout = 0; + if (fetchCommand == null) + { + int commandTimeout = 0; - //----------------Fetch command - fetchCommand=new SqlCommand("sp_cursorfetch"); - fetchCommand.CommandType=CommandType.StoredProcedure; - fetchCommand.CommandTimeout = commandTimeout; - SqlParameter p1 = fetchCommand.Parameters.Add("cursor", SqlDbType.Int); - p1.Direction = ParameterDirection.Input; + //----------------Fetch command + fetchCommand = new SqlCommand("sp_cursorfetch"); + fetchCommand.CommandType = CommandType.StoredProcedure; + fetchCommand.CommandTimeout = commandTimeout; + SqlParameter p1 = fetchCommand.Parameters.Add("cursor", SqlDbType.Int); + p1.Direction = ParameterDirection.Input; - SqlParameter p2 = fetchCommand.Parameters.Add("fetchtype", SqlDbType.Int); - p2.Direction = ParameterDirection.Input; - p2.Value = 2;//0x0002 Next row. + SqlParameter p2 = fetchCommand.Parameters.Add("fetchtype", SqlDbType.Int); + p2.Direction = ParameterDirection.Input; + p2.Value = 2;//0x0002 Next row. - SqlParameter p3 = fetchCommand.Parameters.Add("rownumber", SqlDbType.Int); - p3.Direction = ParameterDirection.Input; - p3.Value = 1; - - SqlParameter p5 = fetchCommand.Parameters.Add("nrows", SqlDbType.Int); - p5.Direction = ParameterDirection.Input; + SqlParameter p3 = fetchCommand.Parameters.Add("rownumber", SqlDbType.Int); + p3.Direction = ParameterDirection.Input; + p3.Value = 1; - //-----------------PrepExec command - prepExecCommand=new SqlCommand("sp_cursorprepexec"); - prepExecCommand.CommandType=CommandType.StoredProcedure; - prepExecCommand.CommandTimeout = commandTimeout; + SqlParameter p5 = fetchCommand.Parameters.Add("nrows", SqlDbType.Int); + p5.Direction = ParameterDirection.Input; - prepExecParms=new SqlParameter[7]; - prepExecParms[0]=new SqlParameter("cursor",SqlDbType.Int); - prepExecParms[0].Direction=ParameterDirection.Output; - prepExecParms[0].Value=DBNull.Value; + //-----------------PrepExec command + prepExecCommand = new SqlCommand("sp_cursorprepexec"); + prepExecCommand.CommandType = CommandType.StoredProcedure; + prepExecCommand.CommandTimeout = commandTimeout; - prepExecParms[1]=new SqlParameter("handle",SqlDbType.Int); - prepExecParms[1].Direction=ParameterDirection.Output; - prepExecParms[1].Value=0; + prepExecParms = new SqlParameter[7]; + prepExecParms[0] = new SqlParameter("cursor", SqlDbType.Int); + prepExecParms[0].Direction = ParameterDirection.Output; + prepExecParms[0].Value = DBNull.Value; - prepExecParms[2]=new SqlParameter("parameters",SqlDbType.NVarChar); - prepExecParms[2].Direction=ParameterDirection.Input; - - prepExecParms[3]=new SqlParameter("stmt",SqlDbType.NVarChar); - prepExecParms[3].Direction=ParameterDirection.Input; - - prepExecParms[4]=new SqlParameter("scrollopt",SqlDbType.Int); - prepExecParms[4].Direction=ParameterDirection.InputOutput; - - prepExecParms[5]=new SqlParameter("ccopt",SqlDbType.Int); - prepExecParms[5].Direction=ParameterDirection.InputOutput; - prepExecParms[5].Value=1;//Readonly - - prepExecParms[6]=new SqlParameter("rowcount",SqlDbType.Int); - prepExecParms[6].Direction=ParameterDirection.InputOutput; - - //-----------------Exec command - execCommand = new SqlCommand("sp_cursorexecute"); - execCommand.CommandType=CommandType.StoredProcedure; - execCommand.CommandTimeout = commandTimeout; - - execParms=new SqlParameter[5]; - execParms[0]=new SqlParameter("cursorId",SqlDbType.Int); - execParms[0].Direction = ParameterDirection.Input; - execParms[1]=new SqlParameter("handle",SqlDbType.Int); - execParms[1].Direction = ParameterDirection.Output; - execParms[2]=new SqlParameter("scrollopt",SqlDbType.Int); - execParms[2].Direction = ParameterDirection.InputOutput; - execParms[3]=new SqlParameter("ccopt",SqlDbType.Int); - execParms[3].Direction = ParameterDirection.InputOutput; - execParms[3].Value=1;//Readonly - execParms[4]=new SqlParameter("rowcount",SqlDbType.Int); - execParms[4].Direction = ParameterDirection.InputOutput; - - //------------------prepareCursor - prepareCommand = new SqlCommand("sp_cursorprepare"); - prepareCommand.CommandType=CommandType.StoredProcedure; - prepareCommand.CommandTimeout = commandTimeout; - prepareParms=new SqlParameter[6]; - - prepareParms[0]=new SqlParameter("cursor",SqlDbType.Int); - prepareParms[0].Direction = ParameterDirection.Output; - prepareParms[0].Value=DBNull.Value; - - prepareParms[1]=new SqlParameter("parameters",SqlDbType.NVarChar); - prepareParms[1].Direction = ParameterDirection.Input; + prepExecParms[1] = new SqlParameter("handle", SqlDbType.Int); + prepExecParms[1].Direction = ParameterDirection.Output; + prepExecParms[1].Value = 0; - prepareParms[2]=new SqlParameter("stmt",SqlDbType.NVarChar); - prepareParms[2].Direction = ParameterDirection.Input; + prepExecParms[2] = new SqlParameter("parameters", SqlDbType.NVarChar); + prepExecParms[2].Direction = ParameterDirection.Input; - prepareParms[3]=new SqlParameter("options",SqlDbType.Int); - prepareParms[3].Direction = ParameterDirection.Input; - prepareParms[3].Value=1; + prepExecParms[3] = new SqlParameter("stmt", SqlDbType.NVarChar); + prepExecParms[3].Direction = ParameterDirection.Input; - prepareParms[4]=new SqlParameter("scrollopt",SqlDbType.Int); - prepareParms[4].Direction = ParameterDirection.InputOutput; + prepExecParms[4] = new SqlParameter("scrollopt", SqlDbType.Int); + prepExecParms[4].Direction = ParameterDirection.InputOutput; - prepareParms[5]=new SqlParameter("ccopt",SqlDbType.Int); - prepareParms[5].Direction = ParameterDirection.InputOutput; - prepareParms[5].Value=4; + prepExecParms[5] = new SqlParameter("ccopt", SqlDbType.Int); + prepExecParms[5].Direction = ParameterDirection.InputOutput; + prepExecParms[5].Value = 1;//Readonly - //----------------Close command - closeCommand = new SqlCommand("sp_cursorclose"); - closeCommand.CommandType=CommandType.StoredProcedure; - closeCommand.CommandTimeout = commandTimeout; - SqlParameter handle4 = closeCommand.Parameters.Add("handle", SqlDbType.Int); - handle4.Direction = ParameterDirection.Input; + prepExecParms[6] = new SqlParameter("rowcount", SqlDbType.Int); + prepExecParms[6].Direction = ParameterDirection.InputOutput; - //------------------unprepareCursor - unprepareCommand =new SqlCommand("sp_cursorunprepare"); - unprepareCommand.CommandType = CommandType.StoredProcedure; - unprepareCommand.CommandTimeout = commandTimeout; + //-----------------Exec command + execCommand = new SqlCommand("sp_cursorexecute"); + execCommand.CommandType = CommandType.StoredProcedure; + execCommand.CommandTimeout = commandTimeout; - SqlParameter cursor1 = unprepareCommand.Parameters.Add("cursor", SqlDbType.Int); - cursor1.Direction = ParameterDirection.Input; + execParms = new SqlParameter[5]; + execParms[0] = new SqlParameter("cursorId", SqlDbType.Int); + execParms[0].Direction = ParameterDirection.Input; + execParms[1] = new SqlParameter("handle", SqlDbType.Int); + execParms[1].Direction = ParameterDirection.Output; + execParms[2] = new SqlParameter("scrollopt", SqlDbType.Int); + execParms[2].Direction = ParameterDirection.InputOutput; + execParms[3] = new SqlParameter("ccopt", SqlDbType.Int); + execParms[3].Direction = ParameterDirection.InputOutput; + execParms[3].Value = 1;//Readonly + execParms[4] = new SqlParameter("rowcount", SqlDbType.Int); + execParms[4].Direction = ParameterDirection.InputOutput; + //------------------prepareCursor + prepareCommand = new SqlCommand("sp_cursorprepare"); + prepareCommand.CommandType = CommandType.StoredProcedure; + prepareCommand.CommandTimeout = commandTimeout; + prepareParms = new SqlParameter[6]; + + prepareParms[0] = new SqlParameter("cursor", SqlDbType.Int); + prepareParms[0].Direction = ParameterDirection.Output; + prepareParms[0].Value = DBNull.Value; + + prepareParms[1] = new SqlParameter("parameters", SqlDbType.NVarChar); + prepareParms[1].Direction = ParameterDirection.Input; + + prepareParms[2] = new SqlParameter("stmt", SqlDbType.NVarChar); + prepareParms[2].Direction = ParameterDirection.Input; + + prepareParms[3] = new SqlParameter("options", SqlDbType.Int); + prepareParms[3].Direction = ParameterDirection.Input; + prepareParms[3].Value = 1; + + prepareParms[4] = new SqlParameter("scrollopt", SqlDbType.Int); + prepareParms[4].Direction = ParameterDirection.InputOutput; + + prepareParms[5] = new SqlParameter("ccopt", SqlDbType.Int); + prepareParms[5].Direction = ParameterDirection.InputOutput; + prepareParms[5].Value = 4; + + //----------------Close command + closeCommand = new SqlCommand("sp_cursorclose"); + closeCommand.CommandType = CommandType.StoredProcedure; + closeCommand.CommandTimeout = commandTimeout; + SqlParameter handle4 = closeCommand.Parameters.Add("handle", SqlDbType.Int); + handle4.Direction = ParameterDirection.Input; + + //------------------unprepareCursor + unprepareCommand = new SqlCommand("sp_cursorunprepare"); + unprepareCommand.CommandType = CommandType.StoredProcedure; + unprepareCommand.CommandTimeout = commandTimeout; + + SqlParameter cursor1 = unprepareCommand.Parameters.Add("cursor", SqlDbType.Int); + cursor1.Direction = ParameterDirection.Input; + } } } @@ -4148,6 +4154,7 @@ public MssqlConnectionWrapper(String connectionString, GxConnectionCache connCac } override public void Open() { + m_connectionCache.InitCommands(); InternalConnection.Open(); if (!m_autoCommit) { @@ -4337,7 +4344,28 @@ public virtual void Close() { InternalConnection.Close(); } +#if NETCORE + internal virtual async Task CloseAsync() + { + try + { + DbConnection dbConnection = InternalConnection as DbConnection; + if (dbConnection != null) + { + await dbConnection.CloseAsync(); + } + else + { + InternalConnection.Close(); + } + } + catch (Exception ex) + { + throw new DataException(ex.Message, ex); + } + } +#endif public void ChangeDatabase(String database) { throw new NotSupportedException("NoChangeMsg00" + database); diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2.cs index 9bc3da634..39d6c41f1 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Text; using System.Threading; +using System.Threading.Tasks; using GeneXus.Application; using GeneXus.Cache; using GeneXus.Metadata; @@ -115,7 +116,7 @@ protected override string BuildConnectionString(string datasourceName, string us public class GxDb2 : GxDataRecord { - private static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static string SQL_NULL_DATE_10="0000-00-00"; public static string SQL_NULL_DATE_8="00000000"; @@ -523,7 +524,7 @@ sealed internal class Db2ConnectionWrapper : GxAbstractConnectionWrapper private static int changeConnState=-1; private static int changeConnStateExecuting = -1; private int openDataReaders; - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public Db2ConnectionWrapper() { try @@ -655,7 +656,20 @@ override public void Close() throw new DataException(ex.Message, ex); } } - +#if NETCORE + internal override async Task CloseAsync() + { + try + { + CheckState(false); + await base.CloseAsync(); + } + catch (Exception ex) + { + throw new DataException(ex.Message, ex); + } + } +#endif override public IDbCommand CreateCommand() { return InternalConnection.CreateCommand(); diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2400.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2400.cs index dddf16097..38ea9a538 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2400.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataDb2400.cs @@ -44,7 +44,7 @@ public override object GetValue(int i) } sealed internal class Db2ISeriesConnectionWrapper : GxAbstractConnectionWrapper { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public Db2ISeriesConnectionWrapper() { try @@ -120,7 +120,7 @@ public override DbDataAdapter CreateDataAdapter() public class GxDb2ISeries : GxDataRecord { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private bool m_UseCharInDate; public static string SQL_NULL_DATE="00000000"; private string m_InitialCatalog; @@ -652,7 +652,7 @@ public override void DisposeCommand(IDbCommand command) } catch (Exception ex) { - GXLogging.Error(log, "DisposeCommand error", ex); + GXLogging.Warn(log, "DisposeCommand error", ex); } } @@ -767,7 +767,7 @@ public override string ToDbmsConstant(DateTime Value) } sealed internal class Db2ISeriesHISConnectionWrapper : GxAbstractConnectionWrapper { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public Db2ISeriesHISConnectionWrapper() : base(new MsDb2Connection()) { } @@ -826,7 +826,7 @@ public override DbDataAdapter CreateDataAdapter() public class GxISeriesHIS : GxDataRecord { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private bool m_UseCharInDate; private string m_InitialCatalog; private string m_SqlPackage; diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs index 256bd7994..14954b0ca 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs @@ -4,6 +4,7 @@ using System.IO; using System.Reflection; using System.Text; +using System.Threading.Tasks; using GeneXus.Cache; using GeneXus.Metadata; using GeneXus.Utils; @@ -13,7 +14,7 @@ namespace GeneXus.Data { public class GxHana : GxDataRecord { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static Assembly _hanaAssembly; const string HanaDbTypeEnum = "Sap.Data.Hana.HanaDbType"; #if NETCORE @@ -320,7 +321,7 @@ sealed internal class HanaConnectionWrapper : GxAbstractConnectionWrapper { const string HanaIsolationEnum = "Sap.Data.Hana.HanaIsolationLevel"; - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public HanaConnectionWrapper() { try @@ -415,7 +416,6 @@ override public void Close() { try { - CheckState(false); InternalConnection.Close(); } catch (Exception ex) @@ -423,7 +423,20 @@ override public void Close() throw new DataException(ex.Message, ex); } } - +#if NETCORE + internal override async Task CloseAsync() + { + try + { + CheckState(false); + await base.CloseAsync(); + } + catch (Exception ex) + { + throw new DataException(ex.Message, ex); + } + } +#endif override public IDbCommand CreateCommand() { return InternalConnection.CreateCommand(); diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataInformix.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataInformix.cs index 1355285af..8a731512f 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataInformix.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataInformix.cs @@ -18,7 +18,7 @@ namespace GeneXus.Data { public class GxInformix : GxDataRecord { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); static Assembly _ifxAssembly; #if NETCORE internal static string InformixAssemblyName = "Informix.Net.Core"; @@ -584,7 +584,7 @@ sealed internal class InformixConnectionWrapper : GxAbstractConnectionWrapper { private static int changeConnState = -1; private int openDataReaders; - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public InformixConnectionWrapper() { try diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs index a804ae6c3..56d6684c2 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlConnector.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Reflection; @@ -570,7 +571,7 @@ public GxMySQLConnectorDataReader(IGxConnectionManager connManager, GxDataRecord reader = cmd.ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open = true; - block = new GxArrayList(fetchSize); + block = new List(fetchSize); pos = -1; if (cached) { @@ -622,7 +623,7 @@ public GxMySQLConnectorCursorDataReader(IGxConnectionManager connManager, GxData reader = cmd.ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open = true; - block = new GxArrayList(fetchSize); + block = new List(fetchSize); pos = -1; if (cached) { diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlDriverCS.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlDriverCS.cs index a2688ea3b..956b7c011 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlDriverCS.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataMysqlDriverCS.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Text; @@ -497,7 +498,7 @@ public GxMySQLDriverCSDataReader(IGxConnectionManager connManager, GxDataRecord reader = cmd.ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open = true; - block = new GxArrayList(fetchSize); + block = new List(fetchSize); pos = -1; if (cached) { @@ -542,7 +543,7 @@ public GxMySQLDriverCSCursorDataReader(IGxConnectionManager connManager, GxDataR reader = cmd.ExecuteReader(); cache.SetAvailableCommand(stmt, false, dynStmt); open = true; - block = new GxArrayList(fetchSize); + block = new List(fetchSize); pos = -1; if (cached) { diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierADO.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierADO.cs index 94b1b7e90..bb448e76e 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierADO.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierADO.cs @@ -210,11 +210,11 @@ public GXFatFieldGetter(GxCommand gxDbCommand) { _gxDbCommand = gxDbCommand; } - void TraceRow(params string[] list) + void TraceRow(Func buildMsg) { - if (_gxDbCommand.HasMoreRows) + if (GXLogging.TraceEnabled(log) && _gxDbCommand.HasMoreRows) { - GXLogging.Trace(log, list); + GXLogging.Trace(log, buildMsg); } } public IDataReader DataReader @@ -225,57 +225,57 @@ public IDataReader DataReader public short getShort(int id) { short value = _gxDbCommand.Db.GetShort(_gxDbCommand, _DR, id - 1); - TraceRow("getShort - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(()=> $"getShort - index : {id} value:{value}"); return value; } public int getInt(int id) { int value = _gxDbCommand.Db.GetInt(_gxDbCommand, _DR, id - 1); - TraceRow("getInt - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getInt - index : {id} value:{value}"); return value; } public bool getBool(int id) { bool value = _gxDbCommand.Db.GetBoolean(_gxDbCommand, _DR, id - 1); - TraceRow("getBool - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getBool - index : {id} value:{value}"); return value; } public Guid getGuid(int id) { Guid value = _gxDbCommand.Db.GetGuid(_gxDbCommand, _DR, id - 1); - TraceRow("getGuid - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getGuid - index : {id} value:{value}"); return value; } public long getLong(int id) { long value = _gxDbCommand.Db.GetLong(_gxDbCommand, _DR, id - 1); - TraceRow("getLong - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getLong - index : {id} value:{value}"); return value; } public double getDouble(int id) { double value= _gxDbCommand.Db.GetDouble(_gxDbCommand, _DR, id - 1); - TraceRow("getDouble - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDouble - index : {id} value:{value}"); return value; } public Decimal getDecimal(int id) { Decimal value= _gxDbCommand.Db.GetDecimal(_gxDbCommand, _DR, id - 1); - TraceRow("getDecimal - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDecimal - index : {id} value:{value}"); return value; } public string getString(int id, int size) { String value = _gxDbCommand.Db.GetString(_gxDbCommand, _DR, id - 1, size); - TraceRow("getString - index : ", id.ToString(), " value:", (value!=null ? value.ToString(): string.Empty)); + TraceRow(() => $"getString - index : {id} value:{(value!=null ? value.ToString(): string.Empty)}"); return value; } public DateTime getDateTime(int id) { DateTime value = _gxDbCommand.Db.GetDateTime(_gxDbCommand, _DR, id - 1); - TraceRow("getDateTime - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDateTime - index : {id} value:{value}"); return value; } public DateTime getDateTime(int id, Boolean precision) @@ -287,19 +287,19 @@ public DateTime getDateTime(int id, Boolean precision) else { value = _gxDbCommand.Db.GetDateTime(_gxDbCommand, _DR, id - 1); } - TraceRow("getDateTime - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDateTime - index : {id} value:{value}"); return value; } public DateTime getDate(int id) { DateTime value = _gxDbCommand.Db.GetDate(_gxDbCommand, _DR, id - 1); - TraceRow("getDate - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDate - index : {id} value:{value}"); return value; } public string getLongVarchar(int id) { string value = _gxDbCommand.Db.GetString(_gxDbCommand, _DR, id - 1); - TraceRow("getLongVarchar - index : ", id.ToString(), " value:", (value!=null ? value.ToString(): string.Empty)); + TraceRow(() => $"getLongVarchar - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } public DateTime getGXDateTime(int id, Boolean precision) @@ -309,7 +309,7 @@ public DateTime getGXDateTime(int id, Boolean precision) #else DateTime value = DateTimeUtil.DBserver2local(getDateTime(id, precision), _gxDbCommand.Conn.ClientTimeZone); #endif - TraceRow("getDateTime - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getDateTime - index : {id} value:{value}"); return value; } public DateTime getGXDateTime(int id) @@ -319,19 +319,19 @@ public DateTime getGXDateTime(int id) #else DateTime value = DateTimeUtil.DBserver2local(getDateTime(id, false), _gxDbCommand.Conn.ClientTimeZone); #endif - TraceRow("getGXDateTime - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getGXDateTime - index : {id} value:{value.ToString()}"); return value; } public DateTime getGXDate(int id) { DateTime value = getDate(id); - TraceRow("getGXDate - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getGXDate - index : {id} value:{value.ToString()}"); return value; } public string getBLOBFile(int id) { string value= getBLOBFile(id, "tmp", ""); - TraceRow("getBLOBFile - index : ", id.ToString(), " value:", (value!=null ? value.ToString() : string.Empty)); + TraceRow(() => $"getBLOBFile - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } @@ -339,7 +339,7 @@ public string getBLOBFile(int id, string extension, string name) { string fileName = FileUtil.getTempFileName(_gxDbCommand.Conn.BlobPath, name, extension, GxFileType.Private); String value = getBLOBFile(id, extension, name, fileName, true); - TraceRow("getBLOBFile - index : ", id.ToString(), " value:", (value!=null ? value.ToString() : string.Empty)); + TraceRow(() => $"getBLOBFile - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } @@ -389,7 +389,7 @@ private string getBLOBFile(int id, string extension, string name, string fileNam } streamClosed = true; - TraceRow("GetBlobFile fileName:" + fileName + ", retval bytes:" + retval); + TraceRow(() => $"GetBlobFile fileName:{fileName}, retval bytes:{retval}"); if (temporary) GXFileWatcher.Instance.AddTemporaryFile(file, _gxDbCommand.Conn.DataStore.Context); @@ -473,20 +473,20 @@ public string getMultimediaUri(int id, bool absUrl) public string getVarchar(int id) { string value = _gxDbCommand.Db.GetString(_gxDbCommand, _DR, id - 1); - TraceRow("getVarchar - index : ", id.ToString(), " value:", (value != null ? value.ToString() : string.Empty)); + TraceRow(() => $"getVarchar - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } public decimal getBigDecimal(int id, int dec) { decimal value =_gxDbCommand.Db.GetDecimal(_gxDbCommand, _DR, id - 1); - TraceRow("getBigDecimal - index : ", id.ToString(), " value:", value.ToString()); + TraceRow(() => $"getBigDecimal - index : {id} value:{value.ToString()}"); return value; } public IGeographicNative getGeospatial(int id) { IGeographicNative value = _gxDbCommand.Db.GetGeospatial(_gxDbCommand, _DR, id - 1); - TraceRow("getGeospatial - index : ", id.ToString(), " value:", (value != null ? value.ToString() : string.Empty)); + TraceRow(() => $"getGeospatial - index : {id} value:{(value != null ? value.ToString() : string.Empty)}"); return value; } diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs index 47b1348b5..57c25823c 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs @@ -56,7 +56,7 @@ public ServiceCursorDef(string name, object query, GxErrorMask nmask, Object[] p public class GxServiceFactory { - protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static GxService Create(string id, string providerId, string serviceClass) { @@ -66,7 +66,7 @@ public static GxService Create(string id, string providerId, string serviceClass public class GxService : GxDataRecord { - protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + protected static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private Type m_ServiceType; private CursorDef m_CursorDef; @@ -307,7 +307,7 @@ public interface IServiceCommand sealed public class ServiceConnectionWrapper : GxAbstractConnectionWrapper { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public ServiceConnectionWrapper(Type runtimeClassType, String connectionString, GxConnectionCache connCache, IsolationLevel isolationLevel, String dataSource) { try @@ -363,7 +363,6 @@ override public void Close() { try { - CheckState(false); InternalConnection.Close(); } catch (Exception ex) diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataOracle.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataOracle.cs index bba71de26..a7fb9acdd 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataOracle.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataOracle.cs @@ -1436,7 +1436,7 @@ public override decimal GetDecimal(int i) #if !NETCORE sealed internal class MSOracleConnectionWrapper : GxAbstractConnectionWrapper { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); int oracle8 = -1; public MSOracleConnectionWrapper() : base(new MSOracleProvider.OracleConnection()) { diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataPostgreSQL.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataPostgreSQL.cs index 21791af2d..d61eedaf5 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataPostgreSQL.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataPostgreSQL.cs @@ -19,7 +19,7 @@ namespace GeneXus.Data { public class GxPostgreSql : GxDataRecord { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); const string ConnectionStringEncoding = "encoding"; private byte[] _buffer; static Assembly _npgsqlAssembly; @@ -586,7 +586,7 @@ public override string ConcatOp(int pos) } sealed internal class PostgresqlConnectionWrapper : GxAbstractConnectionWrapper { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public PostgresqlConnectionWrapper() { _connection = (IDbConnection)ClassLoader.CreateInstance(GxPostgreSql.NpgsqlAssembly, "Npgsql.NpgsqlConnection"); diff --git a/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs b/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs index d1947ed42..078672fcc 100644 --- a/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs +++ b/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs @@ -1,6 +1,9 @@ using GeneXus.Attributes; using GeneXus.Configuration; using log4net; +using System.Collections.Concurrent; + + #if NETCORE using GeneXus.Services.Log; using Microsoft.Extensions.Logging; @@ -21,103 +24,100 @@ private enum LogLevel Error = 20, Fatal = 30 } -#if NETCORE - static ILoggerFactory _instance = GXLogService.GetLogFactory(); -#endif - + + private const string LoggerPrefix = "$"; + private static readonly string DefaultUserLogNamespace = Config.GetValueOf("USER_LOG_NAMESPACE", LogConfiguration.USER_LOG_TOPIC); + private static ConcurrentDictionary LoggerDictionary = new ConcurrentDictionary() {}; internal static IGXLogger GetLogger(string topic) { - string defaultUserLogNamespace = Configuration.Config.GetValueOf("USER_LOG_NAMESPACE", LogConfiguration.USER_LOG_TOPIC); - string loggerName = defaultUserLogNamespace; - if (!string.IsNullOrEmpty(topic)) + if (LoggerDictionary.TryGetValue(topic, out IGXLogger logger)) { - loggerName = topic.StartsWith("$") ? topic.Substring(1) : string.Format("{0}.{1}", defaultUserLogNamespace, topic.Trim()); + return logger; } -#if NETCORE - if (_instance != null) + else { - return new GXLoggerMsExtensions(_instance.CreateLogger(loggerName)); + string loggerName; + if (!string.IsNullOrEmpty(topic)) + loggerName = topic.StartsWith(LoggerPrefix) ? topic.Substring(1) : $"{DefaultUserLogNamespace}.{topic.Trim()}"; + else + loggerName = DefaultUserLogNamespace; + + logger = GXLoggerFactory.GetLogger(loggerName); + LoggerDictionary.TryAdd(topic, logger); + return logger; } - string defaultRepository = LogManager.GetRepository(System.Reflection.Assembly.GetEntryAssembly()).Name; -#else - string defaultRepository = LogManager.GetRepository().Name; -#endif - return new GXLoggerLog4Net(log4net.LogManager.GetLogger(defaultRepository, loggerName)); - } public static void Write(int logLevel, string message, string topic) { Write(message, topic, logLevel); } - public static void Write(string message, string topic, int logLevel) { - IGXLogger log = GetLogger(topic); LogLevel logLvl = (LogLevel)logLevel; + WriteImp(message, topic, logLvl); + } - switch (logLvl) + private static void WriteImp(string message, string topic, LogLevel logLvl) + { + if (logLvl != LogLevel.Off) { - case LogLevel.Off: - break; - case LogLevel.Trace: - GXLogging.Trace(log, message); - break; - case LogLevel.Debug: - GXLogging.Debug(log, message); - break; - case LogLevel.Info: - GXLogging.Info(log, message); - break; - case LogLevel.Warn: - GXLogging.Warn(log, message); - break; - case LogLevel.Error: - GXLogging.Error(log, message); - break; - case LogLevel.Fatal: - GXLogging.Critical(log, message); - break; - default: - GXLogging.Debug(log, message); - break; - } + IGXLogger log = GetLogger(topic); + switch (logLvl) + { + case LogLevel.Trace: + GXLogging.Trace(log, message); + break; + case LogLevel.Debug: + GXLogging.Debug(log, message); + break; + case LogLevel.Info: + GXLogging.Info(log, message); + break; + case LogLevel.Warn: + GXLogging.Warn(log, message); + break; + case LogLevel.Error: + GXLogging.Error(log, message); + break; + case LogLevel.Fatal: + GXLogging.Critical(log, message); + break; + default: + GXLogging.Debug(log, message); + break; + } + } } - + public static void Write(string message, string topic = "") { - IGXLogger log = GetLogger(topic); - GXLogging.Debug(log, message); + WriteImp(message, topic, LogLevel.Debug); } - + public static void Fatal(string message, string topic = "") { - IGXLogger log = GetLogger(topic); - GXLogging.Critical(log, message); + WriteImp(message, topic, LogLevel.Fatal); } public static void Error(string message, string topic = "") { - IGXLogger log = GetLogger(topic); - GXLogging.Error(log, message); + WriteImp(message, topic, LogLevel.Error); } public static void Warning(string message, string topic = "") { - IGXLogger log = GetLogger(topic); - GXLogging.Warn(log, message); + WriteImp(message, topic, LogLevel.Warn); } public static void Info(string message, string topic = "") { - IGXLogger log = GetLogger(topic); - GXLogging.Info(log, message); + WriteImp(message, topic, LogLevel.Info); } public static void Debug(string message, string topic = "") { - IGXLogger log = GetLogger(topic); - GXLogging.Debug(log, message); + WriteImp(message, topic, LogLevel.Debug); } } } diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GXGeolocation.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GXGeolocation.cs index e6c8e6aa2..3de8bcdec 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GXGeolocation.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GXGeolocation.cs @@ -3,7 +3,11 @@ using System.Text; using System.Net; using System.IO; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using GeneXus.Utils; using System.Globalization; using GeneXus; diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GXLDAP.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GXLDAP.cs index 510b08b9d..c1238f28d 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GXLDAP.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GXLDAP.cs @@ -2,6 +2,8 @@ using System.Collections; using System.DirectoryServices; using System.Text; +using GeneXus.Data; + #if NETCORE using Novell.Directory.Ldap; #endif @@ -10,7 +12,7 @@ namespace GeneXus.Utils { public class GXLDAPClient { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); string _server; int _port; diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GXWindow.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GXWindow.cs index bc9443380..f33c73abf 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GXWindow.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GXWindow.cs @@ -1,6 +1,8 @@ using GeneXus.Utils; using System; +#if !NETCORE using Jayrock.Json; +#endif namespace GeneXus.Application { public class GXWindow : IGxJSONAble diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs index e14805a46..6a6a11ab7 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs @@ -8,18 +8,22 @@ namespace GeneXus.Utils using System.ComponentModel; using System.Data; using System.Globalization; +#if !NETCORE + using Jayrock.Json; +#endif using System.Linq; using System.Reflection; using System.Runtime.Serialization; + using System.Runtime.Serialization.Json; using System.Text; using System.Xml; using System.Xml.Serialization; using GeneXus.Application; using GeneXus.Configuration; + using GeneXus.Data; using GeneXus.Http; using GeneXus.Metadata; using GeneXus.XML; - using Jayrock.Json; public class GxParameterCollection : IDataParameterCollection { @@ -235,7 +239,7 @@ public bool FromJSonString(string s, GXBaseCollection Messa } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); return false; } } @@ -706,7 +710,7 @@ public bool FromJSonString(string s, GXBaseCollection Messa } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); return false; } } @@ -1042,8 +1046,8 @@ public string Name public class GxUserType : IGxXMLSerializable, ICloneable, IGxJSONAble, IGxJSONSerializable, IGXAssigned { static readonly IGXLogger log = GXLoggerFactory.GetLogger(); - protected GXProperties dirties = new GXProperties(); - + protected ConcurrentDictionary dirties = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private const string PROPERTY_PREFIX = "gxtpr_"; static object setupChannelObject = null; static bool setupChannelInitialized; [XmlIgnore] @@ -1101,11 +1105,11 @@ public GxUserType() public virtual void SetDirty(string fieldName) { - dirties[fieldName.ToLower()] = "true"; + dirties[fieldName] = 1; } public virtual bool IsDirty(string fieldName) { - if (dirties.ContainsKey(fieldName.ToLower())) + if (dirties.ContainsKey(fieldName)) return true; return false; } @@ -1267,7 +1271,7 @@ public bool FromJSonString(string s, GXBaseCollection Messa } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); return false; } } @@ -1560,13 +1564,25 @@ public void AddObjectProperty(string name, object prop, bool includeState) public void AddObjectProperty(string name, object prop, bool includeState, bool includeNonInitialized) { IGxJSONAble ijsonProp = prop as IGxJSONAble; + IGxExternalObject extObj = prop as IGxExternalObject; if (ijsonProp != null) { GxSilentTrnSdt silentTrn = prop as GxSilentTrnSdt; if (silentTrn != null) silentTrn.GetJSONObject(includeState, includeNonInitialized); - else + else if (extObj != null) + { + object extInstance = extObj.ExternalInstance; + IGxJSONAble gxJSONAble = extInstance as IGxJSONAble; + if (gxJSONAble != null) + { + JsonObj.Put(name, gxJSONAble.GetJSONObject(includeState)); + } + } + else + { JsonObj.Put(name, ijsonProp.GetJSONObject(includeState)); + } } else { @@ -1603,24 +1619,27 @@ public Object GetJSONObject() private ICollection getFromJSONObjectOrderIterator(ICollection it) { - List v = new List(); + if (GxUploadAttrs.IsEmpty && !typeof(GxSilentTrnSdt).IsAssignableFrom(this.GetType())) + { + return it; + } + List _JsonObjectOrderIterator = new List(); + List vAtEnd = new List(); foreach (string name in it) { - string map = JsonMap(name); - PropertyInfo objProperty = GetTypeProperty("gxtpr_" + (!string.IsNullOrEmpty(map) ? map : name).ToLower()); - if (name.EndsWith("_N") || objProperty != null && IsGxUploadAttribute(objProperty)) + if (name.EndsWith("_N") || IsGxUploadAttribute(name)) { vAtEnd.Add(name); } else { - v.Add(name);//keep the order of attributes that do not end with _N. + _JsonObjectOrderIterator.Add(name);//keep the order of attributes that do not end with _N. } } if (vAtEnd.Count > 0) - v.AddRange(vAtEnd); - return v; + _JsonObjectOrderIterator.AddRange(vAtEnd); + return _JsonObjectOrderIterator; } public void FromJSONObject(dynamic obj) @@ -1633,9 +1652,7 @@ public void FromJSONObject(dynamic obj) foreach (string name in jsonIterator) { object currObj = jobj[name]; - string map = JsonMap(name); - PropertyInfo objProperty = GetTypeProperty("gxtpr_" + (map != null ? map : name).ToLower()); - + PropertyInfo objProperty = GetTypeProperty(JsonNameToInternalName(name)); if (objProperty != null) { if (!JSONHelper.IsJsonNull(currObj)) @@ -1699,7 +1716,14 @@ public void FromJSONObject(dynamic obj) IGXBCCollection bcColl; GxSimpleCollection currSimpleColl; IGxJSONAble currJsonProp; + IGxExternalObject currExtProp; CollectionBase currObjColl = currObj as CollectionBase; + + if ((currExtProp = currProp as IGxExternalObject) != null) + { + currProp = currExtProp.ExternalInstance; + } + #if !NETCORE GxObjectCollectionBase currColl; if ((currColl = currProp as GxObjectCollectionBase) != null) @@ -1895,32 +1919,51 @@ private bool TryConvertValueToProperty(object Value, PropertyInfo property, out return success; } - [System.Diagnostics.CodeAnalysis.SuppressMessage("GxFxCopRules", "CR1000:EnforceThreadSafeType")] - private Dictionary gxuploadAttrs = new Dictionary(); - private bool IsGxUploadAttribute(PropertyInfo property) + private GXTypeInfo _compatibilityGxuploadAttrs = null; + private bool IsGxUploadAttribute(string jsonPropertyName) { - string key = property.Name; - if (!gxuploadAttrs.ContainsKey(key)) - { - bool hasAtt = property.IsDefined(typeof(GxUpload), false); - gxuploadAttrs.Add(key, hasAtt); - } - return gxuploadAttrs[key]; + return GxUploadAttrs.ContainsKey(JsonNameToInternalName(jsonPropertyName)); } - - private Hashtable props; - - private PropertyInfo GetTypeProperty(string propName) + private bool IsGxUploadAttribute(PropertyInfo propertyInfo) { - if (props == null) + return GxUploadAttrs.ContainsKey(propertyInfo.Name); + } + private string JsonNameToInternalName(string jsonPropertyName) + { + string map = JsonMap(jsonPropertyName); + if (!string.IsNullOrEmpty(map)) + return $"{PROPERTY_PREFIX}{map}"; + else + return $"{PROPERTY_PREFIX}{jsonPropertyName}"; + } + protected virtual GXTypeInfo TypeInfo { get { return _compatibilityGxuploadAttrs; } set { _compatibilityGxuploadAttrs = value; } } + private ConcurrentDictionary GxUploadAttrs + { + get { - props = new Hashtable(); - foreach (PropertyInfo prop in this.GetType().GetProperties()) + if (TypeInfo == null) { - props.Add(prop.Name.ToLower(), prop); + TypeInfo = new GXTypeInfo(); + + TypeInfo.UploadAttrs = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + foreach (PropertyInfo property in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (property.Name.StartsWith(PROPERTY_PREFIX, StringComparison.OrdinalIgnoreCase)) + { + bool hasAtt = property.IsDefined(typeof(GxUpload), false); + if (hasAtt) + { + TypeInfo.UploadAttrs.TryAdd(property.Name, 1); + } + } + } } + return TypeInfo.UploadAttrs; } - return (PropertyInfo)props[propName]; + } + private PropertyInfo GetTypeProperty(string propName) + { + return this.GetType().GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); } private Hashtable methods; @@ -2002,7 +2045,10 @@ public void SetPropertyValue(string propertyName, object propertyValue) GetType().GetProperty($"gxTpr_{propertyName}").SetValue(this, propertyValue); } } - + public class GXTypeInfo + { + public ConcurrentDictionary UploadAttrs { get; set; } + } public interface IGxJSONAble { void AddObjectProperty(string name, object prop); @@ -2044,6 +2090,10 @@ public GxArrayList(int capacity) { innerArray = new List(capacity); } + internal GxArrayList(List list) + { + innerArray = list; + } public GxArrayList() { innerArray = new List(); @@ -2076,7 +2126,6 @@ public object Item(int index, int i) return innerArray[index][i]; } } - [CollectionDataContract(Name = "GxUnknownObjectCollection")] [KnownType(typeof(GxSimpleCollection))] [KnownType(typeof(GxStringCollection))] @@ -2431,7 +2480,6 @@ public interface IGxCollection : IGxCollection { T GetNumeric(int idx); } - public interface IGxExternalObject { object ExternalInstance { get; set; } @@ -2830,7 +2878,173 @@ public override string ToString() } } + public class GxGenericDictionary : Dictionary, IGxJSONSerializable, IGxJSONAble + { + private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(GxGenericDictionary)); + private readonly DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings() { UseSimpleDictionaryFormat = true }; + public void SetDictionary(GxGenericDictionary dictionary) + { + foreach (var entry in dictionary) + { + this[entry.Key] = entry.Value; + } + } + + public bool Get(TKey key, out TValue value) + { + if (TryGetValue(key, out value)) + { + return true; + } + else + { + return false; + } + } + public TValue Get(TKey key) + { + if (TryGetValue(key, out TValue value)) + { + return value; + } + else + { + return default; + } + } + public List ValueList + { + get{ + return base.Values.ToList(); + } + } + public List KeyList + { + get + { + return base.Keys.ToList(); + } + } + public void Set(TKey key, TValue value) + { + base[key] = value; + } + + public bool RemoveKey(TKey key) + { + return Remove(key); + } + + public void RemoveKeys(List keys) + { + foreach (var key in keys.ToList()) + { + RemoveKey(key); + } + } + + public void RemoveAll(GxGenericDictionary dictionary) + { + foreach (var key in dictionary.Keys.ToList()) + { + RemoveKey(key); + } + } + public string ToJson() + { + try + { + return JSONHelper.Serialize(this, settings); + } + catch (Exception e) + { + log.Error("Could not obtain JSON from Dictionary", e); + return ""; + } + } + + public void FromJson(string json) + { + try + { + Clear(); + Dictionary deserializedDictionary = JSONHelper.Deserialize>(json, Encoding.Unicode, null, null, settings); + foreach (var entry in deserializedDictionary) + { + this[entry.Key] = entry.Value; + } + } + catch (Exception e) + { + log.Error("Could not set Dictionary from JSON", e); + } + } + + public string ToJSonString() + { + return ToJson(); + } + + public bool FromJSonString(string s) + { + throw new NotImplementedException(); + } + + public bool FromJSonString(string s, GXBaseCollection Messages) + { + throw new NotImplementedException(); + } + + public bool FromJSonFile(GxFile file) + { + throw new NotImplementedException(); + } + + public bool FromJSonFile(GxFile file, GXBaseCollection Messages) + { + throw new NotImplementedException(); + } + + public void AddObjectProperty(string name, object prop) + { + throw new NotImplementedException(); + } + + public object GetJSONObject() + { + JObject jObj = new JObject(); + foreach (TKey item in Keys) + { + jObj.Accumulate(item.ToString(), this[item]); + } + return jObj; + + } + + public object GetJSONObject(bool includeState) + { + return GetJSONObject(); + } + + public void FromJSONObject(dynamic obj) + { + this.Clear(); + JObject jObj = obj as JObject; + if (jObj != null) + { + foreach (DictionaryEntry item in jObj) + { + base[(TKey)item.Key]= (TValue)item.Value; + } + } + } + + public string ToJavascriptSource() + { + throw new NotImplementedException(); + } + } public class GxDictionary : Dictionary { public bool HasKey(string key) diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxFtp.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxFtp.cs index bf774c728..9fc057b38 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxFtp.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxFtp.cs @@ -8,12 +8,14 @@ using System.Linq; using GeneXus.Encryption; using GeneXus.Configuration; +using log4net; namespace GeneXus.Utils { [SecuritySafeCritical] public class FtpService { + private static readonly ILog log = log4net.LogManager.GetLogger(typeof(FtpService)); private Socket _DataSocket; private Socket _ControlSocket; @@ -511,8 +513,7 @@ private void OpenDataConnection() } _DataSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); - IPHostEntry localHostEntry = Dns.GetHostEntry(Dns.GetHostName()); - IPAddress hostAddress = localHostEntry.AddressList.First(a => (a.AddressFamily == _DataSocket.AddressFamily)); ; + IPAddress hostAddress = GetIpAddress(Dns.GetHostName()); IPEndPoint epListener = new IPEndPoint(hostAddress, 0); _DataSocket.Bind(epListener); @@ -565,8 +566,7 @@ private void OpenPassiveDataConnection() { ProcessProtocolViolationError("Error in creating Data Socket"); } - IPHostEntry serverHostEntry = Dns.GetHostEntry(_RequestUri.Host); - IPAddress hostAddress = serverHostEntry.AddressList.First(a => (a.AddressFamily == _DataSocket.AddressFamily)); ; ; + IPAddress hostAddress = GetIpAddress(_RequestUri.Host); IPEndPoint serverEndPoint = new IPEndPoint(hostAddress, Port); try @@ -631,10 +631,8 @@ private void OpenControlConnection(Uri uriToConnect) clientEndPoint = _ControlSocket.LocalEndPoint; try { - IPHostEntry serverHostEntry = Dns.GetHostEntry(Host); - IPAddress ipAddresses = serverHostEntry.AddressList.First(a => (a.AddressFamily == _ControlSocket.AddressFamily)); + IPAddress ipAddresses = GetIpAddress(Host); IPEndPoint serverEndPoint = new IPEndPoint(ipAddresses, Port); - try { if (GXUtil.IsWindowsPlatform) @@ -679,6 +677,21 @@ private void OpenControlConnection(Uri uriToConnect) } } + private IPAddress GetIpAddress(string host) + { + IPHostEntry serverHostEntry = Dns.GetHostEntry(host); + IPAddress ipAddresses = serverHostEntry.AddressList.FirstOrDefault(); + GXLogging.Debug(log, $"GetHostEntry({host}) AddressList length: ", serverHostEntry.AddressList.Length.ToString()); + if (ipAddresses == null) + { + serverHostEntry = Dns.GetHostByName(host); + ipAddresses = serverHostEntry.AddressList.FirstOrDefault(); + GXLogging.Debug(log, $"GetHostByName({host}) AddressList length: ", serverHostEntry.AddressList.Length.ToString()); + } + GXLogging.Debug(log, "HostAddress ", ipAddresses.ToString()); + return ipAddresses; + } + void CloseDataConnection() { if(_DataSocket != null) @@ -976,19 +989,19 @@ internal void ProcessApplicationError( string s) { this._status = 1; this._statusDescription = s; - + GXLogging.Error(log, "ProcessApplicationError " + s); } internal void ProcessApplicationError( string s, Exception e) { this._status = 1; this._statusDescription = s + e.Message; - + GXLogging.Error(log, "ProcessApplicationError " + s, e); } internal void ProcessProtocolViolationError( string s) { this._status = 1; this._statusDescription = new ProtocolViolationException(s).ToString(); - + GXLogging.Warn(log, "ProcessProtocolViolationError ", this._statusDescription); } internal string ComposeExceptionMessage(ResponseDescription resp_desc, string log) { diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs index 657b8feaa..fd7f8234e 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs @@ -1,3 +1,6 @@ +#if !NETCORE +using Jayrock.Json; +#endif using System; using System.Collections; using System.Collections.Generic; @@ -5,7 +8,7 @@ using System.Xml.Serialization; using GeneXus.Application; using GeneXus.XML; -using Jayrock.Json; + namespace GeneXus.Utils { @@ -42,6 +45,54 @@ public GXBaseList() base.Insert(idx, TObject); IsAssigned = true; } + public bool AddRange(GXBaseList value, int? index) + { + if (!index.HasValue) + { + base.AddRange(value); + return true; + } + else if (index == 0) + { + base.InsertRange(index.Value, value); + return true; + } + else if (index > 0 && index <= Count+1) + { + base.InsertRange(index.Value - 1, value); + return true; + } + return false; + } + public bool RemoveRange(int index, int? countItemsToRemove) + { + if (index > 0 && index <= Count) + { + if (countItemsToRemove == null) + { + int countToRemove = Count - (index-1); + base.RemoveRange(index - 1, countToRemove); + return true; + } + else if (countItemsToRemove.Value < Count - index) + { + base.RemoveRange(index - 1, countItemsToRemove.Value); + return true; + } + } + return false; + } + + public bool Set(int index, T value) + { + if (index > 0 && index < Count) + { + this[index - 1] = value; + return true; + } + return false; + } + } [Serializable] @@ -468,7 +519,7 @@ public bool FromJSonString(string s, GXBaseCollection Messa } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); return false; } } diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs index 0f3b40e4a..8cda5d449 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs @@ -2,6 +2,7 @@ namespace GeneXus.Http.Client { using System; using System.Collections; + using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; @@ -9,6 +10,7 @@ namespace GeneXus.Http.Client using System.Net; using System.Net.Http; using System.Net.Http.Headers; + using System.Net.Security; using System.Security; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -51,7 +53,7 @@ internal MultiPartTemplate() Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x"); ContentType = $"multipart/form-data; boundary={Boundary}"; FormdataSeparator = "\r\n"; - FormdataTemplate = "--" + Boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}"; + FormdataTemplate = "--" + Boundary + "\r\nContent-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; Boundarybytes = Encoding.ASCII.GetBytes($"\r\n--{Boundary}\r\n"); EndBoundaryBytes = Encoding.ASCII.GetBytes($"\r\n--{Boundary}--"); HeaderTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" + "Content-Type: {2}\r\n\r\n"; @@ -60,6 +62,7 @@ internal MultiPartTemplate() public class GxHttpClient : IGxHttpClient, IDisposable { private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + private const int DEFAULT_TIMEOUT = 30000; public const int _Basic = 0; public const int _Digest = 1; public const int _NTLM = 2; @@ -67,7 +70,7 @@ public class GxHttpClient : IGxHttpClient, IDisposable const int StreamWriterDefaultBufferSize = 1024; Stream _sendStream; byte[] _receiveData; - int _timeout = 30000; + int _timeout = DEFAULT_TIMEOUT; short _statusCode = 0; string _proxyHost; int _proxyPort; @@ -93,18 +96,19 @@ public class GxHttpClient : IGxHttpClient, IDisposable IGxContext _context; #if NETCORE - IWebProxy _proxyObject; + static IWebProxy _proxyObject; + static object syncRootHttpInstance = new Object(); #else - WebProxy _proxyObject; + static WebProxy _proxyObject; #endif ArrayList _authCollection; ArrayList _authProxyCollection; X509Certificate2Collection _certificateCollection; + List _fileCertificateCollection; Encoding _encoding; Encoding _contentEncoding; - - + static object syncRoot = new Object(); public MultiPartTemplate MultiPart { get @@ -144,17 +148,161 @@ internal byte[] ReceiveData } } + #if NETCORE - [SecuritySafeCritical] - private HttpClientHandler GetHandler() + private async Task ReceiveDataAsync() + { + await ReadResponseDataAsync(); + return _receiveData; + } + + private const int POOLED_CONNECTION_LIFETIME_MINUTES = 2; + internal static ConcurrentDictionary _httpClientInstances = new ConcurrentDictionary(); + private static HttpClient GetHttpClientInstance(Uri URI, int timeout, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, List fileCertificateCollection, string proxyHost, int proxyPort, out bool disposableInstance) + { + if (CacheableInstance(authCollection, authProxyCollection)) + { + HttpClient value; + disposableInstance = false; + string key = HttpClientInstanceIdentifier(proxyHost, proxyPort, fileCertificateCollection, timeout); + if (_httpClientInstances.TryGetValue(key, out value)) + { + GXLogging.Debug(log, $"Getting httpClient cached instance"); + return value; + } + else + { + lock (syncRootHttpInstance) + { + if (_httpClientInstances.TryGetValue(key, out value)) + { + GXLogging.Debug(log, $"Getting httpClient cached instance"); + return value; + } + value = new HttpClient(GetHandler(URI, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort)); + value.Timeout = TimeSpan.FromMilliseconds(timeout); + _httpClientInstances.TryAdd(key, value); + return value; + } + } + } + else + { + disposableInstance = true; + return new HttpClient(GetHandler(URI, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort)); + } + } + + private static string HttpClientInstanceIdentifier(string proxyHost, int proxyPort, List fileCertificateCollection, int timeout) + { + bool defaultSslOptions = ServicePointManager.ServerCertificateValidationCallback == null; + if (string.IsNullOrEmpty(proxyHost) && fileCertificateCollection.Count==0 && timeout== DEFAULT_TIMEOUT && defaultSslOptions) + { + return string.Empty; + } + else if (fileCertificateCollection.Count==0) + { + return $"{proxyHost}:{proxyPort}::{timeout}:{defaultSslOptions}"; + } + else + { + return $"{proxyHost}:{proxyPort}:{string.Join(';', fileCertificateCollection)}:{timeout}:{defaultSslOptions}"; + } + } + + private static bool CacheableInstance(ArrayList authCollection, ArrayList authProxyCollection) + { + return authCollection.Count == 0 && authProxyCollection.Count == 0 && Preferences.SingletonHttpClient(); + } + private static SocketsHttpHandler GetHandler(Uri URI, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, string proxyHost, int proxyPort) + { + SocketsHttpHandler handler = new SocketsHttpHandler() + { + PooledConnectionLifetime = TimeSpan.FromMinutes(POOLED_CONNECTION_LIFETIME_MINUTES), + }; + int maxConnections = Preferences.GetHttpClientMaxConnectionPerRoute(); + if (maxConnections != Preferences.DEFAULT_HTTPCLIENT_MAX_PER_ROUTE) + { + handler.MaxConnectionsPerServer = maxConnections; + } + GXLogging.Debug(log, $"Creating SocketsHttpHandler MaxConnectionsPerServer:{handler.MaxConnectionsPerServer}"); + ICredentials cc = getCredentialCache(URI, authCollection); + if (cc != null) + { + handler.Credentials = getCredentialCache(URI, authCollection); + } + + SetSslOptions(handler); + + if (GXUtil.CompressResponse()) + { + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + foreach (X509Certificate2 cert in certificateCollection) + handler.SslOptions.ClientCertificates.Add(cert); + + WebProxy proxy = getProxy(proxyHost, proxyPort, authProxyCollection); + if (proxy != null) + { + handler.Proxy = proxy; + } + handler.UseCookies = false; + return handler; + + } + + private static void SetSslOptions(SocketsHttpHandler handler) { - return new HttpClientHandler(); + if (ServicePointManager.ServerCertificateValidationCallback != null) + { + handler.SslOptions = new SslClientAuthenticationOptions + { + RemoteCertificateValidationCallback = ServicePointManager.ServerCertificateValidationCallback + }; + } } #else [SecuritySafeCritical] - private WinHttpHandler GetHandler() + + private static HttpClient GetHttpClientInstance(Uri URI, int timeout, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, string proxyHost, int proxyPort, CookieContainer cookies) + { + TimeSpan milliseconds = TimeSpan.FromMilliseconds(timeout); + HttpClient value = new HttpClient(GetHandler(URI, milliseconds, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort, cookies)); + value.Timeout = milliseconds; + return value; + } + [SecuritySafeCritical] + private static WinHttpHandler GetHandler(Uri URI, TimeSpan milliseconds, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, string proxyHost, int proxyPort, CookieContainer cookies) { - return new WinHttpHandler(); + WinHttpHandler handler = new WinHttpHandler(); + ICredentials cc = getCredentialCache(URI, authCollection); + if (cc != null) + { + handler.ServerCredentials = cc; + } + if (ServicePointManager.ServerCertificateValidationCallback != null) + { + handler.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => ServicePointManager.ServerCertificateValidationCallback(sender, certificate, chain, sslPolicyErrors)); + } + handler.CookieUsePolicy = CookieUsePolicy.UseSpecifiedCookieContainer; + handler.CookieContainer = cookies; + if (GXUtil.CompressResponse()) + { + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + foreach (X509Certificate2 cert in certificateCollection) + handler.ClientCertificates.Add(cert); + WebProxy proxy = getProxy(proxyHost, proxyPort, authProxyCollection); + if (proxy != null) + { + handler.Proxy = proxy; + handler.WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseCustomProxy; + } + + handler.ReceiveDataTimeout = milliseconds; + handler.ReceiveHeadersTimeout = milliseconds; + + return handler; } #endif public GxHttpClient(IGxContext context) : this() @@ -172,21 +320,18 @@ public GxHttpClient() : base() _authCollection = new ArrayList(); _authProxyCollection = new ArrayList(); _certificateCollection = new X509Certificate2Collection(); + _fileCertificateCollection = new List(); IncludeCookies = true; _proxyHost = string.Empty; try { -#if NETCORE - _proxyObject = WebRequest.GetSystemWebProxy(); - -#else - _proxyObject = WebProxy.GetDefaultProxy(); - if (_proxyObject != null && _proxyObject.Address != null) +#if !NETCORE + if (ProxyObject != null && ProxyObject.Address != null) { - _proxyHost = _proxyObject.Address.Host; - _proxyPort = _proxyObject.Address.Port; + _proxyHost = ProxyObject.Address.Host; + _proxyPort = ProxyObject.Address.Port; } #endif } @@ -196,7 +341,36 @@ public GxHttpClient() : base() } } +#if NETCORE + + private IWebProxy ProxyObject +#else + private static WebProxy ProxyObject +#endif + { + get { + if (_proxyObject == null) + { + lock (syncRoot) + { + try + { +#if NETCORE + _proxyObject = WebRequest.GetSystemWebProxy(); +#else + _proxyObject = WebProxy.GetDefaultProxy(); +#endif + } + catch (Exception e) + { + GXLogging.Warn(log, "Error getting ProxyObject", e); + } + } + } + return _proxyObject; + } + } public short Digest { get { return _Digest; } @@ -532,12 +706,19 @@ private void EndMultipartBoundary(Stream reqStream) if (IsMultipart) reqStream.Write(MultiPart.EndBoundaryBytes, 0, MultiPart.EndBoundaryBytes.Length); } - - void setHeaders(HttpRequestMessage request, CookieContainer cookies) + void setContentHeaders(HttpRequestMessage request, string contentType) + { + if (contentType != null) + { + HttpContentHeaders contentHeaders = request.Content.Headers; + contentHeaders.ContentType = MediaTypeHeaderValue.Parse(contentType); + } + InferContentType(contentType, request); + } + void setHeaders(HttpRequestMessage request, CookieContainer cookies, out string contentType) { - HttpContentHeaders contentHeaders = request.Content.Headers; HttpRequestHeaders headers = request.Headers; - string contentType = null; + contentType = null; for (int i = 0; i < _headers.Count; i++) { string currHeader = _headers.Keys[i]; @@ -550,7 +731,6 @@ void setHeaders(HttpRequestMessage request, CookieContainer cookies) break; case "CONTENT-TYPE": contentType = _headers[i].ToString(); - contentHeaders.ContentType = MediaTypeHeaderValue.Parse(_headers[i].ToString()); break; case "ACCEPT": AddHeader(headers, "Accept", _headers[i]); @@ -584,7 +764,12 @@ void setHeaders(HttpRequestMessage request, CookieContainer cookies) { if (cookie.Contains("=")) { - cookies.Add(new Uri(request.RequestUri.Host), new Cookie(cookie.Split('=')[0], cookie.Split('=')[1]) { Domain = request.RequestUri.Host }); + UriBuilder uriBuilder = new UriBuilder(request.RequestUri.Scheme, request.RequestUri.Host); + Cookie pCookie = ParseCookie(cookie, request.RequestUri.Host); + if (pCookie != null) + { + cookies.Add(uriBuilder.Uri, pCookie); + } } } break; @@ -606,7 +791,16 @@ void setHeaders(HttpRequestMessage request, CookieContainer cookies) else headers.ConnectionClose = false; } - InferContentType(contentType, request); + } + Cookie ParseCookie(string cookie, string domain) + { + string[] values = cookie.TrimEnd(';').Split('='); + if (values.Length >= 2) { + string cookieName = values[0].Trim(); + string cookieValue = values[1]; + return new Cookie(cookieName, cookieValue) { Domain = domain }; + } + return null; } void AddHeader(HttpRequestHeaders headers, string headerName, string headerValue) { @@ -634,85 +828,123 @@ void setHttpVersion(HttpRequestMessage req) req.Version = HttpVersion.Version11; } [SecuritySafeCritical] - HttpResponseMessage ExecuteRequest(string method, string requestUrl, CookieContainer cookies) + HttpResponseMessage ExecuteRequest(string method, string requestUrl, bool contextCookies) { - GXLogging.Debug(log, String.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); + CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); + + GXLogging.Debug(log, string.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); HttpRequestMessage request; - HttpClient client; + HttpClient client = null; int BytesRead; - Byte[] Buffer = new Byte[1024]; + byte[] Buffer = new byte[1024]; + HttpResponseMessage response; - request = new HttpRequestMessage(); - request.RequestUri = new Uri(requestUrl); -#if NETCORE - HttpClientHandler handler = GetHandler(); - handler.Credentials = getCredentialCache(request.RequestUri, _authCollection); - if (ServicePointManager.ServerCertificateValidationCallback != null) + request = new HttpRequestMessage() { - handler.ServerCertificateCustomValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => ServicePointManager.ServerCertificateValidationCallback(sender, certificate, chain, sslPolicyErrors)); - } -#else - WinHttpHandler handler = GetHandler(); - handler.ServerCredentials = getCredentialCache(request.RequestUri, _authCollection); - if (ServicePointManager.ServerCertificateValidationCallback != null) + RequestUri = new Uri(requestUrl), + Method = new HttpMethod(method), + }; + setHeaders(request, cookies, out string contentType); + setHttpVersion(request); + bool disposableInstance = true; + try { - handler.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => ServicePointManager.ServerCertificateValidationCallback(sender, certificate, chain, sslPolicyErrors)); - } - handler.CookieUsePolicy = CookieUsePolicy.UseSpecifiedCookieContainer; +#if NETCORE + request.PopulateCookies(cookies); + client = GetHttpClientInstance(request.RequestUri, _timeout, _authCollection, _authProxyCollection, _certificateCollection, _fileCertificateCollection, _proxyHost, _proxyPort, out disposableInstance); +#else + client = GetHttpClientInstance(request.RequestUri, _timeout, _authCollection, _authProxyCollection, _certificateCollection, _proxyHost, _proxyPort, cookies); #endif - if (GXUtil.CompressResponse()) + using (MemoryStream reqStream = new MemoryStream()) + { + SendVariables(reqStream); + SendStream.Seek(0, SeekOrigin.Begin); + BytesRead = SendStream.Read(Buffer, 0, 1024); + GXLogging.Debug(log, "Start SendStream.Read: BytesRead " + BytesRead); + while (BytesRead > 0) + { + GXLogging.Debug(log, "reqStream.Write: Buffer.length " + Buffer.Length + ",'" + Encoding.UTF8.GetString(Buffer, 0, Buffer.Length) + "'"); + reqStream.Write(Buffer, 0, BytesRead); + BytesRead = SendStream.Read(Buffer, 0, 1024); + } + EndMultipartBoundary(reqStream); + GXLogging.Debug(log, "End SendStream.Read: stream " + reqStream.ToString()); + reqStream.Seek(0, SeekOrigin.Begin); + request.Content = new ByteArrayContent(reqStream.ToArray()); + setContentHeaders(request, contentType); +#if NETCORE + response = client.Send(request, HttpCompletionOption.ResponseHeadersRead); + response.ExtractCookies(cookies); +#else + response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult(); +#endif + } + } + finally { - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + if (disposableInstance && client != null) + { + client.Dispose(); + } } + return response; + } +#if NETCORE + async Task ExecuteRequestAsync(string method, string requestUrl, bool contextCookies) + { + CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); - handler.CookieContainer = cookies; - - foreach (X509Certificate2 cert in _certificateCollection) - handler.ClientCertificates.Add(cert); + GXLogging.Debug(log, string.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); + HttpRequestMessage request; + HttpClient client = null; + int BytesRead; + byte[] Buffer = new byte[1024]; + HttpResponseMessage response; - request.Method = new HttpMethod(method); - setHttpVersion(request); - WebProxy proxy = getProxy(_proxyHost, _proxyPort, _authProxyCollection); - if (proxy != null) + request = new HttpRequestMessage() { - handler.Proxy = proxy; -#if !NETCORE - handler.WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseCustomProxy; -#endif - } - HttpResponseMessage response; - TimeSpan milliseconds = TimeSpan.FromMilliseconds(_timeout); -#if !NETCORE - handler.ReceiveDataTimeout = milliseconds; - handler.ReceiveHeadersTimeout = milliseconds; -#endif - using (client = new HttpClient(handler)) + RequestUri = new Uri(requestUrl), + Method = new HttpMethod(method), + }; + setHeaders(request, cookies, out string contentType); + setHttpVersion(request); + bool disposableInstance = true; + try { - client.Timeout = milliseconds; - client.BaseAddress = request.RequestUri; - + request.PopulateCookies(cookies); + client = GetHttpClientInstance(request.RequestUri, _timeout, _authCollection, _authProxyCollection, _certificateCollection, _fileCertificateCollection, _proxyHost, _proxyPort, out disposableInstance); using (MemoryStream reqStream = new MemoryStream()) { SendVariables(reqStream); SendStream.Seek(0, SeekOrigin.Begin); - BytesRead = SendStream.Read(Buffer, 0, 1024); + BytesRead = await SendStream.ReadAsync(Buffer, 0, 1024); GXLogging.Debug(log, "Start SendStream.Read: BytesRead " + BytesRead); while (BytesRead > 0) { GXLogging.Debug(log, "reqStream.Write: Buffer.length " + Buffer.Length + ",'" + Encoding.UTF8.GetString(Buffer, 0, Buffer.Length) + "'"); - reqStream.Write(Buffer, 0, BytesRead); - BytesRead = SendStream.Read(Buffer, 0, 1024); + await reqStream.WriteAsync(Buffer, 0, BytesRead); + BytesRead = await SendStream.ReadAsync(Buffer, 0, 1024); } EndMultipartBoundary(reqStream); GXLogging.Debug(log, "End SendStream.Read: stream " + reqStream.ToString()); reqStream.Seek(0, SeekOrigin.Begin); request.Content = new ByteArrayContent(reqStream.ToArray()); - setHeaders(request, handler.CookieContainer); - response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult(); + setContentHeaders(request, contentType); + response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + response.ExtractCookies(cookies); + } + } + finally + { + if (disposableInstance && client != null) + { + client.Dispose(); } } return response; - } + } +#endif + void ReadResponseData() { if (_receiveData == null && _response!=null) @@ -720,7 +952,11 @@ void ReadResponseData() _receiveData = Array.Empty(); try { +#if NETCORE + Stream stream = _response.Content.ReadAsStream(); +#else Stream stream = _response.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); +#endif using (MemoryStream ms = new MemoryStream()) { @@ -744,6 +980,39 @@ void ReadResponseData() } } } +#if NETCORE + async Task ReadResponseDataAsync() + { + if (_receiveData == null && _response != null) + { + _receiveData = Array.Empty(); + try + { + Stream stream = await _response.Content.ReadAsStreamAsync(); + + using (MemoryStream ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + _receiveData = ms.ToArray(); + } + _eof = true; + int bytesRead = _receiveData.Length; + GXLogging.Debug(log, "BytesRead " + _receiveData.Length); + if (bytesRead > 0 && !_encodingFound) + { + _encoding = DetectEncoding(_charset, out _encodingFound, _receiveData, bytesRead); + } + } + catch (IOException ioEx) + { + if (_errCode == 1) + GXLogging.Warn(log, "Could not read response", ioEx); + else + throw ioEx; + } + } + } + #endif bool UseOldHttpClient(string name) { if (Config.GetValueOf("useoldhttpclient", out string useOld) && useOld.StartsWith("y", StringComparison.OrdinalIgnoreCase)) @@ -777,6 +1046,20 @@ public void Execute(string method, string name) HttpClientExecute(method, name); } } +#if NETCORE + public async Task ExecuteAsync(string method, string name) + { + if (UseOldHttpClient(name)) + { + GXLogging.Debug(log, "Using legacy GxHttpClient"); + await WebExecuteAsync(method, name); + } + else + { + await HttpClientExecuteAsync(method, name); + } + } +#endif internal void ProcessResponse(HttpResponseMessage httpResponse) { _response = httpResponse; @@ -789,28 +1072,13 @@ internal void ProcessResponse(HttpResponseMessage httpResponse) _errDescription = "The remote server returned an error: (" + _statusCode + ") " + _statusDescription + "."; } } - public void HttpClientExecute(string method, string name) + private void ProcessHttpClientException(Exception ex) { - _receiveData = null; - _response = null; - Byte[] Buffer = new Byte[1024]; - _errCode = 0; - _errDescription = string.Empty; - GXLogging.Debug(log, "Start Execute: method '" + method + "', name '" + name + "'"); - try - { - string requestUrl = GetRequestURL(name); - bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); - CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); - _response = ExecuteRequest(method, requestUrl, cookies); - - if (contextCookies) - _context.UpdateSessionCookieContainer(); - - - } + HttpRequestException httpex; + TaskCanceledException tcex; #if NETCORE - catch (AggregateException aex) + AggregateException aex; + if ((aex = ex as AggregateException) != null) { GXLogging.Warn(log, "Error Execute", aex); _errCode = 1; @@ -822,44 +1090,97 @@ public void HttpClientExecute(string method, string name) _response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); _response.StatusCode = HttpStatusCode.InternalServerError; } + else #endif - catch (HttpRequestException e) + if ((httpex = ex as HttpRequestException) != null) { - GXLogging.Warn(log, "Error Execute", e); + GXLogging.Warn(log, "Error Execute", httpex); _errCode = 1; - if (e.InnerException != null) - _errDescription = e.Message + " " + e.InnerException.Message; + if (httpex.InnerException != null) + _errDescription = httpex.Message + " " + httpex.InnerException.Message; else - _errDescription = e.Message; + _errDescription = httpex.Message; _response = new HttpResponseMessage(); _response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); #if NETCORE - _response.StatusCode = (HttpStatusCode)(e.StatusCode != null ? e.StatusCode : HttpStatusCode.InternalServerError); + _response.StatusCode = (HttpStatusCode)(httpex.StatusCode != null ? httpex.StatusCode : HttpStatusCode.InternalServerError); #else _response.StatusCode = HttpStatusCode.InternalServerError; #endif + } - catch (TaskCanceledException e) + else if ((tcex = ex as TaskCanceledException) != null) { - GXLogging.Warn(log, "Error Execute", e); + GXLogging.Warn(log, "Error Execute", tcex); _errCode = 1; - _errDescription = "The request has timed out. " + e.Message; + _errDescription = "The request has timed out. " + tcex.Message; _response = new HttpResponseMessage(); _response.StatusCode = 0; _response.Content = new StringContent(String.Empty); } - catch (Exception e) + else { - GXLogging.Warn(log, "Error Execute", e); + GXLogging.Warn(log, "Error Execute", ex); _errCode = 1; - if (e.InnerException != null) - _errDescription = e.Message + " " + e.InnerException.Message; + if (ex.InnerException != null) + _errDescription = ex.Message + " " + ex.InnerException.Message; else - _errDescription = e.Message; + _errDescription = ex.Message; _response = new HttpResponseMessage(); _response.Content = new StringContent(HttpHelper.StatusCodeToTitle(HttpStatusCode.InternalServerError)); _response.StatusCode = HttpStatusCode.InternalServerError; } + } +#if NETCORE + internal async Task HttpClientExecuteAsync(string method, string name) + { + _receiveData = null; + _response = null; + _errCode = 0; + _errDescription = string.Empty; + GXLogging.Debug(log, "Start Execute: method '" + method + "', name '" + name + "'"); + try + { + string requestUrl = GetRequestURL(name); + bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); + _response = await ExecuteRequestAsync(method, requestUrl, contextCookies); + + if (contextCookies) + _context.UpdateSessionCookieContainer(); + + } + catch (Exception ex) { + ProcessHttpClientException(ex); + } + + GXLogging.Debug(log, "Reading response..."); + if (_response == null) + return; + ProcessResponse(_response); + ClearSendStream(); + } +#endif + public void HttpClientExecute(string method, string name) + { + _receiveData = null; + _response = null; + _errCode = 0; + _errDescription = string.Empty; + GXLogging.Debug(log, "Start Execute: method '" + method + "', name '" + name + "'"); + try + { + string requestUrl = GetRequestURL(name); + bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); + _response = ExecuteRequest(method, requestUrl, contextCookies); + + if (contextCookies) + _context.UpdateSessionCookieContainer(); + + } + catch (Exception ex) + { + ProcessHttpClientException(ex); + } GXLogging.Debug(log, "Reading response..."); if (_response == null) return; @@ -872,11 +1193,13 @@ public void HttpClientExecute(string method, string name) internal void LoadResponseHeaders(HttpResponseMessage resp) { _respHeaders = new NameValueCollection(); - foreach (KeyValuePair> header in resp.Headers) + HttpResponseHeaders headers = resp.Headers; + foreach (KeyValuePair> header in headers) { _respHeaders.Add(header.Key, String.Join(",", header.Value)); } - foreach (KeyValuePair> header in resp.Content.Headers) + HttpContentHeaders contentHeaders = resp.Content.Headers; + foreach (KeyValuePair> header in contentHeaders) { _respHeaders.Add(header.Key, String.Join(",", header.Value)); } @@ -1020,7 +1343,7 @@ private void InferContentType(string contentType, HttpWebRequest req) } } - WebProxy getProxy(string proxyHost, int proxyPort, ArrayList authenticationCollection) + static WebProxy getProxy(string proxyHost, int proxyPort, ArrayList authenticationCollection) { if (proxyHost.Length > 0) { @@ -1086,7 +1409,8 @@ public void ConfigureHttpClientProtocol(string name, SoapHttpClientProtocol http httpC.Timeout = _timeout; } #endif - HttpWebRequest buildRequest(string method, string requestUrl, CookieContainer cookies) +#if NETCORE + async Task buildRequestAsync(string method, string requestUrl, CookieContainer cookies) { GXLogging.Debug(log, String.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); int BytesRead; @@ -1118,8 +1442,56 @@ HttpWebRequest buildRequest(string method, string requestUrl, CookieContainer co #if !NETCORE using (Stream reqStream = req.GetRequestStream()) #else - using (Stream reqStream = req.GetRequestStreamAsync().GetAwaiter().GetResult()) + using (Stream reqStream = await req.GetRequestStreamAsync()) +#endif + { + SendVariables(reqStream); + SendStream.Seek(0, SeekOrigin.Begin); + BytesRead = await SendStream.ReadAsync(Buffer, 0, 1024); + GXLogging.Debug(log, "Start SendStream.Read: BytesRead " + BytesRead); + while (BytesRead > 0) + { + GXLogging.Debug(log, "reqStream.Write: Buffer.length " + Buffer.Length + ",'" + Encoding.UTF8.GetString(Buffer, 0, Buffer.Length) + "'"); + await reqStream.WriteAsync(Buffer, 0, BytesRead); + BytesRead = await SendStream.ReadAsync(Buffer, 0, 1024); + } + EndMultipartBoundary(reqStream); + } + } + return req; + } #endif + + HttpWebRequest buildRequest(string method, string requestUrl, CookieContainer cookies) + { + GXLogging.Debug(log, String.Format("Start HTTPClient buildRequest: requestUrl:{0} method:{1}", requestUrl, method)); + int BytesRead; + Byte[] Buffer = new Byte[1024]; +#pragma warning disable SYSLIB0014 // WebRequest + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUrl); +#pragma warning disable SYSLIB0014 // WebRequest + + if (GXUtil.CompressResponse()) + { + req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + + req.Credentials = getCredentialCache(req.RequestUri, _authCollection); + req.CookieContainer = cookies; + foreach (X509Certificate2 cert in _certificateCollection) + req.ClientCertificates.Add(cert); + req.Method = method.Trim(); + req.Timeout = _timeout; + setHttpVersion(req); + WebProxy proxy = getProxy(_proxyHost, _proxyPort, _authProxyCollection); + if (proxy != null) + req.Proxy = proxy; + + setHeaders(req); + + if (!method.Equals(HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) && !method.Equals(HttpMethod.Head.Method, StringComparison.OrdinalIgnoreCase)) + { + using (Stream reqStream = req.GetRequestStream()) { SendVariables(reqStream); SendStream.Seek(0, SeekOrigin.Begin); @@ -1141,11 +1513,11 @@ public ICredentials GetCredentials(string url) { return getCredentialCache(new Uri(url), _authCollection); } - ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollection) + static ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollection) { string sScheme; GxAuthScheme auth; - CredentialCache cc = new CredentialCache(); + CredentialCache cc = null; for (int i = 0; i < authenticationCollection.Count; i++) { @@ -1175,10 +1547,18 @@ ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollection) } else if (sScheme != "Basic") { + if (cc == null) + { + cc = new CredentialCache(); + } cc.Add(URI, sScheme, new NetworkCredential(auth.User, auth.Password, auth.Realm)); } else { + if (cc == null) + { + cc = new CredentialCache(); + } cc.Add(URI, sScheme, new NetworkCredential(auth.User, auth.Password)); } } @@ -1188,11 +1568,11 @@ ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollection) } return cc; } - - private void WebExecute(string method, string name) +#if NETCORE + private async Task WebExecuteAsync(string method, string name) { HttpWebRequest req; - HttpWebResponse resp = null; + HttpWebResponse resp; _errCode = 0; _errDescription = string.Empty; @@ -1203,43 +1583,140 @@ private void WebExecute(string method, string name) string requestUrl = GetRequestURL(name); bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); - req = buildRequest(method, requestUrl, cookies); + req = await buildRequestAsync(method, requestUrl, cookies); -#if NETCORE - resp = req.GetResponse() as HttpWebResponse; + resp = await req.GetResponseAsync() as HttpWebResponse; if (contextCookies) _context.UpdateSessionCookieContainer(); -#else - resp = (HttpWebResponse)req.GetResponse(); -#endif } - catch (WebException e) + catch (Exception e) { - GXLogging.Warn(log, "Error Execute", e); - _errCode = 1; - _errDescription = e.Message; - resp = (HttpWebResponse)(e.Response); + resp = ProcessWebExecuteException(e); + } + + + _receiveData = Array.Empty(); + if (resp != null) + { + GXLogging.Debug(log, "Reading response..."); + loadResponseHeaders(resp); + + String charset = resp.ContentType; + using (Stream rStream = resp.GetResponseStream()) + { + try + { + bool encodingFound = false; + if (!string.IsNullOrEmpty(charset)) + { + int idx = charset.IndexOf("charset="); + if (idx > 0) + { + idx += 8; + charset = charset.Substring(idx, charset.Length - idx); + _encoding = GetEncoding(charset); + if (_encoding != null) + encodingFound = true; + } + else + { + charset = String.Empty; + } + } + using (MemoryStream ms = new MemoryStream()) + { + await rStream.CopyToAsync(ms); + _receiveData = ms.ToArray(); + } + int bytesRead = _receiveData.Length; + GXLogging.Debug(log, "BytesRead " + bytesRead); + + if (bytesRead > 0 && !encodingFound) + { + _encoding = DetectEncoding(charset, out encodingFound, _receiveData, bytesRead); + } + } + catch (IOException ioEx) + { + if (_errCode == 1) + GXLogging.Warn(log, "Could not read response", ioEx); + else + throw ioEx; + } + } + _statusCode = (short)resp.StatusCode; + _statusDescription = resp.StatusDescription; + resp.Close(); + + GXLogging.DebugSanitized(log, "_responseString " + ToString()); } + ClearSendStream(); + } +#endif + private HttpWebResponse ProcessWebExecuteException(Exception ex) + { + WebException we; + HttpWebResponse resp=null; #if NETCORE - catch (AggregateException aex) + AggregateException agge; + if ((agge = ex as AggregateException) != null) { - GXLogging.Warn(log, "Error Execute", aex); + GXLogging.Warn(log, "Error Execute", agge); _errCode = 1; - _errDescription = aex.Message; + _errDescription = agge.Message; - var baseEx = aex.GetBaseException() as WebException; + var baseEx = agge.GetBaseException() as WebException; if (baseEx != null) { resp = baseEx.Response as HttpWebResponse; _errDescription = baseEx.Message; } } + else #endif - catch (Exception e) + if ((we = ex as WebException)!=null) { - GXLogging.Warn(log, "Error Execute", e); + GXLogging.Warn(log, "Error Execute", we); _errCode = 1; - _errDescription = e.Message; + _errDescription = we.Message; + resp = (HttpWebResponse)(we.Response); + } + else + { + GXLogging.Warn(log, "Error Execute", ex); + _errCode = 1; + _errDescription = ex.Message; + } + return resp; + + } + private void WebExecute(string method, string name) + { + HttpWebRequest req; + HttpWebResponse resp = null; + + _errCode = 0; + _errDescription = string.Empty; + + GXLogging.Debug(log, "Start Execute: method '" + method + "', name '" + name + "'"); + try + { + string requestUrl = GetRequestURL(name); + bool contextCookies = _context != null && !String.IsNullOrEmpty(requestUrl); + CookieContainer cookies = contextCookies ? _context.GetCookieContainer(requestUrl, IncludeCookies) : new CookieContainer(); + req = buildRequest(method, requestUrl, cookies); + +#if NETCORE + resp = req.GetResponse() as HttpWebResponse; + if (contextCookies) + _context.UpdateSessionCookieContainer(); +#else + resp = (HttpWebResponse)req.GetResponse(); +#endif + } + catch (Exception e) + { + resp = ProcessWebExecuteException(e); } @@ -1370,10 +1847,9 @@ private Encoding ExtractEncodingFromCharset(string responseText, string regExpP, Encoding enc = null; Match m = Regex.Match(responseText, regExpP); - string parsedEncoding = string.Empty; if (m != null && m.Success) { - parsedEncoding = m.Value; + string parsedEncoding = m.Value; parsedEncoding = parsedEncoding.Substring(startAt, parsedEncoding.Length - (startAt + 1)); enc = GetEncoding(parsedEncoding); } @@ -1399,7 +1875,11 @@ public string ReadChunk() { if (_receivedChunkedStream == null) { +#if NETCORE + _receivedChunkedStream = new StreamReader(_response.Content.ReadAsStream()); +#else _receivedChunkedStream = new StreamReader(_response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()); +#endif } _eof = _receivedChunkedStream.EndOfStream; if (!_eof) @@ -1428,6 +1908,20 @@ public override string ToString() GXLogging.DebugSanitized(log, "_responseString " + responseString); return responseString; } +#if NETCORE + public async Task ToStringAsync() + { + byte[] bytes = await ReceiveDataAsync(); + if (bytes == null) + return string.Empty; + if (_encoding == null) + _encoding = Encoding.UTF8; + string responseString = _encoding.GetString(bytes); + GXLogging.DebugSanitized(log, "_responseString " + responseString); + return responseString; + } +#endif + public void ToFile(string fileName) { string pathName = fileName; @@ -1505,6 +1999,7 @@ public void AddCertificate(string file, string pass) { c = new X509Certificate2(file, pass); } + _fileCertificateCollection.Add(file); _certificateCollection.Add(c); } diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxSession.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxSession.cs index af89955f3..13881d355 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxSession.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxSession.cs @@ -5,10 +5,12 @@ using System.Web.SessionState; #else using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Session; #endif using GeneXus.Utils; using GeneXus.Encryption; using GeneXus.Application; +using GeneXus.Configuration; namespace GeneXus.Http { @@ -29,7 +31,12 @@ string Id } public class GxWebSession : IGxSession - { + { +#if NETCORE + const string SESSION_COOKIE_NAME = "SESSION_COOKIE_NAME"; + const string ASPNETCORE_APPL_PATH = "ASPNETCORE_APPL_PATH"; +#endif + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private HttpSessionState _httpSession; #region InternalKeys @@ -215,19 +222,61 @@ public static bool IsSessionExpired(HttpContext httpContext) if (httpContext.IsNewSession()) { string CookieHeaders = httpContext.Request.Headers["Cookie"]; - - if ((null != CookieHeaders) && ((CookieHeaders.IndexOf("ASP.NET_SessionId") >= 0)|| CookieHeaders.IndexOf(".AspNetCore.Session") >= 0)) - { - // IsNewSession is true, but session cookie exists, - // so, ASP.NET session is expired - return true; + if ((null != CookieHeaders) && (CookieHeaders.IndexOf(SessionCookieName) >= 0)) + { + // IsNewSession is true, but session cookie exists, + // so, ASP.NET session is expired + return true; } } } return false; } +#if NETCORE + internal static string GetSessionCookieName(string virtualPath) + { + string cookieName; + if (Config.GetValueOrEnvironmentVarOf(SESSION_COOKIE_NAME, out cookieName)) + return cookieName; + else + { + if (!string.IsNullOrEmpty(virtualPath)) + { + return $"{SessionDefaults.CookieName}.{virtualPath.ToLower()}"; + } + else + { + string applPath = Config.ConfigRoot[ASPNETCORE_APPL_PATH]; - } + if (!string.IsNullOrEmpty(applPath)) + { + cookieName = ToCookieName(applPath).ToLower(); + return $"{SessionDefaults.CookieName}.{cookieName}"; + } + } + return SessionDefaults.CookieName; + } + } + static string ToCookieName(string name) + { + char[] cookieName = new char[name.Length]; + int index = 0; + + foreach (char character in name) + { + if (char.IsLetter(character) || char.IsNumber(character)) + { + cookieName[index] = character; + index++; + } + } + return new string(cookieName, 0, index); + } + internal static string SessionCookieName { get; set; } +#else + const string SessionCookieName="ASP.NET_SessionId"; +#endif + } public class GxSession : IGxSession { diff --git a/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj b/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj index edf352ed3..3d89f45e6 100644 --- a/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj +++ b/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj @@ -13,11 +13,11 @@ - + - + diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs index f2b346659..6da5f4545 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/CsrfHelper.cs @@ -1,7 +1,10 @@ +using System.Net; +using System; using System.Net.Http; using System.Security; using System.Web; using System.Web.Helpers; +using System.Web.Mvc; using GeneXus.Application; using GeneXus.Utils; @@ -9,16 +12,37 @@ namespace GeneXus.Http { internal class CSRFHelper { + [SecuritySafeCritical] + internal static bool HandleException(Exception e, HttpContext httpContext) + { + if (RestAPIHelpers.ValidateCsrfToken()) + { + return HandleExceptionImp(e, httpContext); + } + return false; + } + [SecuritySafeCritical] + private static bool HandleExceptionImp(Exception e, HttpContext httpContext) + { + if (e is HttpAntiForgeryException) + { + httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + httpContext.Response.StatusDescription = HttpHelper.InvalidCSRFToken; + return true; + } + return false; + } + [SecuritySafeCritical] internal static void ValidateAntiforgery(HttpContext context) { if (RestAPIHelpers.ValidateCsrfToken()) { - ValidateAntiforgeryImpl(context); + ValidateAntiforgeryImp(context); } } [SecurityCritical] - static void ValidateAntiforgeryImpl(HttpContext context) + private static void ValidateAntiforgeryImp(HttpContext context) { string cookieToken, formToken; string httpMethod = context.Request.HttpMethod; diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs index 0cfa76acf..9a66cc57a 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs @@ -185,11 +185,15 @@ internal class GXLoggerLog4Net : IGXLogger const string ThreadNameNet6 = ".NET ThreadPool Worker"; const string ThreadId = "threadid"; #endif + private bool _traceEnabled = false; + private bool _debugEnabled = false; internal ILog log { get; set; } internal GXLoggerLog4Net(ILog logInstance) { log = logInstance; + _traceEnabled = log.Logger.IsEnabledFor(Level.Trace); + _debugEnabled = log.IsDebugEnabled; } void SetThreadIdForLogging() { @@ -216,15 +220,15 @@ void SetThreadIdForLogging() } #endif } - public bool IsTraceEnabled { get => TraceEnabled(); } + public bool IsTraceEnabled { get => _traceEnabled; } public bool IsErrorEnabled { get => ErrorEnabled(); } public bool IsWarningEnabled { get => WarningEnabled(); } - public bool IsDebugEnabled { get => DebugEnabled(); } + public bool IsDebugEnabled { get => _debugEnabled; } public bool IsInfoEnabled { get => InfoEnabled(); } public bool IsCriticalEnabled { get => CriticalEnabled(); } public bool TraceEnabled() { - return log.Logger.IsEnabledFor(Level.Trace); + return _traceEnabled; } public bool ErrorEnabled() { @@ -236,7 +240,7 @@ public bool WarningEnabled() } public bool DebugEnabled() { - return log.IsDebugEnabled; + return _debugEnabled; } public bool InfoEnabled() { @@ -425,6 +429,15 @@ public static void Warn(ILog log, string msg, Exception ex) log.Warn(msg, ex); } } + internal static void DebugSanitized(IGXLogger log, string startMsg, Func buildMsg) + { + if (log.IsDebugEnabled) + { + string msg = buildMsg(); + DebugSanitized(log, startMsg + msg); + } + } + public static void DebugSanitized(ILog log, Exception ex, params string[] list) { if (log.IsDebugEnabled) @@ -506,6 +519,25 @@ internal static void Trace(IGXLogger logger, params string[] list) logger.LogTrace(string.Join(" ", list)); } } + internal static void Trace(IGXLogger logger, Func buildMsg) + { + if (logger != null) + { + if (logger.IsTraceEnabled) + { + string msg = buildMsg(); + logger.LogTrace(msg); + } + } + } + internal static bool TraceEnabled(IGXLogger logger) + { + if (logger != null) + return logger.IsTraceEnabled; + else + return false; + } + public static void Critical(IGXLogger logger, params string[] list) { if (logger != null) diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXNavigationHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXNavigationHelper.cs index 1b755911c..218180e38 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXNavigationHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXNavigationHelper.cs @@ -1,7 +1,9 @@ namespace GeneXus.Application { using System; +#if !NETCORE using Jayrock.Json; +#endif using System.Collections.Generic; [Serializable] public class GXNavigationHelper diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs index a429508c6..dbbdf8f64 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestAPIClient.cs @@ -109,7 +109,7 @@ public void AddQueryVar(String varName, Geospatial varValue) public void AddQueryVar(String varName, bool varValue) { - _queryVars[varName] = varValue.ToString(); + _queryVars[varName] = StringUtil.BoolToStr(varValue); } public void AddQueryVar(String varName, GxUserType varValue) @@ -164,7 +164,7 @@ public void AddBodyVar(String varName, long varValue) } public void AddBodyVar(String varName, bool varValue) { - _bodyVars[varName] = varValue.ToString(); + _bodyVars[varName] = StringUtil.BoolToStr(varValue); } public void AddBodyVar(String varName, Guid varValue) { diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestUtils.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestUtils.cs index 27e7b1928..ec16827f6 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestUtils.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXRestUtils.cs @@ -9,7 +9,9 @@ using System.Text.Json.Serialization; #endif using System.IO; +#if !NETCORE using Jayrock.Json; +#endif using GeneXus.Configuration; namespace GeneXus.Utils @@ -141,7 +143,7 @@ public static Dictionary ReadRestBodyParameters(Stream stream) internal static bool ValidateCsrfToken() { - return Config.GetValueOf("ValidateCSRF", Preferences.NO) == Preferences.YES; + return Config.GetValueOf("CSRF_PROTECTION", Preferences.NO) == Preferences.YES; } } } diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXUtilsGeospatial.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXUtilsGeospatial.cs index b760d64ee..74e030fa4 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXUtilsGeospatial.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXUtilsGeospatial.cs @@ -1,11 +1,15 @@ using System; using System.Collections; +#if NETCORE +using GeneXus.Application; +#else +using Jayrock.Json; +#endif using System.Data.SqlTypes; using System.Globalization; using System.Reflection; using System.Runtime.Serialization; using GeneXus.Metadata; -using Jayrock.Json; #if NETCORE using GxClasses.Helpers; #endif diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GxObjectProperties.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GxObjectProperties.cs index fa8bed4b1..e954e8481 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GxObjectProperties.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GxObjectProperties.cs @@ -20,7 +20,6 @@ public class GxObjectProperties public string ErrorMessage { get => errorMessage; set => errorMessage = value; } public int ErrorCode { get => errorCode; set => errorCode = value; } public int StatusCode { get => statusCode; set => statusCode = value; } - public string StatusMessage { get => statusMessage; set => statusMessage = value; } public int Protocol { get => protocol; set => protocol = value; } } diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 0e19a76d5..65d9c4789 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -27,6 +27,7 @@ using Microsoft.Net.Http.Headers; using System.Net.Http; using System.Globalization; +using System.Linq; namespace GeneXus.Http { @@ -72,10 +73,74 @@ public class WrappedJsonError [DataMember(Name = "error")] public HttpJsonError Error; } +#if NETCORE + internal static class CookiesHelper + { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(CookiesHelper).FullName); + + internal static void PopulateCookies(this HttpRequestMessage request, CookieContainer cookieContainer) + { + if (cookieContainer != null) + { + IEnumerable cookies = cookieContainer.GetCookies(); + if (cookies.Any()) + { + request.Headers.Add("Cookie", cookies.ToHeaderFormat()); + } + } + } + private static string ToHeaderFormat(this IEnumerable cookies) + { + return string.Join(";", cookies); + } + internal static void ExtractCookies(this HttpResponseMessage response, CookieContainer cookieContainer) + { + if (response.Headers.TryGetValues("Set-Cookie", out var cookieValues)) + { + Uri uri = response.RequestMessage.RequestUri; + foreach (string cookieValue in cookieValues) + { + try + { + cookieContainer.SetCookies(uri, cookieValue); + } + catch (CookieException ex) + { + GXLogging.Warn(log, $"Ignored cookie for container: {cookieValue} url:{uri}", ex); + } + } + + } + } + } +#endif public class HttpHelper { static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + + internal static Dictionary GAMServices = new Dictionary(){ + {"oauth/access_token","gxoauthaccesstoken.aspx"}, + {"oauth/logout","gxoauthlogout.aspx"}, + {"oauth/userinfo","gxoauthuserinfo.aspx"}, + {"oauth/gam/signin","agamextauthinput.aspx"}, + {"oauth/gam/callback","agamextauthinput.aspx"}, + {"oauth/gam/access_token","agamoauth20getaccesstoken.aspx"}, + {"oauth/gam/userinfo","agamoauth20getuserinfo.aspx"}, + {"oauth/gam/signout","agamextauthinput.aspx"}, + {"saml/gam/signin","Saml2/SignIn"}, + {"saml/gam/callback","gamexternalauthenticationinputsaml20_ws.aspx"}, + {"saml/gam/signout","Saml2/Logout"}, + {"oauth/requesttokenservice","agamstsauthappgetaccesstoken.aspx"}, + {"oauth/queryaccesstoken","agamstsauthappvalidaccesstoken.aspx"}, + {"oauth/gam/v2.0/access_token","agamoauth20getaccesstoken_v20.aspx"}, + {"oauth/gam/v2.0/userinfo","agamoauth20getuserinfo_v20.aspx"}, + {"oauth/gam/v2.0/requesttokenanduserinfo","agamssorequesttokenanduserinfo_v20.aspx"}}; + internal static HashSet GamServicesInternalName = new HashSet(GAMServices.Values.Select(value => value.Replace(ASPX, string.Empty))); + internal const string QUERYVIEWER_NAMESPACE = "QueryViewer.Services"; + internal const string GXFLOW_NSPACE = "GXflow.Programs"; + internal const string GAM_NSPACE = "GeneXus.Security.API"; + /* * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control * Specifying no-cache or max-age=0 indicates that @@ -93,7 +158,7 @@ public class HttpHelper const string GAM_CODE_TFA_USER_MUST_VALIDATE = "410"; const string GAM_CODE_TOKEN_EXPIRED = "103"; static Regex CapitalsToTitle = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); - + internal const string InvalidCSRFToken = "InvalidCSRFToken"; const string CORS_MAX_AGE_SECONDS = "86400"; internal static void CorsHeaders(HttpContext httpContext) { @@ -286,10 +351,14 @@ internal static void TraceUnexpectedError(Exception ex) } internal static void SetUnexpectedError(HttpContext httpContext, HttpStatusCode statusCode, Exception ex) + { + string statusCodeDesc = StatusCodeToTitle(statusCode); + SetUnexpectedError(httpContext, statusCode, statusCodeDesc, ex); + } + internal static void SetUnexpectedError(HttpContext httpContext, HttpStatusCode statusCode, string statusCodeDesc, Exception ex) { TraceUnexpectedError(ex); string statusCodeStr = statusCode.ToString(INT_FORMAT); - string statusCodeDesc = StatusCodeToTitle(statusCode); SetResponseStatus(httpContext, statusCode, statusCodeDesc); SetJsonError(httpContext, statusCodeStr, statusCodeDesc); } diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/JSONHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/JSONHelper.cs index a857c3f27..5cce38a9a 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/JSONHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/JSONHelper.cs @@ -3,13 +3,19 @@ using System.IO; using System.Runtime.Serialization.Json; using System.Text; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using System.Runtime.Serialization; using GeneXus.Configuration; #if NETCORE using System.Buffers.Text; using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Encodings.Web; +using System.Globalization; #endif namespace GeneXus.Utils @@ -33,37 +39,80 @@ public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS return JsonSerializer.Deserialize(ref reader, options); case JsonTokenType.StartObject: return JsonSerializer.Deserialize(ref reader, options); + case JsonTokenType.Number: + if (reader.TryGetInt32(out int l)) + return l; + else + if (reader.TryGetDecimal(out decimal d)) + return d; + else + return reader.GetDouble(); + case JsonTokenType.String: + return reader.GetString(); default: using (JsonDocument document = JsonDocument.ParseValue(ref reader)) { - return document.RootElement.Clone().ToString(); + return document.RootElement.Clone(); } } } - public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { throw new NotImplementedException(); } } + internal class CustomGeospatialConverter : JsonConverter + { + public override Geospatial Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + throw new NotImplementedException("Deserialization is not supported."); + + public override void Write(Utf8JsonWriter writer, Geospatial value, JsonSerializerOptions options) + { + string stringValue = value?.ToString(); + JsonSerializer.Serialize(writer, stringValue, options); + } + } + internal class CustomDateTimeConverter : JsonConverter + { + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException("Deserialization is not supported."); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(Convert.ToString(value, CultureInfo.InvariantCulture)); //"dd/MM/yyyy HH:mm:ss" + } + } internal class TextJsonSerializer : GXJsonSerializer { internal override bool IsJsonNull(object jobject) { return jobject == null; } + static JsonSerializerOptions DeserializationOptions = new JsonSerializerOptions() { Converters = { new GxJsonConverter() }, AllowTrailingCommas=true }; internal override T ReadJSON(string json) { - JsonSerializerOptions opts = new JsonSerializerOptions(); - opts.Converters.Add(new GxJsonConverter()); - return JsonSerializer.Deserialize(json, opts); + return JsonSerializer.Deserialize(json, DeserializationOptions); } internal override string WriteJSON(T kbObject) { - return JsonSerializer.Serialize(kbObject); + if (kbObject != null) + { + return kbObject.ToString(); + } + return null; + } + static JsonSerializerOptions JayrockCompatibleOptions = new JsonSerializerOptions() { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Converters = { new CustomDateTimeConverter() }, + NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals }; + internal static string SerializeToJayrockCompatibleJson(T value) where T : IJayrockCompatible + { + return JsonSerializer.Serialize(value, JayrockCompatibleOptions); } } -#endif +#else internal class JayRockJsonSerializer : GXJsonSerializer { internal override bool IsJsonNull(object jobject) @@ -85,6 +134,7 @@ internal override string WriteJSON(T kbObject) return null; } } +#endif internal enum GXJsonSerializerType { Utf8, @@ -94,8 +144,7 @@ internal enum GXJsonSerializerType internal abstract class GXJsonSerializer { private static GXJsonSerializer s_instance = null; - private static object syncRoot = new Object(); - static GXJsonSerializerType DefaultJSonSerializer= GXJsonSerializerType.Jayrock; + private static object syncRoot = new object(); internal static GXJsonSerializer Instance { get @@ -106,17 +155,11 @@ internal static GXJsonSerializer Instance { if (s_instance == null) { - switch (DefaultJSonSerializer) - { #if NETCORE - case GXJsonSerializerType.TextJson: - s_instance = new TextJsonSerializer(); - break; + s_instance = new TextJsonSerializer(); +#else + s_instance = new JayRockJsonSerializer(); #endif - default: - s_instance = new JayRockJsonSerializer(); - break; - } } } } @@ -156,7 +199,7 @@ public static T ReadJSON(string json, GXBaseCollection M } catch (Exception ex) { - GXUtil.ErrorToMessages("FromJson Error", ex, Messages); + GXUtil.ErrorToMessages("FromJson Error", ex, Messages, false); GXLogging.Error(log, "FromJsonError ", ex); return default(T); } @@ -178,7 +221,7 @@ public static T ReadJavascriptJSON(string json, GXBaseCollection(T kbObject, Encoding encoding, IEnumerable(T kbObject, Encoding encoding, IEnumerable(T kbObject, DataContractJsonSerializerSettings settings) where T : class + { + try + { + Encoding encoding = Encoding.UTF8; + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), settings); + using (MemoryStream stream = new MemoryStream()) + { + serializer.WriteObject(stream, kbObject); + return encoding.GetString(stream.ToArray()); + } + } + catch (Exception ex) + { + GXLogging.Error(log, "Serialize error ", ex); + } + return null; + } internal static string WCFSerialize(T kbObject, Encoding encoding, IEnumerable knownTypes, bool useSimpleDictionaryFormat) where T : class { try @@ -276,12 +337,16 @@ static DataContractJsonSerializerSettings WCFSerializationSettings(IEnumerable(kbObject, encoding, knownTypes, new T()); } public static T Deserialize(string kbObject, Encoding encoding, IEnumerable knownTypes, T defaultValue) where T : class + { + var settings = SerializationSettings(knownTypes); + return Deserialize(kbObject, encoding, knownTypes, defaultValue, settings); + } + internal static T Deserialize(string kbObject, Encoding encoding, IEnumerable knownTypes, T defaultValue, DataContractJsonSerializerSettings settings) where T : class { if (!string.IsNullOrEmpty(kbObject)) { try { - var settings = SerializationSettings(knownTypes); using (MemoryStream stream = new MemoryStream(encoding.GetBytes(kbObject))) { DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), settings); @@ -299,14 +364,13 @@ public static T Deserialize(string kbObject, Encoding encoding, IEnumerable(string kbObject, Encoding encoding) where T : class, new() { - return Deserialize(kbObject, Encoding.Unicode, null, new T()); + return Deserialize(kbObject, encoding, null, new T()); } public static T Deserialize(string kbObject) where T : class, new() { return Deserialize(kbObject, Encoding.Unicode); } - public static T DeserializeNullDefaultValue(string kbObject) where T : class { return Deserialize(kbObject, Encoding.Unicode, null, null); diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs index a91181789..cfe9ed0d5 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs @@ -20,7 +20,9 @@ namespace GeneXus.Http using GeneXus.Utils; using GeneXus.XML; using GeneXus.WebControls; +#if !NETCORE using Jayrock.Json; +#endif using Helpers; using System.Collections.Concurrent; using System.Net.Http; @@ -36,17 +38,17 @@ namespace GeneXus.Http using System.Web; using System.Web.UI; using System.Web.UI.WebControls; - using System.Web.Script.Serialization; using System.Net; using GeneXus.Notifications; using Web.Security; using System.Web.SessionState; - using GeneXus.Mock; using GeneXus.Data.NTier; -#endif + using System.Security; +#endif + using System.Threading.Tasks; #if NETCORE public abstract class GXHttpHandler : GXBaseObject, IHttpHandler #else @@ -229,16 +231,36 @@ private static bool IsNumericType(Type t) int _currParameter; #if NETCORE private GXWebRow _currentGridRow; -#endif + private Dictionary EventsMetadata = new Dictionary(); +#else private Hashtable EventsMetadata = new Hashtable(); +#endif +#if NETCORE + protected void setEventMetadata(string EventName, string Metadata) + { + if (EventsMetadata.ContainsKey(EventName)) + EventsMetadata[EventName] += Metadata; + else + EventsMetadata[EventName] = Metadata; + } + internal async Task WebExecuteExAsync(HttpContext httpContext) + { + if (IsUploadRequest(httpContext)) + new GXObjectUploadServices(context).webExecute(); + else if (IsFullAjaxRequest(httpContext)) + await WebAjaxEventAsync(); + else + await WebExecuteAsync(); + } +#else protected void setEventMetadata(string EventName, string Metadata) { if (EventsMetadata[EventName] == null) EventsMetadata[EventName] = string.Empty; EventsMetadata[EventName] += Metadata; } - +#endif public void webExecuteEx(HttpContext httpContext) { if (IsUploadRequest(httpContext)) @@ -274,6 +296,13 @@ private bool IsFullAjaxRequest(HttpContext httpContext) public virtual void InitializeDynEvents() { throw new Exception("The method or operation is not implemented."); } public virtual void initialize_properties() { throw new Exception("The method or operation is not implemented."); } public virtual void webExecute() { throw new Exception("The method or operation is not implemented."); } + protected virtual Task WebExecuteAsync() + { + GXLogging.Warn(log, this.GetType().FullName + " not generated as async service"); + webExecute(); + return Task.CompletedTask; + } + #if !NETCORE public virtual void initialize() { throw new Exception("The method or operation is not implemented."); } public virtual void cleanup() { } @@ -442,7 +471,7 @@ private void ParseInputJSonMessage(JObject objMessage, GXHttpHandler targetObj) } if (objMessage.Contains("fullPost")) { - this.targetObj._Context.httpAjaxContext.ParseGXState((Jayrock.Json.JObject)objMessage["fullPost"]); + this.targetObj._Context.httpAjaxContext.ParseGXState((JObject)objMessage["fullPost"]); } } private void ParseGridsDataParms(JObject gxGrids) @@ -522,7 +551,12 @@ private void ParseMetadata() int eventCount = 0; foreach (string eventName in events) { +#if NETCORE + + JObject eventMetadata = JSONHelper.ReadJSON(targetObj.EventsMetadata[eventName.ToString()]); +#else JObject eventMetadata = JSONHelper.ReadJSON((string)targetObj.EventsMetadata[eventName.ToString()]); +#endif eventHandlers[eventCount] = (string)eventMetadata["handler"]; JArray eventInputParms = (JArray)eventMetadata["iparms"]; foreach (JObject inputParm in eventInputParms) @@ -531,9 +565,12 @@ private void ParseMetadata() eventUseInternalParms[eventCount] = eventUseInternalParms[eventCount] || IsInternalParm(inputParm); } JArray eventOutputParms = (JArray)eventMetadata["oparms"]; - foreach (JObject outputParm in eventOutputParms) + if (eventOutputParms != null) { - AddParmsMetadata(outputParm, DynAjaxEventContext.outParmsMetadata, DynAjaxEventContext.outParmsMetadataHash); + foreach (JObject outputParm in eventOutputParms) + { + AddParmsMetadata(outputParm, DynAjaxEventContext.outParmsMetadata, DynAjaxEventContext.outParmsMetadataHash); + } } eventCount++; } @@ -882,7 +919,7 @@ private object[] BeforeInvoke() { try { - JObject hashObj = (JObject)(hash_i < inHashValues.Length ? inHashValues[hash_i] : new Jayrock.Json.JObject()); + JObject hashObj = (JObject)(hash_i < inHashValues.Length ? inHashValues[hash_i] : new JObject()); string sRow = hashObj.Contains("row") ? (string)hashObj["row"] : string.Empty; string hash = hashObj.Contains("hsh") ? (string)hashObj["hsh"] : string.Empty; SetScalarOrCollectionValue((string)parm["av"], inParmsValues[parm_i], columnValues); @@ -1028,6 +1065,40 @@ internal string Invoke(string JsonMessage, GXHttpHandler targetObj) return response; } } +#if NETCORE + internal virtual async Task WebAjaxEventAsync() + { + bool isMultipartRequest = context.IsMultipartRequest; + if (isMultipartRequest) + { + localHttpContext.Response.ContentType = MediaTypesNames.TextHtml; + } + else + { + localHttpContext.Response.ContentType = MediaTypesNames.ApplicationJson; + } + setAjaxCallMode(); + context.setFullAjaxMode(); + DynAjaxEvent dynAjaxEvent = new DynAjaxEvent(context.httpAjaxContext.DynAjaxEventContext); + string jsonRequest; + if (context.IsMultipartRequest) + jsonRequest = cgiGet(GX_AJAX_MULTIPART_ID); + else + { + using (StreamReader reader = new StreamReader(localHttpContext.Request.GetInputStream())) + { + jsonRequest = await reader.ReadToEndAsync(); ; + } + } + string jsonResponse = dynAjaxEvent.Invoke(jsonRequest, this); + + + if (!redirect(context)) + { + ((GxContext)context).SendFinalJSONResponse(jsonResponse); + } + } +#endif public virtual void webAjaxEvent() { @@ -1905,8 +1976,73 @@ public bool IsMain get { return _isMain; } } #endif +#if NETCORE + internal async Task ProcessRequestAsync(HttpContext httpContext) + { + localHttpContext = httpContext; + if (IsSpaRequest() && !IsSpaSupported()) + { + this.SendResponseStatus(SPA_NOT_SUPPORTED_STATUS_CODE, "SPA not supported by the object"); + context.CloseConnections(); + await Task.CompletedTask; + } + ControlOutputWriter = new HtmlTextWriter(localHttpContext); + LoadParameters(localHttpContext.Request.QueryString.Value); + context.httpAjaxContext.GetAjaxEncryptionKey(); //Save encryption key in session + InitPrivates(); + try + { + SetStreaming(); + SendHeaders(); + string clientid = context.ClientID; //Send clientid cookie (before response HasStarted) if necessary, since UseResponseBuffering is not in .netcore3.0 + + bool validSession = ValidWebSession(); + if (validSession && IntegratedSecurityEnabled) + validSession = ValidSession(); + if (validSession) + { + if (UseBigStack()) + { + Thread ts = new Thread(new ParameterizedThreadStart(webExecuteWorker)); + ts.Start(httpContext); + ts.Join(); + if (workerException != null) + throw workerException; + } + else + { + await WebExecuteExAsync(httpContext); + } + } + else + { + context.CloseConnections(); + if (IsGxAjaxRequest() || context.isAjaxRequest()) + context.DispatchAjaxCommands(); + } + SetCompression(httpContext); + context.ResponseCommited = true; + } + catch (Exception e) + { + try + { + context.CloseConnections(); + } + catch { } + { + Exception exceptionToHandle = e.InnerException ?? e; + handleException(exceptionToHandle.GetType().FullName, exceptionToHandle.Message, exceptionToHandle.StackTrace); + throw new Exception("GXApplication exception", e); + } + } + } +#endif +#if !NETCORE + [SecuritySafeCritical] +#endif public void ProcessRequest(HttpContext httpContext) { localHttpContext = httpContext; @@ -1934,6 +2070,9 @@ public void ProcessRequest(HttpContext httpContext) SetStreaming(); SendHeaders(); string clientid = context.ClientID; //Send clientid cookie (before response HasStarted) if necessary, since UseResponseBuffering is not in .netcore3.0 +#if !NETCORE + CSRFHelper.ValidateAntiforgery(httpContext); +#endif bool validSession = ValidWebSession(); if (validSession && IntegratedSecurityEnabled) @@ -1978,9 +2117,18 @@ public void ProcessRequest(HttpContext httpContext) context.CloseConnections(); } catch { } - Exception exceptionToHandle = e.InnerException ?? e; - handleException(exceptionToHandle.GetType().FullName, exceptionToHandle.Message, exceptionToHandle.StackTrace); - throw new Exception("GXApplication exception", e); +#if !NETCORE + if (CSRFHelper.HandleException(e, httpContext)) + { + GXLogging.Error(log, $"Validation of antiforgery failed", e); + } + else +#endif + { + Exception exceptionToHandle = e.InnerException ?? e; + handleException(exceptionToHandle.GetType().FullName, exceptionToHandle.Message, exceptionToHandle.StackTrace); + throw new Exception("GXApplication exception", e); + } } } protected virtual bool ChunkedStreaming() { return false; } @@ -2191,7 +2339,7 @@ private string GetGAMNotAuthorizedWebObject() private void SendHeaders() { sendCacheHeaders(); - GXLogging.DebugSanitized(log, "HttpHeaders: ", DumpHeaders(localHttpContext)); + GXLogging.DebugSanitized(log, "HttpHeaders: ", () => DumpHeaders(localHttpContext)); sendAdditionalHeaders(); HttpHelper.CorsHeaders(localHttpContext); HttpHelper.AllowHeader(localHttpContext, new List() { $"{HttpMethod.Get.Method},{HttpMethod.Post.Method}" }); @@ -2248,6 +2396,9 @@ protected virtual void sendSpaHeaders() private void webExecuteWorker(object target) { HttpContext httpContext = (HttpContext)target; +#if !NETCORE + HttpContext.Current = httpContext; +#endif try { webExecuteEx(httpContext); diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs index f88ba3c46..c13b10323 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs @@ -53,7 +53,7 @@ public class MapGroup public class GXAPIModule : IHttpModule { - + static private readonly object syncObject = new object(); public static List servicesPathUrl; public static Dictionary servicesBase; public static Dictionary servicesClass; @@ -135,69 +135,77 @@ public static Boolean serviceInPath(String path, out String actualPath) public void ServicesGroupSetting(string webPath) { if (!String.IsNullOrEmpty(webPath) && servicesMap == null) - { - servicesPathUrl = new List(); - servicesBase = new Dictionary(); - servicesMap = new Dictionary>(); - //servicesVerbs = new Dictionary>(); - servicesMapData = new Dictionary, string>>(); - servicesClass = new Dictionary(); - - if (Directory.Exists(Path.Combine(webPath, PRIVATE_DIR))) + { + lock (syncObject) { - String[] grpFiles = Directory.GetFiles(Path.Combine(webPath, PRIVATE_DIR), "*.grp.json"); - foreach (String grp in grpFiles) + if (servicesMap == null) { -#pragma warning disable SCS0018 // Path traversal: injection possible in {1} argument passed to '{0}' - object p = JSONHelper.Deserialize(File.ReadAllText(grp)); -#pragma warning restore SCS0018 - MapGroup m = p as MapGroup; - if (m != null && m.Name != null && m.Mappings != null ) - { + servicesPathUrl = new List(); + servicesBase = new Dictionary(); + servicesMap = new Dictionary>(); + //servicesVerbs = new Dictionary>(); + servicesMapData = new Dictionary, string>>(); + servicesClass = new Dictionary(); - if (String.IsNullOrEmpty(m.BasePath)) - { - m.BasePath = REST_BASE_URL; - } - String mapPath = (m.BasePath.EndsWith("/")) ? m.BasePath : m.BasePath + "/"; - String mapPathLower = mapPath.ToLower(); - servicesPathUrl.Add(mapPathLower); - servicesBase.Add(mapPathLower, m.Name.ToLower()); - servicesClass.Add(mapPathLower, m.Name.ToLower() + "_services"); - foreach (SingleMap sm in m.Mappings) + if (Directory.Exists(Path.Combine(webPath, PRIVATE_DIR))) + { + String[] grpFiles = Directory.GetFiles(Path.Combine(webPath, PRIVATE_DIR), "*.grp.json"); + foreach (String grp in grpFiles) { - if (String.IsNullOrEmpty(sm.Verb)) - sm.Verb = "GET"; - if (sm.VariableAlias == null) - sm.VariableAlias = new Dictionary(); - else + string content = File.ReadAllText(grp); + if (!string.IsNullOrEmpty(content)) { - Dictionary vMap = new Dictionary(); - foreach (KeyValuePair v in sm.VariableAlias) +#pragma warning disable SCS0018 // Path traversal: injection possible in {1} argument passed to '{0}' + object p = JSONHelper.Deserialize(content); +#pragma warning restore SCS0018 + MapGroup m = p as MapGroup; + if (m != null && m.Name != null && m.Mappings != null) { - vMap.Add(v.Key.ToLower(), v.Value.ToLower()); - } - sm.VariableAlias = vMap; - } - if (servicesMap.ContainsKey(mapPathLower)) - { - if (!servicesMap[mapPathLower].ContainsKey(sm.Name.ToLower())) - { - servicesMapData[mapPathLower].Add(Tuple.Create(sm.Path.ToLower(), sm.Verb), sm.Name.ToLower()); - servicesMap[mapPathLower].Add(sm.Name.ToLower(), sm); + + if (String.IsNullOrEmpty(m.BasePath)) + { + m.BasePath = REST_BASE_URL; + } + String mapPath = (m.BasePath.EndsWith("/")) ? m.BasePath : m.BasePath + "/"; + String mapPathLower = mapPath.ToLower(); + servicesPathUrl.Add(mapPathLower); + servicesBase[mapPathLower]= m.Name.ToLower(); + servicesClass[mapPathLower]= m.Name.ToLower() + "_services"; + foreach (SingleMap sm in m.Mappings) + { + if (String.IsNullOrEmpty(sm.Verb)) + sm.Verb = "GET"; + if (sm.VariableAlias == null) + sm.VariableAlias = new Dictionary(); + else + { + Dictionary vMap = new Dictionary(); + foreach (KeyValuePair v in sm.VariableAlias) + { + vMap.Add(v.Key.ToLower(), v.Value.ToLower()); + } + sm.VariableAlias = vMap; + } + if (servicesMap.ContainsKey(mapPathLower)) + { + if (!servicesMap[mapPathLower].ContainsKey(sm.Name.ToLower())) + { + servicesMapData[mapPathLower].Add(Tuple.Create(sm.Path.ToLower(), sm.Verb), sm.Name.ToLower()); + servicesMap[mapPathLower].Add(sm.Name.ToLower(), sm); + } + } + else + { + servicesMapData.Add(mapPathLower, new Dictionary, string>()); + servicesMapData[mapPathLower].Add(Tuple.Create(sm.Path.ToLower(), sm.Verb), sm.Name.ToLower()); + servicesMap.Add(mapPathLower, new Dictionary()); + servicesMap[mapPathLower].Add(sm.Name.ToLower(), sm); + } + } } } - else - { - servicesMapData.Add(mapPathLower, new Dictionary, string>()); - servicesMapData[mapPathLower].Add(Tuple.Create(sm.Path.ToLower(), sm.Verb), sm.Name.ToLower()); - servicesMap.Add(mapPathLower, new Dictionary()); - servicesMap[mapPathLower].Add(sm.Name.ToLower(), sm); - } - } } - } } } diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpServices.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpServices.cs index 992631cde..b9b993bc1 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpServices.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpServices.cs @@ -13,7 +13,9 @@ namespace GeneXus.Http using GeneXus.Mime; using GeneXus.Security; using GeneXus.Utils; +#if !NETCORE using Jayrock.Json; +#endif using System.Web.SessionState; using System.Web; #if NETCORE diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs index 05c998433..76d1410bb 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/HandlerFactory.cs @@ -58,7 +58,7 @@ public bool IsReusable } class HandlerFactory : IHttpHandlerFactory { - private static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private static List GxNamespaces; public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) @@ -67,88 +67,89 @@ public IHttpHandler GetHandler(HttpContext context, string requestType, string u string relativeURL = context.Request.AppRelativeCurrentExecutionFilePath; string fname = relativeURL.Substring(relativeURL.LastIndexOf('~') + 2); - String cname1 = (fname.Contains(".")) ? fname.Substring(0, fname.LastIndexOf('.')) : fname; + string cname1 = (fname.Contains(".")) ? fname.Substring(0, fname.LastIndexOf('.')) : fname; string cname0 = cname1.ToLower(); - string actualPath = ""; - + string mainNamespace; + string assemblyName, cname; + string actualPath; if (cname0 == "gxoauthlogout") { - return new GeneXus.Http.GXOAuthLogout(); + return new GXOAuthLogout(); } else if (cname0 == "gxoauthuserinfo") { - return new GeneXus.Http.GXOAuthUserInfo(); + return new GXOAuthUserInfo(); } else if (cname0 == "gxoauthaccesstoken") { - return new GeneXus.Http.GXOAuthAccessToken(); + return new GXOAuthAccessToken(); } else if (cname0 == "gxmulticall") { - return new GeneXus.Http.GXMultiCall(); + return new GXMultiCall(); } - string assemblyName, cname; - if (GXAPIModule.serviceInPath(pathTranslated, actualPath: out actualPath)) + else if (HttpHelper.GamServicesInternalName.Contains(cname0)) + { + mainNamespace = HttpHelper.GAM_NSPACE; + } + else { - string nspace; - Config.GetValueOf("AppMainNamespace", out nspace); - String objClass = GXAPIModule.servicesBase[actualPath]; - // - String objectName = GetObjFromPath(cname0, actualPath); - String objectNameUp = GetObjFromPath(cname1, actualPath); - // - Dictionary routeParms; - if (GXAPIModule.servicesMapData.ContainsKey(actualPath)) + if (!Config.GetValueOf("AppMainNamespace", out mainNamespace)) + mainNamespace = "GeneXus.Programs."; + + if (GXAPIModule.serviceInPath(pathTranslated, actualPath: out actualPath)) { - bool IsServiceCall = GetSMap(actualPath, objectName, objectNameUp, requestType, out string mapName, out string mapRegExp, out routeParms); - if (IsServiceCall) + string nspace; + Config.GetValueOf("AppMainNamespace", out nspace); + string objClass = GXAPIModule.servicesBase[actualPath]; + // + string objectName = GetObjFromPath(cname0, actualPath); + string objectNameUp = GetObjFromPath(cname1, actualPath); + // + Dictionary routeParms; + if (GXAPIModule.servicesMapData.ContainsKey(actualPath)) { - if (!String.IsNullOrEmpty(mapName) && GXAPIModule.servicesMap[actualPath].TryGetValue(mapName, out SingleMap value)) + bool IsServiceCall = GetSMap(actualPath, objectName, objectNameUp, requestType, out string mapName, out string mapRegExp, out routeParms); + if (IsServiceCall) { - String tmpController = objClass; - String asssemblycontroller = tmpController; - if (objClass.Contains("\\")) + if (!string.IsNullOrEmpty(mapName) && GXAPIModule.servicesMap[actualPath].TryGetValue(mapName, out SingleMap value)) { - tmpController = objClass.Substring(objClass.LastIndexOf("\\") + 1); - String addNspace = objClass.Substring(0, objClass.LastIndexOf("\\")).Replace("\\", "."); - asssemblycontroller = addNspace + "." + tmpController; - nspace += "." + addNspace; + string tmpController = objClass; + string asssemblycontroller = tmpController; + if (objClass.Contains("\\")) + { + tmpController = objClass.Substring(objClass.LastIndexOf("\\") + 1); + string addNspace = objClass.Substring(0, objClass.LastIndexOf("\\")).Replace("\\", "."); + asssemblycontroller = addNspace + "." + tmpController; + nspace += "." + addNspace; + } + GxContext gxContext = GxContext.CreateDefaultInstance(); + object handler = ClassLoader.FindInstance(asssemblycontroller, nspace, tmpController, new Object[] { gxContext }, null); + + gxContext.HttpContext = context; + GxRestWrapper restWrapper = new Application.GxRestWrapper(handler as GXBaseObject, context, gxContext, value.ServiceMethod, value.VariableAlias, routeParms); + return restWrapper; } - GxContext gxContext = GxContext.CreateDefaultInstance(); - object handler = ClassLoader.FindInstance(asssemblycontroller, nspace, tmpController, new Object[] { gxContext }, null); - - gxContext.HttpContext = context; - GxRestWrapper restWrapper = new Application.GxRestWrapper(handler as GXBaseObject, context, gxContext, value.ServiceMethod, value.VariableAlias, routeParms); - return restWrapper; } - } - else - { - if (requestType.Equals(HttpMethod.Options.Method) && !String.IsNullOrEmpty(actualPath) && GXAPIModule.servicesMapData.ContainsKey(actualPath)) + else { - return new OptionsApiObjectRequestHandler(actualPath, objectName, mapRegExp); + if (requestType.Equals(HttpMethod.Options.Method) && !string.IsNullOrEmpty(actualPath) && GXAPIModule.servicesMapData.ContainsKey(actualPath)) + { + return new OptionsApiObjectRequestHandler(actualPath, objectName, mapRegExp); + } } - } + } + return null; } - return null; } - else + assemblyName = cname0; + cname = cname0; + if (cname.EndsWith("_bc_ws")) { - { - assemblyName = cname0; - cname = cname0; - } - if (cname.EndsWith("_bc_ws")) - { - cname = cname.Substring(0, cname.Length - 3); - assemblyName = cname; - } + cname = cname.Substring(0, cname.Length - 3); + assemblyName = cname; } - string mainNamespace, className; - if (Config.GetValueOf("AppMainNamespace", out mainNamespace)) - className = mainNamespace + "." + cname; - else - className = "GeneXus.Programs." + cname; + string className = mainNamespace + "." + cname; Type objType = GetHandlerType(assemblyName, className); if (objType == null) @@ -190,7 +191,7 @@ public IHttpHandler GetHandler(HttpContext context, string requestType, string u } else { - handlerToReturn = (IHttpHandler)System.Web.UI.PageParser.GetCompiledPageInstance(url, pathTranslated, context); + handlerToReturn = System.Web.UI.PageParser.GetCompiledPageInstance(url, pathTranslated, context); } return handlerToReturn; } @@ -199,7 +200,7 @@ public string GetObjFromPath(string cname, string apath) { if (cname.LastIndexOf("/") == (cname.Length - 1)) cname = cname.Substring(0, cname.Length - 1); - String objectName = cname.Remove(0, apath.Length); + string objectName = cname.Remove(0, apath.Length); return objectName; } @@ -302,7 +303,7 @@ internal static Type GetHandlerType(string assemblyName, string className) try { - objType = GeneXus.Metadata.ClassLoader.FindType(assemblyName, className, null); + objType = ClassLoader.FindType(assemblyName, className, null); if (objType == null) objType = Assembly.Load(assemblyName).GetType(className); } diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs index 6bd6a2911..a3d87da7d 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs @@ -1,3 +1,4 @@ +using GeneXus.Configuration; using GeneXus.Data.NTier; using GeneXus.Encryption; using GeneXus.Http; @@ -6,7 +7,9 @@ using GeneXus.Services.OpenTelemetry; #endif using GeneXus.Utils; +#if !NETCORE using Jayrock.Json; +#endif #if NETCORE using Microsoft.AspNetCore.Http.Extensions; #endif @@ -15,6 +18,7 @@ using System.Diagnostics; using System.Reflection; using System.Threading; +using System.Threading.Tasks; namespace GeneXus.Application { @@ -34,6 +38,7 @@ public class GXBaseObject protected IGxContext _Context; bool _isMain; protected bool _isApi; + protected bool _isDP; #if NETCORE internal static ActivitySource ActivitySource { get { @@ -59,26 +64,80 @@ protected virtual void ExecutePrivate() { } +#if NETCORE + protected virtual bool AsyncEnabled { get; } + + internal bool GetAsyncEnabledInternal() + { + return AsyncEnabled; + } + protected async Task CloseConnectionsAsync() + { + GxContext gxContext = context as GxContext; + if (gxContext != null) + { + await gxContext.CloseConnectionsAsync(); + } + } + + protected virtual Task ExecutePrivateAsync() + { + return Task.CompletedTask; + } +#endif + internal static string GetObjectNameWithoutNamespace(string gxObjFullName) + { + string mainNamespace = Preferences.AppMainNamespace; + if (gxObjFullName.StartsWith(mainNamespace)) + gxObjFullName = gxObjFullName.Remove(0, mainNamespace.Length + 1); + return gxObjFullName; + } #if NETCORE private void ExecuteUsingSpanCode() { - using (Activity activity = ActivitySource.StartActivity($"{this.GetType().FullName}.execute")) + string gxObjFullName = GetObjectNameWithoutNamespace(GetType().FullName); + using (Activity activity = ActivitySource.StartActivity($"{gxObjFullName}.execute")) { ExecutePrivate(); } } + private async Task ExecuteUsingSpanCodeAsync() + { + string gxObjFullName = GetObjectNameWithoutNamespace(GetType().FullName); + using (Activity activity = ActivitySource.StartActivity($"{gxObjFullName}.execute")) + { + await ExecutePrivateAsync(); + } + } #endif protected virtual void ExecuteImpl() { #if NETCORE - if (GenOtelSpanEnabled()) - ExecuteUsingSpanCode(); + if (GetAsyncEnabledInternal()) + { + ExecuteImplAsync().GetAwaiter().GetResult(); + } else - ExecutePrivate(); + { + if (GenOtelSpanEnabled()) + ExecuteUsingSpanCode(); + else + ExecutePrivate(); + } #else ExecutePrivate(); #endif } + +#if NETCORE + protected virtual async Task ExecuteImplAsync() + { + if (GenOtelSpanEnabled()) + await ExecuteUsingSpanCodeAsync(); + else + await ExecutePrivateAsync(); + } +#endif protected virtual void ExecutePrivateCatch(object stateInfo) { try @@ -165,6 +224,11 @@ public bool IsApiObject set { _isApi = value; } get { return _isApi; } } +#if NETCORE + protected virtual Task CleanupAsync() { + return Task.CompletedTask;// throw new NotImplementedException(); + } +#endif public virtual void cleanup() { } virtual public bool UploadEnabled() { return false; } diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs index ceaf3ce9d..c64c26d10 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs @@ -6,7 +6,9 @@ namespace GeneXus.Utils using GeneXus.Application; using System.Xml.Serialization; using System.Collections.Generic; +#if !NETCORE using Jayrock.Json; +#endif using System.Text; using System.Security.Cryptography; using System.Reflection; @@ -191,7 +193,8 @@ private Activity InitializeSpan(string methodName) { if (GenOtelSpanEnabled()) { - return GXBaseObject.ActivitySource.StartActivity($"{this.GetType().FullName}.{methodName}"); + string gxObjFullName = GXBaseObject.GetObjectNameWithoutNamespace(GetType().FullName); + return GXBaseObject.ActivitySource.StartActivity($"{gxObjFullName}.{methodName}"); } return null; } @@ -580,6 +583,7 @@ public GXBaseCollection GetMessages() public interface IGxGenericCollectionWrapped { bool GetIsWrapped(); + string GetWrappedStatus(); void SetIsWrapped(bool value); } @@ -605,6 +609,12 @@ public GxGenericCollection(IGxCollection x, bool wrapped):this(x) isWrapped = wrapped; } + public GxGenericCollection(IGxCollection x, bool wrapped, string wrappedstatus) : this(x) + { + isWrapped = wrapped; + wrappedStatus = wrappedstatus; + } + public void LoadCollection(IGxCollection x) { foreach (IGxGenericCollectionItem x1 in this) @@ -625,6 +635,8 @@ public override string ToString() public bool GetIsWrapped() { + if (wrappedStatus.Equals("unwrapped")) + isWrapped = false; return isWrapped; } @@ -632,6 +644,18 @@ public void SetIsWrapped(bool value) { isWrapped = value; } + + private string wrappedStatus = ""; + + public string GetWrappedStatus() + { + return wrappedStatus; + } + + internal void SetWrappedStatus(string value) + { + wrappedStatus = value; + } } public interface IGxGenericCollectionItem { @@ -646,6 +670,17 @@ public GxUnWrappedJson() } } + [AttributeUsage(AttributeTargets.Class)] + public sealed class GxJsonSerialization : Attribute + { + string unwrapped = default; + public GxJsonSerialization(string jsonunwrapped) + { + unwrapped = jsonunwrapped; + } + public string JsonUnwrapped { get => unwrapped; set => unwrapped = value; } + } + [AttributeUsage(AttributeTargets.Class)] public sealed class GxOmitEmptyCollection : Attribute { @@ -833,6 +868,7 @@ public GxUpload() [XmlRoot(ElementName = "Message")] [XmlType(TypeName = "Message", Namespace = "GeneXus")] [Serializable] + [GxJsonSerialization("wrapped")] public class SdtMessages_Message : GxUserType { public SdtMessages_Message() @@ -935,6 +971,7 @@ public void initialize() [DataContract(Name = @"Messages.Message", Namespace = "GeneXus")] [GxOmitEmptyCollection] + [GxJsonSerialization("wrapped")] public class SdtMessages_Message_RESTInterface : GxGenericCollectionItem, System.Web.SessionState.IRequiresSessionState { public SdtMessages_Message_RESTInterface() diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs index fc01e6f12..67ff7b635 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs @@ -6,14 +6,17 @@ namespace GeneXus.Procedure using System.Globalization; using GeneXus.Http; using GeneXus.Mime; + using System.Net.Mime; #if NETCORE using Microsoft.AspNetCore.Http; + using System.Threading.Tasks; #else using System.Web; #endif public class GXWebProcedure : GXHttpHandler { + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); protected int handle; protected GXReportMetadata reportMetadata; @@ -35,7 +38,14 @@ public class GXWebProcedure : GXHttpHandler protected virtual void printHeaders() { } protected virtual void printFooters() { } +#if NETCORE + public override void webExecute() + { + WebExecuteAsync().GetAwaiter().GetResult(); + } +#else public override void webExecute() { } +#endif public override void initialize() { } protected override void createObjects() { } public override void skipLines(long nToSkip) { } @@ -55,7 +65,7 @@ protected override void SetCompression(HttpContext httpContext) } } public void setContextReportHandler() - { + { oldReportHandler = null; reportHandler = context.reportHandler; @@ -137,8 +147,19 @@ private void setOuputFileName() { fileType = outputType.ToLower(); } - - context.HttpContext.Response.AddHeader(HttpHeader.CONTENT_DISPOSITION, $"inline; filename={fileName}.{fileType}"); + try + { + ContentDisposition contentDisposition = new ContentDisposition + { + Inline = true, + FileName = $"{fileName}.{fileType}" + }; + context.HttpContext.Response.AddHeader(HttpHeader.CONTENT_DISPOSITION, contentDisposition.ToString()); + } + catch (Exception ex) + { + GXLogging.Warn(log, $"{HttpHeader.CONTENT_DISPOSITION} couldn't be set for {fileName}.{fileType}", ex); + } } } @@ -152,9 +173,9 @@ protected bool initPrinter(String output, int gxXPage, int gxYPage, String iniFi if (!Config.GetValueOf("LANGUAGE", out idiom)) idiom = "eng"; fileContentInline = true; -#if NETCORE + setOuputFileName(); -#endif + getPrinter().GxRVSetLanguage(idiom); int xPage = gxXPage; int yPage = gxYPage; diff --git a/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs b/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs index 7098f3803..088c2f917 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs @@ -433,10 +433,11 @@ public class GxReportUtils public static int OUTPUT_RVIEWER_DLL = 2; public static int OUTPUT_PDF = 3; - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); #if NETCORE const string PDF_LIBRARY_ITEXT8 = "ITEXT8"; + const string PDF_LIBRARY_PDFPIG = "PDFPIG"; #endif static public IReportHandler GetPrinter( int outputType, string path, Stream reportOutputStream) { @@ -470,7 +471,13 @@ static public IReportHandler GetPrinter( int outputType, string path, Stream rep Type classType = assem.GetType( "GeneXus.Printer.GxReportBuilderPdf", false, true); reportHandler = (IReportHandler) Activator.CreateInstance(classType,new Object[]{path, reportOutputStream}); #else - string reportBuidler = Preferences.PdfReportLibrary().Equals(PDF_LIBRARY_ITEXT8, StringComparison.OrdinalIgnoreCase) ? "GxReportBuilderPdf8" : "GxReportBuilderPdf"; + string reportBuidler; + if (Preferences.PdfReportLibrary().Equals(PDF_LIBRARY_ITEXT8, StringComparison.OrdinalIgnoreCase)) + reportBuidler = "GxReportBuilderPdf8"; + else if (Preferences.PdfReportLibrary().Equals(PDF_LIBRARY_PDFPIG, StringComparison.OrdinalIgnoreCase)) + reportBuidler = "GxReportBuilderPDFPig"; + else + reportBuidler = "GxReportBuilderPdf"; reportHandler = (IReportHandler)(ClassLoader.FindInstance("GxPdfReportsCS", "GeneXus.Printer", reportBuidler, new Object[] { path, reportOutputStream }, null)); #endif } diff --git a/dotnet/src/dotnetframework/GxClasses/Printer/GxPrinter.cs b/dotnet/src/dotnetframework/GxClasses/Printer/GxPrinter.cs index e47f48096..8c3044799 100644 --- a/dotnet/src/dotnetframework/GxClasses/Printer/GxPrinter.cs +++ b/dotnet/src/dotnetframework/GxClasses/Printer/GxPrinter.cs @@ -16,6 +16,7 @@ namespace GeneXus.Printer using System.Threading; using System.Threading.Tasks; using GeneXus.Configuration; + using GeneXus.Utils; using GeneXus.XML; public interface IPrintHandler @@ -2162,10 +2163,19 @@ public class ReportUtils { static public string AddPath(string name, string path) { - if (Path.IsPathRooted(name) || name.IndexOf(":") != -1 || + if (name.IndexOf(":") != -1 || (name.Length >=2 && (name.Substring( 0,2) == "//" || name.Substring( 0,2) == @"\\")) || (name.StartsWith("http:" ) || name.StartsWith("https:" ))) return name; +#if NETCORE + if (Path.IsPathRooted (name)) + return name; +#else + if (PathUtil.IsValidFilePath(name) && Path.IsPathRooted(name)) + { + return name; + } +#endif return Path.Combine(path, name); } } diff --git a/dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs b/dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs index bb8c6a579..27eb82564 100644 --- a/dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs +++ b/dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs @@ -16,9 +16,9 @@ namespace GeneXus.Web.Security [SecuritySafeCritical] public static class WebSecurityHelper { - static readonly IGXLogger _log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger _log = GXLoggerFactory.GetLogger(typeof(WebSecurityHelper).FullName); - const int SecretKeyMinimumLength = 16; + const int SecretKeyMinimumLength = 32; public static string StripInvalidChars(string input) { @@ -112,7 +112,7 @@ internal static bool VerifySecureSignedSDTToken(string cmpCtx, GxUserType value, public static class SecureTokenHelper { - static readonly IGXLogger _log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger _log = GXLoggerFactory.GetLogger(typeof(SecureTokenHelper).FullName); public enum SecurityMode { @@ -129,6 +129,10 @@ internal static WebSecureToken getWebSecureToken(string signedToken, string secr using (var hmac = new System.Security.Cryptography.HMACSHA256(bSecretKey)) { var handler = new JwtSecurityTokenHandler(); + if (signedToken.Length >= handler.MaximumTokenSizeInBytes) + { + handler.MaximumTokenSizeInBytes = signedToken.Length + 1; + } var validationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.FromMinutes(1), diff --git a/dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs b/dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs index 1bda980fe..63f293597 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/Caching/GxCache.cs @@ -587,7 +587,13 @@ public class CacheItem public CacheItem() { } - + internal CacheItem(List data, bool hasnext, int blockSize, long sizeInBytes) + { + Data = new GxArrayList(data); + HasNext = hasnext; + BlockSize = blockSize; + SizeInBytes = sizeInBytes; + } public CacheItem(GxArrayList data, bool hasnext, int blockSize, long sizeInBytes) { Data = data; diff --git a/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs b/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs index 0d974268a..7f63cc340 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/GXRestServices.cs @@ -389,10 +389,14 @@ public void WebException(Exception ex) { throw ex; } - else if (ex is FormatException || (RestAPIHelpers.ValidateCsrfToken() && AntiForgeryException(ex))) + else if (ex is FormatException) { HttpHelper.SetUnexpectedError(httpContext, HttpStatusCode.BadRequest, ex); } + else if (RestAPIHelpers.ValidateCsrfToken() && AntiForgeryException(ex)) + { + HttpHelper.SetUnexpectedError(httpContext, HttpStatusCode.BadRequest, HttpHelper.InvalidCSRFToken, ex); + } else { HttpHelper.SetUnexpectedError(httpContext, HttpStatusCode.InternalServerError, ex); diff --git a/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs b/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs index 03fd8f122..4f8f98eed 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs @@ -24,10 +24,14 @@ using System.Collections.Specialized; using GeneXus.Security; using System.Collections; +#if !NETCORE using Jayrock.Json; +#endif using System.Net.Http; using System.Diagnostics; using GeneXus.Diagnostics; +using System.Xml.Linq; + namespace GeneXus.Application @@ -118,7 +122,7 @@ public virtual Task MethodBodyExecute(object key) gxobject.webExecute(); return Task.CompletedTask; } - if (!ProcessHeaders(_procWorker.GetType().Name)) + if (!ProcessHeaders(GXBaseObject.GetObjectNameWithoutNamespace(_procWorker.GetType().FullName))) return Task.CompletedTask; _procWorker.IsMain = true; if (bodyParameters == null) @@ -303,7 +307,7 @@ public virtual Task MethodUrlExecute(object key) { return Task.CompletedTask; } - if (!ProcessHeaders(_procWorker.GetType().Name)) + if (!ProcessHeaders(GXBaseObject.GetObjectNameWithoutNamespace(_procWorker.GetType().FullName))) return Task.CompletedTask; _procWorker.IsMain = true; IDictionary queryParameters = ReadQueryParameters(this._variableAlias); @@ -344,31 +348,17 @@ public virtual Task MethodUrlExecute(object key) Cleanup(); } } - bool GetWrappedStatus(GXBaseObject worker, bool defaultWrapped, Dictionary outputParameters, int parCount, int originalParCount) + private bool GetWrappedStatus(GXBaseObject worker, bool defaultWrapped, Dictionary outputParameters, int parCount, int originalParCount) { bool wrapped = defaultWrapped; + if (worker.IsApiObject) { if (outputParameters.Count == 1) { if ((originalParCount == 1) || (originalParCount > 1 && !Preferences.WrapSingleApiOutput)) { - wrapped = false; - Object v = outputParameters.First().Value; - - if (v.GetType().GetInterfaces().Contains(typeof(IGxGenericCollectionWrapped))) - { - IGxGenericCollectionWrapped icollwrapped = v as IGxGenericCollectionWrapped; - if (icollwrapped != null) - wrapped = icollwrapped.GetIsWrapped(); - } - if (v is IGxGenericCollectionItem item) - { - if (item.Sdt is GxSilentTrnSdt) - { - wrapped = (parCount > 1) ? true : false; - } - } + wrapped = GetCollectionWrappedStatus(outputParameters, parCount, false, true); } if (originalParCount > 1 && Preferences.WrapSingleApiOutput) { @@ -376,6 +366,44 @@ bool GetWrappedStatus(GXBaseObject worker, bool defaultWrapped, Dictionary outputParameters , int parCount, bool defaultWrapped, bool isAPI) + { + bool wrapped = defaultWrapped; + if (outputParameters.Count > 0) + { + Object v = outputParameters.First().Value; + if (v.GetType().GetInterfaces().Contains(typeof(IGxGenericCollectionWrapped))) + { + IGxGenericCollectionWrapped icollwrapped = v as IGxGenericCollectionWrapped; + if (icollwrapped != null) + { + if (icollwrapped.GetWrappedStatus().Equals("default")) + wrapped = defaultWrapped; + else + wrapped = icollwrapped.GetIsWrapped(); + } + } + + if (isAPI) + { + if (v is IGxGenericCollectionItem item) + { + if (item.Sdt is GxSilentTrnSdt) + { + wrapped = (parCount > 1) ? true : false; + } + } + } + } return wrapped; } @@ -804,8 +832,10 @@ protected Task Serialize(object value) #else var responseStream = _httpContext.Response.OutputStream; #endif - var knownTypes = new List(); - knownTypes.Add(value.GetType()); + var knownTypes = new List + { + value.GetType() + }; JSONHelper.WCFSerialize(value, Encoding.UTF8, knownTypes, responseStream); return Task.CompletedTask; @@ -843,40 +873,61 @@ private static void RestProcess(GXBaseObject worker, Dictionary } } - protected static object MakeRestType( object v, bool isApiObject) + protected static object MakeRestType( object collectionValue, bool isApiObject) { - Type vType = v.GetType(); + Type vType = collectionValue.GetType(); Type itemType; if (vType.IsConstructedGenericType && typeof(IGxCollection).IsAssignableFrom(vType)) - { + { + bool isWrapped = (isApiObject)?false:true; + bool isEmpty = false; + object collectionObject = null; + string wrappedStatus = ""; Type restItemType=null; - itemType = v.GetType().GetGenericArguments()[0]; - if ((typeof(IGXBCCollection).IsAssignableFrom(vType)) && !isApiObject)//Collection convert to GxGenericCollection + itemType = collectionValue.GetType().GetGenericArguments()[0]; + if (vType.GetGenericTypeDefinition() == typeof(GxSimpleCollection<>) && isApiObject) { - restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTLInterface", null); + restItemType = itemType; + isEmpty = true; + isWrapped = false; + collectionObject = collectionValue; } - if (restItemType == null)//Collection convert to GxGenericCollection + else { - restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTInterface", null); + if ((typeof(IGXBCCollection).IsAssignableFrom(vType)) && !isApiObject)//Collection convert to GxGenericCollection + { + restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTLInterface", null); + } + if (restItemType == null)//Collection convert to GxGenericCollection + { + restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTInterface", null); + } + object[] attributes = restItemType.GetCustomAttributes(typeof(GxJsonSerialization), false); + IEnumerable serializationAttributes = attributes.Where(a => a.GetType() == typeof(GxJsonSerialization)); + if (serializationAttributes != null && serializationAttributes.Any()) + { + GxJsonSerialization attFmt = (GxJsonSerialization)serializationAttributes.FirstOrDefault(); + wrappedStatus = attFmt.JsonUnwrapped; + isWrapped = (isApiObject)? ((wrappedStatus == "wrapped")? true: false): ((wrappedStatus == "unwrapped") ? false : true); + } + isEmpty = !restItemType.IsDefined(typeof(GxOmitEmptyCollection), false); + Type genericListItemType = typeof(GxGenericCollection<>).MakeGenericType(restItemType); + collectionObject = Activator.CreateInstance(genericListItemType, new object[] { collectionValue, isWrapped , wrappedStatus}); } - bool isWrapped = !restItemType.IsDefined(typeof(GxUnWrappedJson), false); - bool isEmpty = !restItemType.IsDefined(typeof(GxOmitEmptyCollection), false); - Type genericListItemType = typeof(GxGenericCollection<>).MakeGenericType(restItemType); - object c = Activator.CreateInstance(genericListItemType, new object[] { v, isWrapped}); // Empty collection serialized w/ noproperty - if (c is IList restList) + if (collectionObject is IList restList) { if (restList.Count == 0 && !isEmpty) return null; } - return c; + return collectionObject; } else if (typeof(GxUserType).IsAssignableFrom(vType)) //SDTType convert to SDTType_RESTInterface { Type restItemType = ClassLoader.FindType(Config.CommonAssemblyName, vType.FullName + "_RESTInterface", null); - return Activator.CreateInstance(restItemType, new object[] { v }); + return Activator.CreateInstance(restItemType, new object[] { collectionValue }); } - return v; + return collectionValue; } #if !NETCORE diff --git a/dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs b/dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs index a7a9a1b34..8d4c98aef 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs @@ -7,7 +7,9 @@ using System.Text.RegularExpressions; using GeneXus.Utils; using System.Linq; +#if !NETCORE using Jayrock.Json; +#endif using Type = System.Type; @@ -150,7 +152,7 @@ private static object ConvertSingleJsonItem(object value, Type newType, IGxConte else if (newType == typeof(DateTime)) { string jsonDate = value as string; - return DateTimeUtil.CToDT2(jsonDate); + return DateTimeUtil.CToDT2(jsonDate, context); } else if (newType == typeof(Geospatial)) { @@ -179,9 +181,9 @@ private static object ConvertStringToNewNonNullableType(object value, Type newTy Type singleItemType = newType.GetElementType(); var elements = new ArrayList(); - foreach (var element in value.ToString().Split(',')) + foreach (string element in value.ToString().Split(',')) { - var convertedSingleItem = ConvertSingleJsonItem(element, singleItemType, context); + object convertedSingleItem = ConvertSingleJsonItem(element, singleItemType, context); elements.Add(convertedSingleItem); } return elements.ToArray(singleItemType); @@ -211,11 +213,11 @@ public static Dictionary ParametersFormat(object instance, strin var methodParameters = methodInfo.GetParameters(); foreach (var methodParameter in methodParameters) { - var gxParameterName = GxParameterName(methodParameter.Name); + string gxParameterName = GxParameterName(methodParameter.Name); if (IsByRefParameter(methodParameter)) { string fmt = ""; - var attributes = methodParameter.GetCustomAttributes(true); + object[] attributes = methodParameter.GetCustomAttributes(true); GxJsonFormatAttribute attFmt = (GxJsonFormatAttribute)attributes.Where(a => a.GetType() == typeof(GxJsonFormatAttribute)).FirstOrDefault(); if (attFmt != null) fmt = attFmt.JsonFormat; @@ -233,7 +235,7 @@ private static Dictionary ProcessParametersAfterInvoke(MethodInf int idx = 0; foreach (var methodParameter in methodParameters) { - var gxParameterName = GxParameterName(methodParameter.Name); + string gxParameterName = GxParameterName(methodParameter); if (IsByRefParameter(methodParameter)) { outputParameters.Add(gxParameterName, parametersForInvocation[idx]); @@ -250,18 +252,25 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IDict { var methodParameters = methodInfo.GetParameters(); object[] parametersForInvocation = new object[methodParameters.Length]; - var idx = 0; + int idx = 0; foreach (var methodParameter in methodParameters) { object value; - var gxParameterName = GxParameterName(methodParameter.Name).ToLower(); + string gxParameterName = GxParameterName(methodParameter.Name).ToLower(); Type parmType = methodParameter.ParameterType; + string jsontypename = ""; + object[] attributes = parmType.GetCustomAttributes(true); + GxJsonName jsonName = (GxJsonName)attributes.Where(a => a.GetType() == typeof(GxJsonName)).FirstOrDefault(); + if (jsonName != null) + jsontypename = jsonName.Name.ToLower(); + else + jsontypename = gxParameterName; if (IsByRefParameter(methodParameter)) { parmType = parmType.GetElementType(); } - if (parameters != null && parameters.TryGetValue(gxParameterName, out value)) + if (parameters != null && parameters.TryGetValue(jsontypename, out value)) { if (value == null || JSONHelper.IsJsonNull(value)) { @@ -269,13 +278,13 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IDict } else { - var convertedValue = ConvertStringToNewType(value, parmType, context); + object convertedValue = ConvertStringToNewType(value, parmType, context); parametersForInvocation[idx] = convertedValue; } } else { - var defaultValue = CreateInstance(parmType); + object defaultValue = CreateInstance(parmType); parametersForInvocation[idx] = defaultValue; } idx++; @@ -287,7 +296,7 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList { var methodParameters = methodInfo.GetParameters(); object[] parametersForInvocation = new object[methodParameters.Length]; - var idx = 0; + int idx = 0; foreach (var methodParameter in methodParameters) { Type parmType = methodParameter.ParameterType; @@ -301,7 +310,7 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList //To avoid convertion from string type if (value.GetType() != typeof(string)) { - var convertedValue = ConvertStringToNewType(value, parmType, context); + object convertedValue = ConvertStringToNewType(value, parmType, context); parametersForInvocation[idx] = convertedValue; } else @@ -318,12 +327,34 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList return parametersForInvocation; } private static Regex attVar = new Regex(@"^AV?\d{1,}", RegexOptions.Compiled); + + private static string GxParameterName(ParameterInfo methodParameter) + { + int idx = methodParameter.Name.IndexOf('_'); + if (idx >= 0) + { + string mparm = methodParameter.Name.Substring(idx + 1); + string PName = methodParameter.ParameterType.FullName; + // The root element name should be in the metadata of the SDT + if (mparm.StartsWith("Gx") && mparm.EndsWith("rootcol") && PName.Contains("_")) + { + int Pos = PName.IndexOf("Sdt") + 3; + mparm = PName.Substring(Pos, PName.IndexOf("_") - Pos); + } + return mparm; + } + else + { + return attVar.Replace(methodParameter.Name, string.Empty); + } + } + private static string GxParameterName(string methodParameterName) { int idx = methodParameterName.IndexOf('_'); if (idx >= 0) { - return methodParameterName.Substring(idx + 1); + return methodParameterName.Substring(idx + 1); } else { @@ -335,7 +366,7 @@ private static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList< { var methodParameters = methodInfo.GetParameters(); object[] parametersForInvocation = new object[methodParameters.Length]; - var idx = 0; + int idx = 0; string pattern = @"(AV\d+)(.*)"; string replacement = "$2"; Regex rgx = new Regex(pattern); @@ -344,7 +375,7 @@ private static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList< { rgx.Replace(methodParameter.Name, replacement); Type parmType = methodParameter.ParameterType; - var convertedValue = ConvertStringToNewType(parametersValues[idx], parmType); + object convertedValue = ConvertStringToNewType(parametersValues[idx], parmType); parametersForInvocation[idx] = convertedValue; idx++; } diff --git a/dotnet/src/dotnetframework/GxClasses/Services/Search/GXSearch.cs b/dotnet/src/dotnetframework/GxClasses/Services/Search/GXSearch.cs index ed83471b8..70c2a49a9 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/Search/GXSearch.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/Search/GXSearch.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Text; using GeneXus.Utils; +#if !NETCORE using Jayrock.Json; +#endif using GeneXus.Application; using System.Reflection; using GeneXus.Metadata; @@ -169,12 +171,16 @@ public class SearchResultItem : ISearchResultItem, IGxJSONAble { #region Internal Data - protected JObject _Properties; + JObject _Properties; protected float m_score; #endregion + public SearchResultItem() + { + _Properties = new JObject(); + } -#region ISearchResultItem Members + #region ISearchResultItem Members public virtual string Id { get { return ""; } } public virtual string Viewer { get { return ""; } } diff --git a/dotnet/src/dotnetframework/GxClasses/View/GXWebControls.cs b/dotnet/src/dotnetframework/GxClasses/View/GXWebControls.cs index 8985e6859..2ca0bd23d 100644 --- a/dotnet/src/dotnetframework/GxClasses/View/GXWebControls.cs +++ b/dotnet/src/dotnetframework/GxClasses/View/GXWebControls.cs @@ -13,7 +13,9 @@ using System.Collections.Specialized; using GeneXus.Utils; using System.Text.RegularExpressions; +#if !NETCORE using Jayrock.Json; +#endif using GeneXus.Configuration; using GeneXus.Application; @@ -323,7 +325,7 @@ public int GetWrapped() return 0; } - public List GetColsPropsCommon() + internal List GetColsPropsCommon() { return this._ColsPropsCommon; } @@ -733,7 +735,7 @@ public abstract class GXWebControl : IGxJSONAble, IGxJSONSerializable Unit _Height = Unit.Empty; ListDictionary _attributes = new ListDictionary(); ListDictionary _styleAttributes = new ListDictionary(); - protected JObject jsonObj = new JObject(); + JObject jsonObj = new JObject(); string _WebTags; static Regex rAttributes = new Regex("\\s*(?\\S*)\\s*=\\s*\"(?[^\"]*)\"", RegexOptions.IgnoreCase | RegexOptions.Compiled); @@ -752,7 +754,16 @@ public GXWebControl() _ID = ""; _title = new GxWebControlTitle(); } - public string CssClass + protected void PutJsonValue(string key, object value) + { + jsonObj.Put(key, value); + } + protected void ClearJsonValues() + { + jsonObj = new JObject(); + } + + public string CssClass { get { @@ -1414,7 +1425,7 @@ public void Render(HtmlTextWriter ControlOutputWriter) public override void ToJSON() { - jsonObj.Put(Value, Value); + PutJsonValue(Value, Value); } } @@ -1525,10 +1536,11 @@ public void removeAllItems() { _IsSet = true; Items.Clear(); - jsonObj = new JObject(); + ClearJsonValues(); _SelectedIndex = -1; } - public void removeItem(string itemValue) + + public void removeItem(string itemValue) { _IsSet = true; foreach (ListItem item in Items) @@ -1625,8 +1637,8 @@ protected virtual void ApplyProperties(ListControl control) public override void ToJSON() { - jsonObj.Put("isset", _IsSet); - jsonObj.Put("s", SelectedItemValue.Trim()); + PutJsonValue("isset", _IsSet); + PutJsonValue("s", SelectedItemValue.Trim()); JArray jsonArrValues = new JArray(); Dictionary itemsHash = new Dictionary(); foreach (ListItem Item in Items) @@ -1645,7 +1657,7 @@ public override void ToJSON() itemsHash[itemValue][1] = Item.Text; } } - jsonObj.Put("v", jsonArrValues); + PutJsonValue("v", jsonArrValues); } public override void FromJSONObject(dynamic Obj) @@ -1729,7 +1741,7 @@ public void Render(HtmlTextWriter ControlOutputWriter) public override void ToJSON() { - jsonObj.Put(CheckedValue, Checked); + PutJsonValue(CheckedValue, Checked); } } diff --git a/dotnet/src/dotnetframework/GxExcel/GxExcelEPPlus.cs b/dotnet/src/dotnetframework/GxExcel/GxExcelEPPlus.cs index c833dea5a..30363b39e 100644 --- a/dotnet/src/dotnetframework/GxExcel/GxExcelEPPlus.cs +++ b/dotnet/src/dotnetframework/GxExcel/GxExcelEPPlus.cs @@ -9,7 +9,7 @@ namespace GeneXus.Office.ExcelGXEPPlus public class ExcelDocument : IGxError, IExcelDocument { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private ExcelPackage p; public string dateFormat = "m/d/yy h:mm"; @@ -356,7 +356,7 @@ public short Init(string previousMsgError) public class ExcelCells : IExcelCells { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private IGxError m_errAccess; private int pWidth, pHeight; diff --git a/dotnet/src/dotnetframework/GxExcel/GxExcelI.cs b/dotnet/src/dotnetframework/GxExcel/GxExcelI.cs index 39be3b6f0..0309e828b 100644 --- a/dotnet/src/dotnetframework/GxExcel/GxExcelI.cs +++ b/dotnet/src/dotnetframework/GxExcel/GxExcelI.cs @@ -11,7 +11,7 @@ namespace GeneXus.Office public class ExcelDocumentI { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public short Index = -1; @@ -589,7 +589,7 @@ public IExcelCellWrapper(Interop.GXOFFICE2Lib.ExcelCells cell) this.cell = cell; } - #region IExcelCells Members + #region IExcelCells Members public DateTime Date { @@ -732,13 +732,13 @@ public short Underline } } - #endregion + #endregion } public class IExcelDocumentWrapper : IExcelDocument { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); Interop.GXOFFICE2Lib.IExcelDocument doc; @@ -746,7 +746,7 @@ public IExcelDocumentWrapper(Interop.GXOFFICE2Lib.IExcelDocument document) { doc = document; } - #region IExcelDocument Members + #region IExcelDocument Members public short Init(string previousMsgError) { @@ -928,13 +928,13 @@ public void CalculateFormulas() throw new NotImplementedException(); } - #endregion + #endregion } #endif public class ExcelUtils { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static void Show(string xlsFileName) { Process p = new Process(); diff --git a/dotnet/src/dotnetframework/GxExcel/GxExcelLite.cs b/dotnet/src/dotnetframework/GxExcel/GxExcelLite.cs index 7e76fe0a9..61a9ff9a9 100644 --- a/dotnet/src/dotnetframework/GxExcel/GxExcelLite.cs +++ b/dotnet/src/dotnetframework/GxExcel/GxExcelLite.cs @@ -11,7 +11,7 @@ namespace GeneXus.Office.ExcelLite { public class ExcelCells : IExcelCells { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public ExcelCells(IGxError errAccess, object ef, int row, int col, int height, int width) { @@ -366,7 +366,7 @@ public int Color public class ExcelDocument : IGxError, IExcelDocument { - static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static readonly IGXLogger log = GXLoggerFactory.GetLogger(); public static string nmspace; #if NETCORE public static string license = "FREE-LIMITED-KEY"; diff --git a/dotnet/src/dotnetframework/GxMail/Exchange/Service.cs b/dotnet/src/dotnetframework/GxMail/Exchange/Service.cs index 1208ba985..a63a18601 100644 --- a/dotnet/src/dotnetframework/GxMail/Exchange/Service.cs +++ b/dotnet/src/dotnetframework/GxMail/Exchange/Service.cs @@ -8,7 +8,7 @@ namespace GeneXus.Mail.Exchange public static class Service { - private static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(Service).FullName); static Service() { CertificateCallback.Initialize(); diff --git a/dotnet/src/dotnetframework/GxMail/Pop3MailKit.cs b/dotnet/src/dotnetframework/GxMail/Pop3MailKit.cs index e2df4bcb3..54ce23d7c 100644 --- a/dotnet/src/dotnetframework/GxMail/Pop3MailKit.cs +++ b/dotnet/src/dotnetframework/GxMail/Pop3MailKit.cs @@ -15,6 +15,7 @@ internal class Pop3MailKit : Pop3SessionBase private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); private Pop3Client client; + const string INLINE_IMAGE_PREFIX = "cid:"; public override int GetMessageCount() { @@ -161,7 +162,7 @@ public override void Receive(GXPOP3Session sessionInfo, GXMailMessage gxmessage) } gxmessage.DateReceived = Internals.Pop3.MailMessage.GetMessageDate(GetHeaderFromMimeMessage(msg,"Delivery-Date")); AddHeader(gxmessage, "DispositionNotificationTo", GetHeaderFromMimeMessage(msg, "Disposition-Notification-To")); - ProcessMailAttachments(gxmessage, msg.Attachments); + ProcessMailAttachments(gxmessage, msg); } } } @@ -192,8 +193,9 @@ private string GetHeaderFromMimeMessage(MimeMessage msg, string headerValue) return msg.Headers.Contains(headerValue) ? msg.Headers[msg.Headers.IndexOf(headerValue)].ToString() : null; } - private void ProcessMailAttachments(GXMailMessage gxmessage, IEnumerable attachs) + private void ProcessMailAttachments(GXMailMessage gxmessage, MimeMessage msg) { + IEnumerable attachs = msg.Attachments; if (attachs == null) return; @@ -202,28 +204,49 @@ private void ProcessMailAttachments(GXMailMessage gxmessage, IEnumerable -1)); + } + + private void ProcessMailAttachment(GXMailMessage gxmessage, MimeEntity attach, string attachName) + { + if (!string.IsNullOrEmpty(attach.ContentId) && attach.ContentDisposition != null && !attach.ContentDisposition.IsAttachment) + { + string cid = INLINE_IMAGE_PREFIX + attach.ContentId; + attachName = String.Format("{0}_{1}", attach.ContentId, attachName); + gxmessage.HTMLText = gxmessage.HTMLText.Replace(cid, attachName); + } + try + { + SaveAttachedFile(attach, attachName); + gxmessage.Attachments.Add(attachName); + } + catch (Exception e) + { + LogError("Could not add Attachment", "Failed to save attachment", MailConstants.MAIL_InvalidAttachment, e, log); + } + + } + private void SaveAttachedFile(MimeEntity attach, string attachName) { using (var stream = File.Create(Path.Combine(AttachDir, attachName))) diff --git a/dotnet/src/dotnetframework/GxOffice/GxOffice.csproj b/dotnet/src/dotnetframework/GxOffice/GxOffice.csproj index c80721437..2ae847298 100644 --- a/dotnet/src/dotnetframework/GxOffice/GxOffice.csproj +++ b/dotnet/src/dotnetframework/GxOffice/GxOffice.csproj @@ -9,6 +9,7 @@ + diff --git a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs index 0bb0a83c8..3374cf1f1 100644 --- a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs +++ b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportCommon.cs @@ -26,11 +26,11 @@ internal enum VerticalAlign BOTTOM = 2, } - public abstract class PDFReportItextBase : IReportHandler + public abstract class PDFReportBase : IReportHandler { protected int lineHeight, pageLines; - static IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static IGXLogger log = GXLoggerFactory.GetLogger(); protected bool fontUnderline; protected bool fontStrikethru; @@ -49,7 +49,7 @@ public abstract class PDFReportItextBase : IReportHandler protected bool modal = false; protected String docName = "PDFReport.pdf"; protected static NativeSharpFunctionsMS nativeCode = new NativeSharpFunctionsMS(); - protected Hashtable fontSubstitutes = new Hashtable(); + protected static Hashtable fontSubstitutes = new Hashtable(); protected static String configurationFile = null; protected static String configurationTemplateFile = null; protected static String defaultRelativePrepend = null; @@ -227,7 +227,7 @@ public static void showReport(String filename1, bool modal) } private static char alternateSeparator = Path.DirectorySeparatorChar == '/' ? '\\' : '/'; - public PDFReportItextBase(String appPath) + public PDFReportBase(String appPath) { try { @@ -724,7 +724,7 @@ public void GxSetDocName(String docName) this.docName = docName.Trim(); if(!Path.IsPathRooted(docName)) { - string outputDir = props.getGeneralProperty(Const.OUTPUT_FILE_DIRECTORY, "").Replace(alternateSeparator, Path.DirectorySeparatorChar).Trim(); + string outputDir = props.getGeneralProperty(Const.OUTPUT_FILE_DIRECTORY, string.Empty).Replace(alternateSeparator, Path.DirectorySeparatorChar).Trim(); if(!string.IsNullOrEmpty(outputDir) && outputDir!=".") { try @@ -733,22 +733,22 @@ public void GxSetDocName(String docName) { outputDir += Path.DirectorySeparatorChar; } - string[] dirs = Directory.GetDirectories(outputDir); - foreach (string dir in dirs) - { - Directory.CreateDirectory(dir); - } + Directory.CreateDirectory(outputDir); }catch (Exception ex) { Exception exDetailed = new Exception($"Error creating {Const.OUTPUT_FILE_DIRECTORY} of {Const.INI_FILE} ({outputDir})", ex); GXLogging.Error(log, "GxSetDocName error", exDetailed); throw exDetailed; } - this.docName = outputDir + this.docName; + this.docName = Path.Combine(outputDir, this.docName); } } - if(this.docName.IndexOf('.') < 0) + this.docName = ReportUtils.AddPath(this.docName, defaultRelativePrepend); + + if (string.IsNullOrEmpty(new FileInfo(this.docName).Extension)) + { this.docName += ".pdf"; + } GXLogging.Debug(log,"GxSetDocName: '" + this.docName + "'"); } @@ -894,12 +894,17 @@ protected float reconvertScale(float value) return result; } + bool PageHeightExceeded(float bottomAux, float drawingPageHeight) + { + return bottomAux > drawingPageHeight; + } + internal abstract bool SetComplainceLevel(PdfConformanceLevel level); } public class ParseINI { - static IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static IGXLogger log = GXLoggerFactory.GetLogger(); private static int MAX_LINE_LENGTH=255; private static String GENERAL="&General&"; @@ -1426,7 +1431,7 @@ public class Const public class NativeSharpFunctionsMS { - static IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static IGXLogger log = GXLoggerFactory.GetLogger(); public int shellExecute(String cmd, String fileName) { Process p = new Process(); @@ -1701,7 +1706,7 @@ public class Type1FontMetrics public class MSPDFFontDescriptor { - static IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); + static IGXLogger log = GXLoggerFactory.GetLogger(); private static String TRUE_TYPE_REGISTRY_SIGNATURE = "(TrueType)"; private static String REGISTRY_FONTS_ENTRY = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"; // Fonts NT/2000 diff --git a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext4.cs b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext4.cs index c84601f6b..bfa28eb7f 100644 --- a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext4.cs +++ b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext4.cs @@ -13,7 +13,7 @@ namespace com.genexus.reports { - public class PDFReportItextSharp : PDFReportItextBase + public class PDFReportItextSharp : PDFReportBase { static IGXLogger log = GXLoggerFactory.GetLogger(); diff --git a/dotnet/src/dotnetframework/GxPdfReportsCS/PdfReportInterface.cs b/dotnet/src/dotnetframework/GxPdfReportsCS/PdfReportInterface.cs index d7c71c5d2..cd4c73ad3 100644 --- a/dotnet/src/dotnetframework/GxPdfReportsCS/PdfReportInterface.cs +++ b/dotnet/src/dotnetframework/GxPdfReportsCS/PdfReportInterface.cs @@ -5,16 +5,15 @@ namespace GeneXus.Printer public class GxReportBuilderPdf : IReportHandler { - static IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); - string _appPath; + static IGXLogger log = GXLoggerFactory.GetLogger(); + protected string _appPath; protected IReportHandler _pdfReport; public GxReportBuilderPdf(string appPath, Stream outputStream) { _appPath = appPath; - { _pdfReport = new com.genexus.reports.PDFReportItextSharp(appPath); - } + if (outputStream != null) { @@ -22,7 +21,7 @@ public GxReportBuilderPdf(string appPath, Stream outputStream) GXLogging.Debug(log,"GxReportBuilderPdf outputStream: binaryWriter"); } } - public GxReportBuilderPdf() : this( "", null) + public GxReportBuilderPdf() { } public bool GxPrintInit(string output, ref int gxXPage, ref int gxYPage, string iniFile, string form, string printer, int mode, int orientation, int pageSize, int pageLength, int pageWidth, int scale, int copies, int defSrc, int quality, int color, int duplex) @@ -117,7 +116,6 @@ public virtual void GxRVSetLanguage(string lang) } public virtual void GxSetDocName(string docName) { - docName = ReportUtils.AddPath( docName, _appPath ); _pdfReport.GxSetDocName(docName); } public void GxSetDocFormat(string docFormat) diff --git a/dotnet/src/dotnetframework/GxSearch/SearchResultCollection.cs b/dotnet/src/dotnetframework/GxSearch/SearchResultCollection.cs index 660dbd961..ad6884684 100644 --- a/dotnet/src/dotnetframework/GxSearch/SearchResultCollection.cs +++ b/dotnet/src/dotnetframework/GxSearch/SearchResultCollection.cs @@ -7,7 +7,11 @@ using GeneXus.Utils; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using System.Globalization; using System.Security; @@ -116,9 +120,8 @@ public LuceneSearchResultItem() #endregion - internal LuceneSearchResultItem(Document document, float score) + internal LuceneSearchResultItem(Document document, float score):base() { - _Properties = new JObject(); m_document = document; m_score = score; } diff --git a/dotnet/src/dotnetframework/GxWebSocket/WSHandler.cs b/dotnet/src/dotnetframework/GxWebSocket/WSHandler.cs index ddf4f9fe4..2c4983fdd 100644 --- a/dotnet/src/dotnetframework/GxWebSocket/WSHandler.cs +++ b/dotnet/src/dotnetframework/GxWebSocket/WSHandler.cs @@ -8,7 +8,11 @@ using GeneXus.Procedure; using GeneXus.Services; using GeneXus.Utils; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using Microsoft.Web.WebSockets; namespace GeneXus.Http.WebSocket diff --git a/dotnet/src/dotnetframework/Projects/StoreManager/Store/Platforms/AppleStoreStoreManager.cs b/dotnet/src/dotnetframework/Projects/StoreManager/Store/Platforms/AppleStoreStoreManager.cs index 6bbc71836..23afb45d3 100644 --- a/dotnet/src/dotnetframework/Projects/StoreManager/Store/Platforms/AppleStoreStoreManager.cs +++ b/dotnet/src/dotnetframework/Projects/StoreManager/Store/Platforms/AppleStoreStoreManager.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; using System.Net; -using System.IO; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using GeneXus.SD.Store.Model; using System.Collections.Concurrent; using System.Net.Http; diff --git a/dotnet/src/dotnetframework/Projects/StoreManager/Store/Platforms/GooglePlayStoreManager.cs b/dotnet/src/dotnetframework/Projects/StoreManager/Store/Platforms/GooglePlayStoreManager.cs index 799e53f5f..126c5b448 100644 --- a/dotnet/src/dotnetframework/Projects/StoreManager/Store/Platforms/GooglePlayStoreManager.cs +++ b/dotnet/src/dotnetframework/Projects/StoreManager/Store/Platforms/GooglePlayStoreManager.cs @@ -1,13 +1,13 @@ using GeneXus.Utils; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Security.Cryptography.X509Certificates; using Google.Apis.Auth.OAuth2; using Google.Apis.AndroidPublisher.v3; using Google.Apis.Services; +#if !NETCORE using Jayrock.Json; +#endif using Google.Apis.AndroidPublisher.v3.Data; using GeneXus.Application; using System.IO; diff --git a/dotnet/src/dotnetframework/Projects/StoreManager/Store/StoreManager.cs b/dotnet/src/dotnetframework/Projects/StoreManager/Store/StoreManager.cs index ff8279c6d..aba1cdb1c 100644 --- a/dotnet/src/dotnetframework/Projects/StoreManager/Store/StoreManager.cs +++ b/dotnet/src/dotnetframework/Projects/StoreManager/Store/StoreManager.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; +#if NETCORE +using GeneXus.Application; +#else +using Jayrock.Json; +#endif using GeneXus.SD.Store.Model; using GeneXus.SD.Store.Platforms; using GeneXus.Utils; -using Jayrock.Json; namespace GeneXus.SD.Store { @@ -165,7 +169,7 @@ public int GetManager(GxUserType gxStoreConfig, int platform, out IStoreManager return errCode; } - public static bool GetConfigValue(string key, JObject storeConfig, out string value) + static bool GetConfigValue(string key, JObject storeConfig, out string value) { value = string.Empty; if (storeConfig.Contains(key)) diff --git a/dotnet/src/dotnetframework/Projects/StoreManager/Store/Util.cs b/dotnet/src/dotnetframework/Projects/StoreManager/Store/Util.cs index ce6a2a344..3544e3732 100644 --- a/dotnet/src/dotnetframework/Projects/StoreManager/Store/Util.cs +++ b/dotnet/src/dotnetframework/Projects/StoreManager/Store/Util.cs @@ -1,5 +1,9 @@ using GeneXus.Utils; +#if NETCORE +using GeneXus.Application; +#else using Jayrock.Json; +#endif using System; using System.Security.Cryptography; using System.Text; @@ -14,7 +18,7 @@ public static DateTime FromUnixTime(long unixMillisecondsTime) return dt.AddMilliseconds(unixMillisecondsTime); } - public static JObject FromJSonString(string s) + internal static JObject FromJSonString(string s) { JObject _jsonArr = null; if (!string.IsNullOrEmpty(s)) diff --git a/dotnet/src/extensions/Azure/Handlers/Dummies/EventGridTriggerDummy.cs b/dotnet/src/extensions/Azure/Handlers/Dummies/EventGridTriggerDummy.cs new file mode 100644 index 000000000..0d0c4c489 --- /dev/null +++ b/dotnet/src/extensions/Azure/Handlers/Dummies/EventGridTriggerDummy.cs @@ -0,0 +1,22 @@ +using Azure.Messaging; +using Azure.Messaging.EventGrid; +using GeneXus.Deploy.AzureFunctions.Handlers.Helpers; +using Microsoft.Azure.Functions.Worker; + +namespace EventGridTriggerDummy +{ + public static class EventGridFunction + { + + [Function("EventGridFunctionCloudSchema")] + public static void Run([EventGridTrigger] CloudEvent input, FunctionContext context) + { + + } + [Function("EventGridFunctionAzureSchema")] + public static void Run([EventGridTrigger] EventGridEvent input, FunctionContext context) + { + + } + } +} \ No newline at end of file diff --git a/dotnet/src/extensions/Azure/Handlers/EventGridHandler/EventGridTriggerHandlerAzure.cs b/dotnet/src/extensions/Azure/Handlers/EventGridHandler/EventGridTriggerHandlerAzure.cs new file mode 100644 index 000000000..4bb5e0337 --- /dev/null +++ b/dotnet/src/extensions/Azure/Handlers/EventGridHandler/EventGridTriggerHandlerAzure.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using Azure.Messaging; +using Azure.Messaging.EventGrid; +using GeneXus.Application; +using GeneXus.Deploy.AzureFunctions.Handlers.Helpers; +using GeneXus.Metadata; +using GeneXus.Utils; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Logging; + +namespace GeneXus.Deploy.AzureFunctions.EventGridHandler +{ + public class EventGridTriggerHandlerAzure + { + private ICallMappings _callmappings; + + public EventGridTriggerHandlerAzure(ICallMappings callMappings) + { + _callmappings = callMappings; + } + public void Run(EventGridEvent input, FunctionContext context) + { + var logger = context.GetLogger("EventGridTriggerHandler"); + string functionName = context.FunctionDefinition.Name; + Guid eventId = new Guid(context.InvocationId); + logger.LogInformation($"GeneXus Event Grid trigger handler. Function processed: {functionName}. Event Id: {eventId}. Function executed at: {DateTime.Now}."); + + try + { + ProcessEvent(context, logger, input, eventId.ToString()); + } + catch (Exception ex) //Catch System exception and retry + { + logger.LogError(ex.ToString()); + throw; + } + } + private void ProcessEvent(FunctionContext context, ILogger log, EventGridEvent input, string eventId) + { + CallMappings callmap = (CallMappings)_callmappings; + + GxAzMappings map = callmap.mappings is object ? callmap.mappings.First(m => m.FunctionName == context.FunctionDefinition.Name) : null; + string gxProcedure = (map != null && map is object) ? map.GXEntrypoint : string.Empty; + string exMessage; + + if (!string.IsNullOrEmpty(gxProcedure)) + { + try + { + StringBuilder sb1 = new StringBuilder(gxProcedure); + sb1.Append(".dll"); + string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), sb1.ToString()); + Assembly obj = Assembly.LoadFile(path); + + StringBuilder sb2 = new StringBuilder("GeneXus.Programs."); + sb2.Append(gxProcedure); + + Type objexec = obj.GetType(sb2.ToString()); + if (objexec != null) + { + object objgxproc = Activator.CreateInstance(objexec); + var method = objexec.GetMethod("execute"); + ParameterInfo[] parameters = method.GetParameters(); + + //Check parameters + + if (parameters.Length != 2) + { + //Thrown to the Azure monitor + + exMessage = string.Format("{0} Error for Event Id {1}: the number of parameters in GeneXus procedure is not correct.", FunctionExceptionType.SysRuntimeError, eventId); + throw new Exception(exMessage); //Send to retry if possible. + } + else + { + //Two valid signatures for the GX procedure: + //parm(in:&EventMessageCollection, out:&ExternalEventMessageResponse ); + //parm(in:&rawData, out:&ExternalEventMessageResponse ); + + GxContext gxcontext = new GxContext(); + object[] parametersdata; + parametersdata = new object[] { null }; + + if (parameters[0].ParameterType == typeof(string)) + { + string eventMessageSerialized = string.Empty; + eventMessageSerialized = JsonSerializer.Serialize(input); + parametersdata = new object[] { eventMessageSerialized, null }; + } + else + { + + //Initialization + + Type eventMessagesType = parameters[0].ParameterType; //SdtEventMessages + GxUserType eventMessages = (GxUserType)Activator.CreateInstance(eventMessagesType, new object[] { gxcontext }); // instance of SdtEventMessages + + IList eventMessage = (IList)ClassLoader.GetPropValue(eventMessages, "gxTpr_Eventmessage");//instance of GXBaseCollection + Type eventMessageItemType = eventMessage.GetType().GetGenericArguments()[0];//SdtEventMessage + + GxUserType eventMessageItem = (GxUserType)Activator.CreateInstance(eventMessageItemType, new object[] { gxcontext }); // instance of SdtEventMessage + + IList eventMessageProperties = (IList)ClassLoader.GetPropValue(eventMessageItem, "gxTpr_Eventmessageproperties");//instance of GXBaseCollection + Type eventMessPropsItemType = eventMessageProperties.GetType().GetGenericArguments()[0];//SdtEventMessageProperty + + GxUserType eventMessageProperty; + + if (input.Subject != null) + { + eventMessageProperty = EventMessagePropertyMapping.CreateEventMessageProperty(eventMessPropsItemType, "Subject", input.Subject, gxcontext); + eventMessageProperties.Add(eventMessageProperty); + } + + if (input.Topic != null) + { + eventMessageProperty = EventMessagePropertyMapping.CreateEventMessageProperty(eventMessPropsItemType, "Topic", input.Topic, gxcontext); + eventMessageProperties.Add(eventMessageProperty); + } + + //Event + + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageid", input.Id); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagesourcetype", input.EventType); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageversion", input.DataVersion); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageproperties", eventMessageProperties); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagedate", input.EventTime.UtcDateTime); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagedata", input.Data.ToString()); + + //List of Events + eventMessage.Add(eventMessageItem); + parametersdata = new object[] { eventMessages, null }; + } + + try + { + method.Invoke(objgxproc, parametersdata); + GxUserType EventMessageResponse = parametersdata[1] as GxUserType;//SdtEventMessageResponse + bool result = (bool)ClassLoader.GetPropValue(EventMessageResponse, "gxTpr_Handlefailure"); + + //Error handling + + if (result == true) //Must retry + { + exMessage = string.Format("{0} {1}", FunctionExceptionType.AppError, ClassLoader.GetPropValue(EventMessageResponse, "gxTpr_Errormessage")); + throw new Exception(exMessage); + } + else + { + log.LogInformation("(GX function handler) Function finished execution."); + } + } + catch (Exception) + { + log.LogError("{0} Error invoking the GX procedure for Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId); + throw; //Throw the exception so the runtime can Retry the operation. + } + } + } + else + { + exMessage = string.Format("{0} GeneXus procedure could not be executed for Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId); + throw new Exception(exMessage); + } + } + catch (Exception) + { + log.LogError("{0} Error processing Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId); + throw; //Throw the exception so the runtime can Retry the operation. + } + } + else + { + exMessage = string.Format("{0} GeneXus procedure could not be executed while processing Event Id {1}. Reason: procedure not specified in configuration file.", FunctionExceptionType.SysRuntimeError, eventId); + throw new Exception(exMessage); + } + } + } +} + diff --git a/dotnet/src/extensions/Azure/Handlers/EventGridHandler/EventGridTriggerHandlerCloud.cs b/dotnet/src/extensions/Azure/Handlers/EventGridHandler/EventGridTriggerHandlerCloud.cs new file mode 100644 index 000000000..4ae40dfc7 --- /dev/null +++ b/dotnet/src/extensions/Azure/Handlers/EventGridHandler/EventGridTriggerHandlerCloud.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using Azure.Messaging; +using GeneXus.Application; +using GeneXus.Deploy.AzureFunctions.Handlers.Helpers; +using GeneXus.Metadata; +using GeneXus.Utils; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Logging; + +namespace GeneXus.Deploy.AzureFunctions.EventGridHandler +{ + public class EventGridTriggerHandlerCloud + { + private ICallMappings _callmappings; + + public EventGridTriggerHandlerCloud(ICallMappings callMappings) + { + _callmappings = callMappings; + } + public void Run(CloudEvent input, FunctionContext context) + { + var logger = context.GetLogger("EventGridTriggerHandler"); + string functionName = context.FunctionDefinition.Name; + Guid eventId = new Guid(context.InvocationId); + logger.LogInformation($"GeneXus Event Grid trigger handler. Function processed: {functionName}. Event Id: {eventId}. Function executed at: {DateTime.Now}."); + + try + { + ProcessEvent(context, logger, input, eventId.ToString()); + } + catch (Exception ex) //Catch System exception and retry + { + logger.LogError(ex.ToString()); + throw; + } + } + private void ProcessEvent(FunctionContext context, ILogger log, CloudEvent input, string eventId) + { + CallMappings callmap = (CallMappings)_callmappings; + + GxAzMappings map = callmap.mappings is object ? callmap.mappings.First(m => m.FunctionName == context.FunctionDefinition.Name) : null; + string gxProcedure = (map != null && map is object) ? map.GXEntrypoint : string.Empty; + string exMessage; + + if (!string.IsNullOrEmpty(gxProcedure)) + { + try + { + StringBuilder sb1 = new StringBuilder(gxProcedure); + sb1.Append(".dll"); + string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), sb1.ToString()); + Assembly obj = Assembly.LoadFile(path); + + StringBuilder sb2 = new StringBuilder("GeneXus.Programs."); + sb2.Append(gxProcedure); + + Type objexec = obj.GetType(sb2.ToString()); + if (objexec != null) + { + object objgxproc = Activator.CreateInstance(objexec); + var method = objexec.GetMethod("execute"); + ParameterInfo[] parameters = method.GetParameters(); + + //Check parameters + + if (parameters.Length != 2) + { + //Thrown to the Azure monitor + + exMessage = string.Format("{0} Error for Event Id {1}: the number of parameters in GeneXus procedure is not correct.", FunctionExceptionType.SysRuntimeError, eventId); + throw new Exception(exMessage); //Send to retry if possible. + } + else + { + //Two valid signatures for the GX procedure: + //parm(in:&EventMessageCollection, out:&ExternalEventMessageResponse ); + //parm(in:&rawData, out:&ExternalEventMessageResponse ); + + GxContext gxcontext = new GxContext(); + object[] parametersdata; + parametersdata = new object[] { null }; + + if (parameters[0].ParameterType == typeof(string)) + { + string eventMessageSerialized = string.Empty; + eventMessageSerialized = JsonSerializer.Serialize(input); + parametersdata = new object[] { eventMessageSerialized, null }; + } + else + { + + //Initialization + + Type eventMessagesType = parameters[0].ParameterType; //SdtEventMessages + GxUserType eventMessages = (GxUserType)Activator.CreateInstance(eventMessagesType, new object[] { gxcontext }); // instance of SdtEventMessages + + IList eventMessage = (IList)ClassLoader.GetPropValue(eventMessages, "gxTpr_Eventmessage");//instance of GXBaseCollection + Type eventMessageItemType = eventMessage.GetType().GetGenericArguments()[0];//SdtEventMessage + + GxUserType eventMessageItem = (GxUserType)Activator.CreateInstance(eventMessageItemType, new object[] { gxcontext }); // instance of SdtEventMessage + + IList eventMessageProperties = (IList)ClassLoader.GetPropValue(eventMessageItem, "gxTpr_Eventmessageproperties");//instance of GXBaseCollection + Type eventMessPropsItemType = eventMessageProperties.GetType().GetGenericArguments()[0];//SdtEventMessageProperty + + GxUserType eventMessageProperty; + + if (input.Subject != null) + { + eventMessageProperty = EventMessagePropertyMapping.CreateEventMessageProperty(eventMessPropsItemType, "Subject", input.Subject, gxcontext); + eventMessageProperties.Add(eventMessageProperty); + } + + if (input.Source != null) + { + eventMessageProperty = EventMessagePropertyMapping.CreateEventMessageProperty(eventMessPropsItemType, "Source", input.Source, gxcontext); + eventMessageProperties.Add(eventMessageProperty); + } + + //Event + + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageid", input.Id); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagesourcetype", input.Type); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageversion", string.Empty); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageproperties", eventMessageProperties); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagedate", input.Time.Value.UtcDateTime); + ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagedata", input.Data.ToString()); + + + //List of Events + eventMessage.Add(eventMessageItem); + parametersdata = new object[] { eventMessages, null }; + } + + try + { + method.Invoke(objgxproc, parametersdata); + GxUserType EventMessageResponse = parametersdata[1] as GxUserType;//SdtEventMessageResponse + bool result = (bool)ClassLoader.GetPropValue(EventMessageResponse, "gxTpr_Handlefailure"); + + //Error handling + + if (result == true) //Must retry + { + exMessage = string.Format("{0} {1}", FunctionExceptionType.AppError, ClassLoader.GetPropValue(EventMessageResponse, "gxTpr_Errormessage")); + throw new Exception(exMessage); + } + else + { + log.LogInformation("(GX function handler) Function finished execution."); + } + } + catch (Exception) + { + log.LogError("{0} Error invoking the GX procedure for Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId); + throw; //Throw the exception so the runtime can Retry the operation. + } + } + } + else + { + exMessage = string.Format("{0} GeneXus procedure could not be executed for Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId); + throw new Exception(exMessage); + } + } + catch (Exception) + { + log.LogError("{0} Error processing Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId); + throw; //Throw the exception so the runtime can Retry the operation. + } + } + else + { + exMessage = string.Format("{0} GeneXus procedure could not be executed while processing Event Id {1}. Reason: procedure not specified in configuration file.", FunctionExceptionType.SysRuntimeError, eventId); + throw new Exception(exMessage); + } + } + } +} + diff --git a/dotnet/src/extensions/Azure/Handlers/GeneXus.Deploy.AzureFunctions.Handlers.csproj b/dotnet/src/extensions/Azure/Handlers/GeneXus.Deploy.AzureFunctions.Handlers.csproj index 609e2d6c2..436ee7ca2 100644 --- a/dotnet/src/extensions/Azure/Handlers/GeneXus.Deploy.AzureFunctions.Handlers.csproj +++ b/dotnet/src/extensions/Azure/Handlers/GeneXus.Deploy.AzureFunctions.Handlers.csproj @@ -17,6 +17,7 @@ true true true + true @@ -61,13 +62,17 @@ + + + + + - + - - - + + @@ -95,7 +100,7 @@ - + ./lib/$(TargetFramework)/ true diff --git a/dotnet/src/extensions/Azure/Handlers/Helpers/EventSourceType.cs b/dotnet/src/extensions/Azure/Handlers/Helpers/EventSourceType.cs index 2d2e94f80..ecc7f6af0 100644 --- a/dotnet/src/extensions/Azure/Handlers/Helpers/EventSourceType.cs +++ b/dotnet/src/extensions/Azure/Handlers/Helpers/EventSourceType.cs @@ -1,7 +1,7 @@ +using System; using GeneXus.Application; using GeneXus.Metadata; using GeneXus.Utils; -using System; namespace GeneXus.Deploy.AzureFunctions.Handlers.Helpers { diff --git a/dotnet/src/extensions/Azure/test/AzureFunctionsTest/AzureFunctionsTest.csproj b/dotnet/src/extensions/Azure/test/AzureFunctionsTest/AzureFunctionsTest.csproj index f47c0cc47..6b54739ec 100644 --- a/dotnet/src/extensions/Azure/test/AzureFunctionsTest/AzureFunctionsTest.csproj +++ b/dotnet/src/extensions/Azure/test/AzureFunctionsTest/AzureFunctionsTest.csproj @@ -7,7 +7,7 @@ - + diff --git a/dotnet/src/extensions/Azure/test/AzureFunctionsTest/EventGridTrigger.cs b/dotnet/src/extensions/Azure/test/AzureFunctionsTest/EventGridTrigger.cs new file mode 100644 index 000000000..96cc8a06d --- /dev/null +++ b/dotnet/src/extensions/Azure/test/AzureFunctionsTest/EventGridTrigger.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Azure.Messaging; +using Azure.Messaging.EventGrid; +using GeneXus.Deploy.AzureFunctions.EventGridHandler; +using GeneXus.Deploy.AzureFunctions.Handlers.Helpers; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Extensions.AzureFunctions.Test +{ + public class EventGridTriggerTest + { + + [Fact] + public void EventGridTestCloudEventSchema() + { + try + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddScoped(); + var serviceProvider = serviceCollection.BuildServiceProvider(); + + var context = new Mock(); + context.SetupProperty(c => c.InstanceServices, serviceProvider); + + context.SetupGet(c => c.FunctionId).Returns("815504e67081459c9ef1d0399a4aeda3"); + context.SetupGet(c => c.FunctionDefinition.Name).Returns("eventgridTest"); + context.SetupGet(c => c.InvocationId).Returns("d1934e45f752478d899ddb87d262f313"); + + ICallMappings callMappings = new CallMappings("."); + + + IReadOnlyDictionary bindingData = new Dictionary() + { + { + "Id", new PropertyId { Id = "75c7ead6-6485-4aeb-9b31-a0778b4d64a3"} + }, + }; + + Object data = "{\r\n \"api\": \"PutBlockList\",\r\n \"clientRequestId\": \"4c5dd7fb-2c48-4a27-bb30-5361b5de920a\",\r\n \"requestId\": \"9aeb0fdf-c01e-0131-0922-9eb549000000\",\r\n \"eTag\": \"0x8D76C39E4407333\",\r\n \"contentType\": \"image/png\",\r\n \"contentLength\": 30699,\r\n \"blobType\": \"BlockBlob\",\r\n \"url\": \"https://gridtesting.blob.core.windows.net/testcontainer/{new-file}\",\r\n \"sequencer\": \"000000000000000000000000000099240000000000c41c18\",\r\n \"storageDiagnostics\": {\r\n \"batchId\": \"681fe319-3006-00a8-0022-9e7cde000000\"\r\n }"; + context.SetupGet(c => c.BindingContext.BindingData).Returns(bindingData); + + CloudEvent cloudEvent = new("\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Storage/storageAccounts/{storage-account}\"", "Microsoft.Storage.BlobCreated", data); + var ex = Record.Exception(() => new EventGridTriggerHandlerCloud(callMappings).Run(cloudEvent, context.Object)); + Assert.Null(ex); + + } catch(Exception ex) + { + throw new Exception("Exception should not be thrown.", ex); + } + + } + + [Fact] + public void EventGridTestAzureSchema() + { + try + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddScoped(); + var serviceProvider = serviceCollection.BuildServiceProvider(); + + var context = new Mock(); + context.SetupProperty(c => c.InstanceServices, serviceProvider); + + context.SetupGet(c => c.FunctionId).Returns("39e90db641d94d81b0e854016dcffef9"); + context.SetupGet(c => c.FunctionDefinition.Name).Returns("eventgridTestAzureSchema"); + context.SetupGet(c => c.InvocationId).Returns("4ceabb102cbc48378f58bbc3911d348d"); + + ICallMappings callMappings = new CallMappings("."); + + + IReadOnlyDictionary bindingData = new Dictionary() + { + { + "Id", new PropertyId { Id = "514fa418-08fc-4aab-ba41-ed00375b6928"} + }, + }; + + + context.SetupGet(c => c.BindingContext.BindingData).Returns(bindingData); + + Object data = "{\r\n \"api\": \"PutBlockList\",\r\n \"clientRequestId\": \"4c5dd7fb-2c48-4a27-bb30-5361b5de920a\",\r\n \"requestId\": \"9aeb0fdf-c01e-0131-0922-9eb549000000\",\r\n \"eTag\": \"0x8D76C39E4407333\",\r\n \"contentType\": \"image/png\",\r\n \"contentLength\": 30699,\r\n \"blobType\": \"BlockBlob\",\r\n \"url\": \"https://gridtesting.blob.core.windows.net/testcontainer/{new-file}\",\r\n \"sequencer\": \"000000000000000000000000000099240000000000c41c18\",\r\n \"storageDiagnostics\": {\r\n \"batchId\": \"681fe319-3006-00a8-0022-9e7cde000000\"\r\n }"; + EventGridEvent eventGridEvent = new EventGridEvent("TestEventAzureEventGridSchema", "GXTest", "1.0", data); + + var ex = Record.Exception(() => new EventGridTriggerHandlerAzure(callMappings).Run(eventGridEvent, context.Object)); + Assert.Null(ex); + + } + catch (Exception ex) + { + throw new Exception("Exception should not be thrown.", ex); + } + + } + } +} \ No newline at end of file diff --git a/dotnet/src/extensions/Azure/test/AzureFunctionsTest/gxazmappings.json b/dotnet/src/extensions/Azure/test/AzureFunctionsTest/gxazmappings.json index ea2febc38..3342d6577 100644 --- a/dotnet/src/extensions/Azure/test/AzureFunctionsTest/gxazmappings.json +++ b/dotnet/src/extensions/Azure/test/AzureFunctionsTest/gxazmappings.json @@ -1 +1,18 @@ -[{"FunctionName":"timerTest","GXEntrypoint":"amyprochandler"},{"FunctionName":"queueTest","GXEntrypoint":"amyprochandler"}] \ No newline at end of file +[ + { + "FunctionName": "timerTest", + "GXEntrypoint": "amyprochandler" + }, + { + "FunctionName": "queueTest", + "GXEntrypoint": "amyprochandler" + }, + { + "FunctionName": "eventgridTest", + "GXEntrypoint": "amyprochandler" + }, + { + "FunctionName": "eventgridTestAzureSchema", + "GXEntrypoint": "amyprochandler" + } +] \ No newline at end of file diff --git a/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/GeneXus.Programs.Common.csproj b/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/GeneXus.Programs.Common.csproj index f9024993a..35573e5fd 100644 --- a/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/GeneXus.Programs.Common.csproj +++ b/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/GeneXus.Programs.Common.csproj @@ -5,6 +5,10 @@ false + + + + diff --git a/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/type_SdtEventMessageProperty.cs b/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/type_SdtEventMessageProperty.cs index f04498b0b..6fcdacad9 100644 --- a/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/type_SdtEventMessageProperty.cs +++ b/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/type_SdtEventMessageProperty.cs @@ -138,6 +138,7 @@ public void initialize( ) } #region Rest interface [GxUnWrappedJson()] + [GxJsonSerialization("default")] [DataContract(Name=@"EventMessageProperty", Namespace="ServerlessAPI")] public class SdtEventMessageProperty_RESTInterface : GxGenericCollectionItem, System.Web.SessionState.IRequiresSessionState { diff --git a/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/type_SdtEventMessageResponse.cs b/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/type_SdtEventMessageResponse.cs index 984b053cf..bcd519d34 100644 --- a/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/type_SdtEventMessageResponse.cs +++ b/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/type_SdtEventMessageResponse.cs @@ -131,6 +131,7 @@ public void initialize() } #region Rest interface [GxUnWrappedJson()] + [GxJsonSerialization("default")] [DataContract(Name = @"EventMessageResponse", Namespace = "GeneXus")] public class SdtEventMessageResponse_RESTInterface : GxGenericCollectionItem, System.Web.SessionState.IRequiresSessionState { diff --git a/dotnet/src/extensions/Azure/test/amyprocedurehandler/amyprochandler.csproj b/dotnet/src/extensions/Azure/test/amyprocedurehandler/amyprochandler.csproj index 9119547f2..e80fca417 100644 --- a/dotnet/src/extensions/Azure/test/amyprocedurehandler/amyprochandler.csproj +++ b/dotnet/src/extensions/Azure/test/amyprocedurehandler/amyprochandler.csproj @@ -11,6 +11,10 @@ + + + + PreserveNewest diff --git a/dotnet/src/extensions/Azure/test/apiattractions/apiattractions.csproj b/dotnet/src/extensions/Azure/test/apiattractions/apiattractions.csproj index 5454ea407..08378cf4a 100644 --- a/dotnet/src/extensions/Azure/test/apiattractions/apiattractions.csproj +++ b/dotnet/src/extensions/Azure/test/apiattractions/apiattractions.csproj @@ -7,6 +7,10 @@ false + + + + diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusCryptographyNetCore/GeneXusCryptographyNetCore.csproj b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusCryptographyNetCore/GeneXusCryptographyNetCore.csproj index 15ab5514c..b3d135f44 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusCryptographyNetCore/GeneXusCryptographyNetCore.csproj +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusCryptographyNetCore/GeneXusCryptographyNetCore.csproj @@ -15,8 +15,11 @@ + + + @@ -30,6 +33,7 @@ + @@ -45,10 +49,6 @@ - - - - @@ -67,4 +67,8 @@ + + + + diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusJWTNetCore/GeneXusJWTNetCore.csproj b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusJWTNetCore/GeneXusJWTNetCore.csproj index 3ffbb4bec..299927dd2 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusJWTNetCore/GeneXusJWTNetCore.csproj +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusJWTNetCore/GeneXusJWTNetCore.csproj @@ -33,12 +33,12 @@ - - - + + + + - - + diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusXmlSignatureNetCore/GeneXusXmlSignatureNetCore.csproj b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusXmlSignatureNetCore/GeneXusXmlSignatureNetCore.csproj index fddff7894..7d3b069e0 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusXmlSignatureNetCore/GeneXusXmlSignatureNetCore.csproj +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/GeneXusXmlSignatureNetCore/GeneXusXmlSignatureNetCore.csproj @@ -26,8 +26,8 @@ - - + + diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/SecurityAPICommonsNetCore/SecurityAPICommonsNetCore.csproj b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/SecurityAPICommonsNetCore/SecurityAPICommonsNetCore.csproj index faba4af3b..64daf268f 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/SecurityAPICommonsNetCore/SecurityAPICommonsNetCore.csproj +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetcore/SecurityAPICommonsNetCore/SecurityAPICommonsNetCore.csproj @@ -34,7 +34,7 @@ - + diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Asymmetric/StandardSigner.cs b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Asymmetric/StandardSigner.cs new file mode 100644 index 000000000..d9049dac3 --- /dev/null +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Asymmetric/StandardSigner.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Security; +using GeneXusCryptography.AsymmetricUtils; +using GeneXusCryptography.Commons; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Encoders; +using SecurityAPICommons.Commons; +using SecurityAPICommons.Config; +using SecurityAPICommons.Keys; +using SecurityAPICommons.Utils; +using Org.BouncyCastle.Utilities.Collections; + + +namespace GeneXusCryptography.Asymmetric +{ + [SecuritySafeCritical] + public class StandardSigner : SecurityAPIObject, IStandardSignerObject + { + + public StandardSigner() : base() + { + + } + + /******** EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/ + + [SecuritySafeCritical] + public string Sign(string plainText, SignatureStandardOptions options) + { + this.error.cleanError(); + + /******* INPUT VERIFICATION - BEGIN *******/ + SecurityUtils.validateObjectInput("signatureStandardOptions", options, this.error); + SecurityUtils.validateObjectInput("private key", options.GetPrivateKey(), this.error); + SecurityUtils.validateObjectInput("certificate", options.GetCertificate(), this.error); + SecurityUtils.validateStringInput("plainText", plainText, this.error); + if (this.HasError()) + { + return ""; + } + + /******* INPUT VERIFICATION - END *******/ + + EncodingUtil eu = new EncodingUtil(); + byte[] inputText = eu.getBytes(plainText); + if (eu.HasError()) + { + this.error = eu.GetError(); + return ""; + } + + string result = ""; + try + { + result = Sign_internal(inputText, options.GetPrivateKey(), options.GetCertificate(), options.GetSignatureStandard(), options.GetEncapsulated()); + } + catch (Exception e) + { + error.setError("SS002", e.Message); + result = ""; + } + + return result; + } + + [SecuritySafeCritical] + + public bool Verify(string signed, string plainText, SignatureStandardOptions options) + { + this.error.cleanError(); + + /******* INPUT VERIFICATION - BEGIN *******/ + SecurityUtils.validateObjectInput("signatureStandardOptions", options, this.error); + //SecurityUtils.validateStringInput("plainText", plainText, this.error); + SecurityUtils.validateStringInput("signed", signed, this.error); + if (this.HasError()) + { + return false; + } + + + /******* INPUT VERIFICATION - END *******/ + + EncodingUtil eu = new EncodingUtil(); + byte[] plainText_bytes = eu.getBytes(plainText); + if (eu.HasError()) + { + this.error = eu.GetError(); + return false; + } + + bool result = false; + try + { + result = Verify_internal(Base64.Decode(signed), plainText_bytes, options.GetEncapsulated()); + } + catch (Exception e) + { + error.setError("SS002", e.Message); + result = false; + } + + return result; + } + + /******** EXTERNAL OBJECT PUBLIC METHODS - END ********/ + + private string Sign_internal(byte[] input, PrivateKeyManager key, CertificateX509 cert, SignatureStandard signatureStandard, bool encapsulated) + { + + PrivateKeyManager keyMan = (PrivateKeyManager)key; + if (keyMan.HasError()) + { + this.error = keyMan.GetError(); + return ""; + } + CertificateX509 certificate = (CertificateX509)cert; + if (certificate.HasError()) + { + + this.error = certificate.GetError(); + return ""; + } + AsymmetricSigningAlgorithm asymmetricSigningAlgorithm = AsymmetricSigningAlgorithmUtils + .GetAsymmetricSigningAlgorithm(keyMan.getAlgorithm(), this.error); + string encryptAlg = AsymmetricSigningAlgorithmUtils.GetCMSSigningAlgortithm(asymmetricSigningAlgorithm, this.error); + if (this.HasError()) { return ""; } + + Org.BouncyCastle.X509.X509Certificate cert2 = DotNetUtilities.FromX509Certificate(certificate.Cert); + + CmsSignedDataGenerator generator = new CmsSignedDataGenerator(); + string digest = asymmetricSigningAlgorithm == AsymmetricSigningAlgorithm.ECDSA ? CmsSignedGenerator.DigestSha1 : DigestCalculator(certificate); + + generator.AddSigner(keyMan.getAsymmetricKeyParameter(), cert2, encryptAlg, digest); + + List certList = new List(); + certList.Add(cert2); + + IStore certStore = CollectionUtilities.CreateStore(certList); + + generator.AddCertificates(certStore); + + CmsSignedData signedData = generator.Generate(new CmsProcessableByteArray(input), encapsulated); + + + return Base64.ToBase64String(signedData.GetEncoded()); + + } + + + private bool Verify_internal(byte[] cmsSignedData, byte[] data, bool encapsulated) + { + CmsSignedData cms = encapsulated ? new CmsSignedData(cmsSignedData) : new CmsSignedData(new CmsProcessableByteArray(data), cmsSignedData); + + SignerInformationStore signers = cms.GetSignerInfos(); + + IStore certificates = cms.GetCertificates(); + var signerInfos = signers.GetSigners(); + foreach (SignerInformation signer in signerInfos) + { + var certCollection = certificates.EnumerateMatches(signer.SignerID); + var certEnum = certCollection.GetEnumerator(); + + certEnum.MoveNext(); + Org.BouncyCastle.X509.X509Certificate cert = certEnum.Current; + var publicKey = cert.GetPublicKey(); + bool res = false; + + res = signer.Verify(publicKey); + + if (!res) + { + return false; + } + } + return true; + } + + + + private string DigestCalculator(CertificateX509 cert) + { + string value = cert.getPublicKeyHash(); + switch (value) + { + case "SHA1": + return CmsSignedGenerator.DigestSha1; + case "SHA256": + return CmsSignedGenerator.DigestSha256; + case "SHA512": + return CmsSignedGenerator.DigestSha512; + default: + this.error.setError("SS003", "Unrecognizable certificate hash algorithm"); + return ""; + } + + } + + } +} diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/AsymmetricSigningAlgorithm.cs b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/AsymmetricSigningAlgorithm.cs index b68931b14..3624f125b 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/AsymmetricSigningAlgorithm.cs +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/AsymmetricSigningAlgorithm.cs @@ -1,4 +1,5 @@ +using Org.BouncyCastle.Cms; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Signers; using SecurityAPICommons.Commons; @@ -97,6 +98,21 @@ public static ISigner GetSigner(AsymmetricSigningAlgorithm asymmetricSigningAlgo return sig; } + public static string GetCMSSigningAlgortithm(AsymmetricSigningAlgorithm asymmetricSigningAlgorithm, Error error) + { + if (error == null) return null; + switch (asymmetricSigningAlgorithm) + { + case AsymmetricSigningAlgorithm.RSA: + return CmsSignedDataGenerator.EncryptionRsa; + case AsymmetricSigningAlgorithm.ECDSA: + return CmsSignedDataGenerator.EncryptionECDsa; + default: + error.setError("AE008", "Not recogrnized AsymmetricSigningAlgorithm"); + return ""; + } + } + /// /// Manage Enumerable enum /// diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/SignatureStandardOptions.cs b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/SignatureStandardOptions.cs new file mode 100644 index 000000000..c1b59e98a --- /dev/null +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/SignatureStandardOptions.cs @@ -0,0 +1,57 @@ +using System; +using System.Security; +using SecurityAPICommons.Commons; +using SecurityAPICommons.Keys; + +namespace GeneXusCryptography.AsymmetricUtils +{ + [SecuritySafeCritical] + public class SignatureStandardOptions : SecurityAPIObject + { + private CertificateX509 certificate; + private PrivateKeyManager privateKey; + + private SignatureStandard signatureStandard; + + private bool encapsulated; + + [SecuritySafeCritical] + public SignatureStandardOptions() : base() + { + this.signatureStandard = SignatureStandard.CMS; + this.encapsulated = false; + } + + /******** EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/ + public void SetPrivateKey(PrivateKeyManager key) + { + this.privateKey = key; + } + + public void SetCertificate(CertificateX509 cert) + { + this.certificate = cert; + } + + public bool SetSignatureStandard(String standard) + { + this.signatureStandard = SignatureStandardUtils.getSignatureStandard(standard, this.error); + return this.HasError() ? false : true; + } + + public void SetEncapsulated(bool value) { this.encapsulated = value; } + + /******** EXTERNAL OBJECT PUBLIC METHODS - END ********/ + + public PrivateKeyManager GetPrivateKey() + { + return this.privateKey; + } + + public CertificateX509 GetCertificate() { return this.certificate; } + + public SignatureStandard GetSignatureStandard() { return this.signatureStandard; } + + public bool GetEncapsulated() { return this.encapsulated; } + } +} diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/SignatureStandardUtils.cs b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/SignatureStandardUtils.cs new file mode 100644 index 000000000..4796f18ee --- /dev/null +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/AsymmetricUtils/SignatureStandardUtils.cs @@ -0,0 +1,47 @@ +using System.Security; +using SecurityAPICommons.Commons; + +namespace GeneXusCryptography.AsymmetricUtils +{ + [SecuritySafeCritical] + public enum SignatureStandard + { + NONE, CMS, + } + + [SecuritySafeCritical] + public static class SignatureStandardUtils + { + public static SignatureStandard getSignatureStandard(string signatureStandard, + Error error) + { + if (error == null) return SignatureStandard.NONE; + if (signatureStandard == null) + { + error.setError("SS001", "Unrecognized SignatureStandard"); + return SignatureStandard.NONE; + } + switch (signatureStandard.ToUpper(System.Globalization.CultureInfo.InvariantCulture).Trim()) + { + case "CMS": + return SignatureStandard.CMS; + default: + error.setError("SS001", "Unrecognized SignatureStandard"); + return SignatureStandard.NONE; + } + } + + public static string valueOf(SignatureStandard signatureStandard, Error error) + { + if (error == null) return ""; + switch (signatureStandard) + { + case SignatureStandard.CMS: + return "CMS"; + default: + error.setError("SS002", "Unrecognized SignatureStandard"); + return ""; + } + } + } +} diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Commons/IStandardSignerObject.cs b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Commons/IStandardSignerObject.cs new file mode 100644 index 000000000..14fe60cd0 --- /dev/null +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Commons/IStandardSignerObject.cs @@ -0,0 +1,12 @@ +using System.Security; +using GeneXusCryptography.AsymmetricUtils; + +namespace GeneXusCryptography.Commons +{ + [SecuritySafeCritical] + public interface IStandardSignerObject + { + string Sign(string plaintText, SignatureStandardOptions signatureStandardOptions); + bool Verify(string signed, string plainText, SignatureStandardOptions signatureStandardOptions); + } +} diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/GeneXusCryptography.csproj b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/GeneXusCryptography.csproj index f97230852..e5f90a379 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/GeneXusCryptography.csproj +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/GeneXusCryptography.csproj @@ -6,10 +6,10 @@ CA1031, CA1801, CA1724 GeneXus.SecurityApi.Cryptography + + + - - - \ No newline at end of file diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Symmetric/SymmetricStreamCipher.cs b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Symmetric/SymmetricStreamCipher.cs index faabdcd93..55b5a09e3 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Symmetric/SymmetricStreamCipher.cs +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/Symmetric/SymmetricStreamCipher.cs @@ -152,9 +152,6 @@ private IStreamCipher getCipherEngine(SymmetricStreamAlgorithm algorithm) case SymmetricStreamAlgorithm.ISAAC: engine = new IsaacEngine(); break; - case SymmetricStreamAlgorithm.VMPC: - engine = new VmpcEngine(); - break; default: this.GetError().setError("SS005", "Cipher " + algorithm + " not recognised."); break; diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/SymmetricUtils/SymmetricStreamAlgorithm.cs b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/SymmetricUtils/SymmetricStreamAlgorithm.cs index 203be27da..b1e2c99e4 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/SymmetricUtils/SymmetricStreamAlgorithm.cs +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusCryptography/SymmetricUtils/SymmetricStreamAlgorithm.cs @@ -15,7 +15,7 @@ namespace GeneXusCryptography.SymmetricUtils public enum SymmetricStreamAlgorithm { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - NONE, RC4, HC128, HC256, CHACHA20, SALSA20, XSALSA20, ISAAC, VMPC + NONE, RC4, HC128, HC256, CHACHA20, SALSA20, XSALSA20, ISAAC #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member } @@ -56,8 +56,6 @@ public static SymmetricStreamAlgorithm getSymmetricStreamAlgorithm(String symmet return SymmetricStreamAlgorithm.XSALSA20; case "ISAAC": return SymmetricStreamAlgorithm.ISAAC; - case "VMPC": - return SymmetricStreamAlgorithm.VMPC; default: error.setError("SSA01", "Unrecognized SymmetricStreamAlgorithm"); return SymmetricStreamAlgorithm.NONE; @@ -88,8 +86,6 @@ public static String valueOf(SymmetricStreamAlgorithm symmetrcStreamAlgorithm, E return "XSALSA20"; case SymmetricStreamAlgorithm.ISAAC: return "ISAAC"; - case SymmetricStreamAlgorithm.VMPC: - return "VMPC"; default: error.setError("SSA02", "Unrecognized SymmetricStreamAlgorithm"); return "Unrecognized algorithm"; @@ -133,11 +129,6 @@ public static int[] getKeySize(SymmetricStreamAlgorithm algorithm, Error error) keySize[1] = 32; keySize[2] = 8192; break; - case SymmetricStreamAlgorithm.VMPC: - keySize[0] = 0; - keySize[1] = 8; - keySize[2] = 6144; - break; default: error.setError("SSA03", "Unrecognized SymmetricStreamAlgorithm"); break; @@ -164,7 +155,6 @@ internal static bool usesIV(SymmetricStreamAlgorithm algorithm, Error error) case SymmetricStreamAlgorithm.SALSA20: case SymmetricStreamAlgorithm.CHACHA20: case SymmetricStreamAlgorithm.XSALSA20: - case SymmetricStreamAlgorithm.VMPC: return true; default: error.setError("SSA04", "Unrecognized SymmetricStreamAlgorithm"); diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusJWT/GeneXusJWT.csproj b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusJWT/GeneXusJWT.csproj index 02dd37267..8d481a451 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusJWT/GeneXusJWT.csproj +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusJWT/GeneXusJWT.csproj @@ -16,12 +16,12 @@ - - - + + + + - - + diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusXmlSignature/GeneXusXmlSignature.csproj b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusXmlSignature/GeneXusXmlSignature.csproj index 5cff1ade1..80c197f05 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusXmlSignature/GeneXusXmlSignature.csproj +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/GeneXusXmlSignature/GeneXusXmlSignature.csproj @@ -6,9 +6,11 @@ CA1031 GeneXus.SecurityApi.XmlSignature + + + - diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/SecurityAPICommons/Keys/PrivateKeyManager.cs b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/SecurityAPICommons/Keys/PrivateKeyManager.cs index 968c884e0..bbef4ad24 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/SecurityAPICommons/Keys/PrivateKeyManager.cs +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/SecurityAPICommons/Keys/PrivateKeyManager.cs @@ -20,6 +20,7 @@ using Org.BouncyCastle.Utilities.Encoders; using System.Security.AccessControl; using System.Globalization; +using System.Runtime.InteropServices; namespace SecurityAPICommons.Keys { @@ -220,8 +221,27 @@ public AsymmetricAlgorithm getPrivateKeyForXML() byte[] serializedPrivateBytes = this.privateKeyInfo.ToAsn1Object().GetDerEncoded(); string serializedPrivate = Convert.ToBase64String(serializedPrivateBytes); RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(serializedPrivate)); + + #if NETCORE - return DotNetUtilities.ToRSA(privateKey); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return DotNetUtilities.ToRSA(privateKey); + } + else + { + try + { + RSA rsa = RSA.Create(); + rsa.ImportPkcs8PrivateKey(serializedPrivateBytes, out int outthing); + return rsa; + }catch(Exception e ) + { + this.error.setError("PK026", e.Message); + return null; + } + } + #else diff --git a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/SecurityAPICommons/SecurityAPICommons.csproj b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/SecurityAPICommons/SecurityAPICommons.csproj index dcc841d69..3b984fcf5 100644 --- a/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/SecurityAPICommons/SecurityAPICommons.csproj +++ b/dotnet/src/extensions/SecurityAPI/dotnet/dotnetframework/SecurityAPICommons/SecurityAPICommons.csproj @@ -7,7 +7,7 @@ - + diff --git a/dotnet/src/extensions/SecurityAPI/test/dotnetcore/SecurityAPITestNetCore/SecurityAPITestNetCore.csproj b/dotnet/src/extensions/SecurityAPI/test/dotnetcore/SecurityAPITestNetCore/SecurityAPITestNetCore.csproj index afda7fe55..a0786cbb3 100644 --- a/dotnet/src/extensions/SecurityAPI/test/dotnetcore/SecurityAPITestNetCore/SecurityAPITestNetCore.csproj +++ b/dotnet/src/extensions/SecurityAPI/test/dotnetcore/SecurityAPITestNetCore/SecurityAPITestNetCore.csproj @@ -15,6 +15,7 @@ + diff --git a/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/Asymmetric/TestRSAStandardSigning.cs b/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/Asymmetric/TestRSAStandardSigning.cs new file mode 100644 index 000000000..5d96be2f5 --- /dev/null +++ b/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/Asymmetric/TestRSAStandardSigning.cs @@ -0,0 +1,349 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GeneXusCryptography.Asymmetric; +using GeneXusCryptography.AsymmetricUtils; +using NUnit.Framework; +using SecurityAPICommons.Config; +using SecurityAPICommons.Keys; +using SecurityAPITest.SecurityAPICommons.commons; + +namespace SecurityAPITest.Cryptography.Asymmetric +{ + [TestFixture] + public class TestRSAStandardSigning: SecurityAPITestObject + { + private static string path_RSA_sha1_1024; + private static string path_RSA_sha256_1024; + private static string path_RSA_sha256_2048; + private static string path_RSA_sha512_2048; + private static string[] encodings; + private static EncodingUtil eu; + + private static string plainText; + + public static string alias; + public static string password; + + [SetUp] + public virtual void SetUp() + { + + path_RSA_sha1_1024 = Path.Combine(BASE_PATH, "dummycerts", "RSA_sha1_1024"); + path_RSA_sha256_1024 = Path.Combine(BASE_PATH, "dummycerts", "RSA_sha256_1024"); + path_RSA_sha256_2048 = Path.Combine(BASE_PATH, "dummycerts", "RSA_sha256_2048"); + path_RSA_sha512_2048 = Path.Combine(BASE_PATH, "dummycerts", "RSA_sha512_2048"); + + //arrayPaddings = new String[] { "OAEPPADDING", "PCKS1PADDING", "ISO97961PADDING" }; + + plainText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam venenatis ex sit amet risus pellentesque, a faucibus quam ultrices. Ut tincidunt quam eu aliquam maximus. Quisque posuere risus at erat blandit eleifend. Curabitur viverra rutrum volutpat. Donec quis quam tellus. Aenean fermentum elementum augue, a semper risus scelerisque sit amet. Nullam vitae sapien vitae dui ullamcorper dapibus quis quis leo. Sed neque felis, pellentesque in risus et, lobortis ultricies nulla. Quisque quis quam risus. Donec vestibulum, lectus vel vestibulum eleifend, velit ante volutpat lacus, ut mattis quam ligula eget est. Sed et pulvinar lectus. In mollis turpis non ipsum vehicula, sit amet rutrum nibh dictum. Duis consectetur convallis ex, eu ultricies enim bibendum vel. Vestibulum vel libero nibh. Morbi nec odio mattis, vestibulum quam blandit, pretium orci.Aenean pellentesque tincidunt nunc a malesuada. Etiam gravida fermentum mi, at dignissim dui aliquam quis. Nullam vel lobortis libero. Phasellus non gravida posuere"; + + + alias = "1"; + password = "dummy"; + + encodings = new string[] { "UTF_8", "UTF_16", "UTF_16BE", "UTF_16LE", "UTF_32", "UTF_32BE", "UTF_32LE", "SJIS", "GB2312" }; + + eu = new EncodingUtil(); + } + + private void bulkTest(SignatureStandardOptions options) + { + for (int i = 0; i < encodings.Length; i++) + { + eu.setEncoding(encodings[i]); + test(options); + } + } + private void test(SignatureStandardOptions options) + { + options.SetEncapsulated(true); + StandardSigner signer = new StandardSigner(); + string signed_encapsulated = signer.Sign(plainText, options); + bool result_encapsulated = signer.Verify(signed_encapsulated, "", options); + Assert.IsTrue(result_encapsulated); + True(result_encapsulated, signer); + + options.SetEncapsulated(false); + string signed = signer.Sign(plainText, options); + bool result = signer.Verify(signed, plainText, options); + Assert.IsTrue(result); + True(result, signer); + } + + [Test] + public void Test_sha1_1024_DER() + { + + string pathKey = Path.Combine(path_RSA_sha1_1024, "sha1d_key.pem"); + string pathCert = Path.Combine(path_RSA_sha1_1024, "sha1_cert.crt"); + PrivateKeyManager key = new PrivateKeyManager(); + key.Load(pathKey); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + } + + [Test] + public void Test_sha1_1024_PEM() + { + string pathKey = Path.Combine(path_RSA_sha1_1024, "sha1d_key.pem"); + string pathCert = Path.Combine(path_RSA_sha1_1024, "sha1_cert.pem"); + PrivateKeyManager key = new PrivateKeyManager(); + key.Load(pathKey); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha1_1024_PKCS12() + { + string pathKey = Path.Combine(path_RSA_sha1_1024, "sha1_cert.p12"); + string pathCert = Path.Combine(path_RSA_sha1_1024, "sha1_cert.p12"); + PrivateKeyManager key = new PrivateKeyManager(); + key.LoadPKCS12(pathKey, alias, password); + CertificateX509 cert = new CertificateX509(); + cert.LoadPKCS12(pathCert, alias, password); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha256_1024_DER() + { + string pathKey = Path.Combine(path_RSA_sha256_1024, "sha256d_key.pem"); + string pathCert = Path.Combine(path_RSA_sha256_1024, "sha256_cert.crt"); + PrivateKeyManager key = new PrivateKeyManager(); + key.Load(pathKey); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha256_1024_PEM() + { + string pathKey = Path.Combine(path_RSA_sha256_1024, "sha256d_key.pem"); + string pathCert = Path.Combine(path_RSA_sha256_1024, "sha256_cert.pem"); + PrivateKeyManager key = new PrivateKeyManager(); + key.Load(pathKey); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha256_1024_PKCS12() + { + string pathKey = Path.Combine(path_RSA_sha256_1024, "sha256_cert.p12"); + string pathCert = Path.Combine(path_RSA_sha256_1024, "sha256_cert.p12"); + PrivateKeyManager key = new PrivateKeyManager(); + key.LoadPKCS12(pathKey, alias, password); + CertificateX509 cert = new CertificateX509(); + cert.LoadPKCS12(pathCert, alias, password); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha256_2048_DER() + { + string pathKey = Path.Combine(path_RSA_sha256_2048, "sha256d_key.pem"); + string pathCert = Path.Combine(path_RSA_sha256_2048, "sha256_cert.crt"); + PrivateKeyManager key = new PrivateKeyManager(); + key.Load(pathKey); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha256_2048_PEM() + { + string pathKey = Path.Combine(path_RSA_sha256_2048, "sha256d_key.pem"); + string pathCert = Path.Combine(path_RSA_sha256_2048, "sha256_cert.pem"); + PrivateKeyManager key = new PrivateKeyManager(); + key.Load(pathKey); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + + [Test] + public void Test_sha256_2048_PKCS12() + { + string pathKey = Path.Combine(path_RSA_sha256_2048, "sha256_cert.p12"); + string pathCert = Path.Combine(path_RSA_sha256_2048, "sha256_cert.p12"); + PrivateKeyManager key = new PrivateKeyManager(); + key.LoadPKCS12(pathKey, alias, password); + CertificateX509 cert = new CertificateX509(); + cert.LoadPKCS12(pathCert, alias, password); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha512_2048_DER() + { + string pathKey = Path.Combine(path_RSA_sha512_2048, "sha512d_key.pem"); + string pathCert = Path.Combine(path_RSA_sha512_2048, "sha512_cert.crt"); + PrivateKeyManager key = new PrivateKeyManager(); + key.Load(pathKey); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha512_2048_PEM() + { + string pathKey = Path.Combine(path_RSA_sha512_2048, "sha512d_key.pem"); + string pathCert = Path.Combine(path_RSA_sha512_2048, "sha512_cert.pem"); + PrivateKeyManager key = new PrivateKeyManager(); + key.Load(pathKey); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + + [Test] + public void Test_sha512_2048_PKCS12() + { + string pathKey = Path.Combine(path_RSA_sha512_2048, "sha512_cert.p12"); + string pathCert = Path.Combine(path_RSA_sha512_2048, "sha512_cert.p12"); + PrivateKeyManager key = new PrivateKeyManager(); + key.LoadPKCS12(pathKey, alias, password); + CertificateX509 cert = new CertificateX509(); + cert.LoadPKCS12(pathCert, alias, password); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + } + + [Test] + public void Test_base64() + { + string base64stringCert = "MIIC/DCCAmWgAwIBAgIJAPmCVmfcc0IXMA0GCSqGSIb3DQEBCwUAMIGWMQswCQYDVQQGEwJVWTETMBEGA1UECAwKTW9udGV2aWRlbzETMBEGA1UEBwwKTW9udGV2aWRlbzEQMA4GA1UECgwHR2VuZVh1czERMA8GA1UECwwIU2VjdXJpdHkxEjAQBgNVBAMMCXNncmFtcG9uZTEkMCIGCSqGSIb3DQEJARYVc2dyYW1wb25lQGdlbmV4dXMuY29tMB4XDTIwMDcwODE4NDkxNVoXDTI1MDcwNzE4NDkxNVowgZYxCzAJBgNVBAYTAlVZMRMwEQYDVQQIDApNb250ZXZpZGVvMRMwEQYDVQQHDApNb250ZXZpZGVvMRAwDgYDVQQKDAdHZW5lWHVzMREwDwYDVQQLDAhTZWN1cml0eTESMBAGA1UEAwwJc2dyYW1wb25lMSQwIgYJKoZIhvcNAQkBFhVzZ3JhbXBvbmVAZ2VuZXh1cy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMZ8m4ftIhfrdugi5kEszRZr5IRuqGDLTex+CfVnhnBYXyQgJXeCI0eyRYUAbNzw/9MPdFN//pV26AXeH/ajORVu1JVoOACZdNOIPFnwXXh8oBxNxLAYlqoK2rAL+/tns8rKqqS4p8HSat9tj07TUXnsYJmmbXJM/eB94Ex66D1ZAgMBAAGjUDBOMB0GA1UdDgQWBBTfXY8eOfDONCZpFE0V34mJJeCYtTAfBgNVHSMEGDAWgBTfXY8eOfDONCZpFE0V34mJJeCYtTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAAPv7AFlCSpJ32c/VYowlbk6UBhOKmVWBQlrAtvVQYtCKO/y9CEB8ikG19c8lHM9axnsbZR+G7g04Rfuiea3T7VPkSmUXPpz5fl6Zyk4LZg5Oji7MMMXGmr+7cpYWRhifCVwoxSgZEXt3d962IZ1Wei0LMO+4w4gnzPxqr8wVHnT"; + string base64stringKey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMZ8m4ftIhfrdugi5kEszRZr5IRuqGDLTex+CfVnhnBYXyQgJXeCI0eyRYUAbNzw/9MPdFN//pV26AXeH/ajORVu1JVoOACZdNOIPFnwXXh8oBxNxLAYlqoK2rAL+/tns8rKqqS4p8HSat9tj07TUXnsYJmmbXJM/eB94Ex66D1ZAgMBAAECgYA1xrTs0taV3HnO0wXHSrgWBw1WxBRihTKLjGpuTqoh7g943izIgD3GwwoKyt6zzafCK0G9DcSQAjNCw7etPvPL3FxwhDl+AHSv9JcChk/auICtMWwjurG4npto+s3byj/N00Idpz1xuOgKd8k9sdoPBGKa8l+LL+adSXzoivLG8QJBAPDvbOLSs9petB2iM6w5/DiC8EoxqDaBc7I1JFCvPOfB7i1GFFxkQ7hlgxpvaPX3NHXjAZpgdOW68P/SjU0izKsCQQDS5bjrNo3xn/MbYKojzwprR/Bo8Kvbi4/2M9NE3GwHegVmx5I+df+J0aObrbBNPLs/rhrFtt12OtgxJaac+FYLAkEA8DUUbvO4wj7m/iBnug65irHo1V+6oFThv0tCIHsFkt4DEvoqdI62AZKbafCnSYqjr+CaCYqfIScG/Vay77OBLwJBAI8EYAmKPmn7+SW4wMh1z+/+ogbYJwNEOoVQkdXh0JSlZ+JSNleLN5ajhtq8x5EpPSYrEFbB8p8JurBhgwJx2g8CQQDrp9scoK8eKBJ2p/63xqLGYSN6OZQo/4Lkq3983rmHoDCAp3Bz1zUyxQB3UVyrOj4U44C7RtDNiMSZuCwvjYAI"; + PrivateKeyManager key = new PrivateKeyManager(); + key.FromBase64(base64stringKey); + CertificateX509 cert = new CertificateX509(); + cert.FromBase64(base64stringCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + } + + [Test] + public void Test_sha1_1024_PEM_Encrypted() + { + string pathKey = Path.Combine(path_RSA_sha1_1024, "sha1_key.pem"); + string pathCert = Path.Combine(path_RSA_sha1_1024, "sha1_cert.pem"); + PrivateKeyManager key = new PrivateKeyManager(); + key.LoadEncrypted(pathKey, password); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha256_1024_PEM_Encrypted() + { + string pathKey = Path.Combine(path_RSA_sha256_1024, "sha256_key.pem"); + string pathCert = Path.Combine(path_RSA_sha256_1024, "sha256_cert.pem"); + PrivateKeyManager key = new PrivateKeyManager(); + key.LoadEncrypted(pathKey, password); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha256_2048_PEM_Encrypted() + { + string pathKey = Path.Combine(path_RSA_sha256_2048, "sha256_key.pem"); + string pathCert = Path.Combine(path_RSA_sha256_2048, "sha256_cert.pem"); + PrivateKeyManager key = new PrivateKeyManager(); + key.LoadEncrypted(pathKey, password); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + + [Test] + public void Test_sha512_2048_PEM_Encrypted() + { + string pathKey = Path.Combine(path_RSA_sha512_2048, "sha512_key.pem"); + string pathCert = Path.Combine(path_RSA_sha512_2048, "sha512_cert.pem"); + PrivateKeyManager key = new PrivateKeyManager(); + key.LoadEncrypted(pathKey, password); + CertificateX509 cert = new CertificateX509(); + cert.Load(pathCert); + SignatureStandardOptions options = new SignatureStandardOptions(); + options.SetCertificate(cert); + options.SetPrivateKey(key); + bulkTest(options); + + } + } +} diff --git a/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/PasswordDerivation/TestPasswordDerivation.cs b/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/PasswordDerivation/TestPasswordDerivation.cs index a87fa9079..296801d38 100644 --- a/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/PasswordDerivation/TestPasswordDerivation.cs +++ b/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/PasswordDerivation/TestPasswordDerivation.cs @@ -1,4 +1,4 @@ -using SecurityAPITest.SecurityAPICommons.commons; +using SecurityAPITest.SecurityAPICommons.commons; using System; using NUnit.Framework; using SecurityAPICommons.Config; @@ -78,15 +78,17 @@ public void TestScrypt() string derivated = pd.DoGenerateSCrypt(password, saltScrypt, N, r, p, keyLenght); Assert.AreEqual(expectedScrypt, derivated); Equals(expectedScrypt, derivated, pd); + } - [Test] + /*[Test] public void TestDefaultScrypt() { - string derivated = pd.DoGenerateDefaultSCrypt(password, saltScrypt); - Assert.AreEqual(expectedScrypt, derivated); - Equals(expectedScrypt, derivated, pd); - } + string derivated1 = pd.DoGenerateDefaultSCrypt(password, saltScrypt); + Assert.AreEqual(expectedScrypt, derivated1); + Equals(expectedScrypt, derivated1, pd); + + }*/ [Test] public void TestBcrypt() diff --git a/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/Symmetric/TestStreamEncryption.cs b/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/Symmetric/TestStreamEncryption.cs index 29c007d9a..e90e28551 100644 --- a/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/Symmetric/TestStreamEncryption.cs +++ b/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/Cryptography/Symmetric/TestStreamEncryption.cs @@ -1,4 +1,4 @@ -using SecurityAPITest.SecurityAPICommons.commons; +using SecurityAPITest.SecurityAPICommons.commons; using NUnit.Framework; using SecurityAPICommons.Config; using SecurityAPICommons.Keys; @@ -118,28 +118,6 @@ public void TestISAAC() testBulkAlgorithms("ISAAC", key8192, ""); } - [Test] - public void TestVMPC() - { - // key 8 o 6144, nonce 1...6144 bits - testBulkAlgorithms("VMPC", key8, IV64); - testBulkAlgorithms("VMPC", key8, IV128); - testBulkAlgorithms("VMPC", key8, IV192); - testBulkAlgorithms("VMPC", key8, IV256); - testBulkAlgorithms("VMPC", key8, IV512); - testBulkAlgorithms("VMPC", key8, IV1024); - testBulkAlgorithms("VMPC", key8, IV6144); - - testBulkAlgorithms("VMPC", key6144, IV64); - testBulkAlgorithms("VMPC", key6144, IV128); - testBulkAlgorithms("VMPC", key6144, IV192); - testBulkAlgorithms("VMPC", key6144, IV256); - testBulkAlgorithms("VMPC", key6144, IV512); - testBulkAlgorithms("VMPC", key6144, IV1024); - testBulkAlgorithms("VMPC", key6144, IV6144); - - } - private void testBulkAlgorithms(string algorithm, string key, string IV) { for (int i = 0; i < encodings.Length; i++) diff --git a/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/SecurityAPITest.csproj b/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/SecurityAPITest.csproj index c8be949f4..c9262da04 100644 --- a/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/SecurityAPITest.csproj +++ b/dotnet/src/extensions/SecurityAPI/test/dotnetframework/SecurityAPITest/SecurityAPITest.csproj @@ -369,7 +369,7 @@ PreserveNewest - + PreserveNewest diff --git a/dotnet/src/extensions/mocking/src/MockDBAccess/MockDBAccess.csproj b/dotnet/src/extensions/mocking/src/MockDBAccess/MockDBAccess.csproj index 4487458fe..631daf306 100644 --- a/dotnet/src/extensions/mocking/src/MockDBAccess/MockDBAccess.csproj +++ b/dotnet/src/extensions/mocking/src/MockDBAccess/MockDBAccess.csproj @@ -4,8 +4,13 @@ net6.0;net462 net462 618;1607;1698 + false + + + + diff --git a/dotnet/src/extensions/mocking/test/TestMockDBAccess/TestMockDBAccess.csproj b/dotnet/src/extensions/mocking/test/TestMockDBAccess/TestMockDBAccess.csproj index 94877d3c3..60c55c99a 100644 --- a/dotnet/src/extensions/mocking/test/TestMockDBAccess/TestMockDBAccess.csproj +++ b/dotnet/src/extensions/mocking/test/TestMockDBAccess/TestMockDBAccess.csproj @@ -10,7 +10,7 @@ - + diff --git a/dotnet/test/Directory.Build.props b/dotnet/test/Directory.Build.props index 52a699878..b065977e2 100644 --- a/dotnet/test/Directory.Build.props +++ b/dotnet/test/Directory.Build.props @@ -5,5 +5,6 @@ false $(OutputPath) trx + NU1900;NU1901;NU1902;NU1903;NU1904; diff --git a/dotnet/test/DotNetCoreAttackMitigationTest/DotNetCoreAttackMitigationTest.csproj b/dotnet/test/DotNetCoreAttackMitigationTest/DotNetCoreAttackMitigationTest.csproj index fa3f01cd2..4821322b2 100644 --- a/dotnet/test/DotNetCoreAttackMitigationTest/DotNetCoreAttackMitigationTest.csproj +++ b/dotnet/test/DotNetCoreAttackMitigationTest/DotNetCoreAttackMitigationTest.csproj @@ -14,6 +14,7 @@ + diff --git a/dotnet/test/DotNetCoreAttackMitigationTest/Middleware/RestServiceTest.cs b/dotnet/test/DotNetCoreAttackMitigationTest/Middleware/RestServiceTest.cs index d292393c7..987a51b60 100644 --- a/dotnet/test/DotNetCoreAttackMitigationTest/Middleware/RestServiceTest.cs +++ b/dotnet/test/DotNetCoreAttackMitigationTest/Middleware/RestServiceTest.cs @@ -24,21 +24,19 @@ public RestServiceTest() : base() server.AllowSynchronousIO = true; } - - public async Task TestSimpleRestPost() + [Fact] + public async Task InvalidPostToRestService() { server.AllowSynchronousIO = true; HttpClient client = server.CreateClient(); StringContent body = new StringContent("{\"Image\":\"imageName\",\"ImageDescription\":\"imageDescription\"}"); HttpResponseMessage response = await client.PostAsync("rest/apps/saveimage", body); - Assert.Equal(System.Net.HttpStatusCode.BadRequest, response.StatusCode); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } - - public async Task RunController() + [Fact] + public async Task ValidPostToRestService() { - - - CookieContainer cookies = new System.Net.CookieContainer(); + CookieContainer cookies = new CookieContainer(); HttpClient client = server.CreateClient(); string requestUri = "rest/apps/append"; Uri requestUriObj = new Uri("http://localhost/" + requestUri); @@ -51,43 +49,76 @@ public async Task RunController() foreach (var item in SetCookieHeaderValue.ParseList(values.ToList())) cookies.Add(requestUriObj, new Cookie(item.Name.Value, item.Value.Value, item.Path.Value)); - var setCookie = SetCookieHeaderValue.ParseList(values.ToList()).FirstOrDefault(t => t.Name.Equals(HttpHeader.X_CSRF_TOKEN_COOKIE, StringComparison.OrdinalIgnoreCase)); + SetCookieHeaderValue setCookie = SetCookieHeaderValue.ParseList(values.ToList()).FirstOrDefault(t => t.Name.Equals(HttpHeader.X_CSRF_TOKEN_COOKIE, StringComparison.OrdinalIgnoreCase)); + Assert.NotNull(setCookie); + Assert.True(setCookie.Value.HasValue); csrfToken = setCookie.Value.Value; response.EnsureSuccessStatusCode(); - Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); //When failed, turn on log.config to see server side error. + Assert.Equal(HttpStatusCode.OK, response.StatusCode); //When failed, turn on log.config to see server side error. StringContent body = new StringContent("{\"Image\":\"imageName\",\"ImageDescription\":\"imageDescription\"}"); client.DefaultRequestHeaders.Add(HttpHeader.X_CSRF_TOKEN_HEADER, csrfToken); client.DefaultRequestHeaders.Add("Cookie", values);// //cookies.GetCookieHeader(requestUriObj)); response = await client.PostAsync("rest/apps/saveimage", body); - Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + [Fact] + public async Task ValidPostToHttpService() + { + CookieContainer cookies = new CookieContainer(); + HttpClient client = server.CreateClient(); + string requestUri = "webhook.aspx"; + Uri requestUriObj = new Uri("http://localhost/" + requestUri); + HttpResponseMessage response = await client.GetAsync(requestUri); + string csrfToken = string.Empty; + + IEnumerable values; + Assert.True(response.Headers.TryGetValues("Set-Cookie", out values)); + + foreach (var item in SetCookieHeaderValue.ParseList(values.ToList())) + cookies.Add(requestUriObj, new Cookie(item.Name.Value, item.Value.Value, item.Path.Value)); + + SetCookieHeaderValue setCookie = SetCookieHeaderValue.ParseList(values.ToList()).FirstOrDefault(t => t.Name.Equals(HttpHeader.X_CSRF_TOKEN_COOKIE, StringComparison.OrdinalIgnoreCase)); + Assert.NotNull(setCookie); + Assert.True(setCookie.Value.HasValue); + csrfToken = setCookie.Value.Value; + + response.EnsureSuccessStatusCode(); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); //When failed, turn on log.config to see server side error. + + client.DefaultRequestHeaders.Add(HttpHeader.X_CSRF_TOKEN_HEADER, csrfToken); + client.DefaultRequestHeaders.Add("Cookie", values);// //cookies.GetCookieHeader(requestUriObj)); + + response = await client.PostAsync(requestUri, null); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); } - public async Task HttpFirstPost() + [Fact] + public async Task InvalidPostToHttpService() { HttpClient client = server.CreateClient(); HttpResponseMessage response = await client.PostAsync("webhook.aspx", null); IEnumerable cookies = response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value; - foreach (string cookie in cookies) + if (cookies != null) { - Assert.False(cookie.StartsWith(HttpHeader.X_CSRF_TOKEN_COOKIE)); + foreach (string cookie in cookies) + { + Assert.False(cookie.StartsWith(HttpHeader.X_CSRF_TOKEN_COOKIE)); + } } - response.EnsureSuccessStatusCode(); - Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.Contains("InvalidCSRFToken", response.ReasonPhrase, StringComparison.OrdinalIgnoreCase); } - public async Task HttpFirstGet() + [Fact] + public async Task GetToHttpService() { HttpClient client = server.CreateClient(); HttpResponseMessage response = await client.GetAsync("webhook.aspx"); IEnumerable cookies = response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value; - foreach (string cookie in cookies) - { - Assert.False(cookie.StartsWith(HttpHeader.X_CSRF_TOKEN_COOKIE)); - } - + Assert.Contains(cookies, x => x.StartsWith(HttpHeader.X_CSRF_TOKEN_COOKIE, StringComparison.OrdinalIgnoreCase)); response.EnsureSuccessStatusCode(); - Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); } } } diff --git a/dotnet/test/DotNetCoreAttackMitigationTest/appsettings.json b/dotnet/test/DotNetCoreAttackMitigationTest/appsettings.json index 4eb415947..f97425b16 100644 --- a/dotnet/test/DotNetCoreAttackMitigationTest/appsettings.json +++ b/dotnet/test/DotNetCoreAttackMitigationTest/appsettings.json @@ -50,7 +50,7 @@ "CACHE_INVALIDATION_TOKEN": "20216211291931", "CORS_ALLOW_ORIGIN": "https://normal-website.com", "MY_CUSTOM_PTY": "DEFAULT_VALUE", - "ValidateCSRF": "1" + "CSRF_PROTECTION": "1" }, "languages": { "English": { diff --git a/dotnet/test/DotNetCoreChunkedTest/DotNetCoreChunkedTest.csproj b/dotnet/test/DotNetCoreChunkedTest/DotNetCoreChunkedTest.csproj index 4e4d04086..09b55c7b9 100644 --- a/dotnet/test/DotNetCoreChunkedTest/DotNetCoreChunkedTest.csproj +++ b/dotnet/test/DotNetCoreChunkedTest/DotNetCoreChunkedTest.csproj @@ -14,11 +14,12 @@ + - - + + diff --git a/dotnet/test/DotNetCoreCmdTest/DotNetCoreCmdTest.csproj b/dotnet/test/DotNetCoreCmdTest/DotNetCoreCmdTest.csproj index 7d3f1be7d..8b376f5fc 100644 --- a/dotnet/test/DotNetCoreCmdTest/DotNetCoreCmdTest.csproj +++ b/dotnet/test/DotNetCoreCmdTest/DotNetCoreCmdTest.csproj @@ -14,6 +14,7 @@ + diff --git a/dotnet/test/DotNetCoreOpenTelemetryTest/DotNetCoreOpenTelemetryTest.csproj b/dotnet/test/DotNetCoreOpenTelemetryTest/DotNetCoreOpenTelemetryTest.csproj index 03f844c27..6b8ae9e00 100644 --- a/dotnet/test/DotNetCoreOpenTelemetryTest/DotNetCoreOpenTelemetryTest.csproj +++ b/dotnet/test/DotNetCoreOpenTelemetryTest/DotNetCoreOpenTelemetryTest.csproj @@ -14,9 +14,10 @@ + - - + + diff --git a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj index aa96f1c8b..1bf70900a 100644 --- a/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj +++ b/dotnet/test/DotNetCoreUnitTest/DotNetCoreUnitTest.csproj @@ -15,6 +15,7 @@ + @@ -36,7 +37,8 @@ - + + @@ -55,14 +57,12 @@ - - - - + + @@ -74,7 +74,7 @@ all - + @@ -180,10 +180,4 @@ - - - - ..\..\src\dotnetcore\libs\Jayrock.dll - - diff --git a/dotnet/test/DotNetCoreUnitTest/StringUtil/JsonUtilTest.cs b/dotnet/test/DotNetCoreUnitTest/StringUtil/JsonUtilTest.cs new file mode 100644 index 000000000..b40b38c43 --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/StringUtil/JsonUtilTest.cs @@ -0,0 +1,332 @@ +using System; +using System.Collections; +using System.Xml.Serialization; +using GeneXus.Application; +using GeneXus.Utils; +using System.ServiceModel; +using GeneXus.Programs; +#if !NETCORE +using Jayrock.Json; +#endif +using Xunit; + +namespace xUnitTesting +{ + public class JsonUtilTest + { + [Fact] + public void PropertiesSerialization() + { + GXProperties AV9properties = new GXProperties(); + string url = "http://localhost/OuvidoriaOuvidoriaAndroidHomologa/login.aspx?pmsa"; + string user = "mpsa"; + string AV8json = "{\"Url\" : \""+ url + "\",\"User\" : \"" + user + "\"}"; + AV9properties.FromJSonString(AV8json, null); + GxKeyValuePair AV10property = AV9properties.GetFirst(); + Assert.Equal("Url", AV10property.Key); + Assert.Equal(url, AV10property.Value); + AV10property = AV9properties.GetNext(); + Assert.Equal("User", AV10property.Key); + Assert.Equal(user, AV10property.Value); + + } + [Fact] + public void StringSerialization() + { + string result = JSONHelper.WriteJSON("myvalue"); + Assert.Equal("myvalue", result); + + } + [Fact] + public void SerializationFloatingNumbers() + { + SdtSDTGeneric genericSdt = new SdtSDTGeneric(); + genericSdt.gxTpr_Itemnum = 1; + genericSdt.gxTpr_Itemchar = "Caso Base"; + genericSdt.gxTpr_Itemgeopoint = new Geospatial("POINT(-56.248367 -34.873821)"); + genericSdt.gxTpr_Itemgeography = new Geospatial("-34.873821, -56.248367"); + string json = genericSdt.ToJSonString(); + string expectedJson = "{\"ItemNum\":1,\"ItemChar\":\"Caso Base\",\"ItemGeoPoint\":\"POINT (-56.248367 -34.873821)\",\"ItemGeography\":\"\",\"ItemGEolocation\":\"\"}"; + Assert.Equal(expectedJson, json); + } + [Fact] + public void DeserializationInvalidJsonNoDuplicateError() + { + string invalidJson1 = "{\"id\":1,\"name\":\"uno\",\"date\":\"2016-02-24\"'"; + GXBaseCollection messages = new GXBaseCollection(); + JSONHelper.ReadJSON(invalidJson1, messages); + string errMessage = messages.ToJSonString(); +#if NETCORE + string expectedError = "[{\"Id\":\"FromJson Error\",\"Type\":1,\"Description\":\"''' is invalid after a value. Expected either ',', '}', or ']'. Path: $.date | LineNumber: 0 | BytePositionInLine: 40.\"}]"; +#else + string expectedError = "[{\"Id\":\"FromJson Error\",\"Type\":1,\"Description\":\"Expected a ',' or '}'.\"}]"; +#endif + Assert.Equal(expectedError, errMessage); + } + + [Fact] + public void SerializationWithDateTimes() + { + object[] parms = new object[] + { + new DateTime(2016, 6, 29, 0, 0, 0), + new DateTime(2016, 6, 29, 12, 12, 0), + true + }; + JArray jarray = new JArray(parms); + string json = jarray.ToString(); + Assert.Equal("[\"06/29/2016 00:00:00\",\"06/29/2016 12:12:00\",true]", json); + + JObject jObject = new JObject(); + jObject.Put("datetime", new DateTime(2016, 6, 29, 12, 12, 0)); + json = jObject.ToString(); + Assert.Equal("{\"datetime\":\"06/29/2016 12:12:00\"}", json); + } + [Fact] + public void DeserializationWithDateTimes() + { + string json = "{\"parms\": [\"2016/06/29 00:00:00\", \"2016/06/29 12:12:00\", true]}"; + JObject jObj = JSONHelper.ReadJSON(json); + string parm = (string)((JArray)jObj["parms"])[1]; + GxContext gxContext = new GxContext(); + DateTime dParm = gxContext.localUtil.CToT(parm, 0, 0); + Assert.Equal(new DateTime(2016, 6, 29, 12, 12, 0), dParm); + } + [Fact] + public void SerializationWithNumbers() + { + JObject jObject = new JObject(); + jObject.Put("gridId", 2); + string json = JSONHelper.WriteJSON(jObject); + Assert.Contains(":2", json, StringComparison.OrdinalIgnoreCase); + } + [Fact] + public void DeserializationWithNumbers() + { + string json = "{\"MPage\":false,\"cmpCtx\":\"\",\"parms\":[0,\"\",\"\",\"\",[{\"pRow\":\"\",\"c\":[],\"v\":null}],\"\",\"0\",{\"gridRC\":53903859.090,\"hsh\":true,\"grid\":2},\"0\",null],\"hsh\":[],\"objClass\":\"testsac42351\",\"pkgName\":\"GeneXus.Programs\",\"events\":[\"'BTN_SEARCH'\"],\"grids\":{\"Grid1\":{\"id\":2,\"lastRow\":0,\"pRow\":\"\"}}}"; + JObject jObj = JSONHelper.ReadJSON(json); + JObject parm = (JObject)((JArray)jObj["parms"])[7]; + Assert.NotNull(parm); + int gridId = (int)(parm["grid"]); + Assert.Equal(2, gridId); + decimal gridRC = Convert.ToDecimal(parm["gridRC"]); + Assert.Equal(53903859.090M, gridRC); + bool hash = (bool)parm["hsh"]; + Assert.True(hash); + } + + [Fact] + public void DeserializationWithBigDecimalsTest_Issue70446() + { + decimal expectedBigdecimal = 53903859.09090909M; + GxContext context = new GxContext(); + SdtSDTteste sdt = new SdtSDTteste(context); + string json = "{\"testeID\":1,\"testeName\":\"um\",\"testeNumero\":53903859.09090909}"; + sdt.FromJSonString(json, null); + Assert.Equal(expectedBigdecimal, sdt.gxTpr_Testenumero); + Assert.Equal(1, sdt.gxTpr_Testeid); + Assert.Equal("um", sdt.gxTpr_Testename); + } + [Fact] + public void SerializationWithBigDecimalsTest_Issue70446() + { + decimal expectedBigdecimal = 53903859.09090909M; + GxContext context = new GxContext(); + SdtSDTteste sdt = new SdtSDTteste(context); + sdt.gxTpr_Testeid = 1; + sdt.gxTpr_Testename = "um"; + sdt.gxTpr_Testenumero = expectedBigdecimal; + + string expectedJson = "{\"testeID\":1,\"testeName\":\"um\",\"testeNumero\":\"53903859.09090909\"}"; + string json = sdt.ToJSonString(); + Assert.Equal(expectedJson, json); + } + [Fact] + public void SerializationWithSpecialCharacters_Issue69271() + { + string specialCharacters = $"1:{StringUtil.Chr(30)}:1-2:{StringUtil.Chr(29)}:2"; + GxContext context = new GxContext(); + SdtSDTteste sdt = new SdtSDTteste(context); + string json = "{\"testeID\":1,\"testeName\":\"" + StringUtil.JSONEncode(specialCharacters) + "\"}"; + sdt.FromJSonString(json, null); + Assert.Equal(specialCharacters, sdt.gxTpr_Testename); + } + + [Fact] + public void SerializationWithSpecialCharacters_js() + { + JObject jObject = new JObject(); + jObject.Put("Grid1ContainerData", "{\"GridName\":\"Links\",\"CmpContext\":\"MPW0020\",\"Caption\":\"One+Two<>&\"}"); + string json = JSONHelper.WriteJSON(jObject); + Assert.Contains("\\\"GridName\\\"", json, StringComparison.OrdinalIgnoreCase); + } + [Fact] + public void DeserializationWithSpecialCharacters_js() + { + string json="{\"GridName\":\"Links\",\"CmpContext\":\"MPW0020\",\"Caption\":\"One+Two<>&\"}"; + JObject jObject = JSONHelper.ReadJSON(json); + string caption = (string)jObject["Caption"]; + Assert.Equal("One+Two<>&", caption); + } + + [Fact] + public void SerializationPropertiesKeepOrder() + { + string json = JSONHelper.WriteJSON(GetJSONObject()); + Assert.Equal("{\"ClientId\":0,\"ClientName\":\"John\",\"ClientActive\":false}", json); + } + object GetJSONObject() + { + JObject jObject = new JObject(); + jObject.Put("ClientId", 0); + jObject.Put("ClientName", "John"); + jObject.Put("ClientActive", false); + return jObject; + } + [Fact] + public void DeserializationTrailingCommasCompatibility() + { + string json = "[[\"Client\",\"5a4ff115ab7e9d0f6c290b4ef33e34ce\"],[\"Invoice\",\"6c656e4034128018024144afdcc756aa\"],[\"InvoiceLine\",\"cd07f8e2bc014e27340005d9f26ec626\"],[\"Product\",\"84da0573bd448e9761ce20a4c4f9fce1\"],[\"ClientAutonumber\",\"479484926ef142cc026b362e2579a9f3\"],[\"Orders\",\"d857b7ff7dbf9329047b52918f7c5cac\"],[\"TiposDeDatos\",\"0c9a48d30dc26bd787ce466f235a5b3f\"],]"; + JArray array = JSONHelper.ReadJSON(json); + Assert.Equal(7, array.Count); + } +#if NETCORE + [Fact] + public void DeserializationTwoLevels() + { + string json = "{\"Name\":\"MainName\",\"Level\":[{\"LevelName\":\"LevelName1\"}, {\"LevelName\":\"LevelName2\"}]}"; + GxContext context = new GxContext(); + SdtSDTTwoLevels sdt = new SdtSDTTwoLevels(context); + sdt.FromJSonString(json, null); + Assert.Equal("MainName", sdt.gxTpr_Name); + Assert.Equal("LevelName1",((SdtSDTTwoLevels_LevelItem)sdt.gxTpr_Level.Item(1)).gxTpr_Levelname); + Assert.Equal("LevelName2", ((SdtSDTTwoLevels_LevelItem)sdt.gxTpr_Level.Item(2)).gxTpr_Levelname); + } +#endif + } + + [XmlSerializerFormat] + [XmlRoot(ElementName = "SDTteste")] + [XmlType(TypeName = "SDTteste", Namespace = "JsonSerialization")] + [Serializable] + public class SdtSDTteste : GxUserType + { + public SdtSDTteste() + { + /* Constructor for serialization */ + gxTv_SdtSDTteste_Testename = ""; + + } + public SdtSDTteste(IGxContext context) + { + this.context = context; + initialize(); + } + #region Json + private static Hashtable mapper; + public override string JsonMap(string value) + { + if (mapper == null) + { + mapper = new Hashtable(); + } + return (string)mapper[value]; ; + } + public override void ToJSON() + { + ToJSON(true); + return; + } + public override void ToJSON(bool includeState) + { + AddObjectProperty("testeID", gxTpr_Testeid, false); + AddObjectProperty("testeName", gxTpr_Testename, false); + AddObjectProperty("testeNumero", StringUtil.LTrim(StringUtil.Str(gxTpr_Testenumero, 21, 8)), false); + return; + } + #endregion + + #region Properties + + [SoapElement(ElementName = "testeID")] + [XmlElement(ElementName = "testeID")] + public short gxTpr_Testeid + { + get + { + return gxTv_SdtSDTteste_Testeid; + } + set + { + gxTv_SdtSDTteste_Testeid = value; + SetDirty("Testeid"); + } + } + + [SoapElement(ElementName = "testeName")] + [XmlElement(ElementName = "testeName")] + public string gxTpr_Testename + { + get + { + return gxTv_SdtSDTteste_Testename; + } + set + { + gxTv_SdtSDTteste_Testename = value; + SetDirty("Testename"); + } + } + [SoapElement(ElementName = "testeNumero")] + [XmlElement(ElementName = "testeNumero")] + public string gxTpr_Testenumero_double + { + get + { + return Convert.ToString(gxTv_SdtSDTteste_Testenumero, System.Globalization.CultureInfo.InvariantCulture); + } + set + { + gxTv_SdtSDTteste_Testenumero = Convert.ToDecimal(value, System.Globalization.CultureInfo.InvariantCulture); + } + } + [SoapIgnore] + [XmlIgnore] + public decimal gxTpr_Testenumero + { + get + { + return gxTv_SdtSDTteste_Testenumero; + } + set + { + gxTv_SdtSDTteste_Testenumero = value; + SetDirty("Testenumero"); + } + } + public override bool ShouldSerializeSdtJson() + { + return true; + } + + #endregion + + #region Initialization + + public void initialize() + { + gxTv_SdtSDTteste_Testename = ""; + + return; + } + + #endregion + + #region Declaration + + protected short gxTv_SdtSDTteste_Testeid; + protected string gxTv_SdtSDTteste_Testename; + protected decimal gxTv_SdtSDTteste_Testenumero; + #endregion + } +} diff --git a/dotnet/test/DotNetCoreUnitTest/StringUtil/StringUtilTests.cs b/dotnet/test/DotNetCoreUnitTest/StringUtil/StringUtilTests.cs deleted file mode 100644 index c7b1b9225..000000000 --- a/dotnet/test/DotNetCoreUnitTest/StringUtil/StringUtilTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -using GeneXus.Programs; -using GeneXus.Utils; -using Newtonsoft.Json.Linq; -using Xunit; - -namespace xUnitTesting -{ - public class StringUtilTests - { - [Fact] - public void TestJSONEncodeDoNotEncodeGreaterCharacter() - { - string json = ""; - json += ""; - json +=" "; - json +=" "; - json +=" "; - json +=" -88.076680,43.945580 -88.077480,43.945930 -88.078530,43.946390 -88.078960"; - json +=" "; - json +=" #MyLine"; - json +=" "; - json +=""; - json +=""; - - string expectedJsonEncoded = "\\t\\t\\t\\t\\t\\t\\t-88.076680,43.945580 -88.077480,43.945930 -88.078530,43.946390 -88.078960\\t\\t\\t\\t#MyLine\\t"; - string jsonEncoded = StringUtil.JSONEncode(json); - Assert.Equal(jsonEncoded, expectedJsonEncoded); - - } - [Fact] - public void TestFromJsonSDTWithBlankDateTime() - { - SdtSDT1_SDT1Item sdt = new SdtSDT1_SDT1Item(); - Jayrock.Json.JObject json = new Jayrock.Json.JObject(); - json["SDT1_DateTime"] = " 00:00:00"; - json["SDT1_Name"]=string.Empty; - json["SDT1_No"] = 0; - - sdt.FromJSONObject(json); - Assert.Equal(sdt.gxTpr_Sdt1_datetime, DateTimeUtil.NullDate()); - } - - [Fact] - public void TestFromJsonSDTWithTimeMustNotApplyTimezone() - { - SdtSDT1_SDT1Item sdt = new SdtSDT1_SDT1Item(); - Jayrock.Json.JObject json = new Jayrock.Json.JObject(); - json["SDT1_DateTime"] = "2014-04-29T14:29:40"; - json["SDT1_Name"] = string.Empty; - json["SDT1_No"] = 0; - - sdt.FromJSONObject(json); - Assert.Equal(14, sdt.gxTpr_Sdt1_datetime.Hour); - Assert.Equal(29, sdt.gxTpr_Sdt1_datetime.Minute); - } - - - } -} diff --git a/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTGeneric.cs b/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTGeneric.cs new file mode 100644 index 000000000..2ba8db20f --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTGeneric.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections; +using System.Xml.Serialization; +using GeneXus.Application; +using GeneXus.Utils; + + +namespace GeneXus.Programs +{ + [XmlRoot(ElementName="SDTGeneric")] + [XmlType(TypeName="SDTGeneric" , Namespace="TestGeographyDatatype" )] + [Serializable] + public class SdtSDTGeneric : GxUserType + { + public SdtSDTGeneric( ) + { + /* Constructor for serialization */ + gxTv_SdtSDTGeneric_Itemchar = ""; + + gxTv_SdtSDTGeneric_Itemgeopoint = new Geospatial(""); + + gxTv_SdtSDTGeneric_Itemgeography = new Geospatial(""); + + gxTv_SdtSDTGeneric_Itemgeolocation = ""; + + } + + public SdtSDTGeneric(IGxContext context) + { + this.context = context; + initialize(); + } + + #region Json + private static Hashtable mapper; + public override string JsonMap(string value) + { + if (mapper == null) + { + mapper = new Hashtable(); + } + return (string)mapper[value]; ; + } + + public override void ToJSON() + { + ToJSON(true) ; + return; + } + + public override void ToJSON(bool includeState) + { + AddObjectProperty("ItemNum", gxTpr_Itemnum, false); + + + AddObjectProperty("ItemChar", gxTpr_Itemchar, false); + + + AddObjectProperty("ItemGeoPoint", gxTpr_Itemgeopoint, false); + + + AddObjectProperty("ItemGeography", gxTpr_Itemgeography, false); + + + AddObjectProperty("ItemGEolocation", gxTpr_Itemgeolocation, false); + + return; + } + #endregion + + #region Properties + + [SoapElement(ElementName="ItemNum")] + [XmlElement(ElementName="ItemNum")] + public short gxTpr_Itemnum + { + get { + return gxTv_SdtSDTGeneric_Itemnum; + } + set { + gxTv_SdtSDTGeneric_Itemnum = value; + SetDirty("Itemnum"); + } + } + + + + + [SoapElement(ElementName="ItemChar")] + [XmlElement(ElementName="ItemChar")] + public string gxTpr_Itemchar + { + get { + return gxTv_SdtSDTGeneric_Itemchar; + } + set { + gxTv_SdtSDTGeneric_Itemchar = value; + SetDirty("Itemchar"); + } + } + + + + + [SoapElement(ElementName="ItemGeoPoint")] + [XmlElement(ElementName="ItemGeoPoint")] + public Geospatial gxTpr_Itemgeopoint + { + get { + return gxTv_SdtSDTGeneric_Itemgeopoint; + } + set { + gxTv_SdtSDTGeneric_Itemgeopoint = value; + SetDirty("Itemgeopoint"); + } + } + + + + + [SoapElement(ElementName="ItemGeography")] + [XmlElement(ElementName="ItemGeography")] + public Geospatial gxTpr_Itemgeography + { + get { + return gxTv_SdtSDTGeneric_Itemgeography; + } + set { + gxTv_SdtSDTGeneric_Itemgeography = value; + SetDirty("Itemgeography"); + } + } + + + + + [SoapElement(ElementName="ItemGEolocation")] + [XmlElement(ElementName="ItemGEolocation")] + public string gxTpr_Itemgeolocation + { + get { + return gxTv_SdtSDTGeneric_Itemgeolocation; + } + set { + gxTv_SdtSDTGeneric_Itemgeolocation = value; + SetDirty("Itemgeolocation"); + } + } + + + + public override bool ShouldSerializeSdtJson() + { + return true; + } + + + + #endregion + + #region Initialization + + public void initialize( ) + { + gxTv_SdtSDTGeneric_Itemchar = ""; + gxTv_SdtSDTGeneric_Itemgeopoint = new Geospatial(""); + gxTv_SdtSDTGeneric_Itemgeography = new Geospatial(""); + gxTv_SdtSDTGeneric_Itemgeolocation = ""; + return ; + } + + + + #endregion + + #region Declaration + + protected short gxTv_SdtSDTGeneric_Itemnum; + + + protected string gxTv_SdtSDTGeneric_Itemchar; + + + protected Geospatial gxTv_SdtSDTGeneric_Itemgeopoint; + + + protected Geospatial gxTv_SdtSDTGeneric_Itemgeography; + + + protected string gxTv_SdtSDTGeneric_Itemgeolocation; + + + + #endregion + } + +} \ No newline at end of file diff --git a/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTTwoLevels.cs b/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTTwoLevels.cs new file mode 100644 index 000000000..7b7b2a2dc --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTTwoLevels.cs @@ -0,0 +1,227 @@ +/* + File: type_SdtSDTTwoLevels + Description: SDTTwoLevels + Author: Nemo 🐠 for C# (.NET) version 18.0.9.180423 + Program type: Callable routine + Main DBMS: +*/ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Reflection; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using GeneXus.Application; +using GeneXus.Utils; + + +namespace GeneXus.Programs +{ + [XmlRoot(ElementName="SDTTwoLevels")] + [XmlType(TypeName="SDTTwoLevels" , Namespace="MassiveSearch" )] + [Serializable] + public class SdtSDTTwoLevels : GxUserType + { + public SdtSDTTwoLevels( ) + { + /* Constructor for serialization */ + gxTv_SdtSDTTwoLevels_Name = ""; + + } + + public SdtSDTTwoLevels(IGxContext context) + { + this.context = context; + initialize(); + } + + #region Json + private static Hashtable mapper; + public override string JsonMap(string value) + { + if (mapper == null) + { + mapper = new Hashtable(); + } + return (string)mapper[value]; ; + } + + public override void ToJSON() + { + ToJSON(true) ; + return; + } + + public override void ToJSON(bool includeState) + { + AddObjectProperty("Name", gxTpr_Name, false); + + if (gxTv_SdtSDTTwoLevels_Level != null) + { + AddObjectProperty("Level", gxTv_SdtSDTTwoLevels_Level, false); + } + return; + } + #endregion + + #region Properties + + [SoapElement(ElementName="Name")] + [XmlElement(ElementName="Name")] + public string gxTpr_Name + { + get { + return gxTv_SdtSDTTwoLevels_Name; + } + set { + gxTv_SdtSDTTwoLevels_Name = value; + SetDirty("Name"); + } + } + + + + + [SoapElement(ElementName="Level" )] + [XmlArray(ElementName="Level" )] + [XmlArrayItemAttribute(ElementName="LevelItem" , IsNullable=false )] + public GXBaseCollection gxTpr_Level + { + get { + if ( gxTv_SdtSDTTwoLevels_Level == null ) + { + gxTv_SdtSDTTwoLevels_Level = new GXBaseCollection( context, "SDTTwoLevels.LevelItem", ""); + } + return gxTv_SdtSDTTwoLevels_Level; + } + set { + gxTv_SdtSDTTwoLevels_Level_N = false; + gxTv_SdtSDTTwoLevels_Level = value; + SetDirty("Level"); + } + } + + public void gxTv_SdtSDTTwoLevels_Level_SetNull() + { + gxTv_SdtSDTTwoLevels_Level_N = true; + gxTv_SdtSDTTwoLevels_Level = null; + } + + public bool gxTv_SdtSDTTwoLevels_Level_IsNull() + { + return gxTv_SdtSDTTwoLevels_Level == null; + } + public bool ShouldSerializegxTpr_Level_GxSimpleCollection_Json() + { + return gxTv_SdtSDTTwoLevels_Level != null && gxTv_SdtSDTTwoLevels_Level.Count > 0; + + } + + + public override bool ShouldSerializeSdtJson() + { + return true; + } + + + + #endregion + + #region Static Type Properties + [SoapIgnore] + [XmlIgnore] + private static GXTypeInfo _typeProps; + protected override GXTypeInfo TypeInfo { get { return _typeProps; } set { _typeProps = value; } } + #endregion + + #region Initialization + + public void initialize( ) + { + gxTv_SdtSDTTwoLevels_Name = ""; + + gxTv_SdtSDTTwoLevels_Level_N = true; + + return ; + } + + + + #endregion + + #region Declaration + + protected string gxTv_SdtSDTTwoLevels_Name; + + protected bool gxTv_SdtSDTTwoLevels_Level_N; + protected GXBaseCollection gxTv_SdtSDTTwoLevels_Level = null; + + + + #endregion + } + #region Rest interface + [GxUnWrappedJson()] + [DataContract(Name=@"SDTTwoLevels", Namespace="MassiveSearch")] + public class SdtSDTTwoLevels_RESTInterface : GxGenericCollectionItem, System.Web.SessionState.IRequiresSessionState + { + public SdtSDTTwoLevels_RESTInterface( ) : base() + { + } + + public SdtSDTTwoLevels_RESTInterface( SdtSDTTwoLevels psdt ) : base(psdt) + { + } + + #region Rest Properties + [DataMember(Name="Name", Order=0)] + public string gxTpr_Name + { + get { + return StringUtil.RTrim( sdt.gxTpr_Name); + + } + set { + sdt.gxTpr_Name = value; + } + } + + [DataMember(Name="Level", Order=1, EmitDefaultValue=false)] + public GxGenericCollection gxTpr_Level + { + get { + if (sdt.ShouldSerializegxTpr_Level_GxSimpleCollection_Json()) + return new GxGenericCollection(sdt.gxTpr_Level); + else + return null; + + } + set { + value.LoadCollection(sdt.gxTpr_Level); + } + } + + + #endregion + + public SdtSDTTwoLevels sdt + { + get { + return (SdtSDTTwoLevels)Sdt; + } + set { + Sdt = value; + } + } + + [OnDeserializing] + void checkSdt( StreamingContext ctx ) + { + if ( sdt == null ) + { + sdt = new SdtSDTTwoLevels() ; + } + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTTwoLevels_LevelItem.cs b/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTTwoLevels_LevelItem.cs new file mode 100644 index 000000000..76020f9ff --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/StringUtil/type_SdtSDTTwoLevels_LevelItem.cs @@ -0,0 +1,170 @@ +/* + File: type_SdtSDTTwoLevels_LevelItem + Description: Level + Author: Nemo 🐠 for C# (.NET) version 18.0.9.180423 + Program type: Callable routine + Main DBMS: +*/ +using System; +using System.Collections; +using GeneXus.Utils; +using GeneXus.Resources; +using GeneXus.Application; +using GeneXus.Metadata; +using GeneXus.Cryptography; +using GeneXus.Encryption; +using GeneXus.Http.Client; +using GeneXus.Http.Server; +using System.Reflection; +using System.Xml.Serialization; +using System.Runtime.Serialization; +using System.Collections.Concurrent; + + +namespace GeneXus.Programs +{ + [XmlRoot(ElementName="SDTTwoLevels.LevelItem")] + [XmlType(TypeName="SDTTwoLevels.LevelItem" , Namespace="MassiveSearch" )] + [Serializable] + public class SdtSDTTwoLevels_LevelItem : GxUserType + { + public SdtSDTTwoLevels_LevelItem( ) + { + /* Constructor for serialization */ + gxTv_SdtSDTTwoLevels_LevelItem_Levelname = ""; + + } + + public SdtSDTTwoLevels_LevelItem(IGxContext context) + { + this.context = context; + initialize(); + } + + #region Json + private static Hashtable mapper; + public override string JsonMap(string value) + { + if (mapper == null) + { + mapper = new Hashtable(); + } + return (string)mapper[value]; ; + } + + public override void ToJSON() + { + ToJSON(true) ; + return; + } + + public override void ToJSON(bool includeState) + { + AddObjectProperty("LevelName", gxTpr_Levelname, false); + + return; + } + #endregion + + #region Properties + + [SoapElement(ElementName="LevelName")] + [XmlElement(ElementName="LevelName")] + public string gxTpr_Levelname + { + get { + return gxTv_SdtSDTTwoLevels_LevelItem_Levelname; + } + set { + gxTv_SdtSDTTwoLevels_LevelItem_Levelname = value; + SetDirty("Levelname"); + } + } + + + + public override bool ShouldSerializeSdtJson() + { + return true; + } + + + + #endregion + + #region Static Type Properties + [SoapIgnore] + [XmlIgnore] + private static GXTypeInfo _typeProps; + protected override GXTypeInfo TypeInfo { get { return _typeProps; } set { _typeProps = value; } } + #endregion + + #region Initialization + + public void initialize( ) + { + gxTv_SdtSDTTwoLevels_LevelItem_Levelname = ""; + return ; + } + + + + #endregion + + #region Declaration + + protected string gxTv_SdtSDTTwoLevels_LevelItem_Levelname; + + + + #endregion + } + #region Rest interface + [DataContract(Name=@"SDTTwoLevels.LevelItem", Namespace="MassiveSearch")] + public class SdtSDTTwoLevels_LevelItem_RESTInterface : GxGenericCollectionItem, System.Web.SessionState.IRequiresSessionState + { + public SdtSDTTwoLevels_LevelItem_RESTInterface( ) : base() + { + } + + public SdtSDTTwoLevels_LevelItem_RESTInterface( SdtSDTTwoLevels_LevelItem psdt ) : base(psdt) + { + } + + #region Rest Properties + [DataMember(Name="LevelName", Order=0)] + public string gxTpr_Levelname + { + get { + return StringUtil.RTrim( sdt.gxTpr_Levelname); + + } + set { + sdt.gxTpr_Levelname = value; + } + } + + + #endregion + + public SdtSDTTwoLevels_LevelItem sdt + { + get { + return (SdtSDTTwoLevels_LevelItem)Sdt; + } + set { + Sdt = value; + } + } + + [OnDeserializing] + void checkSdt( StreamingContext ctx ) + { + if ( sdt == null ) + { + sdt = new SdtSDTTwoLevels_LevelItem() ; + } + } + } + #endregion +} \ No newline at end of file diff --git a/dotnet/test/DotNetCoreUnitTest/appsettings.json b/dotnet/test/DotNetCoreUnitTest/appsettings.json index 31bce50e5..d38ee5c57 100644 --- a/dotnet/test/DotNetCoreUnitTest/appsettings.json +++ b/dotnet/test/DotNetCoreUnitTest/appsettings.json @@ -49,6 +49,7 @@ "SAMESITE_COOKIE": "Lax", "CACHE_INVALIDATION_TOKEN": "20216211291931", "CORS_ALLOW_ORIGIN": "https://normal-website.com", + "USE_JAYROCK": "0", "MY_CUSTOM_PTY": "DEFAULT_VALUE" }, "languages": { diff --git a/dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj b/dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj index 8dcbb4c25..9e1a026c4 100644 --- a/dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj +++ b/dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj @@ -14,11 +14,12 @@ + - - + + @@ -38,6 +39,7 @@ + @@ -58,6 +60,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/dotnet/test/DotNetCoreWebUnitTest/Middleware/HeadersTest.cs b/dotnet/test/DotNetCoreWebUnitTest/Middleware/HeadersTest.cs new file mode 100644 index 000000000..da4184ca4 --- /dev/null +++ b/dotnet/test/DotNetCoreWebUnitTest/Middleware/HeadersTest.cs @@ -0,0 +1,45 @@ +using System; +using System.Net.Http; +using System.Reflection; +using System.Threading.Tasks; +using GeneXus.Metadata; +using GeneXus.Utils; +using Xunit; +namespace xUnitTesting +{ + public class HeadersTest : MiddlewareTest + { + public HeadersTest() : base() + { + ClassLoader.FindType("apps.httpheaders", "GeneXus.Programs.apps", "httpheaders", Assembly.GetExecutingAssembly(), true);//Force loading assembly for append procedure + server.AllowSynchronousIO = true; + + } + protected override void SetEnvironmentVars() + { + Environment.SetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED", "true", EnvironmentVariableTarget.Process); + + } + [Fact] + public async Task TestForwardedHeaders() + { + const string host = "192.168.1.100"; + const string scheme = "https"; + const string remoteUrl = $"{scheme}:\\/\\/{host}"; + const string passwordWithSpecialCharacters = "mypasswordwithspecialcharacters:!*"; + const string userId = "myuser"; + HttpClient client = server.CreateClient(); + client.DefaultRequestHeaders.Add("X-Forwarded-For", host); + client.DefaultRequestHeaders.Add("X-Forwarded-Proto", scheme); + client.DefaultRequestHeaders.Add("Authorization", $"Basic {StringUtil.ToBase64(userId+ ":" + passwordWithSpecialCharacters)}"); + + HttpResponseMessage response = await client.GetAsync("/rest/apps/httpheaders"); + response.EnsureSuccessStatusCode(); + string resp = await response.Content.ReadAsStringAsync(); + Assert.Contains(remoteUrl, resp, StringComparison.OrdinalIgnoreCase); + Assert.Contains(userId, resp, StringComparison.OrdinalIgnoreCase); + Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + } + + } +} \ No newline at end of file diff --git a/dotnet/test/DotNetCoreWebUnitTest/Middleware/HttpProcTest.cs b/dotnet/test/DotNetCoreWebUnitTest/Middleware/HttpProcTest.cs new file mode 100644 index 000000000..83f9ce60f --- /dev/null +++ b/dotnet/test/DotNetCoreWebUnitTest/Middleware/HttpProcTest.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Reflection; +using System.Threading.Tasks; +using GeneXus.Metadata; +using Xunit; +namespace xUnitTesting +{ + public class HttpProcTest : MiddlewareTest + { + Dictionary parms = new Dictionary(); + FormUrlEncodedContent formUrlEncodedContent; + public HttpProcTest():base() + { + ClassLoader.FindType("aprochttpgetstatic", "GeneXus.Programs", "aprochttpgetstatic", Assembly.GetExecutingAssembly(), true);//Force loading assembly for webhook procedure + server.AllowSynchronousIO=true; + parms.Add("client_id", "SM40d2cbda93b2de0a15df7a1598c7db83"); + parms.Add("refresh_token", "99"); + formUrlEncodedContent = new FormUrlEncodedContent(parms); + } + [Fact] + public async Task HtttpPostTest() + { + HttpClient client = server.CreateClient(); + + HttpResponseMessage response = await client.PostAsync("aprochttpgetstatic.aspx", formUrlEncodedContent);//"application/x-www-form-urlencoded" + response.EnsureSuccessStatusCode(); + string resp = await response.Content.ReadAsStringAsync(); + Assert.NotEmpty(resp); + Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + } + } + +} diff --git a/dotnet/test/DotNetCoreWebUnitTest/Middleware/MiddlewareTest.cs b/dotnet/test/DotNetCoreWebUnitTest/Middleware/MiddlewareTest.cs index 15c2c86c5..a034ebc0b 100644 --- a/dotnet/test/DotNetCoreWebUnitTest/Middleware/MiddlewareTest.cs +++ b/dotnet/test/DotNetCoreWebUnitTest/Middleware/MiddlewareTest.cs @@ -18,6 +18,7 @@ public class MiddlewareTest public MiddlewareTest() { SetEnvironmentVars(); + BeforeStartup(); server = new TestServer(WebHost.CreateDefaultBuilder().UseStartup().UseEnvironment(DOTNET_ENVIRONMENT)); GXRouting.ContentRootPath = Directory.GetCurrentDirectory(); server.PreserveExecutionContext= true; @@ -26,6 +27,10 @@ public MiddlewareTest() protected virtual void SetEnvironmentVars() { + } + protected virtual void BeforeStartup() + { + } protected string GetHeader(HttpResponseMessage response, string headerName) { diff --git a/dotnet/test/DotNetCoreWebUnitTest/Middleware/RestServiceTest.cs b/dotnet/test/DotNetCoreWebUnitTest/Middleware/RestServiceTest.cs index c2e3bec54..49dbbfe4f 100644 --- a/dotnet/test/DotNetCoreWebUnitTest/Middleware/RestServiceTest.cs +++ b/dotnet/test/DotNetCoreWebUnitTest/Middleware/RestServiceTest.cs @@ -49,7 +49,7 @@ public async Task TestSimpleRestPost() response.EnsureSuccessStatusCode(); Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); string responseBody = await response.Content.ReadAsStringAsync(); - Assert.Equal("{}", responseBody); + Assert.Equal("{\"ImagePath\":\"\\/imageName\"}", responseBody); } [Fact(Skip = "Non deterministic")] diff --git a/dotnet/test/DotNetCoreWebUnitTest/apps/aprochttpgetstatic.cs b/dotnet/test/DotNetCoreWebUnitTest/apps/aprochttpgetstatic.cs new file mode 100644 index 000000000..8fef42f0f --- /dev/null +++ b/dotnet/test/DotNetCoreWebUnitTest/apps/aprochttpgetstatic.cs @@ -0,0 +1,111 @@ +using System.Threading.Tasks; +using GeneXus.Application; +using GeneXus.Data.NTier; +using GeneXus.Http.Client; +using GeneXus.Http.Server; +using GeneXus.Procedure; +using GeneXus.Utils; +namespace GeneXus.Programs +{ + public class aprochttpgetstatic : GXWebProcedure + { + protected override async Task WebExecuteAsync( ) + { + context.SetDefaultTheme("HttpClientTest", true); + initialize(); + { + await ExecutePrivateAsync(); + } + await CleanupAsync(); + } + + public aprochttpgetstatic( ) + { + context = new GxContext( ); + DataStoreUtil.LoadDataStores( context); + IsMain = true; + context.SetDefaultTheme("HttpClientTest", true); + } + + public aprochttpgetstatic( IGxContext context ) + { + this.context = context; + IsMain = false; + } + + public void execute( ) + { + initialize(); + ExecuteImpl(); + } + + public void executeSubmit( ) + { + SubmitImpl(); + } + + protected override async Task ExecutePrivateAsync( ) + { + /* GeneXus formulas */ + /* Output device settings */ + AV14grant_type = "refresh_token"; + AV12ClientId = AV10HttpRequest.GetValue("client_id"); + AV16Refresh_Token = AV10HttpRequest.GetValue("refresh_token"); + AV8baseUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"; + AV17baseUrlWithParms = StringUtil.Format( "%1?grant_type=%2&client_id=%3&refresh_token=%4", AV8baseUrl, StringUtil.Trim( AV14grant_type), StringUtil.Trim( AV12ClientId), StringUtil.Trim( AV16Refresh_Token), "", "", "", "", ""); + AV9Httpclient.AddHeader("Content-Type", "application/x-www-form-urlencoded"); + AV9Httpclient.Execute("GET", StringUtil.Trim( AV17baseUrlWithParms)); + AV18Httpclientstr = AV9Httpclient.ToString(); + AV11HttpResponse.AddString(AV18Httpclientstr); + if ( context.WillRedirect( ) ) + { + context.Redirect( context.wjLoc ); + context.wjLoc = ""; + } + await CleanupAsync(); + } + + protected override async Task CleanupAsync( ) + { + CloseCursors(); + base.cleanup(); + if ( IsMain ) + { + await CloseConnectionsAsync(); + } + ExitApp(); + } + + + public override void initialize( ) + { + // GXKey = ""; + //gxfirstwebparm = ""; + AV14grant_type = ""; + AV12ClientId = ""; + AV10HttpRequest = new GxHttpRequest( context); + AV16Refresh_Token = ""; + AV8baseUrl = ""; + AV17baseUrlWithParms = ""; + AV9Httpclient = new GxHttpClient( context); + AV18Httpclientstr = ""; + AV11HttpResponse = new GxHttpResponse( context); + /* GeneXus formulas. */ + } + + //private short gxcookieaux ; + //private string GXKey ; + //private string gxfirstwebparm ; + private string AV14grant_type ; + private string AV12ClientId ; + private string AV16Refresh_Token ; + private string AV17baseUrlWithParms ; + private string AV18Httpclientstr ; + //private bool entryPointCalled ; + private string AV8baseUrl ; + private GxHttpRequest AV10HttpRequest ; + private GxHttpClient AV9Httpclient ; + private GxHttpResponse AV11HttpResponse ; + } + +} diff --git a/dotnet/test/DotNetCoreWebUnitTest/apps/httpheaders.cs b/dotnet/test/DotNetCoreWebUnitTest/apps/httpheaders.cs new file mode 100644 index 000000000..9c0a293a7 --- /dev/null +++ b/dotnet/test/DotNetCoreWebUnitTest/apps/httpheaders.cs @@ -0,0 +1,90 @@ +using System; +using GeneXus.Application; +using GeneXus.Data.NTier; +using GeneXus.Data.NTier.ADO; +using GeneXus.Procedure; +using GeneXus.Utils; + +namespace GeneXus.Programs.apps +{ + public class httpheaders : GXWebProcedure + { + + public httpheaders() + { + context = new GxContext(); + DataStoreUtil.LoadDataStores(context); + IsMain = true; + context.SetDefaultTheme("Carmine"); + } + + public httpheaders(IGxContext context) + { + this.context = context; + IsMain = false; + } + + public void execute(out string result) + { + initialize(); + executePrivate(out result); + } + + void executePrivate(out string result) + { + result = (context.GetHttpSecure() == 1 ? "https://" : "http://") + context.GetRemoteAddress(); + result += StringUtil.NewLine() + GXUtil.UserId(string.Empty, context, pr_default); + cleanup(); + } + + public override void cleanup() + { + CloseOpenCursors(); + base.cleanup(); + if (IsMain) + { + context.CloseConnections(); + } + ExitApp(); + } + + protected void CloseOpenCursors() + { + } + + public override void initialize() + { + pr_default = new DataStoreProvider(context, new httpheaders__default(), + new Object[][] { + } + ); + } + private IDataStoreProvider pr_default; + } + public class httpheaders__default : DataStoreHelperBase, IDataStoreHelper + { + public ICursor[] getCursors() + { + cursorDefinitions(); + return new Cursor[] { + }; + } + + private static CursorDef[] def; + private void cursorDefinitions() + { + if (def == null) + { + def = new CursorDef[] { + }; + } + } + + public void getResults(int cursor, + IFieldGetter rslt, + Object[] buf) + { + } + + } +} diff --git a/dotnet/test/DotNetCoreWebUnitTest/apps/httpheaders.svc b/dotnet/test/DotNetCoreWebUnitTest/apps/httpheaders.svc new file mode 100644 index 000000000..bbfa49ec1 --- /dev/null +++ b/dotnet/test/DotNetCoreWebUnitTest/apps/httpheaders.svc @@ -0,0 +1 @@ +<%@ServiceHost Service= "GeneXus.Programs.apps.httpheaders,apps.httpheaders" %> diff --git a/dotnet/test/DotNetCoreWebUnitTest/apps/saveimage.cs b/dotnet/test/DotNetCoreWebUnitTest/apps/saveimage.cs index e8c415716..3e7ab5e12 100644 --- a/dotnet/test/DotNetCoreWebUnitTest/apps/saveimage.cs +++ b/dotnet/test/DotNetCoreWebUnitTest/apps/saveimage.cs @@ -1,7 +1,8 @@ using GeneXus.Application; using GeneXus.Data.NTier; - +using GeneXus.Http.Server; using GeneXus.Procedure; +using Stubble.Core.Contexts; namespace GeneXus.Programs.apps { public class saveimage : GXProcedure @@ -19,9 +20,10 @@ public saveimage(IGxContext context) IsMain = false; } - public void execute(string aP0_ImageDescription, string aP1_Image) + public void execute(string aP0_ImageDescription, string aP1_Image, out string aP2_ImagePath) { System.Console.WriteLine("SaveImage executed:" + aP0_ImageDescription); + aP2_ImagePath= context.GetScriptPath() + aP1_Image; } public override bool UploadEnabled() @@ -32,8 +34,9 @@ public override bool UploadEnabled() public override void initialize() { - + httpResponse = new GxHttpResponse(context); } + GxHttpResponse httpResponse; } } diff --git a/dotnet/test/DotNetPdfTest/DotNetPDFUnitTest.csproj b/dotnet/test/DotNetPdfTest/DotNetPDFUnitTest.csproj index 43cbea27f..1b5cd1bed 100644 --- a/dotnet/test/DotNetPdfTest/DotNetPDFUnitTest.csproj +++ b/dotnet/test/DotNetPdfTest/DotNetPDFUnitTest.csproj @@ -18,6 +18,8 @@ + + diff --git a/dotnet/test/DotNetPdfTest/PDFReport.ini b/dotnet/test/DotNetPdfTest/PDFReport.ini index 57a516fb5..0f77d4cac 100644 --- a/dotnet/test/DotNetPdfTest/PDFReport.ini +++ b/dotnet/test/DotNetPdfTest/PDFReport.ini @@ -14,7 +14,7 @@ LongDashedStyle= 6;2 EmbeedNotSpecifiedFonts= false Barcode128AsImage= true MarginsInsideBorder= false -OutputFileDirectory= . +OutputFileDirectory= .\temp\ LineCapProjectingSquare= true DottedStyle= 1;2 JustifiedTypeAll= false diff --git a/dotnet/test/DotNetPdfTest/PDFTests.cs b/dotnet/test/DotNetPdfTest/PDFTests.cs index 8996fb004..7e17ac513 100644 --- a/dotnet/test/DotNetPdfTest/PDFTests.cs +++ b/dotnet/test/DotNetPdfTest/PDFTests.cs @@ -1,8 +1,6 @@ using System; using System.IO; -using GeneXus.Configuration; using GeneXus.Programs; -using GeneXus.Utils; using Xunit; namespace UnitTesting @@ -13,13 +11,15 @@ public class PDFTests public void TestITextFormat() { string report = "PDFFormat.pdf"; - if (File.Exists(report)) - File.Delete(report); + string outputFileDirectory = @".\temp\"; + + string reportFullPath = Path.Combine(Directory.GetCurrentDirectory(), outputFileDirectory, report); + if (File.Exists(reportFullPath)) + File.Delete(reportFullPath); try { apdfformats2 test = new apdfformats2(); test.execute(); - longHtml test2 = new longHtml(); test2.execute(); } @@ -29,7 +29,7 @@ public void TestITextFormat() { Console.WriteLine(ex.ToString()); } - Assert.True(File.Exists(report)); + Assert.True(File.Exists(reportFullPath)); } } } diff --git a/dotnet/test/DotNetRedisTest/DotNetRedisTest.csproj b/dotnet/test/DotNetRedisTest/DotNetRedisTest.csproj index c71055ece..7180cd953 100644 --- a/dotnet/test/DotNetRedisTest/DotNetRedisTest.csproj +++ b/dotnet/test/DotNetRedisTest/DotNetRedisTest.csproj @@ -3,7 +3,7 @@ net6.0;net8.0 - + @@ -15,6 +15,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/dotnet/test/DotNetUnitTest/Crypto/SignatureTest.cs b/dotnet/test/DotNetUnitTest/Crypto/SignatureTest.cs new file mode 100644 index 000000000..c84ba792b --- /dev/null +++ b/dotnet/test/DotNetUnitTest/Crypto/SignatureTest.cs @@ -0,0 +1,22 @@ +using System; +using System.Diagnostics; +using System.Web.Configuration; +using GeneXus.Application; +using GeneXus.Utils; +using GeneXus.Web.Security; +using Xunit; + +namespace UnitTesting +{ + public class SignatureTest + { + [Fact] + public void SignSecurityToken() + { + GxContext context = new GxContext(); + string signed = WebSecurityHelper.Sign("WFPROTOTYPER", string.Empty, "Customer.CustomerRegistration", SecureTokenHelper.SecurityMode.Sign, context); + Assert.NotEmpty(signed); + + } + } +} diff --git a/dotnet/test/DotNetUnitTest/Domain/GxGenericDictionaryTest.cs b/dotnet/test/DotNetUnitTest/Domain/GxGenericDictionaryTest.cs new file mode 100644 index 000000000..b76067c32 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/Domain/GxGenericDictionaryTest.cs @@ -0,0 +1,22 @@ +using GeneXus.Utils; +using Xunit; + +namespace xUnitTesting +{ + public class GxGenericDictionaryTest + { + [Fact] + public void ToJsonTest() + { + GxGenericDictionary dic = new GxGenericDictionary + { + { "key1", 1 }, + { "key2", 2 } + }; + string json = dic.ToJson(); + string expectedJson = "{\"key1\":1,\"key2\":2}"; + Assert.Equal(expectedJson, json); + } + + } +} diff --git a/dotnet/test/DotNetUnitTest/Domain/GxHttpClientTest.cs b/dotnet/test/DotNetUnitTest/Domain/GxHttpClientTest.cs index 4dd50cdd7..68f3fa9ab 100644 --- a/dotnet/test/DotNetUnitTest/Domain/GxHttpClientTest.cs +++ b/dotnet/test/DotNetUnitTest/Domain/GxHttpClientTest.cs @@ -1,5 +1,10 @@ +using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; +using System.Net.Http; +using System.Threading.Tasks; using GeneXus.Application; using GeneXus.Http.Client; using Xunit; @@ -13,6 +18,10 @@ namespace xUnitTesting public class GxHttpClientTest { + const int MAX_CONNECTIONS= 5; + public GxHttpClientTest() { + Environment.SetEnvironmentVariable("GX_HTTPCLIENT_MAX_PER_ROUTE", MAX_CONNECTIONS.ToString(), EnvironmentVariableTarget.Process); + } [Fact] public void AddHeaderWithSpecialCharactersDoesNotThrowException() { @@ -27,7 +36,6 @@ public void AddHeaderWithSpecialCharactersDoesNotThrowException() httpclient.Execute("GET", string.Empty); Assert.NotEqual(((int)HttpStatusCode.InternalServerError), httpclient.StatusCode); } - } [Fact] @@ -45,6 +53,141 @@ public void HttpClientInvalidURLWithCustomPort() Assert.NotEqual(((int)HttpStatusCode.InternalServerError), httpclient.StatusCode); } } + + [Fact] + public void HttpClientCookieHeader() + { + string headerValue = "CognitoIdentityServiceProvider.3tgmin25m9bkg6vgi7vpavu7a9.M00000936.refreshToken=eyJjdHkiOiJKV1QiLCJlbmMiSkRCAmMpYqndvORnWLTfHw; CognitoIdentityServiceProvider.3tgmin25m9bkg6vgi7vpavu7a9.LastAuthUser=M00000936"; + string headerName = "Cookie"; + using (GxHttpClient httpclient = new GxHttpClient()) + { + httpclient.AddHeader(headerName, headerValue); + httpclient.Host = "localhost"; + httpclient.Port = 80; + httpclient.BaseURL = @"NotFound/NotFound.php"; + httpclient.HttpClientExecute("GET", string.Empty); + Assert.NotEqual(((int)HttpStatusCode.InternalServerError), httpclient.StatusCode); + } + using (GxHttpClient oldHttpclient = new GxHttpClient()) + { + oldHttpclient.AddHeader(headerName, headerValue); + oldHttpclient.Host = "localhost"; + oldHttpclient.Port = 80; + oldHttpclient.BaseURL = @"NotFound/NotFound.php"; + oldHttpclient.Execute("GET", string.Empty); + Assert.NotEqual(((int)HttpStatusCode.InternalServerError), oldHttpclient.StatusCode); + } + } +#if NETCORE + [Fact(Skip = "For local testing only")] + public void TestHttpClientMaxPoolSize() + { + HttpClientMaxPoolSize().Wait(); + } + async Task HttpClientMaxPoolSize() { + GxContext context = new GxContext(); + string baseUrl = "http://localhost:8082/HttpClientTestNETSQLServer/testcookies.aspx"; + + var tasks = new List(); + + for (int i = 0; i < MAX_CONNECTIONS * 10; i++) + { + string url = $"{baseUrl}?id=" + i; + tasks.Add(Task.Run(() => ExecuteGet(url))); + } + await Task.WhenAll(tasks); + + Assert.Single(GxHttpClient._httpClientInstances); + + HttpClient c = GxHttpClient._httpClientInstances.First().Value; + Assert.NotNull(c); + + Assert.True(pendingRequestsCount <= MAX_CONNECTIONS, $"Active connections ({pendingRequestsCount}) exceed MaxConnectionsPerServer ({MAX_CONNECTIONS})"); + } + static private int pendingRequestsCount = 0; + static private readonly object syncObject = new object(); + static private void IncrementPendingRequestsCount() + { + lock (syncObject) + { + pendingRequestsCount++; + } + } + + static private void DecrementPendingRequestsCount() + { + lock (syncObject) + { + pendingRequestsCount--; + } + } + private string ExecuteGet(string url) + { + GxContext context = new GxContext(); + using (GxHttpClient httpclient = new GxHttpClient(context)) + { + IncrementPendingRequestsCount(); + httpclient.HttpClientExecute("GET", url); + Assert.Equal((int)HttpStatusCode.OK, httpclient.StatusCode); //When failed, turn on log.config to see server side error. + DecrementPendingRequestsCount(); + return httpclient.ToString(); + } + } + +#endif + [Fact(Skip = "For local testing only")] + public void HttpClientCookiesTest() + { + GxContext context = new GxContext(); + string baseUrl = "http://localhost:8082/HttpClientTestNETSQLServer/testcookies.aspx"; + + using (GxHttpClient httpclient = new GxHttpClient(context)) + { + string url = $"{baseUrl}?id=1"; + httpclient.HttpClientExecute("GET", url); + Assert.Equal((int)HttpStatusCode.OK, httpclient.StatusCode); + CookieContainer cookies = context.GetCookieContainer(url, true); + Assert.NotNull(cookies); + CookieCollection responseCookies = cookies.GetCookies(new Uri(url)); + Assert.NotEmpty(responseCookies); + string result = httpclient.ToString(); + Assert.Contains("1", result, StringComparison.OrdinalIgnoreCase); + + } + using (GxHttpClient httpclient = new GxHttpClient(context)) + { + string url = $"{baseUrl}?id=2"; + httpclient.IncludeCookies = true; + httpclient.HttpClientExecute("GET", url); + Assert.Equal((int)HttpStatusCode.OK, httpclient.StatusCode); + string result = httpclient.ToString(); + Assert.StartsWith("Cookie found ", result, StringComparison.OrdinalIgnoreCase); + Assert.Contains("2", result, StringComparison.OrdinalIgnoreCase); + } + using (GxHttpClient httpclient = new GxHttpClient(context)) + { + string url = $"{baseUrl}?id=3"; + httpclient.IncludeCookies = false; + httpclient.HttpClientExecute("GET", url); + Assert.Equal((int)HttpStatusCode.OK, httpclient.StatusCode); + string result = httpclient.ToString(); + Assert.StartsWith("Cookie not found", result, StringComparison.OrdinalIgnoreCase); + Assert.Contains("3", result, StringComparison.OrdinalIgnoreCase); + } + using (GxHttpClient httpclient = new GxHttpClient(context)) + { + string url = "https://www.google.com/"; + httpclient.HttpClientExecute("GET", url); + Assert.Equal((int)HttpStatusCode.OK, httpclient.StatusCode); + CookieContainer cookies = context.GetCookieContainer(url, true); + Assert.NotNull(cookies); + CookieCollection responseCookies = cookies.GetCookies(new Uri(url)); + Assert.NotEmpty(responseCookies); + string result = httpclient.ToString(); + } + + } + #if !NETCORE [Fact] public void NoStoreHeader() diff --git a/dotnet/test/DotNetUnitTest/Domain/XMLWriterTest.cs b/dotnet/test/DotNetUnitTest/Domain/XMLWriterTest.cs index b3e1ce1b0..d428e6764 100644 --- a/dotnet/test/DotNetUnitTest/Domain/XMLWriterTest.cs +++ b/dotnet/test/DotNetUnitTest/Domain/XMLWriterTest.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using GeneXus.Application; using GeneXus.XML; using Xunit; @@ -27,7 +23,7 @@ public void dfwpnumEmptyElementWithoutEndTest() internal string dfwpnumTest(string varcharValue, bool closeElements) { GxContext context = new GxContext(); - string fileName = Path.Combine(BaseDir, "dfwpnumTest.txt"); + string fileName = Path.Combine(BaseDir, "xmlDfwpnumTest.txt"); GXXMLWriter GXSoapXMLWriter = new GXXMLWriter(context.GetPhysicalPath()); GXSoapXMLWriter.Open(fileName); GXSoapXMLWriter.WriteStartDocument("utf-8", 0); diff --git a/dotnet/test/DotNetUnitTest/DotNetUnitTest.csproj b/dotnet/test/DotNetUnitTest/DotNetUnitTest.csproj index ed384f5fb..164d730a1 100644 --- a/dotnet/test/DotNetUnitTest/DotNetUnitTest.csproj +++ b/dotnet/test/DotNetUnitTest/DotNetUnitTest.csproj @@ -10,6 +10,13 @@ + + + + + + + PreserveNewest @@ -51,6 +58,10 @@ + + ..\..\src\dotnetframework\libs\Jayrock-JSON.dll + + ..\..\src\dotnetframework\libs\TZ4Net.dll @@ -62,6 +73,9 @@ + + PreserveNewest + PreserveNewest @@ -71,6 +85,36 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + SettingsSingleFileGenerator Settings.Designer.cs @@ -92,6 +136,7 @@ + @@ -101,5 +146,6 @@ Settings.settings + diff --git a/dotnet/test/DotNetUnitTest/FileIO/DfrgFunctions.cs b/dotnet/test/DotNetUnitTest/FileIO/DfrgFunctions.cs index 855ddbd6b..97d67bdab 100644 --- a/dotnet/test/DotNetUnitTest/FileIO/DfrgFunctions.cs +++ b/dotnet/test/DotNetUnitTest/FileIO/DfrgFunctions.cs @@ -10,6 +10,20 @@ public class DfrgFunctions : FileSystemTest const string APPLICATIONS_CONTENT = "[ { \"Id\": \"4caaaed5-1160-4132-b54f-0191e527a84a\", \"Type\": 1, \"EnvironmentGUID\": \"b3730606-0f2a-4e8a-b395-d8fdf226def8\", \"IsNew\": false }]"; const string DOCUMENT_CONTENT = "Line 1Line 2Line 3"; const string MS923_CONTENT = "1234567890123"; + + [Fact] + public void dfwpnumTest() + { + GxContext context = new GxContext(); + string fileName = Path.Combine(BaseDir, "dfwpnumTest.txt"); + context.FileIOInstance.dfwopen(fileName, ",", "\"", 0, "UTF-8"); + context.FileIOInstance.dfwpnum(99999999999999999.5M, 5); + context.FileIOInstance.dfwnext(); + context.FileIOInstance.dfwclose(); + + string content = File.ReadAllText(fileName); + Assert.Contains("99999999999999999.5", content, StringComparison.OrdinalIgnoreCase); + } [Fact] public void dfrgtxtANSITest() { diff --git a/dotnet/test/DotNetUnitTest/FileIO/FileIOTests.cs b/dotnet/test/DotNetUnitTest/FileIO/FileIOTests.cs index 28decb1d7..91d2f9c8e 100644 --- a/dotnet/test/DotNetUnitTest/FileIO/FileIOTests.cs +++ b/dotnet/test/DotNetUnitTest/FileIO/FileIOTests.cs @@ -117,5 +117,15 @@ public void ReportUtilAddPathHttp() string fullPath = ReportUtils.AddPath(name, path); Assert.Equal(name, fullPath); } + [Fact] + public void ReportUtilAddPathWithIllegalCharacters() + { + string name = "https://chart.googleapis.com/chart?chs=400x400&cht=qr&chl=http://sistemas.gov/nfceweb/consultarNFCe.jsp?p=13231205514674000128650020009504049878593990|2|1|09|1337.07|4558626967746769617A304E4B7A6D34504B4E61524A474F4D32513D|1|14D0A30916C6C7EA709E7E33E330EE3F290FE25D"; + string path = "C:/Models/Report/NETModel/Web/"; + string fullPath = ReportUtils.AddPath(name, path); + + Assert.Equal(name, fullPath); + } + } } diff --git a/dotnet/test/DotNetUnitTest/Routing.cs b/dotnet/test/DotNetUnitTest/Routing.cs new file mode 100644 index 000000000..030997efc --- /dev/null +++ b/dotnet/test/DotNetUnitTest/Routing.cs @@ -0,0 +1,18 @@ +using System.IO; +using GeneXus.Http.HttpModules; +using Xunit; + +namespace xUnitTesting +{ + public class RoutingTest + { + + [Fact] + public void TestLoadGRPC() + { + GXAPIModule gxAPIModule = new GXAPIModule(); + gxAPIModule.ServicesGroupSetting(Path.Combine(Directory.GetCurrentDirectory())); + Assert.NotEmpty(GXAPIModule.servicesMap); + } + } +} diff --git a/dotnet/test/DotNetUnitTest/StringUtil/StringUtilTests.cs b/dotnet/test/DotNetUnitTest/StringUtil/StringUtilTests.cs new file mode 100644 index 000000000..58308c98a --- /dev/null +++ b/dotnet/test/DotNetUnitTest/StringUtil/StringUtilTests.cs @@ -0,0 +1,180 @@ +using GeneXus.Application; +using GeneXus.Programs; +using GeneXus.Utils; +#if !NETCORE +using Jayrock.Json; +#endif +using Xunit; + +namespace xUnitTesting +{ + public class StringUtilTests + { + [Fact] + public void TestJSONEncodeDoNotEncodeGreaterCharacter() + { + string json = ""; + json += ""; + json +=" "; + json +=" "; + json +=" "; + json +=" -88.076680,43.945580 -88.077480,43.945930 -88.078530,43.946390 -88.078960"; + json +=" "; + json +=" #MyLine"; + json +=" "; + json +=""; + json +=""; + + string expectedJsonEncoded = "\\t\\t\\t\\t\\t\\t\\t-88.076680,43.945580 -88.077480,43.945930 -88.078530,43.946390 -88.078960\\t\\t\\t\\t#MyLine\\t"; + string jsonEncoded = StringUtil.JSONEncode(json); + Assert.Equal(jsonEncoded, expectedJsonEncoded); + + } + [Fact] + public void TestFromJsonSDTWithBlankDateTime() + { + SdtSDT1_SDT1Item sdt = new SdtSDT1_SDT1Item(); + JObject json = new JObject(); + json["SDT1_DateTime"] = " 00:00:00"; + json["SDT1_Name"]=string.Empty; + json["SDT1_No"] = 0; + + sdt.FromJSONObject(json); + Assert.Equal(sdt.gxTpr_Sdt1_datetime, DateTimeUtil.NullDate()); + } + + [Fact] + public void TestFromJsonSDTWithTimeMustNotApplyTimezone() + { + SdtSDT1_SDT1Item sdt = new SdtSDT1_SDT1Item(); + JObject json = new JObject(); + json["SDT1_DateTime"] = "2014-04-29T14:29:40"; + json["SDT1_Name"] = string.Empty; + json["SDT1_No"] = 0; + + sdt.FromJSONObject(json); + Assert.Equal(14, sdt.gxTpr_Sdt1_datetime.Hour); + Assert.Equal(29, sdt.gxTpr_Sdt1_datetime.Minute); + } + [Fact] + public void TestZPictureCompatibiliy() + { + string picture = "$ 9.99"; + GxContext context = new GxContext(); + decimal decNumber = 5; + string decStr = context.localUtil.Format(decNumber, picture); + Assert.Equal("$ 5.00", decStr); + } + [Fact] + public void TestZPictureWithEscapeChar() + { + string picture = "\\\\\" ZZ,ZZZ,ZZ9"; + GxContext context = new GxContext(); + decimal decNumber = 87654321; + string decStr = context.localUtil.Format(decNumber, picture); + Assert.Equal("\\\" 87,654,321", decStr); + } + [Fact] + public void TestZPicture() + { + GxContext context = new GxContext(); + decimal decNumber = 123456.12M; + string decStr = context.localUtil.Format(decNumber, "ZZZZZZZZZZ9.ZZZZZZ"); + Assert.Equal(" 123456.120000", decStr); + + decStr = context.localUtil.Format(decNumber, "ZZZZZZZZZZ9.999999"); + Assert.Equal(" 123456.120000", decStr); + + decStr = context.localUtil.Format(decNumber, "99999999999.999999"); + Assert.Equal("00000123456.120000", decStr); + + decStr = context.localUtil.Format(decNumber, "##########9.######"); + Assert.Equal(" 123456.12", decStr); + + decStr = context.localUtil.Format(decNumber, "??????????9.??????"); + Assert.Equal(" 123456.12 ", decStr); + + decStr = context.localUtil.Format(decNumber, "\\# ??????????9.??????"); + Assert.Equal("# 123456.12 ", decStr); + + + decStr = context.localUtil.Format(decNumber, "##,###,###,##9.######"); + Assert.Equal(" 123,456.12", decStr); + + decStr = context.localUtil.Format(decNumber, "??,???,???,??9.??????"); + Assert.Equal(" 123456.12 ", decStr); + + //=====================Zero======================================== + + decNumber = 0; + + decStr = context.localUtil.Format(decNumber, "ZZZZZZZZZZ9.ZZZZZZ"); + Assert.Equal(" 0.000000", decStr); + + decStr = context.localUtil.Format(decNumber, "###########.######"); + Assert.Equal(" ", decStr); + + decStr = context.localUtil.Format(decNumber, "???????????.??????"); + Assert.Equal(" ", decStr); + + decStr = context.localUtil.Format(decNumber, "(??????????9.??????)"); + Assert.Equal(" 0 ", decStr); + + decStr = context.localUtil.Format(decNumber, "\\# ??????????9.??????"); + Assert.Equal("# 0 ", decStr); + + decStr = context.localUtil.Format(decNumber, "(##########9.######)"); + Assert.Equal(" 0 ", decStr); + + //=====================One======================================== + + decNumber = 1; + + decStr = context.localUtil.Format(decNumber, "ZZZZZZZZZZ9.ZZZZZZ"); + Assert.Equal(" 1.000000", decStr); + + decStr = context.localUtil.Format(decNumber, "###########.######"); + Assert.Equal(" 1", decStr); + + decStr = context.localUtil.Format(decNumber, "???????????.??????"); + Assert.Equal(" 1 ", decStr); + + decStr = context.localUtil.Format(decNumber, "(??????????9.??????)"); + Assert.Equal(" 1 ", decStr); + + decStr = context.localUtil.Format(decNumber, "\\# ??????????9.??????"); + Assert.Equal("# 1 ", decStr); + + decStr = context.localUtil.Format(decNumber, "(##########9.######)"); + Assert.Equal(" 1 ", decStr); + + //=====================0.1======================================== + decNumber = 0.1M; + decStr = context.localUtil.Format(decNumber, "???????????.??????"); + Assert.Equal(" .1 ", decStr); + + + //=====================Negatives======================================== + decNumber = -123456.12M; + decStr = context.localUtil.Format(decNumber, "(??????????9.??????)"); + Assert.Equal("( 123456.12 )", decStr); + + decStr = context.localUtil.Format(decNumber, "(##########9.######)"); + Assert.Equal(" (123456.12)", decStr); + + //=====================Positives======================================== + decNumber = 123456.12M; + decStr = context.localUtil.Format(decNumber, "+ ??????????9.??????"); + Assert.Equal("+ 123456.12 ", decStr); + + decStr = context.localUtil.Format(decNumber, "+ ##########9.######"); + Assert.Equal(" + 123456.12", decStr); + + } + } +} diff --git a/dotnet/test/DotNetUnitTest/application.key b/dotnet/test/DotNetUnitTest/application.key new file mode 100644 index 000000000..5f6a84812 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/application.key @@ -0,0 +1,2 @@ +7E2E22D26FF2989E2444852A85E57867 +7E2E22D26FF2989E2444852A85E57867 \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/client.exe.config b/dotnet/test/DotNetUnitTest/client.exe.config index 0351277f5..bbccba89c 100644 --- a/dotnet/test/DotNetUnitTest/client.exe.config +++ b/dotnet/test/DotNetUnitTest/client.exe.config @@ -90,9 +90,10 @@ - - - + + + + diff --git a/dotnet/test/DotNetUnitTest/private/api1.grp.json b/dotnet/test/DotNetUnitTest/private/api1.grp.json new file mode 100644 index 000000000..e69de29bb diff --git a/dotnet/test/DotNetUnitTest/private/api_4r.grp.json b/dotnet/test/DotNetUnitTest/private/api_4r.grp.json new file mode 100644 index 000000000..26a48a199 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/api_4r.grp.json @@ -0,0 +1,23 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"API_4R", +"BasePath":"API_4R", +"Mappings": +[ +{ +"Name":"consultaget", +"Path":"consultaget", +"ServiceMethod":"gxep_consultaget", +"Implementation":"ConsultarIPTU", +"Bodystyle":"Wrapped", +"Verb":"GET" +} +, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"consultapost__post", +"Path":"consultapost", +"ServiceMethod":"gxep_consultapost__post", +"Implementation":"ConsultarIPTU" +}]} \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/private/c003_api.grp.json b/dotnet/test/DotNetUnitTest/private/c003_api.grp.json new file mode 100644 index 000000000..f10b5f1a5 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/c003_api.grp.json @@ -0,0 +1,71 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"C003_API", +"BasePath":"C003_API", +"Mappings": +[ +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"c003_return1sdtsimple__post", +"Path":"c003_return1sdtsimple", +"ServiceMethod":"gxep_c003_return1sdtsimple__post", +"Implementation":"C003_Return1SdtSimple" +} +, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"c003_return1sdtsimpleempty__post", +"Path":"c003_return1sdtsimpleempty", +"ServiceMethod":"gxep_c003_return1sdtsimpleempty__post", +"Implementation":"C003_Return1SdtSimpleEmpty" +}, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"c003_returnvariables__post", +"Path":"c003_returnvariables", +"ServiceMethod":"gxep_c003_returnvariables__post", +"Implementation":"C003_ReturnVariables" +}, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"c003_return1sdtout__post", +"Path":"c003_return1sdtout", +"ServiceMethod":"gxep_c003_return1sdtout__post", +"Implementation":"C003_Return1SdtOut" +}, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"c003_return1sdtoutempty__post", +"Path":"c003_return1sdtoutempty", +"ServiceMethod":"gxep_c003_return1sdtoutempty__post", +"Implementation":"C003_Return1SdtOutEmpty" +}, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"c003_return2sdtout__post", +"Path":"c003_return2sdtout", +"ServiceMethod":"gxep_c003_return2sdtout__post", +"Implementation":"C003_Return2SdtOut" +}, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"c003_returnsdtcollection__post", +"Path":"c003_returnsdtcollection", +"ServiceMethod":"gxep_c003_returnsdtcollection__post", +"Implementation":"C003_ReturnSDTCollection" +}, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"c003_returnsdtcollectionempty__post", +"Path":"c003_returnsdtcollectionempty", +"ServiceMethod":"gxep_c003_returnsdtcollectionempty__post", +"Implementation":"C003_ReturnSDTCollectionEmpty" +}]} \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/private/c007_api.grp.json b/dotnet/test/DotNetUnitTest/private/c007_api.grp.json new file mode 100644 index 000000000..e923051da --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/c007_api.grp.json @@ -0,0 +1,31 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"C007_API", +"BasePath":"C007_API", +"Mappings": +[ +{ +"Name":"c007_getsdt1", +"Path":"c007_getsdt1", +"ServiceMethod":"gxep_c007_getsdt1", +"Implementation":"C007_GetSDT1", +"Bodystyle":"Wrapped", +"Verb":"GET" +} +, +{ +"Name":"c007_getsdt2wrapped", +"Path":"c007_getsdt2wrapped", +"ServiceMethod":"gxep_c007_getsdt2wrapped", +"Implementation":"C007_GetSDT2Wrapped", +"Bodystyle":"Wrapped", +"Verb":"GET" +}, +{ +"Name":"c007_getsdt2sequence", +"Path":"c007_getsdt2sequence", +"ServiceMethod":"gxep_c007_getsdt2sequence", +"Implementation":"C007_GetSDT2Sequence", +"Bodystyle":"Wrapped", +"Verb":"GET" +}]} \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/private/casosdates.grp.json b/dotnet/test/DotNetUnitTest/private/casosdates.grp.json new file mode 100644 index 000000000..ac979d9a0 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/casosdates.grp.json @@ -0,0 +1,15 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"CasosDates", +"BasePath":"CasosDates", +"Mappings": +[ +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"testdatedef__post", +"Path":"testdatedef", +"ServiceMethod":"gxep_testdatedef__post", +"Implementation":"SampleProc" +} +]} \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/private/casosdatetimes.grp.json b/dotnet/test/DotNetUnitTest/private/casosdatetimes.grp.json new file mode 100644 index 000000000..ceee82f19 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/casosdatetimes.grp.json @@ -0,0 +1,23 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"CasosDatetimes", +"BasePath":"CasosDatetimes", +"Mappings": +[ +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"testdtdef__post", +"Path":"testdtdef", +"ServiceMethod":"gxep_testdtdef__post", +"Implementation":"SampleProc" +} +, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"testdtconms__post", +"Path":"testdtconms", +"ServiceMethod":"gxep_testdtconms__post", +"Implementation":"SampleProc" +}]} \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/private/mailmanagement.grp.json b/dotnet/test/DotNetUnitTest/private/mailmanagement.grp.json new file mode 100644 index 000000000..085717211 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/mailmanagement.grp.json @@ -0,0 +1,25 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"mailmanagement", +"BasePath":"mailmanagement", +"Mappings": +[ +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"enviarmail__post", +"Path":"enviarmail", +"VariableAlias": [], +"ServiceMethod":"gxep_enviarmail__post", +"Implementation":"MailServices\\SendMail" +} +, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"enviarmailv1__post", +"Path":"enviarmailv1", +"VariableAlias": [], +"ServiceMethod":"gxep_enviarmailv1__post", +"Implementation":"MailServices\\SendMailSMTP" +}]} \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/private/mailmanagement_a.grp.json b/dotnet/test/DotNetUnitTest/private/mailmanagement_a.grp.json new file mode 100644 index 000000000..3a9ede1e0 --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/mailmanagement_a.grp.json @@ -0,0 +1,25 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"mailmanagement_A", +"BasePath":"mailmanagementSSO", +"Mappings": +[ +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"enviarmail__post", +"Path":"enviarmail", +"VariableAlias": [], +"ServiceMethod":"gxep_enviarmail__post", +"Implementation":"MailServices\\SendMail" +} +, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"enviarmailv1__post", +"Path":"enviarmailv1", +"VariableAlias": [], +"ServiceMethod":"gxep_enviarmailv1__post", +"Implementation":"MailServices\\SendMailSMTP_SSORest" +}]} \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/private/mailmanagementsso.grp.json b/dotnet/test/DotNetUnitTest/private/mailmanagementsso.grp.json new file mode 100644 index 000000000..b1ca1831f --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/mailmanagementsso.grp.json @@ -0,0 +1,25 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"mailmanagementsso", +"BasePath":"mailmanagementsso", +"Mappings": +[ +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"enviarmail__post", +"Path":"enviarmail", +"VariableAlias": [], +"ServiceMethod":"gxep_enviarmail__post", +"Implementation":"MailServices\\SendMail" +} +, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"enviarmailv1__post", +"Path":"enviarmailv1", +"VariableAlias": [], +"ServiceMethod":"gxep_enviarmailv1__post", +"Implementation":"MailServices\\SendMailSMTP_SSORest" +}]} \ No newline at end of file diff --git a/dotnet/test/DotNetUnitTest/private/mailmanagementv2.grp.json b/dotnet/test/DotNetUnitTest/private/mailmanagementv2.grp.json new file mode 100644 index 000000000..64fc85f3e --- /dev/null +++ b/dotnet/test/DotNetUnitTest/private/mailmanagementv2.grp.json @@ -0,0 +1,25 @@ +{ +"ObjectType":"ServiceGroup", +"Name":"mailmanagementV2", +"BasePath":"mailmanagementV2", +"Mappings": +[ +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"enviarmail__post", +"Path":"enviarmail", +"VariableAlias": [], +"ServiceMethod":"gxep_enviarmail__post", +"Implementation":"MailServices\\SendMail" +} +, +{ +"Bodystyle":"Wrapped", +"Verb":"POST", +"Name":"enviarmailv1__post", +"Path":"enviarmailv1", +"VariableAlias": [], +"ServiceMethod":"gxep_enviarmailv1__post", +"Implementation":"MailServices\\SendMailProxy" +}]} \ No newline at end of file diff --git a/dotnet/test/NativeAccessControllerTest/AccessTokenController_Test.cs b/dotnet/test/NativeAccessControllerTest/AccessTokenController_Test.cs new file mode 100644 index 000000000..6b8aeca31 --- /dev/null +++ b/dotnet/test/NativeAccessControllerTest/AccessTokenController_Test.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace GeneXus.Programs +{ + public class RequestParameters + { + public string message { get; set; } + } + + [ApiController] + [Route("dummy.aspx")] + + public class AccessTokenControllerDummy : ControllerBase + { + [HttpPost] + public async Task> Post([FromForm] string message) + { + return await Task.FromResult(message); + } + } + +} \ No newline at end of file diff --git a/dotnet/test/NativeAccessControllerTest/AccessTokenController_Test.csproj b/dotnet/test/NativeAccessControllerTest/AccessTokenController_Test.csproj new file mode 100644 index 000000000..68adc9bd2 --- /dev/null +++ b/dotnet/test/NativeAccessControllerTest/AccessTokenController_Test.csproj @@ -0,0 +1,7 @@ + + + + net6.0;net8.0 + Library + + diff --git a/dotnet/test/NativeAccessControllerTest/Properties/launchSettings.json b/dotnet/test/NativeAccessControllerTest/Properties/launchSettings.json new file mode 100644 index 000000000..1826f57c1 --- /dev/null +++ b/dotnet/test/NativeAccessControllerTest/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "AccessTokenController_Test": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:63881;http://localhost:63882" + } + } +} \ No newline at end of file diff --git a/dotnet/test/ProjectHealthTest/PackageVersionsConsistency.cs b/dotnet/test/ProjectHealthTest/PackageVersionsConsistency.cs index 29be3c9ad..bce4ec15e 100644 --- a/dotnet/test/ProjectHealthTest/PackageVersionsConsistency.cs +++ b/dotnet/test/ProjectHealthTest/PackageVersionsConsistency.cs @@ -216,9 +216,18 @@ private bool AnyDirectReferenceLessThanTransitiveVersion(ICollection GetVersion(k.Version) > directVersion); + return value.Any(k => DirectReferenceLessThanTransitiveVersion(directVersion, k) || DiferentDirectReferences(directVersion, k)); } } + private bool DiferentDirectReferences(Version directVersion, PackageVersionItem k) + { + return (!k.Transitive && GetVersion(k.Version) != directVersion); + } + private bool DirectReferenceLessThanTransitiveVersion(Version directVersion, PackageVersionItem k) + { + return (k.Transitive && GetVersion(k.Version) > directVersion); + } + private Version GetVersion(String versionString) { return Version.Parse(Regex.Match(versionString, VersionPattern).Value); diff --git a/dotnet/test/benchmarks/LogTest/LogTest.cs b/dotnet/test/benchmarks/LogTest/LogTest.cs new file mode 100644 index 000000000..95bcd7f87 --- /dev/null +++ b/dotnet/test/benchmarks/LogTest/LogTest.cs @@ -0,0 +1,53 @@ +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; +using GeneXus.Diagnostics; +using Xunit; +namespace BenchmarkTest +{ +#if DEBUG + [Config(typeof(CustomBenchmarkConfig))] +#endif + public class LoggerBenchmark + { + [Params(100)] + public int N; + const int Debug = 5; + + [Benchmark] + public void LogOnPerformanceTest() + { + for ( int i = 0; i < N; i++) + { + Log.Write("Test Message", "Test Topic", Debug); + } + } + } + public class CustomBenchmarkConfig : ManualConfig + { + public CustomBenchmarkConfig() + { + WithOptions(ConfigOptions.DisableOptimizationsValidator); + } + } + public class PerformanceTests + { + const double MAX_NANOSECONDS = 150000; + [Fact(Skip = "Temporarily disabling this test for investigation/debugging purposes.")] + public void RunLoggerBenchmark() + { + Summary summary = BenchmarkRunner.Run(); + Assert.Single(summary.Reports); + + foreach (BenchmarkReport report in summary.Reports) + { + Assert.NotEmpty(report.AllMeasurements); + Measurement runMeasure = report.AllMeasurements.FirstOrDefault(); + Assert.InRange(runMeasure.Nanoseconds, low: 0, high: MAX_NANOSECONDS); + + } + } + } +} \ No newline at end of file diff --git a/dotnet/test/benchmarks/LogTest/LogTest.csproj b/dotnet/test/benchmarks/LogTest/LogTest.csproj new file mode 100644 index 000000000..2a90ace29 --- /dev/null +++ b/dotnet/test/benchmarks/LogTest/LogTest.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/dotnet/test/benchmarks/LogTest/appsettings.json b/dotnet/test/benchmarks/LogTest/appsettings.json new file mode 100644 index 000000000..31bce50e5 --- /dev/null +++ b/dotnet/test/benchmarks/LogTest/appsettings.json @@ -0,0 +1,64 @@ +{ + "appSettings": { + "AppMainNamespace": "GeneXus.Programs", + "DataStore1": "Default", + "DataStore-Count": "1", + "DataStore-Default": "Default", + "Connection-Default-DBMS": "sqlserver", + "Connection-Default-Port": "", + "Connection-Default-LockTimeout": "0", + "Connection-Default-LockRetryCount": "10", + "Connection-Default-IsolationLevel": "CR", + "Connection-Default-Datasource": "", + "Connection-Default-User": "", + "Connection-Default-Password": "", + "Connection-Default-DB": "", + "Connection-Default-Schema": "", + "Connection-Default-Opts": "", + "Connection-Default-TrnInt": "1", + "DateFormat": "MDY", + "YearLimit": "1940", + "TimeAmPmFormat": "12", + "VER_STAMP": "20210602.093942", + "CS_BLOB_PATH": "PublicTempStorage", + "TMPMEDIA_DIR": "PrivateTempStorage", + "PRINT_LAYOUT_METADATA_DIR": "LayoutMetadata", + "StorageTimeZone": "1", + "LOGIN_AS_USERID": "0", + "LANGUAGE": "eng", + "LANG_NAME": "English", + "DECIMAL_POINT": ".", + "DATE_FMT": "MDY", + "CTOD_DATE_FMT": "L", + "Culture": "en-US", + "Theme": "Carmine", + "UseNamedParameters": "1", + "EnableIntegratedSecurity": "0", + "MAX_CURSOR": "100", + "STATIC_CONTENT": "", + "GX_BUILD_NUMBER": "11103481", + "CACHE_CONTENT_EXPIRATION": "36", + "ENABLE_MANAGEMENT": "0", + "COMPRESS_HTML": "1", + "IE_COMPATIBILITY_VIEW": "EmulateIE7", + "DocumentType": "HTML5", + "EXPOSE_METADATA": "0", + "SMART_CACHING": "0", + "wcf:serviceHostingEnvironment:useClassicReadEntityBodyMode": "true", + "HTTP_PROTOCOL": "Unsecure", + "SAMESITE_COOKIE": "Lax", + "CACHE_INVALIDATION_TOKEN": "20216211291931", + "CORS_ALLOW_ORIGIN": "https://normal-website.com", + "MY_CUSTOM_PTY": "DEFAULT_VALUE" + }, + "languages": { + "English": { + "code": "eng", + "culture": "en-US", + "date_fmt": "MDY", + "decimal_point": ".", + "thousand_sep": ",", + "time_fmt": "12" + } + } +} \ No newline at end of file diff --git a/dotnet/test/benchmarks/LogTest/log.console.config b/dotnet/test/benchmarks/LogTest/log.console.config new file mode 100644 index 000000000..b52a4d897 --- /dev/null +++ b/dotnet/test/benchmarks/LogTest/log.console.config @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +