1+ using System . Diagnostics ;
12using System . IO . Compression ;
23using System . Net ;
34using System . Net . Http . Json ;
@@ -13,6 +14,7 @@ namespace PostHog.Library;
1314/// </summary>
1415internal static class HttpClientExtensions
1516{
17+ internal static Func < object , CancellationToken , Task < ByteArrayContent > > CreateCompressedJsonContentAsync = CreateGzipJsonContentAsync ;
1618 /// <summary>
1719 /// Sends a POST request to the specified Uri containing the value serialized as JSON in the request body.
1820 /// Returns the response body deserialized as <typeparamref name="TBody"/>.
@@ -148,7 +150,7 @@ or SocketError.NetworkReset
148150 try
149151 {
150152 response = enableCompression
151- ? await PostCompressedJsonAsync ( httpClient , requestUri , content , cancellationToken )
153+ ? await PostCompressedJsonWithFallbackAsync ( httpClient , requestUri , content , cancellationToken )
152154 : await httpClient . PostAsJsonAsync (
153155 requestUri ,
154156 content ,
@@ -347,13 +349,48 @@ static Task Delay(TimeProvider timeProvider, TimeSpan delay, CancellationToken c
347349#endif
348350 }
349351
350- static async Task < HttpResponseMessage > PostCompressedJsonAsync (
352+ static async Task < HttpResponseMessage > PostCompressedJsonWithFallbackAsync (
351353 HttpClient httpClient ,
352354 Uri requestUri ,
353355 object content ,
354356 CancellationToken cancellationToken )
355357 {
356- // Stream JSON directly into gzip to avoid intermediate allocation
358+ var compressedContent = await TryCreateCompressedJsonContentAsync ( content , cancellationToken ) ;
359+ if ( compressedContent is null )
360+ {
361+ return await httpClient . PostAsJsonAsync (
362+ requestUri ,
363+ content ,
364+ JsonSerializerHelper . Options ,
365+ cancellationToken ) ;
366+ }
367+
368+ using ( compressedContent )
369+ {
370+ return await httpClient . PostAsync ( requestUri , compressedContent , cancellationToken ) ;
371+ }
372+ }
373+
374+ static async Task < ByteArrayContent ? > TryCreateCompressedJsonContentAsync (
375+ object content ,
376+ CancellationToken cancellationToken )
377+ {
378+ try
379+ {
380+ return await CreateCompressedJsonContentAsync ( content , cancellationToken ) ;
381+ }
382+ catch ( Exception ex ) when ( ex is IOException or InvalidDataException or NotSupportedException or ObjectDisposedException )
383+ {
384+ Debug . WriteLine ( $ "Failed to gzip request body, sending uncompressed: { ex } ") ;
385+ return null ;
386+ }
387+ }
388+
389+ static async Task < ByteArrayContent > CreateGzipJsonContentAsync (
390+ object content ,
391+ CancellationToken cancellationToken )
392+ {
393+ // Stream JSON directly into gzip to avoid intermediate allocation and honor cancellation during serialization.
357394 using var memoryStream = new MemoryStream ( 4096 ) ;
358395 using ( var gzipStream = new GZipStream ( memoryStream , CompressionLevel . Fastest , leaveOpen : true ) )
359396 {
@@ -365,13 +402,9 @@ static async Task<HttpResponseMessage> PostCompressedJsonAsync(
365402 ? new ByteArrayContent ( buffer . Array ! , buffer . Offset , buffer . Count )
366403 : new ByteArrayContent ( memoryStream . ToArray ( ) ) ;
367404
368- using ( compressedContent )
369- {
370- compressedContent . Headers . ContentType = new System . Net . Http . Headers . MediaTypeHeaderValue ( "application/json" ) ;
371- compressedContent . Headers . ContentEncoding . Add ( "gzip" ) ;
372-
373- return await httpClient . PostAsync ( requestUri , compressedContent , cancellationToken ) ;
374- }
405+ compressedContent . Headers . ContentType = new System . Net . Http . Headers . MediaTypeHeaderValue ( "application/json" ) ;
406+ compressedContent . Headers . ContentEncoding . Add ( "gzip" ) ;
407+ return compressedContent ;
375408 }
376409
377410 public static async Task EnsureSuccessfulApiCall (
0 commit comments