Request Decompression

Accept request bodies compressed with Content-Encoding (for example gzip) by enabling the Request Decompression middleware.

This is request-level decompression: the client compresses the entire HTTP request body, and middleware decompresses it before Kestrun reads/parses the body.

Full source

File: pwsh/tutorial/examples/15.11-Request-Decompression.ps1

<#
    15.11 Request Decompression Demo

    Demonstrates request-level decompression using RequestDecompression middleware.

    Key idea:
      - Client compresses the entire request body (Content-Encoding: gzip)
      - Middleware decompresses before Kestrun reads/parses the body (e.g. JSON)

    Try (PowerShell 7+):
        pwsh .\docs\_includes\examples\pwsh\15.11-Request-Decompression.ps1

        $payloadObject = @{
            message = (('hello ' * 20000).Trim())
            count   = 1
            items   = 1..2000
            meta    = @{ note = ('x' * 20000) }
        }
        $payload = $payloadObject | ConvertTo-Json -Compress -Depth 6
        $bytes = [System.Text.Encoding]::UTF8.GetBytes($payload)
        $compressed = New-Object System.IO.MemoryStream
        $gzip = [System.IO.Compression.GZipStream]::new($compressed, [System.IO.Compression.CompressionMode]::Compress, $true)
        $gzip.Write($bytes, 0, $bytes.Length)
        $gzip.Dispose()

        Invoke-WebRequest -Method Post -Uri "http://127.0.0.1:5000/api/echo" \
          -ContentType 'application/json' \
          -Headers @{ 'Content-Encoding' = 'gzip' } \
          -Body $compressed.ToArray()
#>
param(
    [int]$Port = 5000,
    [IPAddress]$IPAddress = [IPAddress]::Loopback
)

if (-not (Get-Module Kestrun)) { Import-Module Kestrun }

Initialize-KrRoot -Path $PSScriptRoot

New-KrLogger |
    Set-KrLoggerLevel -Value Debug |
    Add-KrSinkConsole |
    Register-KrLogger -Name 'console' -SetAsDefault

New-KrServer -Name 'Request Decompression Demo'

Add-KrEndpoint -Port $Port -IPAddress $IPAddress | Out-Null

# Enable request-level decompression (entire body compressed; Content-Encoding: gzip)
Add-KrRequestDecompressionMiddleware -AllowedEncoding gzip

Enable-KrConfiguration

Add-KrMapRoute -Verbs Get -Pattern '/' -ScriptBlock {
    Write-KrTextResponse -InputObject 'POST /api/echo with Content-Encoding: gzip (JSON body) to see request decompression in action.' -StatusCode 200
}

Add-KrMapRoute -Verbs Post -Pattern '/api/echo' -ScriptBlock {
    $raw = Get-KrRequestBody -Raw
    $receivedBytes = [System.Text.Encoding]::UTF8.GetByteCount($raw)

    $messageStartsWith = ''
    $receivedCount = $null
    try {
        $body = $raw | ConvertFrom-Json -AsHashtable
        $messageText = [string]($body.message ?? '')
        $messageStartsWith = if ($messageText.Length -ge 5) { $messageText.Substring(0, 5) } else { $messageText }
        $receivedCount = $body.count
    } catch {
        $messageStartsWith = ''
        $receivedCount = $null
    }

    Write-KrJsonResponse -InputObject @{
        ok = $true
        receivedBytes = $receivedBytes
        receivedCount = $receivedCount
        messageStartsWith = $messageStartsWith
    } -StatusCode 200
}

Add-KrMapRoute -Verbs Post -Pattern '/api/text' -ScriptBlock {
    $raw = Get-KrRequestBody -Raw
    $receivedBytes = [System.Text.Encoding]::UTF8.GetByteCount($raw)

    Write-KrJsonResponse -InputObject @{
        ok = $true
        receivedBytes = $receivedBytes
        contentType = $Context.Request.ContentType
    } -StatusCode 200
}

Start-KrServer -CloseLogsOnExit

Step-by-step

  1. Logging: Register a console logger for visibility.
  2. Server: Create a server and listener (IP + port).
  3. Middleware: Add Add-KrRequestDecompressionMiddleware -AllowedEncoding gzip.
  4. Configuration: Call Enable-KrConfiguration so middleware/routes are active.
  5. Echo route: Implement POST /api/echo that reads the decompressed body via Get-KrRequestBody -Raw.
  6. Run: Start the server and send a gzipped JSON request body.

Try it

Start the server:

pwsh .\docs\_includes\examples\pwsh\15.11-Request-Decompression.ps1

Verify the server is up:

curl -i http://127.0.0.1:5000/

Send a request-compressed JSON body (PowerShell 7+):

$payloadObject = @{
  message = (('hello ' * 20000).Trim())
  count   = 1
  items   = 1..2000
  meta    = @{ note = ('x' * 20000) }
}
$payload = $payloadObject | ConvertTo-Json -Compress -Depth 6
$bytes = [System.Text.Encoding]::UTF8.GetBytes($payload)

$ms = [System.IO.MemoryStream]::new()
$gzip = [System.IO.Compression.GZipStream]::new($ms, [System.IO.Compression.CompressionMode]::Compress, $true)
$gzip.Write($bytes, 0, $bytes.Length)
$gzip.Dispose()

Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:5000/api/echo' \
  -ContentType 'application/json' \
  -Headers @{ 'Content-Encoding' = 'gzip' } \
  -Body $ms.ToArray()

Expected output:

{"ok":true,"receivedBytes":123456,"receivedCount":1,"messageStartsWith":"hello"}

Key points

  • Response compression uses Accept-Encoding (client asks), and the server replies with Content-Encoding.
  • Request decompression uses Content-Encoding (client sends), and Get-KrRequestBody -Raw returns the decompressed body.
  • -AllowedEncoding should be set to the encodings you want to accept (commonly gzip).

Troubleshooting

Symptom Cause Fix
415 / 400 when sending gzip body Middleware missing or not configured Ensure Add-KrRequestDecompressionMiddleware -AllowedEncoding gzip is present before Enable-KrConfiguration.
400 JSON parsing error Body not valid JSON after decompression Confirm you send -ContentType 'application/json' and gzip a valid JSON payload.
Server accepts uncompressed but not compressed Missing Content-Encoding header Include -Headers @{ 'Content-Encoding'='gzip' }.

References


Previous / Next

Previous: SSE Broadcast Next: Health