PowerShell 練習 - 使用 Socket 取得 HTTP/HTTPS 回應
0 | 2,664 |
要存取網站內容,PowerShell 有個 Invoke-WebRequest 滿足各式需求(延伸閱讀:野外求生系列- 無工具WebApi 徒手測試-黑暗執行緒),甚至呼叫 WCF 也難不倒它。
我有個需求是要偵測位於負載平衡設備(Network Load Balancer,以下簡稱 NLB)後方的 Web Farm,平時 NLB 平均分配 HTTP 請求給 A、B、C 三台網站伺服器處理,我想做到 1) 驗證流量會平均送到三台主機上 2) 測試三台主機都在正常提供服務。
直覺做法是連續發送多個 HTTP,統計落在各台主機的比例,順便確認三台主機都有提供服務。
一開始用 Invoke-WebRequest 標準寫法,發現它會引用 HttpWebRequest 的連線共用機制,無論跑幾次都是連同一台。依據在 HttpWebRequest 如何重複使用 TCP 連線?所做的研究,關掉 KeepAlive 可以迴避連線共用,而強大的 Invoke-WebRequest 也真有個 -DisableKeepAlive 參數,實測停用後如預期每次建立新的 TCP 連線,也會被 NLB 平均分配到各伺服器,滿足了要求。
雖然有現成工具可以解決問題,但手癢問題無法解決,我還是想造顆輪子伸展一下 - 便試著用 PowerShell 自己建 Socket 連上網站取回 HTTP/HTTPS 回應。
程式邏輯如下:
- 輸入 URL,用 System.Uri 解析出 Scheme、Host、Port、PathAndQuery
- 建立 Socket 連向 Host 的 80/443 Port
- 取得 Stream 以寫入及讀取資料
- 若走 HTTPS,則建立 SslStream 把 Stream 包起來 (裝飾者模式(Decorator Pattern))
- 以 StreamWriter 依 HTTP 規範寫入 GET /PathAndQuery HTTP/1.1... 請求
- 宣告 byte[] 呼叫 Stream.Read(byteArray, startIndex, dataLength) 分次讀取資料直到讀取長度為 0,讀取的資料寫入 MemoryStream 累積起來
- 以 Encoding.UTF8.GetString() 將 MemoryString.ToArray() 轉為文字
Param (
[string]$url
)
$ErrorActionPreference = "STOP"
[System.Uri]$uri = New-Object System.Uri($url)
$isSsl = $uri.Scheme -eq 'https'
$hostName = $uri.Host
$path = $uri.PathAndQuery
$req = @"
GET $path HTTP/1.1
Host: $hostName
Connection: close
User-Agent: Mozilla/5.0
Accept: */*
"@
$port = 80
if ($isSsl) { $port = 443 }
if (!$uri.IsDefaultPort) { $port = $uri.Port }
$socket = New-Object System.Net.Sockets.TcpClient($hostName, $port)
$stream = $socket.GetStream()
if ($isSsl) {
$sslStream = New-Object System.Net.Security.SslStream($stream, $false)
$sslStream.AuthenticateAsClient($hostName)
$stream = $sslStream
}
$sw = New-Object System.IO.StreamWriter($stream)
$sw.WriteLine($req)
$sw.Flush()
$buff = New-Object System.Byte[] 1024
$ms = New-Object System.IO.MemoryStream
do {
Start-Sleep -Milliseconds 100
$stream.ReadTimeout = 1000
$more = $false
$readBytes = 0
do {
try {
$readBytes = $stream.Read($buff, 0, 1024)
if ($readBytes -gt 0) {
$more = $true
$ms.Write($buff, 0, $readBytes)
}
}
catch {
$more = $false
$readBytes = 0
}
} while ($readBytes -gt 0)
} while ($more)
# TODO Support encoding besides UTF8
$enc = [System.Text.Encoding]::UTF8
$resp = $enc.GetString($ms.ToArray())
$ms.Dispose()
$stream.Dispose()
$socket.Dispose()
Write-Output $resp
如此,一個簡單的 HTTP/HTTPS 讀取程式就完成囉~
Example of using PowerShell to open socket, send HTTP/HTTPS GET requqest and read response.
Comments
Be the first to post a comment