1+ <#
2+ . SYNOPSIS
3+ A debug server.
4+ . DESCRIPTION
5+ A server that runs on the current thread, so you can debug it.
6+
7+ You can run this with -AsJob, but then you cannot debug in PowerShell.
8+ . NOTES
9+ A few notes:
10+
11+ 1. This will effectively lock the current thread (CTRL+C works).
12+ 2. Because of the way requests are processed, you may need to refresh to hit the breakpoint.
13+ 3. Be aware that browsers will request a `favicon.ico` first.
14+ #>
15+ param (
16+ # The rootUrl of the server. By default, a random loopback address.
17+ [string ]$RootUrl =
18+ " http://127.0.0.1:$ ( Get-Random - Minimum 4200 - Maximum 42000 ) /" ,
19+
20+ # If set, will run in a background job.
21+ [switch ]
22+ $AsJob
23+ )
24+
25+ $httpListener = [Net.HttpListener ]::new()
26+ $httpListener.Prefixes.add ($RootUrl )
27+ $httpListener.Start ()
28+ Write-Warning " Listening on $rootUrl "
29+
30+ $listenScript = {
31+ param ([Net.HttpListener ]$httpListener )
32+ # Listen for the next request
33+ :nextRequest while ($httpListener.IsListening ) {
34+ $getContext = $httpListener.GetContextAsync ()
35+
36+ while (-not $getContext.Wait (17 )) { }
37+
38+ $context = $getContext.Result
39+ $requestTime = [DateTime ]::Now
40+ $request , $reply = $context.Request , $context.Response
41+ $debugObject = $request |
42+ Select-Object HttpMethod, Url, Is* |
43+ Add-Member NoteProperty Headers ([Ordered ]@ {}) - Force - passThru |
44+ Add-Member NoteProperty Query ([Ordered ]@ {}) - Force - passThru
45+
46+ foreach ($headerName in $request.Headers ) {
47+ $debugObject.headers [$headerName ] = $request.Headers [$headerName ]
48+ }
49+ if ($request.Url.Query ) {
50+
51+ foreach ($chunk in $request.Url.Query -split ' &' ) {
52+ $parsedQuery =
53+ [Web.HttpUtility ]::ParseQueryString($chunk )
54+ $key = @ ($parsedQuery.Keys )[0 ]
55+ if ($debugObject.Query [$key ]) {
56+ $debugObject.Query [$key ] = @ (
57+ $debugObject.Query [$key ]
58+ ) + $parsedQuery [$key ]
59+ } else {
60+ $debugObject.Query [$key ] = $parsedQuery [$key ]
61+ }
62+ }
63+ }
64+ $reply.ContentType = ' application/json'
65+ $reply.Close (
66+ $OutputEncoding.GetBytes (
67+ ($debugObject | ConvertTo-Json - Depth 5 )
68+ ), $false )
69+ " Responded to $ ( $Request.Url ) in $ ( [DateTime ]::Now - $requestTime ) "
70+ }
71+ }
72+
73+ if ($AsJob ) {
74+ Start-ThreadJob - ScriptBlock $listenerScript - ArgumentList $httpListener
75+ } else {
76+ . $listenScript $httpListener
77+ }
0 commit comments