From e94c5ecb7143b3b2019ef02dcd683ffc47a9912b Mon Sep 17 00:00:00 2001 From: claudiamurialdo <33756655+claudiamurialdo@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:22:53 -0300 Subject: [PATCH] Add async interfaces for http procedures (#987) * Add async interfaces for http procedures * Define protected property AsyncEnabled and internal method GetAsyncEnabledInternal. * Improve performance on GetAjaxEncryptionKey. Don not print DumpHeaders if log is off --- .../Middleware/HandlerFactory.cs | 13 +- .../GxClasses/Core/GXApplication.cs | 17 +++ .../GxClasses/Core/Web/HttpAjaxContext.cs | 39 +++--- .../GxClasses/Data/GXDataADO.cs | 83 ++++++++++++- .../GxClasses/Data/GXDataCommon.cs | 22 ++++ .../GxClasses/Data/GXDataDb2.cs | 16 ++- .../GxClasses/Data/GXDataHana.cs | 17 ++- .../GxClasses/Data/GXDataNTierService.cs | 1 - .../GxClasses/Helpers/GXLogging.cs | 9 ++ .../GxClasses/Middleware/GXHttp.cs | 117 +++++++++++++++++- .../GxClasses/Model/GXBaseObject.cs | 59 ++++++++- .../GxClasses/Model/GXWebProcedure.cs | 8 ++ .../Middleware/HttpProcTest.cs | 34 +++++ .../apps/aprochttpgetstatic.cs | 111 +++++++++++++++++ 14 files changed, 518 insertions(+), 28 deletions(-) create mode 100644 dotnet/test/DotNetCoreWebUnitTest/Middleware/HttpProcTest.cs create mode 100644 dotnet/test/DotNetCoreWebUnitTest/apps/aprochttpgetstatic.cs diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs index e71b29279..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; @@ -74,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 diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs index b1e9a1469..8ec7b8ee8 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs @@ -49,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 { @@ -1407,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/Web/HttpAjaxContext.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs index 7092be321..b78405faa 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs @@ -685,29 +685,36 @@ internal 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; } } } diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs index d48cea8a9..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;} @@ -2842,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 756a05e18..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 { @@ -4343,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 a52b846f0..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; @@ -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/GXDataHana.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataHana.cs index 967c738eb..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; @@ -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/GXDataNTierService.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs index 570c47873..57c25823c 100644 --- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs +++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataNTierService.cs @@ -363,7 +363,6 @@ override public void Close() { try { - CheckState(false); InternalConnection.Close(); } catch (Exception ex) diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs index 97a9a1685..9a66cc57a 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs @@ -429,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) diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs index d67069d3f..cfe9ed0d5 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs @@ -48,6 +48,7 @@ namespace GeneXus.Http #endif + using System.Threading.Tasks; #if NETCORE public abstract class GXHttpHandler : GXBaseObject, IHttpHandler #else @@ -243,6 +244,15 @@ protected void setEventMetadata(string EventName, string 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) { @@ -286,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() { } @@ -1048,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() { @@ -1925,6 +1976,70 @@ 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 @@ -2224,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}" }); diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs index d5da20307..a3d87da7d 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs @@ -18,6 +18,7 @@ using System.Diagnostics; using System.Reflection; using System.Threading; +using System.Threading.Tasks; namespace GeneXus.Application { @@ -63,6 +64,27 @@ 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; @@ -79,18 +101,43 @@ private void ExecuteUsingSpanCode() 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 @@ -177,7 +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/GXWebProcedure.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs index 7a0a689ae..67ff7b635 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXWebProcedure.cs @@ -9,6 +9,7 @@ namespace GeneXus.Procedure using System.Net.Mime; #if NETCORE using Microsoft.AspNetCore.Http; + using System.Threading.Tasks; #else using System.Web; #endif @@ -37,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) { } 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/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 ; + } + +}