diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs index 0670eb172..8222dd9e4 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs @@ -5,6 +5,8 @@ using System.IO; using System.Text; using GeneXus; +using GeneXus.Http; +using GeneXus.Utils; using iText.Barcodes; using iText.Html2pdf; using iText.Html2pdf.Resolver.Font; @@ -481,16 +483,22 @@ public override void GxDrawBitMap(String bitmap, int left, int top, int right, i { if (!Path.IsPathRooted(bitmap)) { - - image = new Image(ImageDataFactory.Create(defaultRelativePrepend + bitmap)); - if (image == null) + if (PathUtil.IsAbsoluteUrl(bitmap)) { - bitmap = webAppDir + bitmap; - image = new Image(ImageDataFactory.Create(bitmap)); + image = new Image(ImageDataFactory.Create(HttpHelper.DownloadFile(bitmap, out _))); } else { - bitmap = defaultRelativePrepend + bitmap; + image = new Image(ImageDataFactory.Create(defaultRelativePrepend + bitmap)); + if (image == null) + { + bitmap = webAppDir + bitmap; + image = new Image(ImageDataFactory.Create(bitmap)); + } + else + { + bitmap = defaultRelativePrepend + bitmap; + } } } else diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs index 9a6f45587..6fd6d90c1 100644 --- a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs +++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs @@ -16,7 +16,6 @@ using Font = System.Drawing.Font; #endif using System.IO; -using System.Net.Http; using System.Text; using GeneXus; using UglyToad.PdfPig.Core; @@ -27,6 +26,9 @@ using static UglyToad.PdfPig.Writer.PdfPageBuilder; using PageSize = UglyToad.PdfPig.Content.PageSize; using PdfRectangle = UglyToad.PdfPig.Core.PdfRectangle; +using System.Net; +using GeneXus.Http; +using GeneXus.Utils; namespace GeneXus.Printer { @@ -227,24 +229,34 @@ public override void GxDrawBitMap(string bitmap, int left, int top, int right, i { try { + byte[] imageBytes; if (!Path.IsPathRooted(bitmap)) { - - image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(defaultRelativePrepend + bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(defaultRelativePrepend + bitmap), position); - if (image == null) + if (PathUtil.IsAbsoluteUrl(bitmap)) { - bitmap = webAppDir + bitmap; - image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(bitmap), position); + imageBytes = HttpHelper.DownloadFile(bitmap, out _); } else { - bitmap = defaultRelativePrepend + bitmap; + string bitmapPath = Path.Combine(defaultRelativePrepend, bitmap); + if (File.Exists(bitmapPath)) + { + imageBytes = File.ReadAllBytes(bitmapPath); + bitmap = bitmapPath; + } + else + { + bitmapPath = Path.Combine(webAppDir, bitmap); + imageBytes = File.ReadAllBytes(bitmapPath); + bitmap = bitmapPath; + } } } else { - image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(bitmap), position); + imageBytes = File.ReadAllBytes(bitmap); } + image = imageType == "jpeg" ? pageBuilder.AddJpeg(imageBytes, position) : pageBuilder.AddPng(imageBytes, position); } catch (Exception) { @@ -272,17 +284,14 @@ public override void GxDrawBitMap(string bitmap, int left, int top, int right, i private AddedImage AddImageFromURL(string url, PdfRectangle position) { AddedImage image = null; - using (HttpClient httpClient = new HttpClient()) + byte[] imageBytes = HttpHelper.DownloadFile(url, out HttpStatusCode statusCode); + try { - byte[] imageBytes = httpClient.GetByteArrayAsync(url).Result; - try - { - image = pageBuilder.AddJpeg(imageBytes, position); - } - catch (Exception) - { - pageBuilder.AddPng(imageBytes, position); - } + image = pageBuilder.AddJpeg(imageBytes, position); + } + catch (Exception) + { + pageBuilder.AddPng(imageBytes, position); } if (image == null) { diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs index 9ce09894b..98a888608 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs @@ -15,8 +15,8 @@ #if NETCORE using Microsoft.AspNetCore.Http; using GxClasses.Helpers; -using System.Net; #endif +using System.Net; using NodaTime; using NUglify; using NUglify.Html; @@ -38,6 +38,11 @@ using GeneXus.Http; using System.Security; using System.Net.Http.Headers; +using System.Security.Policy; +using GeneXus.Http.Client; + + + #if NETCORE using Image = GeneXus.Drawing.Image; using GeneXus.Drawing; @@ -51,6 +56,7 @@ #endif using System.Net.Http; + namespace GeneXus.Utils { public class GxDefaultProps @@ -5885,21 +5891,18 @@ private static Bitmap BitmapCreateFromStream(string filePathOrUrl) Uri uri; if (Uri.TryCreate(filePathOrUrl, UriKind.Absolute, out uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) { - using (HttpClient httpClient = new HttpClient()) + try { - try + byte[] data = HttpHelper.DownloadFile(uri.AbsoluteUri, out HttpStatusCode statusCode); + using (MemoryStream mem = new MemoryStream(data)) { - byte[] data = httpClient.GetByteArrayAsync(uri).Result; - using (MemoryStream mem = new MemoryStream(data)) - { - return new Bitmap(mem); - } - } - catch - { - return null; + return new Bitmap(mem); } } + catch + { + return null; + } } else { @@ -5917,21 +5920,18 @@ private static Image ImageCreateFromStream(string filePathOrUrl) Uri uri; if (Uri.TryCreate(filePathOrUrl, UriKind.Absolute, out uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) { - using (HttpClient httpClient = new HttpClient()) + try { - try - { - byte[] data = httpClient.GetByteArrayAsync(uri).Result; - using (MemoryStream mem = new MemoryStream(data)) - { - return Image.FromStream(mem); - } - } - catch + byte[] data = HttpHelper.DownloadFile(uri.AbsoluteUri, out HttpStatusCode statusCode); + using (MemoryStream mem = new MemoryStream(data)) { - return null; + return Image.FromStream(mem); } } + catch + { + return null; + } } else { diff --git a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs index 34406657f..3ccf5b845 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs @@ -860,6 +860,7 @@ public class Preferences public static string DefaultRewriteFile = "rewrite.config"; const string USE_NAMED_PARAMETERS = "UseNamedParameters"; const string REST_DATES_WITH_MILLIS = "REST_DATES_WITH_MILLIS"; + const string UserAgentHeader = "UserAgentHeader"; internal const string YES = "1"; internal const string NO = "0"; static string defaultDatastore; @@ -875,6 +876,17 @@ internal static string AppMainNamespace return nameSpace; } } + internal static string HttpClientUserAgent + { + get + { + if (Config.GetValueOrEnvironmentVarOf(UserAgentHeader, out string userAgent)) + return userAgent; + else + return string.Empty; + } + } + internal static bool IsBeforeConnectEventConfigured() { return (Config.GetValueOf("EVENT_BEFORE_CONNECT", out string evtProcName) && !string.IsNullOrEmpty(evtProcName)); diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs index b1bbcb3be..d8fac1c36 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs @@ -2833,6 +2833,7 @@ static public object ConvertToExternal(Type to, Object i) } return o; } + internal static bool IsNullOrEmpty(IList collection) => collection == null || collection.Count == 0; } public interface IGxCollectionConverter { diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs index 62e3de2c0..d52120486 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxHttpClient.cs @@ -158,50 +158,60 @@ private async Task ReceiveDataAsync() } private const int POOLED_CONNECTION_LIFETIME_MINUTES = 2; + internal static ConcurrentDictionary _httpClientInstances = new ConcurrentDictionary(); + internal static HttpClient GetHttpClientInstance(Uri uri, out bool disposableInstance) + { + return GetHttpClientInstance(uri, 0, null, null, null, null, string.Empty, 0, out disposableInstance); + } private static HttpClient GetHttpClientInstance(Uri URI, int timeout, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, List fileCertificateCollection, string proxyHost, int proxyPort, out bool disposableInstance) { + HttpClient client; if (CacheableInstance(authCollection, authProxyCollection)) { - HttpClient value; disposableInstance = false; string key = HttpClientInstanceIdentifier(proxyHost, proxyPort, fileCertificateCollection, timeout); - if (_httpClientInstances.TryGetValue(key, out value)) + if (_httpClientInstances.TryGetValue(key, out client)) { GXLogging.Debug(log, $"Getting httpClient cached instance"); - return value; + return client; } else { lock (syncRootHttpInstance) { - if (_httpClientInstances.TryGetValue(key, out value)) + if (_httpClientInstances.TryGetValue(key, out client)) { GXLogging.Debug(log, $"Getting httpClient cached instance"); - return value; + return client; + } + client = new HttpClient(GetHandler(URI, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort)); + if (timeout != 0) + { + client.Timeout = TimeSpan.FromMilliseconds(timeout); } - value = new HttpClient(GetHandler(URI, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort)); - value.Timeout = TimeSpan.FromMilliseconds(timeout); - _httpClientInstances.TryAdd(key, value); - return value; + _httpClientInstances.TryAdd(key, client); } } } else { disposableInstance = true; - return new HttpClient(GetHandler(URI, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort)); + client = new HttpClient(GetHandler(URI, authCollection, authProxyCollection, certificateCollection, proxyHost, proxyPort)); } + if (!string.IsNullOrEmpty(Preferences.HttpClientUserAgent)) + client.DefaultRequestHeaders.UserAgent.ParseAdd(Preferences.HttpClientUserAgent); + return client; } 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) + if (string.IsNullOrEmpty(proxyHost) && CollectionUtils.IsNullOrEmpty(fileCertificateCollection) && timeout== DEFAULT_TIMEOUT && defaultSslOptions) { return string.Empty; } - else if (fileCertificateCollection.Count==0) + else if (CollectionUtils.IsNullOrEmpty(fileCertificateCollection)) { return $"{proxyHost}:{proxyPort}::{timeout}:{defaultSslOptions}"; } @@ -213,7 +223,7 @@ private static string HttpClientInstanceIdentifier(string proxyHost, int proxyPo private static bool CacheableInstance(ArrayList authCollection, ArrayList authProxyCollection) { - return authCollection.Count == 0 && authProxyCollection.Count == 0 && Preferences.SingletonHttpClient(); + return CollectionUtils.IsNullOrEmpty(authCollection) && CollectionUtils.IsNullOrEmpty(authProxyCollection) && Preferences.SingletonHttpClient(); } private static SocketsHttpHandler GetHandler(Uri URI, ArrayList authCollection, ArrayList authProxyCollection, X509Certificate2Collection certificateCollection, string proxyHost, int proxyPort) { @@ -239,7 +249,7 @@ private static SocketsHttpHandler GetHandler(Uri URI, ArrayList authCollection, { handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; } - if (certificateCollection.Count > 0) + if (!CollectionUtils.IsNullOrEmpty(certificateCollection)) { if (handler.SslOptions.ClientCertificates == null) { @@ -1590,6 +1600,8 @@ static ICredentials getCredentialCache(Uri URI, ArrayList authenticationCollecti string sScheme; GxAuthScheme auth; CredentialCache cc = null; + if (CollectionUtils.IsNullOrEmpty(authenticationCollection)) + return null; for (int i = 0; i < authenticationCollection.Count; i++) { diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs index 4554f8415..dbeabfce2 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs @@ -28,6 +28,8 @@ using System.Net.Http; using System.Globalization; using System.Linq; +using GeneXus.Http.Client; +using System.Net.Http.Headers; namespace GeneXus.Http { @@ -55,6 +57,7 @@ public class HttpHeader internal static string X_CSRF_TOKEN_COOKIE = "XSRF-TOKEN"; internal static string AUTHORIZATION = "Authorization"; internal static string CONTENT_TYPE = "Content-Type"; + internal static string USER_AGENT = "User-Agent"; } internal class HttpHeaderValue { @@ -530,9 +533,10 @@ public static string RequestPhysicalApplicationPath(HttpContext context = null) public static byte[] DownloadFile(string url, out HttpStatusCode statusCode) { byte[] buffer = Array.Empty(); - using (var client = new HttpClient()) + HttpClient httpClient = GxHttpClient.GetHttpClientInstance(new Uri(url), out bool disposableInstance); + try { - using (HttpResponseMessage response = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result) + using (HttpResponseMessage response = httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result) { if (response.IsSuccessStatusCode) { @@ -548,16 +552,23 @@ public static byte[] DownloadFile(string url, out HttpStatusCode statusCode) } } } + finally + { + if (disposableInstance) + httpClient.Dispose(); + } return buffer; } #else - internal static byte[] DownloadFile(string fileName, out HttpStatusCode statusCode) + public static byte[] DownloadFile(string fileName, out HttpStatusCode statusCode) { byte[] binary = Array.Empty(); try { WebClient Client = new WebClient(); + if (!string.IsNullOrEmpty(Preferences.HttpClientUserAgent)) + Client.Headers.Add(HttpHeader.USER_AGENT, Preferences.HttpClientUserAgent); binary = Client.DownloadData(fileName); statusCode = HttpStatusCode.OK; } diff --git a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext4.cs b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext4.cs index 033f4eae3..4e4e71842 100644 --- a/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext4.cs +++ b/dotnet/src/dotnetframework/GxPdfReportsCS/PDFReportItext4.cs @@ -6,7 +6,9 @@ using System.Reflection; using GeneXus; using GeneXus.Configuration; +using GeneXus.Http; using GeneXus.Metadata; +using GeneXus.Utils; using iTextSharp.text; using iTextSharp.text.html.simpleparser; using iTextSharp.text.pdf; @@ -417,8 +419,8 @@ public override void GxDrawBitMap(String bitmap, int left, int top, int right, i EnsureDocumentOpen(); try { - iTextSharp.text.Image image; - iTextSharp.text.Image imageRef; + Image image; + Image imageRef; if (documentImages != null && documentImages.TryGetValue(bitmap, out imageRef)) { image = imageRef; @@ -429,24 +431,30 @@ public override void GxDrawBitMap(String bitmap, int left, int top, int right, i { if (!Path.IsPathRooted(bitmap)) { - - image = iTextSharp.text.Image.GetInstance(defaultRelativePrepend + bitmap); - if (image == null) + if (PathUtil.IsAbsoluteUrl(bitmap)) { - bitmap = webAppDir + bitmap; - image = iTextSharp.text.Image.GetInstance(bitmap); + image = Image.GetInstance(HttpHelper.DownloadFile(bitmap, out _)); } else { - bitmap = defaultRelativePrepend + bitmap; + image = Image.GetInstance(defaultRelativePrepend + bitmap); + if (image == null) + { + bitmap = webAppDir + bitmap; + image = Image.GetInstance(bitmap); + } + else + { + bitmap = defaultRelativePrepend + bitmap; + } } } else { - image = iTextSharp.text.Image.GetInstance(bitmap); + image = Image.GetInstance(bitmap); } } - catch (Exception)//absolute url + catch (Exception) { Uri uri = new Uri(bitmap); image = Image.GetInstance(uri);