websocket包实现了在RFC 6455中定义的WebSocket协议
包中文件:client.go\client_clone.go\compression.go\conn.go\conn_write.go\doc.go\join.go\json.go\mask.go\prepared.go\proxy.go\server.go\trace.go\util.go\x_net_proxy.go
server.go
type HandshakeError&func (HandshakeError) Error
// HandshakeError describes an error with the handshake from the peer.
/@zh HandshakeError(握手错误) 描述当与节点握手时的错误
type HandshakeError struct {
message string
}
func (e HandshakeError) Error() string { return e.message }
type Upgrader&func (*Upgrader) returnError
// Upgrader specifies parameters for upgrading an HTTP connection to a
// WebSocket connection.
/@zh Upgrader(协议升级器) 指定参数升级一个HTTP连接到WebSoket连接
type Upgrader struct {
// HandshakeTimeout specifies the duration for the handshake to complete.
/@zh HandshakeTimeout(握手超时) 指定完成握手所需的时间
HandshakeTimeout time.Duration
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
// size is zero, then buffers allocated by the HTTP server are used. The
// I/O buffer sizes do not limit the size of the messages that can be sent
// or received.
/@zh ReadBufferSize(读取缓冲区大小)和WriteBufferSize(写入缓冲区大小)指定读写缓冲区的字节大小。
/@zh 如果缓冲区大小为0,则会使用对应HTTP服务器分配的缓冲区。
/@zh 读写缓冲区大小并不会限制发送和接收消息的大小。
ReadBufferSize, WriteBufferSize int
// WriteBufferPool is a pool of buffers for write operations. If the value
// is not set, then write buffers are allocated to the connection for the
// lifetime of the connection.
/@zh WriteBufferPool(写缓冲池) 是一个写操作的缓冲池。
/@zh 如果这个值未设定,则连接的写缓冲区会被分配到它的整个生命周期。
//
// A pool is most useful when the application has a modest volume of writes
// across a large number of connections.
/@zh 当应用在连接数很大的情况下进行写入时,池是很有用的。
//
// Applications should use a single pool for each unique value of
// WriteBufferSize.
/@zh 应用要为每个不同的WriteBufferSize(写缓冲区大小)使用独立的池。
WriteBufferPool BufferPool
// Subprotocols specifies the server's supported protocols in order of
// preference. If this field is not nil, then the Upgrade method negotiates a
// subprotocol by selecting the first match in this list with a protocol
// requested by the client. If there's no match, then no protocol is
// negotiated (the Sec-Websocket-Protocol header is not included in the
// handshake response).
/@zh 子协议按照优先顺序指定了服务器支持的协议。
/@zh 如果这个值不为nil,则Upgrade(升级)则Upgrade方法通过选择此列表中与客户端请求的协议的第一个匹配项来协商子协议。
/@zh 如果没有匹配项,则不会协商任何协议(握手的响应中不会包含 Sec-Websocket-Protocol头)。
Subprotocols []string
// Error specifies the function for generating HTTP error responses. If Error
// is nil, then http.Error is used to generate the HTTP response.
/@zh Error(错误)指定了生成HTTP错误响应的函数。如果Error(错误)是nil,则使用http.Error来生成HTTP响应
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
// CheckOrigin returns true if the request Origin header is acceptable. If
// CheckOrigin is nil, then a safe default is used: return false if the
// Origin request header is present and the origin host is not equal to
// request Host header.
/@zh 如果请求的Origin头是合理的,CheckOrigin(Origin校验)会返回true。
/@zh 如果CheckOrigin(Origin校验)是nil,则使用安全默认值:若现有Origin请求头和主机源的请求Host头不符,返回false。
//
// A CheckOrigin function should carefully validate the request origin to
// prevent cross-site request forgery.
/@zh 一个CheckOrigin(校验Origin)函数应仔细验证请求origin来防止伪造的跨站请求。
CheckOrigin func(r *http.Request) bool
// EnableCompression specify if the server should attempt to negotiate per
// message compression (RFC 7692). Setting this value to true does not
// guarantee that compression will be supported. Currently only "no context
// takeover" modes are supported.
/@zh EnableCompression(启用压缩)定义了服务器是否应协商每个消息压缩(RFC 7692)。
/@zh 设定此值为true并不能保证能支持压缩。当前只支持“无上下文”模式。
EnableCompression bool
}
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
err := HandshakeError{reason}
if u.Error != nil {
u.Error(w, r, status, err)
} else {
w.Header().Set("Sec-Websocket-Version", "13")
http.Error(w, http.StatusText(status), status)
}
return nil, err
}
func (*Upgrader) Upgrade
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
/@zh Upgrader(协议升级器)升级HTTP服务器连接到WebSoket协议
//
// The responseHeader is included in the response to the client's upgrade
// request. Use the responseHeader to specify cookies (Set-Cookie). To specify
// subprotocols supported by the server, set Upgrader.Subprotocols directly.
/@zh 对客户端升级请求的响应中包含了响应头responseHeader。用响应头responseHeader来设定cookies(Set-Cookie)。
/@zh 若要指定服务器支持的子协议,直接设置Upgrader.Subprotocols
//
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
// response.
/@zh 如果升级失败,Upgrader(协议升级器)将会回应一个HTTP错误响应给客户端
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
const badHandshake = "websocket: the client is not using the websocket protocol: "
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header")
}
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
}
if r.Method != "GET" {
return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
}
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
}
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported")
}
checkOrigin := u.CheckOrigin
if checkOrigin == nil {
checkOrigin = checkSameOrigin
}
if !checkOrigin(r) {
return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin")
}
challengeKey := r.Header.Get("Sec-Websocket-Key")
if challengeKey == "" {
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank")
}
subprotocol := u.selectSubprotocol(r, responseHeader)
// Negotiate PMCE
var compress bool
if u.EnableCompression {
for _, ext := range parseExtensions(r.Header) {
if ext[""] != "permessage-deflate" {
continue
}
compress = true
break
}
}
h, ok := w.(http.Hijacker)
if !ok {
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
}
var brw *bufio.ReadWriter
netConn, brw, err := h.Hijack()
if err != nil {
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
}
if brw.Reader.Buffered() > 0 {
netConn.Close()
return nil, errors.New("websocket: client sent data before handshake is complete")
}
var br *bufio.Reader
if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 {
// Reuse hijacked buffered reader as connection reader.
br = brw.Reader
}
buf := bufioWriterBuffer(netConn, brw.Writer)
var writeBuf []byte
if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {
// Reuse hijacked write buffer as connection buffer.
/@zh 重用被劫持的写缓冲器作为连接缓冲区
writeBuf = buf
}
c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)
c.subprotocol = subprotocol
if compress {
c.newCompressionWriter = compressNoContextTakeover
c.newDecompressionReader = decompressNoContextTakeover
}
// Use larger of hijacked buffer and connection write buffer for header.
p := buf
if len(c.writeBuf) > len(p) {
p = c.writeBuf
}
p = p[:0]
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"...)
if c.subprotocol != "" {
p = append(p, "Sec-WebSocket-Protocol: "...)
p = append(p, c.subprotocol...)
p = append(p, "\r\n"...)
}
if compress {
p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
}
for k, vs := range responseHeader {
if k == "Sec-Websocket-Protocol" {
continue
}
for _, v := range vs {
p = append(p, k...)
p = append(p, ": "...)
for i := 0; i < len(v); i++ {
b := v[i]
if b <= 31 {
// prevent response splitting.
b = ' '
}
p = append(p, b)
}
p = append(p, "\r\n"...)
}
}
p = append(p, "\r\n"...)
// Clear deadlines set by HTTP server.
netConn.SetDeadline(time.Time{})
if u.HandshakeTimeout > 0 {
netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
}
if _, err = netConn.Write(p); err != nil {
netConn.Close()
return nil, err
}
if u.HandshakeTimeout > 0 {
netConn.SetWriteDeadline(time.Time{})
}
return c, nil
}