PowerShell als HTTPServer

PowerShell kann nicht nur als HTTPClient agieren und mit einem Webserver sprechen, sondern über den HTTPListener lässt sich mit PowerShell auch ganz schnell ein kleiner Webserver aufsetzen. Versuchen Sie nun aber nicht mit PowerShell einen IIS nachzubilden. Interessanter ist eher der Ansatz mit PowerShell einen kleinen REST-Server aufzubauen oder bestehende Skripte über einen HTTP-Nebeneingang abzufragen. Oder eben über den Weg über das Netzwerk Daten zu einem anderen PowerShell-Dienst zu senden.

Simpler HTTP Server

Die einfachste Form eines HTTP-Servers mit PowerShell besteht aus ganz wenigen Zeilen. Ein HTTP-Listener startet den Webserver, der ab dem Moment auf Anfragen wartet. Dann wartet das PowerShell-Skript auf einen Request um diesen dann zu verarbeiten. Der HTTPListener ist selbst schon als Thread gebaut, d.h. während das Skript einen Request verarbeitet, kann der nächste Request weiter eingehen und baut eine Queue auf.

Bei Windows 7/2008R2/2012 oder höher müssen Sie erst die Firewall informieren, dass eingehende Verbindungen erlaubt sind.

REM nicht erforderlich wenn man das Skript als Admin startet

netsh http add URLacl URL=http://+:81/ user=domain\user

Hier ein Beispielcode, mit dem ich in dem Beispiel auf Port 81 lausche.

$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add('http://+:81/')  # Must GENAU mit der Angabe in NETSH übereinstimmen 
$listener.Start()

$basename = (get-date -Format yyyyMMddHHmmss)
$count = 0

$Host.UI.RawUI.FlushInputBuffer()
[console]::TreatControlCAsInput = $true
write-host "Press any key to end after the next incoming request"
while (!([console]::KeyAvailable)) {
   $count++
   write-host ("Listening on " + $listener.Prefixes )
   $context = $listener.GetContext() # Warte auf eingehende Anfragen
   write-host "------- New Request ($count) arrived ------------"
   $request = $context.Request
   write-host (" URL.AbsoluteUri:" + $request.URL.AbsoluteUri)
   write-host (" HttpMethod     :" + $request.HttpMethod)
   if ($request.HasEntityBody) {
      write-host "Exporting Body"
	
      # converting streamreader to string
      [byte[]]$buffer = new-object byte[] 4096
      $encoding = [System.Text.Encoding]::GetEncoding($null)
      [int]$count = 0
      [string]$rcvStream =""
      do {
         $count = $request.InputStream.Read($buffer, 0, $buffer.Length)
         $rcvStream += $encoding.GetString($buffer, 0, $count)
      } while ($count -gt 0)		
		
      $rcvStream | out-file -filepath ("request"+$basename+$count+".txt")
   }
   else {
      write-host "No Body"
   }

   write-host "------- Sending OK Response ------------"
   $response = $context.Response
   $response.ContentType = 'text/plain'
   $message = "Anfrage verarbeitet"
   [byte[]] $buffer = [System.Text.Encoding]::UTF8.GetBytes($message)
   $response.ContentLength64 = $buffer.length
   $response.OutputStream.Write($buffer, 0, $buffer.length)
   $response.OutputStream.close()
}
$listener.Stop()

Diese einfache Version hat natürlich ein paar Einschränkungen:

  • Unvollständige Exit-Routing
    Es ist wichtig am Ende den Listener auch wieder zu stoppen. Wenn Sie das Skript mit "CTRL-C einfach abbrechen könnten, dann bleibt der Listener belegt und aktiv, bis die PowerShell geschlossen wird. Daher verhindert das Skript einen CTRL und beendet sich nach einem Tastendruck nach dem nächsten verarbeiteten Request.
  • Sicherheit (SSL/Auth)
    Dieser ganz einfache Code enthält keine Funktion für eine Verschlüsselung per SSL oder Authentifizierung. Wer hier aktiv werden will, sollte vielleicht doch besser eine Applikation im IIS entwickeln. Das Rad muss man ja nicht mehrfach erfinden.
  • Serielle Abarbeitung
    Dieses Beispielcode arbeitet einen Request nach dem anderen ab. Es ist eine reine serielle Abarbeitung und skaliert also nicht gut, wenn viele parallele Clients mit mehreren Threads Daten abrufen. Ein "lang dauernder Prozess" lähmt also andere nachfolgende Prozesse

Aber das Ziel ist ja nicht gleich einen Apache oder IIS-Wettbewerber zu entwickeln, sondern erst mal einen einfachen Service zu bauen, der per HTTP Daten annehmen und ausliefern kann.

Weitere Links