go websocket 问题(Hijacker)

go websocket 问题(Hijacker)

在写websocket包的时候发现一个比较有趣问题!go 使用 TLS验证的时候发现 websocket 使用不了。深入了解发现其中奥秘:go 在执行 TLS 验证时候默认是使用 http2 协议进行的!但是 websocket 是无法支持 http2 协议(暂时),导致这个问题所在的原因!

解决方法:

  1. 不开启 http/2 协议
  2. 使用 Hijacker
第一种:不开始 http/2 协议

使用空 map 来使用 http1.x协议

server := http.Server {
Addr : ":8080",
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), // 这
}
log.Fatal(server.ListenAndServeTLS("./coms.crt", "./coms.key"))

因为默认支持h2,所有我们把降到http1.x。

第二种:使用 Hijack
// w 是 http.ResponseWriter
hj,ok := w.(http.Hijacker)
  if !ok {
    return
}
conn , _ , err := hj.Hijack()
if err != nil {
    return
}

使用 http.Hijacker 对其进行劫持 net.Conn , 让程序员自己控制使用!其实这个时候已经脱离 http 协议规范!

//hijacker请求
http.HandleFunc("/hijacker", func(w http.ResponseWriter, r *http.Request) {
      hj, _ := w.(http.Hijacker)
      conn, buf, err := hj.Hijack()
      if err != nil {
          return
       }
       defer conn.Close()
       buf.WriteString("coms is good boy\r\n")
       buf.Flush()
})

//正常的http请求
http.HandleFunc("/http", func(writer http.ResponseWriter, request *http.Request) {
    io.WriteString(w,"coms is good boy")
})
$ curl "http://127.0.0.1:9001/hijacker" -i
coms is good boy

$ curl "http://127.0.0.1:9001/http" -i
HTTP/1.1 200 OK
Date: Mon, 30 September 2020 01:11:52 GMT
Content-Length: 16
Content-Type: text/plain; charset=utf-8

coms is good boy

发现使用 Hijacker 会脱离 http 协议范畴,可以解决h2与websocket的相关问题!

使用Hijacker实现 websocket 实现连接测试

协议: 状态码:101 , Upgrade , Connection, Sec-WebSocket-Accept

package main

import (
    "crypto/sha1"
    "encoding/base64"
    "fmt"
    "net/http"
)

var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")

func computeAcceptKey(challengeKey string) string {
    h := sha1.New()
    h.Write([]byte(challengeKey))
    h.Write(keyGUID)
    return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func echo(w http.ResponseWriter,r *http.Request) {
    hj,ok := w.(http.Hijacker)
    if !ok {
        return
    }

    conn , _ , err := hj.Hijack()
    if err != nil {
        return
    }


    challengeKey := r.Header.Get("Sec-Websocket-Key")
    var p []byte
    p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
    p = append(p, computeAcceptKey(challengeKey)...)
    p = append(p, "\r\n"...)
    p = append(p, "\r\n"...)
    if _, err = conn.Write(p); err != nil {
        fmt.Println(err)
        conn.Close()
    }
}

func main() {
    http.HandleFunc("/",echo)
    http.ListenAndServe(":8082",nil)
}

实现最简单 websocket 连接!


QQ截图20201001220741.png

websocket连接已经建立,有些 sec 是默认添加上去!

对 Hijack 源码分析
  1. 如何脱离协议范畴
  2. 如何劫持连接

根据 http.Listen 进行向下追踪
在 func (srv *Server) Serve(l net.Listener) error 发现原由

func (srv *Server) Serve(l net.Listener) error {
  ...
  c.setState(c.rwc, StateNew) 
  go c.serve(connCtx)
}

对 c.setState 进入分析

func (c *conn) setState(nc net.Conn, state ConnState) {
    srv := c.server
    switch state {
    case StateNew:
        srv.trackConn(c, true)
    case StateHijacked, StateClosed:
        srv.trackConn(c, false)
    }
    if state > 0xff || state < 0 {
        panic("internal error")
    }
    packedState := uint64(time.Now().Unix()<<8) | uint64(state)
    atomic.StoreUint64(&c.curState.atomic, packedState)
    if hook := srv.ConnState; hook != nil {
        hook(nc, state)
    }
}

发现当 case 是 Hijack【StateHijacked, StateClosed】状态时候执行 trackConn

func (s *Server) trackConn(c *conn, add bool) {
    s.mu.Lock()
    defer s.mu.Unlock()
    if s.activeConn == nil {
        s.activeConn = make(map[*conn]struct{})
    }
    if add {
        s.activeConn[c] = struct{}{}
    } else {
        delete(s.activeConn, c)
    }
}

add 为 false ,对其 delete(s.activeConn,c) 。Hijacker是满足相关条件
所以在 go c.serve(connCtx) 里面不在给 Hijacker 进行操作!导致 http header 无法设置!最后交给程序员自己操作!

使用场景

RPC使用

go中自带的rpc可以直接复用http server处理请求的那一套流程去创建连接,连接创建完毕后再使用Hijack方法拿到连接。

func (server *server) serveset(w http.responsewriter, req *http.request) {
    if req.method != "connect" {
        w.header().set("content-type", "text/plain; charset=utf-8")
        w.writeheader(http.statusmethodnotallowed)
        io.writestring(w, "405 must connect\n")
        return
    }
    conn, _, err := w.(http.hijacker).hijack()
    if err != nil {
        log.print("rpc hijacking ", req.remoteaddr, ": ", err.error())
        return
    }
    io.writestring(conn, "http/1.0 "+connected+"\r\n")
    server.serveconn(conn)
}
还有在上述的 websocket 实用

注: github.com/gorilla/websocket
这包就接入 HIjacker ,直接使用现成包就香~~~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355