Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Bolt's Performance Journal ⚡

## 2025-05-14 - [Webhook Latency Optimization & PowerShell Efficiency]
**Learning:** In event-driven systems like webhook listeners, the "Early Response" pattern is critical for minimizing sender-side latency. By closing the HTTP response immediately after reading the payload, we decouple the processing/logging time from the network turnaround time. Additionally, in PowerShell, cmdlets like `Get-Date` and the use of the pipeline (`|`) introduce significant overhead compared to direct .NET methods and parameter passing (`-InputObject`).

**Action:**
1. Move `$response.Close()` to immediately follow the request body read.
2. Replace `Get-Date` with `[DateTime]::Now` for timestamping.
3. Replace pipeline operations with direct parameter passing in high-frequency loops.
4. Pre-allocate static buffers (like response bytes) outside the main request loop.
24 changes: 14 additions & 10 deletions tests/Project.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Describe "System Automation Hub Structure" {
Describe "System Automation Hub Structure" {
It "Should have the main entry point (start-automation.ps1)" {
Test-Path "./start-automation.ps1" | Should -Be \$true
Test-Path "./start-automation.ps1" | Should -Be $true
}

It "Should have the webhooks/listener.ps1 script" {
Test-Path "./webhooks/listener.ps1" | Should -Be \$true
Test-Path "./webhooks/listener.ps1" | Should -Be $true
}

It "Should have at least one script in the scripts/ directory" {
Expand All @@ -14,15 +14,19 @@

Describe "PowerShell Script Syntax Verification" {
Context "Checking all .ps1 files" {
\$psFiles = Get-ChildItem -Path . -Include *.ps1 -Recurse
$psFiles = Get-ChildItem -Path . -Include *.ps1 -Recurse

foreach (\$file in \$psFiles) {
It "Should have valid syntax for \$(\$file.Name)" {
\$errorActionPreference = "Stop"
Get-Command -ErrorAction SilentlyContinue -Name Out-Null # Ensure we can run commands
foreach ($file in $psFiles) {
It "Should have valid syntax for $($file.Name)" {
$errors = $null
$tokens = $null
# Use the built-in Parser to verify syntax without external module dependencies
[System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$errors) | Out-Null

# Check for syntax errors by parsing the script
{ [Microsoft.PowerShell.Commands.ScriptAnalyzer.Helper]::GetTokens(\$file.FullName, [ref]\$null, [ref]\$null) } | Should -Not -Throw
if ($errors) {
$errorMessages = $errors | ForEach-Object { "$($_.Message) at line $($_.Extent.StartLineNumber):$($_.Extent.StartColumnNumber)" }
throw "Syntax errors found in $($file.Name):`n$($errorMessages -join "`n")"
}
}
}
}
Expand Down
39 changes: 26 additions & 13 deletions webhooks/listener.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ param()
$port = 9000
$endpoint = "http://localhost:$port/"

# Performance: Pre-calculate encoding and response buffer to avoid redundant allocations in the loop
$utf8 = [System.Text.Encoding]::UTF8
$responseBytes = $utf8.GetBytes("System Automation Hub: Event Received")

# Ensure we don't try to start another listener if one is already running in this session
if ($null -ne $listener) {
try { $listener.Stop() } catch { Write-Verbose "Listener already stopped." }
Expand All @@ -29,29 +33,43 @@ try {
$request = $context.Request
$response = $context.Response

$timestamp = Get-Date -Format 'HH:mm:ss'
# Performance: Use .NET [DateTime]::Now for faster timestamp generation than Get-Date cmdlet
$timestamp = [DateTime]::Now.ToString('HH:mm:ss')
$method = $request.HttpMethod
$remote = $request.RemoteEndPoint
$userAgent = $request.UserAgent
$isGitHub = $userAgent -match "GitHub-Hookshot"

$sourceIcon = if ($isGitHub) { "🐙 GitHub " } else { "🔗 Web " }

# Read body if available
$body = $null
if ($request.HasEntityBody) {
# Performance: Use constructor directly and ensure proper disposal of the stream reader
$reader = [System.IO.StreamReader]::new($request.InputStream, $utf8)
$body = $reader.ReadToEnd()
$reader.Dispose()
}

# ⚡ BOLT OPTIMIZATION: Early Response
# We send the response IMMEDIATELY after reading the body to minimize latency for the sender (e.g. GitHub).
# Expensive operations like JSON pretty-printing and console logging happen AFTER the connection is closed.
$response.ContentLength64 = $responseBytes.Length
$response.OutputStream.Write($responseBytes, 0, $responseBytes.Length)
$response.Close()

Write-Host "[$timestamp] " -ForegroundColor Gray -NoNewline
Write-Host "$sourceIcon" -ForegroundColor Magenta -NoNewline
Write-Host "$method " -ForegroundColor Yellow -NoNewline
Write-Host "from " -ForegroundColor Gray -NoNewline
Write-Host "$remote" -ForegroundColor White

# Read body if available
if ($request.HasEntityBody) {
$reader = New-Object System.IO.StreamReader($request.InputStream, [System.Text.Encoding]::UTF8)
$body = $reader.ReadToEnd()

if ($null -ne $body) {
try {
if ($request.ContentType -match "application/json") {
$jsonObj = $body | ConvertFrom-Json
$prettyBody = $jsonObj | ConvertTo-Json -Depth 10
# Performance: Use -InputObject parameter instead of pipeline for faster processing
$jsonObj = ConvertFrom-Json -InputObject $body
$prettyBody = ConvertTo-Json -InputObject $jsonObj -Depth 10
Write-Host "Payload (JSON):" -ForegroundColor Cyan
Write-Host $prettyBody -ForegroundColor DarkGray
} else {
Expand All @@ -64,11 +82,6 @@ try {
}
}

# Simple response
$buffer = [System.Text.Encoding]::UTF8.GetBytes("System Automation Hub: Event Received")
$response.ContentLength64 = $buffer.Length
$response.OutputStream.Write($buffer, 0, $buffer.Length)
$response.Close()
Write-Host "Done.`n" -ForegroundColor DarkGray
}
} catch {
Expand Down
Loading