From dc0c4f18f5050b673ae5d7321f2b4493ddbb5df7 Mon Sep 17 00:00:00 2001 From: Will Horne Date: Tue, 15 Oct 2024 19:42:53 -0700 Subject: [PATCH 1/4] Add SynchronousTransport implementation, enabled similarly to SynchronousWorker --- .../Sentry/private/SynchronousTransport.ps1 | 64 +++++++++++++++++++ modules/Sentry/public/Start-Sentry.ps1 | 13 ++++ 2 files changed, 77 insertions(+) create mode 100644 modules/Sentry/private/SynchronousTransport.ps1 diff --git a/modules/Sentry/private/SynchronousTransport.ps1 b/modules/Sentry/private/SynchronousTransport.ps1 new file mode 100644 index 0000000..aa14f04 --- /dev/null +++ b/modules/Sentry/private/SynchronousTransport.ps1 @@ -0,0 +1,64 @@ +Add-Type -AssemblyName System.Net.Http + +class SynchronousTransport : Sentry.Http.HttpTransportBase, Sentry.Extensibility.ITransport +{ + hidden [System.Net.Http.HttpClient] $httpClient + hidden [Sentry.SentryOptions] $options + + SynchronousTransport([Sentry.SentryOptions] $options) + : base($options) + { + $this.options = $options + $this.httpClient = [System.Net.Http.HttpClient]::new() + } + + [System.Threading.Tasks.Task] SendEnvelopeAsync([Sentry.Protocol.Envelopes.Envelope] $envelope, [System.Threading.CancellationToken]$cancellationToken = [System.Threading.CancellationToken]::None) + { + # Take Sentry's SerializableHttpContent, convert it to a string, and send via PowerShell's Invoke-WebRequest, then translate the response back to a .NET HttpResponseMessage. + # There are limited options to perform synchronous operations in Windows PowerShell 5.1 on .NET 4.6, so this is a workaround. + $assembly = [Sentry.SentrySdk].Assembly + $type = $assembly.GetType('Sentry.Http.HttpTransportBase') + $ProcessEnvelope = $type.GetMethod('ProcessEnvelope', [System.Reflection.BindingFlags]::Instance + [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Public) + $CreateRequest = $type.GetMethod('CreateRequest', [System.Reflection.BindingFlags]::Instance + [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Public) + $HandleResponse = $type.GetMethod('HandleResponse', [System.Reflection.BindingFlags]::Instance + [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Public) + + $processedEnvelope = $ProcessEnvelope.Invoke($this, @($envelope)) + if ($processedEnvelope.Items.count -gt 0) + { + $request = $CreateRequest.Invoke($this, @($processedEnvelope)) + + $headers = @{} + foreach ($header in $request.Headers) { + $Key = $header.Key + $Value = $header.Value.Trim() -join ", " + $headers[$Key] = $Value + } + + $EnvelopeHttpContentType = $assembly.GetType('Sentry.Internal.Http.EnvelopeHttpContent') + $SerializeToStream = $EnvelopeHttpContentType.GetMethod('SerializeToStream', [System.Reflection.BindingFlags]::Instance + [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Public) + + $memoryStream = [System.IO.MemoryStream]::new() + $SerializeToStream.Invoke($request.Content, @($memoryStream, $null, $cancellationToken)) + $memoryStream.Position = 0 + + $reader = New-Object System.IO.StreamReader($memoryStream) + $content = $reader.ReadToEnd() + $reader.Close() + + $this.options.DiagnosticLogger.Log([Sentry.SentryLevel]::Debug, "Sending content synchronously, Content-Length: {0}", $null, $content.Length) + + $psResponse = Invoke-WebRequest -Uri $request.RequestUri -Method "POST" -Headers $headers -Body $content -UseBasicParsing + + $response = [System.Net.Http.HttpResponseMessage]::new($psResponse.StatusCode) + $response.Content = [System.Net.Http.StringContent]::new($psResponse.Content, [System.Text.Encoding]::UTF8, "application/json") + + foreach ($header in $psResponse.Headers.GetEnumerator()) { + $response.Headers.TryAddWithoutValidation($header.Key, $header.Value) + } + + $HandleResponse.Invoke($this, @($response, $processedEnvelope)) + } + + return [System.Threading.Tasks.Task]::CompletedTask + } +} \ No newline at end of file diff --git a/modules/Sentry/public/Start-Sentry.ps1 b/modules/Sentry/public/Start-Sentry.ps1 index e272dec..91d7c68 100644 --- a/modules/Sentry/public/Start-Sentry.ps1 +++ b/modules/Sentry/public/Start-Sentry.ps1 @@ -1,6 +1,7 @@ . "$privateDir/DiagnosticLogger.ps1" . "$privateDir/ScopeIntegration.ps1" . "$privateDir/SynchronousWorker.ps1" +. "$privateDir/SynchronousTransport.ps1" . "$privateDir/EventUpdater.ps1" function Start-Sentry @@ -48,6 +49,18 @@ function Start-Sentry $logger = [DiagnosticLogger]::new($options.DiagnosticLevel) $options.DiagnosticLogger = $logger + if ($null -eq $options.Transport) + { + try + { + $options.Transport = [SynchronousTransport]::new($options) + } + catch + { + $logger.Log([Sentry.SentryLevel]::Warning, 'Failed to create a PowerShell-specific synchronous transport', $_.Exception, @()) + } + } + if ($null -eq $options.BackgroundWorker) { try From cde6a4120f73260e43f8f83a357cfa7a76037a7f Mon Sep 17 00:00:00 2001 From: Will Horne Date: Tue, 15 Oct 2024 19:59:00 -0700 Subject: [PATCH 2/4] Switch to using directive for loading .NET assembly Add CHANGELOG.md entry for new SynchronousTransport feature --- CHANGELOG.md | 4 ++++ modules/Sentry/private/SynchronousTransport.ps1 | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b7e1ad..75655c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- Add SynchronousTransport implementation ([#4](https://github.com/SummitHosting/sentry-powershell/pull/4)) + ### Fixes - StackTrace parsing on Windows Powershell 5.1 ([#50](https://github.com/getsentry/sentry-powershell/pull/50)) diff --git a/modules/Sentry/private/SynchronousTransport.ps1 b/modules/Sentry/private/SynchronousTransport.ps1 index aa14f04..eb5bc9d 100644 --- a/modules/Sentry/private/SynchronousTransport.ps1 +++ b/modules/Sentry/private/SynchronousTransport.ps1 @@ -1,4 +1,4 @@ -Add-Type -AssemblyName System.Net.Http +using assembly System.Net.Http class SynchronousTransport : Sentry.Http.HttpTransportBase, Sentry.Extensibility.ITransport { From 905a652e5df32c1585df893fe7845798426d0343 Mon Sep 17 00:00:00 2001 From: Will Horne Date: Tue, 15 Oct 2024 20:17:04 -0700 Subject: [PATCH 3/4] Require System.Net.Http assembly --- modules/Sentry/Sentry.psd1 | 3 +++ modules/Sentry/private/SynchronousTransport.ps1 | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/Sentry/Sentry.psd1 b/modules/Sentry/Sentry.psd1 index 48525bc..180512a 100644 --- a/modules/Sentry/Sentry.psd1 +++ b/modules/Sentry/Sentry.psd1 @@ -30,6 +30,9 @@ # Script files (.ps1) that are run in the caller's environment prior to importing this module. ScriptsToProcess = @('assemblies-loader.ps1') + # Require System.Net.Http for use by SynchronousTransport + RequiredAssemblies = @('System.Net.Http') + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @( 'Add-SentryBreadcrumb', diff --git a/modules/Sentry/private/SynchronousTransport.ps1 b/modules/Sentry/private/SynchronousTransport.ps1 index eb5bc9d..4bc1415 100644 --- a/modules/Sentry/private/SynchronousTransport.ps1 +++ b/modules/Sentry/private/SynchronousTransport.ps1 @@ -1,5 +1,3 @@ -using assembly System.Net.Http - class SynchronousTransport : Sentry.Http.HttpTransportBase, Sentry.Extensibility.ITransport { hidden [System.Net.Http.HttpClient] $httpClient From 548611797c2de32f7c67f08ee7bc133d850674d1 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 15 Oct 2024 20:48:39 -0700 Subject: [PATCH 4/4] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75655c0..a59798e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- Add SynchronousTransport implementation ([#4](https://github.com/SummitHosting/sentry-powershell/pull/4)) +- Add SynchronousTransport implementation ([#59](https://github.com/SummitHosting/sentry-powershell/pull/59)) ### Fixes