dounangshen6553 2018-11-04 00:29
浏览 171

WebSocket反向代理中无法使用的响应主体

Go's httputil.ReverseProxy doesn't work for WebSocket traffic. The issues with it are detailed here. It looks like the fix/feature may be released in Go 1.12 (see "added this to the Go1.12 milestone" here).

As the fix/feature is not out yet, I am trying to write my own reverse proxy to solve the problem until it is released. On my server, I wrote a simple gorilla/websocket handler that upgrades the connection and then waits for a message from the client.

On my reverse proxy server, I wrote a reverse proxy handler based on the Go 1.12 change submission here, especially the additions in reverseproxy.go. Here is the core of my reverse proxy handler, in relevant part:

outreq := r.WithContext(ctx)
if r.ContentLength == 0 {
    outreq.Body = nil
}
p.director(outreq)
outreq.Header = copyHeaders(browserHeaders)
setForwardHeader(r.RemoteAddr, outreq)
log.Infof("outreq URL is %+v", outreq.URL)

transport := http.DefaultTransport
res, err := transport.RoundTrip(outreq)
if err != nil {
    log.Errorf("RoundTrip(outreq) failed: %s", err)
}
log.Infof("Server response was %+v", res)

if res.StatusCode != http.StatusSwitchingProtocols {
    log.Errorf("Did not receive HTTP status 101 switchingn protocols: %d
", res.StatusCode)
    return
}
if !hasUpgradeHeaders(res.Header) {
    log.Error("request does not contain upgrade headers")
    return
}
backConn, ok := res.Body.(io.ReadWriteCloser)
if !ok {
    log.Errorf("internal error: 101 switching protocols response with non-writable body: %v (%T)", res.Body, res.Body)
    return
}

When I try to connect a websocket from my browser, I get as far as that last if statement. The type assertion of the response body to type io.ReadWriteCloser failed. When I log the response I see this:

Server response was &{Status:101 Switching Protocols StatusCode:101 Proto:HTTP/1.1 
ProtoMajor:1 ProtoMinor:1 Header:map[Upgrade:[websocket] Connection:[Upgrade]
Sec-Websocket-Accept:[B1fWETPTtNo0bAHFWkFn2lAm9iw=]] Body:{} ContentLength:0 
TransferEncoding:[] Close:false Uncompressed:false 
Trailer:map[] Request:0xc000112100 TLS:<nil>}

The error log output is this:

ERRO[0012] internal error: 101 switching protocols response with non-writable body: {} (http.noBody)

Why am I getting http.noBody in the response body rather than something implementing the io.ReadWriteCloser interface? How can I resolve the issue so that I can proceed to hijack the connection and facilitate communication between the browser and the server?

Update

I think I found the answer to the "why" question. The latest release of Go does not include this line in persistConn's readLoop function in transport.go in the net/http package:

if resp.isProtocolSwitch() {
    resp.Body = newReadWriteCloserBody(pc.br, pc.conn)
}

Source

Compare to current here.

Any ideas for how to resolve?

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
    • ¥15 想问一下树莓派接上显示屏后出现如图所示画面,是什么问题导致的
    • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
    • ¥15 cmd cl 0x000007b
    • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
    • ¥500 火焰左右视图、视差(基于双目相机)
    • ¥100 set_link_state
    • ¥15 虚幻5 UE美术毛发渲染
    • ¥15 CVRP 图论 物流运输优化
    • ¥15 Tableau online 嵌入ppt失败