SHOW:
|
|
- or go back to the newest paste.
1 | <# | |
2 | .Synopsis | |
3 | Starts powershell webserver | |
4 | .Description | |
5 | Starts webserver as powershell process. | |
6 | Call of the root page (e.g. http://localhost:8080/) returns a powershell execution web form. | |
7 | Call of /script uploads a powershell script and executes it (as a function). | |
8 | Call of /log returns the webserver logs, /starttime the start time of the webserver, /time the current time. | |
9 | /download downloads and /upload uploads a file. /beep generates a sound and /quit or /exit stops the webserver. | |
10 | ||
11 | You may have to configure a firewall exception to allow access to the chosen port, e.g. with: | |
12 | netsh advfirewall firewall add rule name="Powershell Webserver" dir=in action=allow protocol=TCP localport=8080 | |
13 | ||
14 | After stopping the webserver you should remove the rule, e.g.: | |
15 | netsh advfirewall firewall delete rule name="Powershell Webserver" | |
16 | .Parameter BINDING | |
17 | Binding of the webserver | |
18 | .Inputs | |
19 | None | |
20 | .Outputs | |
21 | None | |
22 | .Example | |
23 | Start-Webserver.ps1 | |
24 | ||
25 | Starts webserver with binding to http://localhost:8080/ | |
26 | .Example | |
27 | Start-Webserver.ps1 "http://+:8080/" | |
28 | ||
29 | Starts webserver with binding to all IP addresses of the system. | |
30 | Administrative rights are necessary. | |
31 | .Example | |
32 | schtasks.exe /Create /TN "Powershell Webserver" /TR "powershell -file C:\Users\Markus\Documents\Start-WebServer.ps1 http://+:8080/" /SC ONSTART /RU SYSTEM /RL HIGHEST /F | |
33 | ||
34 | Starts powershell webserver as scheduled task as user local system every time the computer starts (when the | |
35 | correct path to the file Start-WebServer.ps1 is given). | |
36 | You can start the webserver task manually with | |
37 | schtasks.exe /Run /TN "Powershell Webserver" | |
38 | Delete the webserver task with | |
39 | schtasks.exe /Delete /TN "Powershell Webserver" | |
40 | Scheduled tasks are always running with low priority, so some functions might be slow. | |
41 | .Notes | |
42 | Author: Markus Scholtes, 2016-10-22 | |
43 | #> | |
44 | Param([STRING]$BINDING = 'http://localhost:8080/') | |
45 | ||
46 | # No adminstrative permissions are required for a binding to "localhost" | |
47 | # $BINDING = 'http://localhost:8080/' | |
48 | # Adminstrative permissions are required for a binding to network names or addresses. | |
49 | # + takes all requests to the port regardless of name or ip, * only requests that no other listener answers: | |
50 | # $BINDING = 'http://+:8080/' | |
51 | ||
52 | # HTML answer templates for specific calls, placeholders !RESULT, !FORMFIELD, !PROMPT are allowed | |
53 | $HTMLRESPONSECONTENTS = @{ | |
54 | 'GET /' = @" | |
55 | <html><body> | |
56 | !HEADERLINE | |
57 | <pre>!RESULT</pre> | |
58 | <form method="GET" action="/"> | |
59 | <b>!PROMPT </b><input type="text" maxlength=255 size=80 name="command" value='!FORMFIELD'> | |
60 | <input type="submit" name="button" value="Enter"> | |
61 | </form> | |
62 | </body></html> | |
63 | "@ | |
64 | 'GET /script' = @" | |
65 | <html><body> | |
66 | !HEADERLINE | |
67 | <form method="POST" enctype="multipart/form-data" action="/script"> | |
68 | <p><b>Script to execute:</b><input type="file" name="filedata"></p> | |
69 | <b>Parameters:</b><input type="text" maxlength=255 size=80 name="parameter"> | |
70 | <input type="submit" name="button" value="Execute"> | |
71 | </form> | |
72 | </body></html> | |
73 | "@ | |
74 | 'GET /download' = @" | |
75 | <html><body> | |
76 | !HEADERLINE | |
77 | <pre>!RESULT</pre> | |
78 | <form method="POST" action="/download"> | |
79 | <b>Path to file:</b><input type="text" maxlength=255 size=80 name="filepath" value='!FORMFIELD'> | |
80 | <input type="submit" name="button" value="Download"> | |
81 | </form> | |
82 | </body></html> | |
83 | "@ | |
84 | 'POST /download' = @" | |
85 | <html><body> | |
86 | !HEADERLINE | |
87 | <pre>!RESULT</pre> | |
88 | <form method="POST" action="/download"> | |
89 | <b>Path to file:</b><input type="text" maxlength=255 size=80 name="filepath" value='!FORMFIELD'> | |
90 | <input type="submit" name="button" value="Download"> | |
91 | </form> | |
92 | </body></html> | |
93 | "@ | |
94 | 'GET /upload' = @" | |
95 | <html><body> | |
96 | !HEADERLINE | |
97 | <form method="POST" enctype="multipart/form-data" action="/upload"> | |
98 | <p><b>File to upload:</b><input type="file" name="filedata"></p> | |
99 | <b>Path to store on webserver:</b><input type="text" maxlength=255 size=80 name="filepath"> | |
100 | <input type="submit" name="button" value="Upload"> | |
101 | </form> | |
102 | </body></html> | |
103 | "@ | |
104 | 'POST /script' = "<html><body>!HEADERLINE<pre>!RESULT</pre></body></html>" | |
105 | 'POST /upload' = "<html><body>!HEADERLINE<pre>!RESULT</pre></body></html>" | |
106 | 'GET /exit' = "<html><body>Stopped powershell webserver</body></html>" | |
107 | 'GET /quit' = "<html><body>Stopped powershell webserver</body></html>" | |
108 | 'GET /log' = "<html><body>!HEADERLINELog of powershell webserver:<br /><pre>!RESULT</pre></body></html>" | |
109 | 'GET /starttime' = "<html><body>!HEADERLINEPowershell webserver started at $(Get-Date -Format s)</body></html>" | |
110 | 'GET /time' = "<html><body>!HEADERLINECurrent time: !RESULT</body></html>" | |
111 | 'GET /beep' = "<html><body>!HEADERLINEBEEP...</body></html>" | |
112 | } | |
113 | ||
114 | # Set navigation header line for all web pages | |
115 | $HEADERLINE = "<p><a href='/'>Command execution</a> <a href='/script'>Execute script</a> <a href='/download'>Download file</a> <a href='/upload'>Upload file</a> <a href='/log'>Web logs</a> <a href='/starttime'>Webserver start time</a> <a href='/time'>Current time</a> <a href='/beep'>Beep</a> <a href='/quit'>Stop webserver</a></p>" | |
116 | ||
117 | # Starting the powershell webserver | |
118 | "$(Get-Date -Format s) Starting powershell webserver..." | |
119 | $LISTENER = New-Object System.Net.HttpListener | |
120 | $LISTENER.Prefixes.Add($BINDING) | |
121 | $LISTENER.Start() | |
122 | $Error.Clear() | |
123 | ||
124 | try | |
125 | { | |
126 | "$(Get-Date -Format s) Powershell webserver started." | |
127 | $WEBLOG = "$(Get-Date -Format s) Powershell webserver started.`n" | |
128 | while ($LISTENER.IsListening) | |
129 | { | |
130 | # analyze incoming request | |
131 | $CONTEXT = $LISTENER.GetContext() | |
132 | $REQUEST = $CONTEXT.Request | |
133 | $RESPONSE = $CONTEXT.Response | |
134 | $RESPONSEWRITTEN = $FALSE | |
135 | ||
136 | # log to console | |
137 | "$(Get-Date -Format s) $($REQUEST.RemoteEndPoint.Address.ToString()) $($REQUEST.httpMethod) $($REQUEST.Url.PathAndQuery)" | |
138 | # and in log variable | |
139 | $WEBLOG += "$(Get-Date -Format s) $($REQUEST.RemoteEndPoint.Address.ToString()) $($REQUEST.httpMethod) $($REQUEST.Url.PathAndQuery)`n" | |
140 | ||
141 | # is there a fixed coding for the request? | |
142 | $RECEIVED = '{0} {1}' -f $REQUEST.httpMethod, $REQUEST.Url.LocalPath | |
143 | $HTMLRESPONSE = $HTMLRESPONSECONTENTS[$RECEIVED] | |
144 | $RESULT = '' | |
145 | ||
146 | # check for known commands | |
147 | switch ($RECEIVED) | |
148 | { | |
149 | "GET /" | |
150 | { # execute command | |
151 | # retrieve GET query string | |
152 | $FORMFIELD = '' | |
153 | $FORMFIELD = [URI]::UnescapeDataString(($REQUEST.Url.Query -replace "\+"," ")) | |
154 | # remove fixed form fields out of query string | |
155 | $FORMFIELD = $FORMFIELD -replace "\?command=","" -replace "\?button=enter","" -replace "&command=","" -replace "&button=enter","" | |
156 | # when command is given... | |
157 | if (![STRING]::IsNullOrEmpty($FORMFIELD)) | |
158 | { | |
159 | try { | |
160 | # ... execute command | |
161 | $RESULT = Invoke-Expression -EA SilentlyContinue $FORMFIELD 2> $NULL | Out-String | |
162 | } | |
163 | catch {} | |
164 | if ($Error.Count -gt 0) | |
165 | { # retrieve error message on error | |
166 | $RESULT += "`nError while executing '$FORMFIELD'`n`n" | |
167 | $RESULT += $Error[0] | |
168 | $Error.Clear() | |
169 | } | |
170 | } | |
171 | # preset form value with command for the caller's convenience | |
172 | $HTMLRESPONSE = $HTMLRESPONSE -replace '!FORMFIELD', $FORMFIELD | |
173 | # insert powershell prompt to form | |
174 | $PROMPT = "PS $PWD>" | |
175 | $HTMLRESPONSE = $HTMLRESPONSE -replace '!PROMPT', $PROMPT | |
176 | break | |
177 | } | |
178 | ||
179 | "GET /script" | |
180 | { # present upload form, nothing to do here | |
181 | break | |
182 | } | |
183 | ||
184 | "POST /script" | |
185 | { # upload and execute script | |
186 | ||
187 | # only if there is body data in the request | |
188 | if ($REQUEST.HasEntityBody) | |
189 | { | |
190 | # set default message to error message (since we just stop processing on error) | |
191 | $RESULT = "Received corrupt or incomplete form data" | |
192 | ||
193 | # check content type | |
194 | if ($REQUEST.ContentType) | |
195 | { | |
196 | # retrieve boundary marker for header separation | |
197 | $BOUNDARY = $NULL | |
198 | if ($REQUEST.ContentType -match "boundary=(.*);") | |
199 | { $BOUNDARY = "--" + $MATCHES[1] } | |
200 | else | |
201 | { # marker might be at the end of the line | |
202 | if ($REQUEST.ContentType -match "boundary=(.*)$") | |
203 | { $BOUNDARY = "--" + $MATCHES[1] } | |
204 | } | |
205 | ||
206 | if ($BOUNDARY) | |
207 | { # only if header separator was found | |
208 | ||
209 | # read complete header (inkl. file data) into string | |
210 | $READER = New-Object System.IO.StreamReader($REQUEST.InputStream, $REQUEST.ContentEncoding) | |
211 | $DATA = $READER.ReadToEnd() | |
212 | $READER.Close() | |
213 | $REQUEST.InputStream.Close() | |
214 | ||
215 | $PARAMETERS = "" | |
216 | $SOURCENAME = "" | |
217 | ||
218 | # separate headers by boundary string | |
219 | $DATA -replace "$BOUNDARY--\r\n", "$BOUNDARY`r`n--" -split "$BOUNDARY\r\n" | % { | |
220 | # omit leading empty header and end marker header | |
221 | if (($_ -ne "") -and ($_ -ne "--")) | |
222 | { | |
223 | # only if well defined header (separation between meta data and data) | |
224 | if ($_.IndexOf("`r`n`r`n") -gt 0) | |
225 | { | |
226 | # header data before two CRs is meta data | |
227 | # first look for the file in header "filedata" | |
228 | if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "Content-Disposition: form-data; name=(.*);") | |
229 | { | |
230 | $HEADERNAME = $MATCHES[1] -replace '\"' | |
231 | # headername "filedata"? | |
232 | if ($HEADERNAME -eq "filedata") | |
233 | { # yes, look for source filename | |
234 | if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "filename=(.*)") | |
235 | { # source filename found | |
236 | $SOURCENAME = $MATCHES[1] -replace "`r`n$" -replace "`r$" -replace '\"' | |
237 | # store content of file in variable | |
238 | $FILEDATA = $_.Substring($_.IndexOf("`r`n`r`n") + 4) -replace "`r`n$" | |
239 | } | |
240 | } | |
241 | } | |
242 | else | |
243 | { # look for other headers (we need "parameter") | |
244 | if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "Content-Disposition: form-data; name=(.*)") | |
245 | { # header found | |
246 | $HEADERNAME = $MATCHES[1] -replace '\"' | |
247 | # headername "parameter"? | |
248 | if ($HEADERNAME -eq "parameter") | |
249 | { # yes, look for paramaters | |
250 | $PARAMETERS = $_.Substring($_.IndexOf("`r`n`r`n") + 4) -replace "`r`n$" -replace "`r$" | |
251 | } | |
252 | } | |
253 | } | |
254 | } | |
255 | } | |
256 | } | |
257 | ||
258 | if ($SOURCENAME -ne "") | |
259 | { # execute only if a source file exists | |
260 | ||
261 | $EXECUTE = "function Powershell-WebServer-Func {`n" + $FILEDATA + "`n}`nPowershell-WebServer-Func " + $PARAMETERS | |
262 | try { | |
263 | # ... execute script | |
264 | $RESULT = Invoke-Expression -EA SilentlyContinue $EXECUTE 2> $NULL | Out-String | |
265 | } | |
266 | catch {} | |
267 | if ($Error.Count -gt 0) | |
268 | { # retrieve error message on error | |
269 | $RESULT += "`nError while executing script $SOURCENAME`n`n" | |
270 | $RESULT += $Error[0] | |
271 | $Error.Clear() | |
272 | } | |
273 | } | |
274 | else | |
275 | { | |
276 | $RESULT = "No file data received" | |
277 | } | |
278 | } | |
279 | } | |
280 | } | |
281 | else | |
282 | { | |
283 | $RESULT = "No client data received" | |
284 | } | |
285 | break | |
286 | } | |
287 | ||
288 | { $_ -like "* /download" } # GET or POST method are allowed for download page | |
289 | { # download file | |
290 | ||
291 | # is POST data in the request? | |
292 | if ($REQUEST.HasEntityBody) | |
293 | { # POST request | |
294 | # read complete header into string | |
295 | $READER = New-Object System.IO.StreamReader($REQUEST.InputStream, $REQUEST.ContentEncoding) | |
296 | $DATA = $READER.ReadToEnd() | |
297 | $READER.Close() | |
298 | $REQUEST.InputStream.Close() | |
299 | ||
300 | # get headers into hash table | |
301 | $HEADER = @{} | |
302 | $DATA.Split('&') | % { $HEADER.Add([URI]::UnescapeDataString(($_.Split('=')[0] -replace "\+"," ")), [URI]::UnescapeDataString(($_.Split('=')[1] -replace "\+"," "))) } | |
303 | ||
304 | # read header 'filepath' | |
305 | $FORMFIELD = $HEADER.Item('filepath') | |
306 | # remove leading and trailing double quotes since Test-Path does not like them | |
307 | $FORMFIELD = $FORMFIELD -replace "^`"","" -replace "`"$","" | |
308 | } | |
309 | else | |
310 | { # GET request | |
311 | ||
312 | # retrieve GET query string | |
313 | $FORMFIELD = '' | |
314 | $FORMFIELD = [URI]::UnescapeDataString(($REQUEST.Url.Query -replace "\+"," ")) | |
315 | # remove fixed form fields out of query string | |
316 | $FORMFIELD = $FORMFIELD -replace "\?filepath=","" -replace "\?button=download","" -replace "&filepath=","" -replace "&button=download","" | |
317 | # remove leading and trailing double quotes since Test-Path does not like them | |
318 | $FORMFIELD = $FORMFIELD -replace "^`"","" -replace "`"$","" | |
319 | } | |
320 | ||
321 | # when path is given... | |
322 | if (![STRING]::IsNullOrEmpty($FORMFIELD)) | |
323 | { # check if file exists | |
324 | if (Test-Path $FORMFIELD -PathType Leaf) | |
325 | { | |
326 | try { | |
327 | # ... download file | |
328 | $BUFFER = [System.IO.File]::ReadAllBytes($FORMFIELD) | |
329 | $RESPONSE.ContentLength64 = $BUFFER.Length | |
330 | $RESPONSE.SendChunked = $FALSE | |
331 | $RESPONSE.ContentType = "application/octet-stream" | |
332 | $FILENAME = Split-Path -Leaf $FORMFIELD | |
333 | $RESPONSE.AddHeader("Content-disposition", "attachment; filename=$FILENAME") | |
334 | $RESPONSE.OutputStream.Write($BUFFER, 0, $BUFFER.Length) | |
335 | # mark response as already given | |
336 | $RESPONSEWRITTEN = $TRUE | |
337 | } | |
338 | catch {} | |
339 | if ($Error.Count -gt 0) | |
340 | { # retrieve error message on error | |
341 | $RESULT += "`nError while downloading '$FORMFIELD'`n`n" | |
342 | $RESULT += $Error[0] | |
343 | $Error.Clear() | |
344 | } | |
345 | } | |
346 | else | |
347 | { | |
348 | # ... file not found | |
349 | $RESULT = "File $FORMFIELD not found" | |
350 | } | |
351 | } | |
352 | # preset form value with file path for the caller's convenience | |
353 | $HTMLRESPONSE = $HTMLRESPONSE -replace '!FORMFIELD', $FORMFIELD | |
354 | break | |
355 | } | |
356 | ||
357 | "GET /upload" | |
358 | { # present upload form, nothing to do here | |
359 | break | |
360 | } | |
361 | ||
362 | "POST /upload" | |
363 | { # upload file | |
364 | ||
365 | # only if there is body data in the request | |
366 | if ($REQUEST.HasEntityBody) | |
367 | { | |
368 | # set default message to error message (since we just stop processing on error) | |
369 | $RESULT = "Received corrupt or incomplete form data" | |
370 | ||
371 | # check content type | |
372 | if ($REQUEST.ContentType) | |
373 | { | |
374 | # retrieve boundary marker for header separation | |
375 | $BOUNDARY = $NULL | |
376 | if ($REQUEST.ContentType -match "boundary=(.*);") | |
377 | { $BOUNDARY = "--" + $MATCHES[1] } | |
378 | else | |
379 | { # marker might be at the end of the line | |
380 | if ($REQUEST.ContentType -match "boundary=(.*)$") | |
381 | { $BOUNDARY = "--" + $MATCHES[1] } | |
382 | } | |
383 | ||
384 | if ($BOUNDARY) | |
385 | { # only if header separator was found | |
386 | ||
387 | # read complete header (inkl. file data) into string | |
388 | $READER = New-Object System.IO.StreamReader($REQUEST.InputStream, $REQUEST.ContentEncoding) | |
389 | $DATA = $READER.ReadToEnd() | |
390 | $READER.Close() | |
391 | $REQUEST.InputStream.Close() | |
392 | ||
393 | # variables for filenames | |
394 | $FILENAME = "" | |
395 | $SOURCENAME = "" | |
396 | ||
397 | # separate headers by boundary string | |
398 | $DATA -replace "$BOUNDARY--\r\n", "$BOUNDARY`r`n--" -split "$BOUNDARY\r\n" | % { | |
399 | # omit leading empty header and end marker header | |
400 | if (($_ -ne "") -and ($_ -ne "--")) | |
401 | { | |
402 | # only if well defined header (seperation between meta data and data) | |
403 | if ($_.IndexOf("`r`n`r`n") -gt 0) | |
404 | { | |
405 | # header data before two CRs is meta data | |
406 | # first look for the file in header "filedata" | |
407 | if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "Content-Disposition: form-data; name=(.*);") | |
408 | { | |
409 | $HEADERNAME = $MATCHES[1] -replace '\"' | |
410 | # headername "filedata"? | |
411 | if ($HEADERNAME -eq "filedata") | |
412 | { # yes, look for source filename | |
413 | if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "filename=(.*)") | |
414 | { # source filename found | |
415 | $SOURCENAME = $MATCHES[1] -replace "`r`n$" -replace "`r$" -replace '\"' | |
416 | # store content of file in variable | |
417 | $FILEDATA = $_.Substring($_.IndexOf("`r`n`r`n") + 4) -replace "`r`n$" | |
418 | } | |
419 | } | |
420 | } | |
421 | else | |
422 | { # look for other headers (we need "filepath" to know where to store the file) | |
423 | if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "Content-Disposition: form-data; name=(.*)") | |
424 | { # header found | |
425 | $HEADERNAME = $MATCHES[1] -replace '\"' | |
426 | # headername "filepath"? | |
427 | if ($HEADERNAME -eq "filepath") | |
428 | { # yes, look for target filename | |
429 | $FILENAME = $_.Substring($_.IndexOf("`r`n`r`n") + 4) -replace "`r`n$" -replace "`r$" -replace '\"' | |
430 | } | |
431 | } | |
432 | } | |
433 | } | |
434 | } | |
435 | } | |
436 | ||
437 | if ($FILENAME -ne "") | |
438 | { # upload only if a targetname is given | |
439 | if ($SOURCENAME -ne "") | |
440 | { # only upload if source file exists | |
441 | ||
442 | # check or construct a valid filename to store | |
443 | $TARGETNAME = "" | |
444 | # if filename is a container name, add source filename to it | |
445 | if (Test-Path $FILENAME -PathType Container) | |
446 | { | |
447 | $TARGETNAME = Join-Path $FILENAME -ChildPath $(Split-Path $SOURCENAME -Leaf) | |
448 | } else { | |
449 | # try name in the header | |
450 | $TARGETNAME = $FILENAME | |
451 | } | |
452 | ||
453 | try { | |
454 | # ... save file with the same encoding as received | |
455 | [IO.File]::WriteAllText($TARGETNAME, $FILEDATA, $REQUEST.ContentEncoding) | |
456 | } | |
457 | catch {} | |
458 | if ($Error.Count -gt 0) | |
459 | { # retrieve error message on error | |
460 | $RESULT += "`nError saving '$TARGETNAME'`n`n" | |
461 | $RESULT += $Error[0] | |
462 | $Error.Clear() | |
463 | } | |
464 | else | |
465 | { # success | |
466 | $RESULT = "File $SOURCENAME successfully uploaded as $TARGETNAME" | |
467 | } | |
468 | } | |
469 | else | |
470 | { | |
471 | $RESULT = "No file data received" | |
472 | } | |
473 | } | |
474 | else | |
475 | { | |
476 | $RESULT = "Missing target file name" | |
477 | } | |
478 | } | |
479 | } | |
480 | } | |
481 | else | |
482 | { | |
483 | $RESULT = "No client data received" | |
484 | } | |
485 | break | |
486 | } | |
487 | ||
488 | "GET /log" | |
489 | { # return the webserver log (stored in log variable) | |
490 | $RESULT = $WEBLOG | |
491 | break | |
492 | } | |
493 | ||
494 | "GET /time" | |
495 | { # return current time | |
496 | $RESULT = Get-Date -Format s | |
497 | break | |
498 | } | |
499 | ||
500 | "GET /starttime" | |
501 | { # return start time of the powershell webserver (already contained in $HTMLRESPONSE, nothing to do here) | |
502 | break | |
503 | } | |
504 | ||
505 | "GET /beep" | |
506 | { # Beep | |
507 | [CONSOLE]::beep(800, 300) # or "`a" or [char]7 | |
508 | break | |
509 | } | |
510 | ||
511 | "GET /quit" | |
512 | { # stop powershell webserver, nothing to do here | |
513 | break | |
514 | } | |
515 | ||
516 | "GET /exit" | |
517 | { # stop powershell webserver, nothing to do here | |
518 | break | |
519 | } | |
520 | ||
521 | default | |
522 | { # unknown command, return error | |
523 | $RESPONSE.StatusCode = 404 | |
524 | $HTMLRESPONSE = '<html><body>Page not found</body></html>' | |
525 | } | |
526 | ||
527 | } | |
528 | ||
529 | # only send response if not already done | |
530 | if (!$RESPONSEWRITTEN) | |
531 | { | |
532 | # insert header line string into HTML template | |
533 | $HTMLRESPONSE = $HTMLRESPONSE -replace '!HEADERLINE', $HEADERLINE | |
534 | ||
535 | # insert result string into HTML template | |
536 | $HTMLRESPONSE = $HTMLRESPONSE -replace '!RESULT', $RESULT | |
537 | ||
538 | # return HTML answer to caller | |
539 | $BUFFER = [Text.Encoding]::UTF8.GetBytes($HTMLRESPONSE) | |
540 | $RESPONSE.ContentLength64 = $BUFFER.Length | |
541 | $RESPONSE.OutputStream.Write($BUFFER, 0, $BUFFER.Length) | |
542 | } | |
543 | ||
544 | # and finish answer to client | |
545 | $RESPONSE.Close() | |
546 | ||
547 | # received command to stop webserver? | |
548 | if ($RECEIVED -eq 'GET /exit' -or $RECEIVED -eq 'GET /quit') | |
549 | { # then break out of while loop | |
550 | "$(Get-Date -Format s) Stopping powershell webserver..." | |
551 | break; | |
552 | } | |
553 | } | |
554 | } | |
555 | finally | |
556 | { | |
557 | # Stop powershell webserver | |
558 | $LISTENER.Stop() | |
559 | $LISTENER.Close() | |
560 | "$(Get-Date -Format s) Powershell webserver stopped." | |
561 | } |