如何从状态为 101 切换协议的响应中读取正文

Posted

技术标签:

【中文标题】如何从状态为 101 切换协议的响应中读取正文【英文标题】:How to read body from response with status 101 Switching Protocols 【发布时间】:2020-07-16 21:38:48 【问题描述】:

我已连接到 Kubernetes 集群中的服务器,使用 POST 请求和标头来升级请求。我正在使用以下功能:

func PostRequest(client *http.Client, url string, bodyData []byte) (*http.Response, error)
    req, _ := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
    //req.Header.Set("Authorization", "Bearer " + BEARER_TOKEN)
    req.Header.Set("X-Stream-Protocol-Version", "v2.channel.k8s.io")
    req.Header.Set("X-Stream-Protocol-Version", "channel.k8s.io")
    req.Header.Set("Upgrade", "SPDY/3.1")
    req.Header.Set("Connection","upgrade")
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

    resp, err := (*client).Do(req)

    return resp, err

收到回复后,我尝试阅读,但阅读正文时卡住了:

url2 := "https://<serveri_ip>:10250/exec/default/mypod/mycontainer?command=ls&command=/&input=1&output=1&tty=1"

resp, err := PostRequest(api.GlobalClient, url2, []byte(""))
fmt.Println(r.Status)
fmt.Println(r.Header)
bodyBytes, err := ioutil.ReadAll(r.Body) // -> it stuck here
fmt.Println(string(bodyBytes))  

我想它试图打开 websocket,所以我尝试像这样使用gorilla websocket library:

u := url.URLScheme: "ws", Host: "<node_ip>:10250", Path: "/exec/default/mypod/mycontainer?command=ls&command=/&input=1&output=1&tty=1"

interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)

c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil 
  log.Fatal("dial:", err)

defer c.Close()

但它打印了一个错误:

2020/04/04 20:51:25 dial:websocket: 握手错误

我做错了什么? 如何从状态“切换协议”中读取响应正文

【问题讨论】:

关于使用 Gorilla 包。该错误表明服务器未使用 Web 套接字握手进行回复。端点可能由于多种原因导致握手失败:端点不支持协议、身份验证失败、重定向等。打印从 Dial 返回的响应以调试问题。此外,101 响应用于其他协议,例如 spdy 和 h2。 我认为这是因为端点正在使用 SPDY。所以我需要检查如何从 SPDY 协议中读取。我正在检查这个项目:github.com/amahi/spdy 我不确定您在这里期望什么。您正在执行升级请求 - 对此没有响应正文,但如果响应成功(即也是升级),您需要继续使用新协议。如果您不知道如何处理您请求的新协议(即 SPDY,而不是 Websockets 或其他),则不应请求它。 我不知道如何处理我需要的新协议。因为这就是端点的工作方式,所以他们正在使用 SPDY。所以我想了解如何使用这个协议来阅读它。试了上面写的SPDY库还是没有成功。 【参考方案1】:

我设法用 Kubernetes go-client 库做到了:

package main

import (
    "crypto/tls"
    "fmt"
    "net/http"
    "net/url"
    "os"
    "time"

    restclient "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/remotecommand"
)


func main()

    /*req.Header.Add("X-Stream-Protocol-Version", "v4.channel.k8s.io")
    req.Header.Add("X-Stream-Protocol-Version", "v3.channel.k8s.io")
    req.Header.Add("X-Stream-Protocol-Version", "v2.channel.k8s.io")
    req.Header.Add("X-Stream-Protocol-Version", "channel.k8s.io")
    req.Header.Add("Connection", "upgrade")
    req.Header.Add("Upgrade", "SPDY/3.1")*/
    //url2 := "https://123.123.123.123:10250/exec/default/my-pod/nginx?command=ls&command=/&input=1&output=1&tty=1"
    tr := &http.Transport
        MaxIdleConns:       10,
        IdleConnTimeout:    30 * time.Second,
        DisableCompression: true,
        TLSClientConfig: &tls.ConfigInsecureSkipVerify: true,
    

    config := &restclient.Config
        Host:                "https://123.123.123.123:10250",
        APIPath:             "/exec/default/my-pod/nginx",
        TLSClientConfig:     restclient.TLSClientConfig
            Insecure: true,
        ,
        Transport:           tr,
    

    url3 := &url.URL
        Scheme:     "https",
        Opaque:     "",
        User:       nil,
        Host:       "123.123.123.123:10250",
        Path:       "/exec/default/my-pod/nginx",
        RawPath:    "",
        RawQuery:   "command=ls&command=/&input=1&output=1&tty=1",
    
    exec, err := remotecommand.NewSPDYExecutor(config, "POST", url3)
    if err != nil 
        fmt.Println(err)
    

    // Thanks for this blog post https://www.henryxieblogs.com/2019/05/
    err = exec.Stream(remotecommand.StreamOptions
        Stdin:  os.Stdin,
        Stdout: os.Stdout,
        Stderr: os.Stderr,
        Tty:true,
    )

    fmt.Println(err)

【讨论】:

以上是关于如何从状态为 101 切换协议的响应中读取正文的主要内容,如果未能解决你的问题,请参考以下文章

网页状态码为:200,是啥意思

从 http 响应正文读取 golang

如何理解HTTP响应的状态码

HTTP状态码(响应码)

2021-11-27 WPF上位机 101-西门子S7协议之S7.NET

OkHttp 2.0 响应(POST 请求)正文字符串为空字符串