如何正确拒绝websocket升级请求?

How to properly refuse websocket upgrade request?

有时候我想拒绝一个http客户端升级websocket连接的请求。


代码

(使用 goGingorilla/websocket 框架:)

允许升级:

c, err := ctl.upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
err = c.WriteJSON(resp)

拒绝升级(由于请求参数无效):

if contentId == "" || !exists {
    // FIXME: provide response in a way that ws client can recognize & show tip?
    ctx.String(http.StatusBadRequest, "invalid or non-existing contentId")
    return
}

说明:这里拒绝升级我只是return一个http400代码,然后终止连接,并没有做升级完全没有。


问题

使用about代码拒绝websocket升级请求的问题是,websocket客户端(例如js,无法读取数据 (文本或 json) 在我的回复中。

代码 - 客户端 (js):

ws.onerror = function (evt) {
    // TOOD: handle error, (e.g print error msg?),
    print("ERROR");
}

它确实在拒绝时打印了 "ERROR",但是在检查了关于 evt 对象的 chrome 开发者工具之后,找不到获取服务器响应数据的方法,所以我无法向前端显示小费 UI,原因是拒绝。


问题

要拒绝 websocket 连接,请不要按照问题中的描述升级连接。 浏览器 API 不提供有关连接被拒绝原因的信息,因为该信息可能违反 same-origin policy

执行以下操作将错误原因发送回客户端应用程序或用户:

  1. 升级连接。
  2. 发送一条包含错误原因的关闭消息。
  3. 关闭连接。

这是一个例子:

c, err := ctl.upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
if err != nil {
    // TODO: handle error
}
if contentId == "" || !exists {
    c.WriteMessage(websocket.CloseMessage,
         websocket.FormatCloseMessage(websocket.ClosePolicyViolation, 
         "bad content id or not exist"))
    c.Close()
    return
}
// Continue with non-error case here.

从 JS 中的关闭处理程序访问原因:

ws.onclose = function (evt) {
    if (evt.code == 1008) {  // 1008 is policy violation
       console.log(evt.reason)
    }
}