From b3ff42484edff0631d7acbfb08c75d86924096c0 Mon Sep 17 00:00:00 2001 From: Claudia Murialdo Date: Tue, 31 May 2022 18:28:27 -0300 Subject: [PATCH 1/4] HttpRequest.Body was not working when content-type is form url encoded. --- .../GxClasses/Core/Web/GxHttpServer.cs | 10 +- .../GxClasses/Helpers/HttpHelper.cs | 32 +++++- .../Middleware/WebPanelTest.cs | 50 +++++++++ .../DotNetCoreUnitTest/Middleware/webhook.cs | 103 ++++++++++++++++++ 4 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs create mode 100644 dotnet/test/DotNetCoreUnitTest/Middleware/webhook.cs diff --git a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs index 14d4b2e10..1af295604 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/GxHttpServer.cs @@ -11,6 +11,12 @@ namespace GeneXus.Http.Server using Microsoft.AspNetCore.Http.Extensions; using System.Linq; using Microsoft.AspNetCore.Http.Features; + using System.Text; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Mvc.Formatters; + using System.Net.Http; + using Stubble.Core.Contexts; + using System.Net.Mime; #endif public class GxHttpCookie @@ -391,9 +397,9 @@ public string GetValue( string name) public override string ToString() { if (_httpReq == null) - return ""; + return String.Empty; #if NETCORE - return (new StreamReader(_httpReq.Body)).ReadToEnd(); + return _httpReq.GetRawBodyString(); #else return (new StreamReader(_httpReq.InputStream)).ReadToEnd(); #endif diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 8fb9bf054..a11111581 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Primitives; using System.Net.Http; +using Microsoft.AspNetCore.Mvc.Formatters; #else using System.ServiceModel.Web; using System.ServiceModel; @@ -21,10 +22,6 @@ using System.Net; using System.Text; using System.Web; -using System.Threading.Tasks; -using System.Threading; -using System.Linq; -using GeneXus.Data; using System.Runtime.Serialization; using GeneXus.Mime; using System.Text.RegularExpressions; @@ -711,8 +708,35 @@ public static NameValueCollection GetParams(this HttpRequest request) return request.Params; #endif } +#if NETCORE + static readonly MediaType URLEncoded = new MediaType("application/x-www-form-urlencoded"); + public static string GetRawBodyString(this HttpRequest request, Encoding encoding = null) + { + if (encoding == null) + encoding = Encoding.UTF8; + MediaType mediaType = new MediaType(request.ContentType); + + if (mediaType.IsSubsetOf(URLEncoded)) + { + string content = string.Empty; + foreach (string key in request.Form.Keys) + { + if (request.Form.TryGetValue(key, out var value)) + { + content += $"{key}={value}&"; + } + } + content = content.TrimEnd('&'); + return content; + } + else + { + return (new StreamReader(request.Body, encoding)).ReadToEnd(); + } + } +#endif public static string GetRawUrl(this HttpRequest request) { #if NETCORE diff --git a/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs b/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs new file mode 100644 index 000000000..4fb6d30eb --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using GeneXus.Metadata; +using Xunit; +namespace xUnitTesting +{ + public class WebPanelTest : MiddlewareTest + { + Dictionary parms = new Dictionary(); + FormUrlEncodedContent formUrlEncodedContent; + public WebPanelTest():base() + { + ClassLoader.FindType("webhook", "GeneXus.Programs", "webhook", Assembly.GetExecutingAssembly(), true);//Force loading assembly for webhook procedure + server.AllowSynchronousIO=true; + parms.Add("SmsMessageSid", "SM40d2cbda93b2de0a15df7a1598c7db83"); + parms.Add("NumMedia", "99"); + parms.Add("WaId", "5215532327636"); + formUrlEncodedContent = new FormUrlEncodedContent(parms); + } + [Fact] + public async Task HtttpResponseBodyNotEmpty_WhenFormURLEncoded() + { + HttpClient client = server.CreateClient(); + + HttpResponseMessage response = await client.PostAsync("webhook.aspx", formUrlEncodedContent);//"application/x-www-form-urlencoded" + response.EnsureSuccessStatusCode(); + string resp = await response.Content.ReadAsStringAsync(); + string expected = await formUrlEncodedContent.ReadAsStringAsync(); + Assert.Equal(expected, resp); + Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + } + [Fact] + public async Task HtttpResponseBodyNotEmpty_WhenTextPlain() + { + HttpClient client = server.CreateClient(); + string plainText = await formUrlEncodedContent.ReadAsStringAsync(); + StringContent content = new StringContent(plainText, Encoding.UTF8, "text/plain"); + HttpResponseMessage response = await client.PostAsync("webhook.aspx", content); + response.EnsureSuccessStatusCode(); + string resp = await response.Content.ReadAsStringAsync(); + string expected = await content.ReadAsStringAsync(); + Assert.Equal(expected, resp); + Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + } + } + +} diff --git a/dotnet/test/DotNetCoreUnitTest/Middleware/webhook.cs b/dotnet/test/DotNetCoreUnitTest/Middleware/webhook.cs new file mode 100644 index 000000000..7dfef187b --- /dev/null +++ b/dotnet/test/DotNetCoreUnitTest/Middleware/webhook.cs @@ -0,0 +1,103 @@ +using System; +using GeneXus.Application; +using GeneXus.Data.NTier; +using GeneXus.Http.Server; +using GeneXus.Procedure; +using GeneXus.Utils; +namespace GeneXus.Programs +{ + public class webhook : GXWebProcedure + { + public override void webExecute() + { + context.SetDefaultTheme("Carmine"); + initialize(); + executePrivate(); + cleanup(); + } + + public webhook() + { + context = new GxContext(); + DataStoreUtil.LoadDataStores(context); + IsMain = true; + context.SetDefaultTheme("Carmine"); + } + + public webhook(IGxContext context) + { + this.context = context; + IsMain = false; + } + + public void execute() + { + initialize(); + executePrivate(); + } + + public void executeSubmit() + { + webhook objwebhook; + objwebhook = new webhook(); + objwebhook.context.SetSubmitInitialConfig(context); + objwebhook.initialize(); + Submit(executePrivateCatch, objwebhook); + } + + void executePrivateCatch(object stateInfo) + { + try + { + ((webhook)stateInfo).executePrivate(); + } + catch (Exception e) + { + GXUtil.SaveToEventLog("Design", e); + throw; + } + } + + void executePrivate() + { + /* GeneXus formulas */ + /* Output device settings */ + AV9body = AV8httprequest.ToString(); + AV10httpresponse.AddString(AV9body); + if (context.WillRedirect()) + { + context.Redirect(context.wjLoc); + context.wjLoc = ""; + } + this.cleanup(); + } + + public override void cleanup() + { + CloseOpenCursors(); + base.cleanup(); + if (IsMain) + { + context.CloseConnections(); + } + ExitApp(); + } + + protected void CloseOpenCursors() + { + } + + public override void initialize() + { + AV8httprequest = new GxHttpRequest(context); + AV10httpresponse = new GxHttpResponse(context); + /* GeneXus formulas. */ + context.Gx_err = 0; + } + + private string AV9body; + private GxHttpRequest AV8httprequest; + private GxHttpResponse AV10httpresponse; + } + +} From 491b3cf29f2c5195a005680b8780535afe62671d Mon Sep 17 00:00:00 2001 From: Claudia Murialdo Date: Thu, 2 Jun 2022 14:53:58 -0300 Subject: [PATCH 2/4] URLEncode variables to build httprequest raw body. --- .../GxClasses/Helpers/HttpHelper.cs | 25 +++++++++---------- .../Middleware/WebPanelTest.cs | 2 ++ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index a11111581..517baad21 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -715,26 +715,25 @@ public static string GetRawBodyString(this HttpRequest request, Encoding encodin if (encoding == null) encoding = Encoding.UTF8; - MediaType mediaType = new MediaType(request.ContentType); - - if (mediaType.IsSubsetOf(URLEncoded)) + if (!string.IsNullOrEmpty(request.ContentType)) { + MediaType mediaType = new MediaType(request.ContentType); - string content = string.Empty; - foreach (string key in request.Form.Keys) + if (mediaType.IsSubsetOf(URLEncoded)) { - if (request.Form.TryGetValue(key, out var value)) + string content = string.Empty; + foreach (string key in request.Form.Keys) { - content += $"{key}={value}&"; + if (request.Form.TryGetValue(key, out var value)) + { + content += $"{HttpUtility.UrlEncode(key)}={HttpUtility.UrlEncode(value)}&"; + } } + content = content.TrimEnd('&'); + return content; } - content = content.TrimEnd('&'); - return content; - } - else - { - return (new StreamReader(request.Body, encoding)).ReadToEnd(); } + return (new StreamReader(request.Body, encoding)).ReadToEnd(); } #endif public static string GetRawUrl(this HttpRequest request) diff --git a/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs b/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs index 4fb6d30eb..bab8a9549 100644 --- a/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs +++ b/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs @@ -18,6 +18,8 @@ public WebPanelTest():base() parms.Add("SmsMessageSid", "SM40d2cbda93b2de0a15df7a1598c7db83"); parms.Add("NumMedia", "99"); parms.Add("WaId", "5215532327636"); + parms.Add("From", "whatsapp%3A%2B5215532327636"); + parms.Add("ProfileName", "Jhon Thomas"); formUrlEncodedContent = new FormUrlEncodedContent(parms); } [Fact] From 57594f91f3cb6afdccf9c0807433188ed012cd76 Mon Sep 17 00:00:00 2001 From: Claudia Murialdo Date: Thu, 4 Aug 2022 15:49:58 -0300 Subject: [PATCH 3/4] Add GXUtil.UrlEncode to GetRawBodyString to get the raw body content as it is in .NET Framework. --- dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs | 2 +- dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 517baad21..294d1c652 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -726,7 +726,7 @@ public static string GetRawBodyString(this HttpRequest request, Encoding encodin { if (request.Form.TryGetValue(key, out var value)) { - content += $"{HttpUtility.UrlEncode(key)}={HttpUtility.UrlEncode(value)}&"; + content += $"{GXUtil.UrlEncode(key)}={GXUtil.UrlEncode(value)}&"; } } content = content.TrimEnd('&'); diff --git a/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs b/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs index bab8a9549..e38816304 100644 --- a/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs +++ b/dotnet/test/DotNetCoreUnitTest/Middleware/WebPanelTest.cs @@ -18,7 +18,7 @@ public WebPanelTest():base() parms.Add("SmsMessageSid", "SM40d2cbda93b2de0a15df7a1598c7db83"); parms.Add("NumMedia", "99"); parms.Add("WaId", "5215532327636"); - parms.Add("From", "whatsapp%3A%2B5215532327636"); + parms.Add("From", "whatsapp:+5215532327636"); parms.Add("ProfileName", "Jhon Thomas"); formUrlEncodedContent = new FormUrlEncodedContent(parms); } From e824703782efe1db6f16855d4c83ac0fbaa4fd12 Mon Sep 17 00:00:00 2001 From: Claudia Murialdo Date: Thu, 4 Aug 2022 15:51:25 -0300 Subject: [PATCH 4/4] Release stream properly. --- dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 294d1c652..109c871e6 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -733,7 +733,10 @@ public static string GetRawBodyString(this HttpRequest request, Encoding encodin return content; } } - return (new StreamReader(request.Body, encoding)).ReadToEnd(); + using (StreamReader sr = new StreamReader(request.Body, encoding)) + { + return sr.ReadToEnd(); + } } #endif public static string GetRawUrl(this HttpRequest request)