private/SynchronousTransport.ps1

# 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.
class SynchronousTransport : Sentry.Http.HttpTransportBase, Sentry.Extensibility.ITransport
{
    hidden [Sentry.Extensibility.IDiagnosticLogger] $logger
    hidden [System.Reflection.MethodInfo] $ProcessEnvelope
    hidden [System.Reflection.MethodInfo] $CreateRequest
    hidden [System.Reflection.MethodInfo] $SerializeToStream

    SynchronousTransport([Sentry.SentryOptions] $options) : base($options)
    {
        $this.logger = $options.DiagnosticLogger

        # These are internal methods, so we need to use reflection to access them.
        $instanceMethod = [System.Reflection.BindingFlags]::Instance + [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Public;
        $this.ProcessEnvelope = [Sentry.Http.HttpTransportBase].GetMethod('ProcessEnvelope', $instanceMethod)
        $this.CreateRequest = [Sentry.Http.HttpTransportBase].GetMethod('CreateRequest', $instanceMethod)
        $EnvelopeHttpContentType = [Sentry.SentrySdk].Assembly.GetType('Sentry.Internal.Http.EnvelopeHttpContent')
        $this.SerializeToStream = $EnvelopeHttpContentType.GetMethod('SerializeToStream', $instanceMethod)
    }

    [System.Threading.Tasks.Task] SendEnvelopeAsync([Sentry.Protocol.Envelopes.Envelope] $envelope, [System.Threading.CancellationToken]$cancellationToken = [System.Threading.CancellationToken]::None)
    {
        $processedEnvelope = $this.ProcessEnvelope.Invoke($this, @($envelope))
        if ($processedEnvelope.Items.count -gt 0)
        {
            $request = $this.CreateRequest.Invoke($this, @($processedEnvelope))

            $headers = @{}
            foreach ($header in $request.Headers)
            {
                $Key = $header.Key
                $Value = $header.Value.Trim() -join ', '
                $headers[$Key] = $Value
            }

            $memoryStream = [System.IO.MemoryStream]::new()
            $this.SerializeToStream.Invoke($request.Content, @($memoryStream, $null, $cancellationToken))
            $memoryStream.Position = 0

            $reader = New-Object System.IO.StreamReader($memoryStream)
            $content = $reader.ReadToEnd()
            $reader.Close()

            $this.logger.Log([Sentry.SentryLevel]::Debug, 'Sending content synchronously, Content-Length: {0}', $null, $content.Length)

            $ProgressPreference = 'SilentlyContinue'
            $psResponse = Invoke-WebRequest -Uri $request.RequestUri -Method $request.Method.Method -Headers $headers -Body $content -UseBasicParsing

            $response = [System.Net.Http.HttpResponseMessage]::new($psResponse.StatusCode)
            $contentType = $psResponse.Headers['Content-Type']
            if ($null -eq $contentType)
            {
                $contentType = 'application/json'
            }
            $response.Content = [System.Net.Http.StringContent]::new($psResponse.Content, [System.Text.Encoding]::UTF8, $contentType)

            foreach ($header in $psResponse.Headers.GetEnumerator())
            {
                $response.Headers.TryAddWithoutValidation($header.Key, $header.Value)
            }

            $this.HandleResponse($response, $processedEnvelope)
        }

        return [System.Threading.Tasks.Task]::CompletedTask
    }
}