github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/gorilla/websocket/server.go (about) 1 // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package websocket 6 7 import ( 8 "bufio" 9 "errors" 10 "net" 11 "net/http" 12 "net/url" 13 "strings" 14 "time" 15 ) 16 17 // HandshakeError describes an error with the handshake from the peer. 18 type HandshakeError struct { 19 message string 20 } 21 22 func (e HandshakeError) Error() string { return e.message } 23 24 // Upgrader specifies parameters for upgrading an HTTP connection to a 25 // WebSocket connection. 26 type Upgrader struct { 27 // HandshakeTimeout specifies the duration for the handshake to complete. 28 HandshakeTimeout time.Duration 29 30 // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer 31 // size is zero, then a default value of 4096 is used. The I/O buffer sizes 32 // do not limit the size of the messages that can be sent or received. 33 ReadBufferSize, WriteBufferSize int 34 35 // Subprotocols specifies the server's supported protocols in order of 36 // preference. If this field is set, then the Upgrade method negotiates a 37 // subprotocol by selecting the first match in this list with a protocol 38 // requested by the client. 39 Subprotocols []string 40 41 // Error specifies the function for generating HTTP error responses. If Error 42 // is nil, then http.Error is used to generate the HTTP response. 43 Error func(w http.ResponseWriter, r *http.Request, status int, reason error) 44 45 // CheckOrigin returns true if the request Origin header is acceptable. If 46 // CheckOrigin is nil, the host in the Origin header must not be set or 47 // must match the host of the request. 48 CheckOrigin func(r *http.Request) bool 49 } 50 51 func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { 52 err := HandshakeError{reason} 53 if u.Error != nil { 54 u.Error(w, r, status, err) 55 } else { 56 http.Error(w, http.StatusText(status), status) 57 } 58 return nil, err 59 } 60 61 // checkSameOrigin returns true if the origin is not set or is equal to the request host. 62 func checkSameOrigin(r *http.Request) bool { 63 origin := r.Header["Origin"] 64 if len(origin) == 0 { 65 return true 66 } 67 u, err := url.Parse(origin[0]) 68 if err != nil { 69 return false 70 } 71 return u.Host == r.Host 72 } 73 74 func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { 75 if u.Subprotocols != nil { 76 clientProtocols := Subprotocols(r) 77 for _, serverProtocol := range u.Subprotocols { 78 for _, clientProtocol := range clientProtocols { 79 if clientProtocol == serverProtocol { 80 return clientProtocol 81 } 82 } 83 } 84 } else if responseHeader != nil { 85 return responseHeader.Get("Sec-Websocket-Protocol") 86 } 87 return "" 88 } 89 90 // Upgrade upgrades the HTTP server connection to the WebSocket protocol. 91 // 92 // The responseHeader is included in the response to the client's upgrade 93 // request. Use the responseHeader to specify cookies (Set-Cookie) and the 94 // application negotiated subprotocol (Sec-Websocket-Protocol). 95 // 96 // If the upgrade fails, then Upgrade replies to the client with an HTTP error 97 // response. 98 func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { 99 if r.Method != "GET" { 100 return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET") 101 } 102 if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" { 103 return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13") 104 } 105 106 if !tokenListContainsValue(r.Header, "Connection", "upgrade") { 107 return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'") 108 } 109 110 if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { 111 return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'") 112 } 113 114 checkOrigin := u.CheckOrigin 115 if checkOrigin == nil { 116 checkOrigin = checkSameOrigin 117 } 118 if !checkOrigin(r) { 119 return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed") 120 } 121 122 challengeKey := r.Header.Get("Sec-Websocket-Key") 123 if challengeKey == "" { 124 return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank") 125 } 126 127 subprotocol := u.selectSubprotocol(r, responseHeader) 128 129 var ( 130 netConn net.Conn 131 br *bufio.Reader 132 err error 133 ) 134 135 h, ok := w.(http.Hijacker) 136 if !ok { 137 return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") 138 } 139 var rw *bufio.ReadWriter 140 netConn, rw, err = h.Hijack() 141 if err != nil { 142 return u.returnError(w, r, http.StatusInternalServerError, err.Error()) 143 } 144 br = rw.Reader 145 146 if br.Buffered() > 0 { 147 netConn.Close() 148 return nil, errors.New("websocket: client sent data before handshake is complete") 149 } 150 151 c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize) 152 c.subprotocol = subprotocol 153 154 p := c.writeBuf[:0] 155 p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) 156 p = append(p, computeAcceptKey(challengeKey)...) 157 p = append(p, "\r\n"...) 158 if c.subprotocol != "" { 159 p = append(p, "Sec-Websocket-Protocol: "...) 160 p = append(p, c.subprotocol...) 161 p = append(p, "\r\n"...) 162 } 163 for k, vs := range responseHeader { 164 if k == "Sec-Websocket-Protocol" { 165 continue 166 } 167 for _, v := range vs { 168 p = append(p, k...) 169 p = append(p, ": "...) 170 for i := 0; i < len(v); i++ { 171 b := v[i] 172 if b <= 31 { 173 // prevent response splitting. 174 b = ' ' 175 } 176 p = append(p, b) 177 } 178 p = append(p, "\r\n"...) 179 } 180 } 181 p = append(p, "\r\n"...) 182 183 // Clear deadlines set by HTTP server. 184 netConn.SetDeadline(time.Time{}) 185 186 if u.HandshakeTimeout > 0 { 187 netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) 188 } 189 if _, err = netConn.Write(p); err != nil { 190 netConn.Close() 191 return nil, err 192 } 193 if u.HandshakeTimeout > 0 { 194 netConn.SetWriteDeadline(time.Time{}) 195 } 196 197 return c, nil 198 } 199 200 // Upgrade upgrades the HTTP server connection to the WebSocket protocol. 201 // 202 // This function is deprecated, use websocket.Upgrader instead. 203 // 204 // The application is responsible for checking the request origin before 205 // calling Upgrade. An example implementation of the same origin policy is: 206 // 207 // if req.Header.Get("Origin") != "http://"+req.Host { 208 // http.Error(w, "Origin not allowed", 403) 209 // return 210 // } 211 // 212 // If the endpoint supports subprotocols, then the application is responsible 213 // for negotiating the protocol used on the connection. Use the Subprotocols() 214 // function to get the subprotocols requested by the client. Use the 215 // Sec-Websocket-Protocol response header to specify the subprotocol selected 216 // by the application. 217 // 218 // The responseHeader is included in the response to the client's upgrade 219 // request. Use the responseHeader to specify cookies (Set-Cookie) and the 220 // negotiated subprotocol (Sec-Websocket-Protocol). 221 // 222 // The connection buffers IO to the underlying network connection. The 223 // readBufSize and writeBufSize parameters specify the size of the buffers to 224 // use. Messages can be larger than the buffers. 225 // 226 // If the request is not a valid WebSocket handshake, then Upgrade returns an 227 // error of type HandshakeError. Applications should handle this error by 228 // replying to the client with an HTTP error response. 229 func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { 230 u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} 231 u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { 232 // don't return errors to maintain backwards compatibility 233 } 234 u.CheckOrigin = func(r *http.Request) bool { 235 // allow all connections by default 236 return true 237 } 238 return u.Upgrade(w, r, responseHeader) 239 } 240 241 // Subprotocols returns the subprotocols requested by the client in the 242 // Sec-Websocket-Protocol header. 243 func Subprotocols(r *http.Request) []string { 244 h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) 245 if h == "" { 246 return nil 247 } 248 protocols := strings.Split(h, ",") 249 for i := range protocols { 250 protocols[i] = strings.TrimSpace(protocols[i]) 251 } 252 return protocols 253 } 254 255 // IsWebSocketUpgrade returns true if the client requested upgrade to the 256 // WebSocket protocol. 257 func IsWebSocketUpgrade(r *http.Request) bool { 258 return tokenListContainsValue(r.Header, "Connection", "upgrade") && 259 tokenListContainsValue(r.Header, "Upgrade", "websocket") 260 }