2014-04-19 01:25:11 +04:00
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
2013-10-16 20:41:47 +04:00
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
2014-04-18 01:45:40 +04:00
"bufio"
"errors"
2018-08-18 05:50:34 +03:00
"io"
2013-10-16 20:41:47 +04:00
"net/http"
2014-04-18 02:07:36 +04:00
"net/url"
2013-10-26 17:52:30 +04:00
"strings"
2014-04-18 01:45:40 +04:00
"time"
2013-10-16 20:41:47 +04:00
)
// HandshakeError describes an error with the handshake from the peer.
type HandshakeError struct {
2013-10-27 19:34:33 +04:00
message string
2013-10-16 20:41:47 +04:00
}
2013-10-27 19:34:33 +04:00
func ( e HandshakeError ) Error ( ) string { return e . message }
2013-10-16 20:41:47 +04:00
2014-04-20 22:38:35 +04:00
// Upgrader specifies parameters for upgrading an HTTP connection to a
// WebSocket connection.
2021-12-16 22:07:50 +03:00
//
// It is safe to call Upgrader's methods concurrently.
2014-04-18 01:45:40 +04:00
type Upgrader struct {
// HandshakeTimeout specifies the duration for the handshake to complete.
HandshakeTimeout time . Duration
2018-10-30 17:45:53 +03:00
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
2017-03-03 01:46:13 +03:00
// 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.
2014-04-18 01:45:40 +04:00
ReadBufferSize , WriteBufferSize int
2018-08-18 05:50:34 +03:00
// 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.
//
// A pool is most useful when the application has a modest volume of writes
// across a large number of connections.
//
// Applications should use a single pool for each unique value of
// WriteBufferSize.
WriteBufferPool BufferPool
2014-04-20 22:38:35 +04:00
// Subprotocols specifies the server's supported protocols in order of
2018-08-17 01:18:03 +03:00
// preference. If this field is not nil, then the Upgrade method negotiates a
2014-04-20 22:38:35 +04:00
// subprotocol by selecting the first match in this list with a protocol
2018-08-17 01:18:03 +03:00
// 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).
2014-04-20 22:38:35 +04:00
Subprotocols [ ] string
2014-04-18 01:45:40 +04:00
// Error specifies the function for generating HTTP error responses. If Error
// is nil, then http.Error is used to generate the HTTP response.
Error func ( w http . ResponseWriter , r * http . Request , status int , reason error )
2014-04-20 22:38:35 +04:00
// CheckOrigin returns true if the request Origin header is acceptable. If
2018-01-25 21:51:21 +03:00
// 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.
//
// A CheckOrigin function should carefully validate the request origin to
// prevent cross-site request forgery.
2014-04-18 01:45:40 +04:00
CheckOrigin func ( r * http . Request ) bool
2016-09-30 07:52:15 +03:00
2016-10-18 03:30:22 +03:00
// EnableCompression specify if the server should attempt to negotiate per
2016-09-30 07:52:15 +03:00
// 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.
2016-10-18 03:30:22 +03:00
EnableCompression bool
2014-04-18 01:45:40 +04:00
}
2014-04-18 02:20:46 +04:00
func ( u * Upgrader ) returnError ( w http . ResponseWriter , r * http . Request , status int , reason string ) ( * Conn , error ) {
err := HandshakeError { reason }
2014-04-18 01:45:40 +04:00
if u . Error != nil {
2014-04-18 02:20:46 +04:00
u . Error ( w , r , status , err )
2014-04-18 01:45:40 +04:00
} else {
2016-06-29 23:56:55 +03:00
w . Header ( ) . Set ( "Sec-Websocket-Version" , "13" )
2014-04-20 22:38:35 +04:00
http . Error ( w , http . StatusText ( status ) , status )
2014-04-18 01:45:40 +04:00
}
2014-04-18 02:20:46 +04:00
return nil , err
2014-04-18 01:45:40 +04:00
}
2014-07-01 01:57:20 +04:00
// checkSameOrigin returns true if the origin is not set or is equal to the request host.
2014-04-20 22:38:35 +04:00
func checkSameOrigin ( r * http . Request ) bool {
2014-07-01 01:57:20 +04:00
origin := r . Header [ "Origin" ]
if len ( origin ) == 0 {
return true
2014-04-18 02:07:36 +04:00
}
2014-07-01 01:57:20 +04:00
u , err := url . Parse ( origin [ 0 ] )
2014-04-18 02:07:36 +04:00
if err != nil {
return false
}
2017-11-28 03:10:45 +03:00
return equalASCIIFold ( u . Host , r . Host )
2014-04-20 22:38:35 +04:00
}
func ( u * Upgrader ) selectSubprotocol ( r * http . Request , responseHeader http . Header ) string {
if u . Subprotocols != nil {
clientProtocols := Subprotocols ( r )
2022-10-20 11:04:56 +03:00
for _ , clientProtocol := range clientProtocols {
for _ , serverProtocol := range u . Subprotocols {
2014-04-20 22:38:35 +04:00
if clientProtocol == serverProtocol {
return clientProtocol
}
}
}
} else if responseHeader != nil {
return responseHeader . Get ( "Sec-Websocket-Protocol" )
}
return ""
2014-04-18 02:07:36 +04:00
}
2014-04-18 01:45:40 +04:00
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
//
// The responseHeader is included in the response to the client's upgrade
2020-08-23 00:03:32 +03:00
// request. Use the responseHeader to specify cookies (Set-Cookie). To specify
// subprotocols supported by the server, set Upgrader.Subprotocols directly.
2016-02-22 00:34:30 +03:00
//
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
// response.
2014-04-18 01:45:40 +04:00
func ( u * Upgrader ) Upgrade ( w http . ResponseWriter , r * http . Request , responseHeader http . Header ) ( * Conn , error ) {
2017-12-01 03:45:44 +03:00
const badHandshake = "websocket: the client is not using the websocket protocol: "
2014-04-18 01:45:40 +04:00
if ! tokenListContainsValue ( r . Header , "Connection" , "upgrade" ) {
2017-12-01 03:45:44 +03:00
return u . returnError ( w , r , http . StatusBadRequest , badHandshake + "'upgrade' token not found in 'Connection' header" )
2014-04-18 01:45:40 +04:00
}
if ! tokenListContainsValue ( r . Header , "Upgrade" , "websocket" ) {
2017-12-01 03:45:44 +03:00
return u . returnError ( w , r , http . StatusBadRequest , badHandshake + "'websocket' token not found in 'Upgrade' header" )
}
2021-12-19 19:21:45 +03:00
if r . Method != http . MethodGet {
2017-12-01 03:45:44 +03:00
return u . returnError ( w , r , http . StatusMethodNotAllowed , badHandshake + "request method is not GET" )
2017-01-23 21:52:19 +03:00
}
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" )
2014-04-18 01:45:40 +04:00
}
2017-12-01 03:45:44 +03:00
if _ , ok := responseHeader [ "Sec-Websocket-Extensions" ] ; ok {
2018-04-20 20:16:12 +03:00
return u . returnError ( w , r , http . StatusInternalServerError , "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported" )
2017-12-01 03:45:44 +03:00
}
2014-04-18 02:07:36 +04:00
checkOrigin := u . CheckOrigin
if checkOrigin == nil {
2014-04-20 22:38:35 +04:00
checkOrigin = checkSameOrigin
2014-04-18 02:07:36 +04:00
}
if ! checkOrigin ( r ) {
2018-01-25 21:51:21 +03:00
return u . returnError ( w , r , http . StatusForbidden , "websocket: request origin not allowed by Upgrader.CheckOrigin" )
2014-04-18 01:45:40 +04:00
}
2014-04-20 22:38:35 +04:00
challengeKey := r . Header . Get ( "Sec-Websocket-Key" )
2022-02-16 04:15:20 +03:00
if ! isValidChallengeKey ( challengeKey ) {
return u . returnError ( w , r , http . StatusBadRequest , "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length" )
2014-04-18 01:45:40 +04:00
}
2014-04-20 22:38:35 +04:00
subprotocol := u . selectSubprotocol ( r , responseHeader )
2014-04-18 01:45:40 +04:00
2016-09-30 07:52:15 +03:00
// Negotiate PMCE
var compress bool
2016-10-18 03:30:22 +03:00
if u . EnableCompression {
2016-09-30 07:52:15 +03:00
for _ , ext := range parseExtensions ( r . Header ) {
if ext [ "" ] != "permessage-deflate" {
continue
}
compress = true
break
}
}
2024-06-14 05:53:52 +03:00
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 ( )
2023-12-13 23:28:24 +03:00
if err != nil {
2014-08-16 07:25:38 +04:00
return u . returnError ( w , r , http . StatusInternalServerError , err . Error ( ) )
}
2014-04-18 01:45:40 +04:00
2017-03-01 20:36:54 +03:00
if brw . Reader . Buffered ( ) > 0 {
2024-04-04 16:32:47 +03:00
netConn . Close ( )
2024-03-11 10:06:55 +03:00
return nil , errors . New ( "websocket: client sent data before handshake is complete" )
2014-04-18 01:45:40 +04:00
}
2018-08-18 05:50:34 +03:00
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.
writeBuf = buf
}
c := newConn ( netConn , true , u . ReadBufferSize , u . WriteBufferSize , u . WriteBufferPool , br , writeBuf )
2014-04-20 22:38:35 +04:00
c . subprotocol = subprotocol
2014-04-18 01:45:40 +04:00
2016-09-30 07:52:15 +03:00
if compress {
c . newCompressionWriter = compressNoContextTakeover
c . newDecompressionReader = decompressNoContextTakeover
}
2018-08-18 05:50:34 +03:00
// 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 ]
2014-04-18 01:45:40 +04:00
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 != "" {
2018-04-20 20:16:12 +03:00
p = append ( p , "Sec-WebSocket-Protocol: " ... )
2014-04-18 01:45:40 +04:00
p = append ( p , c . subprotocol ... )
p = append ( p , "\r\n" ... )
}
2016-09-30 07:52:15 +03:00
if compress {
2018-04-20 20:16:12 +03:00
p = append ( p , "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n" ... )
2016-09-30 07:52:15 +03:00
}
2014-04-18 01:45:40 +04:00
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" ... )
2014-06-30 06:47:15 +04:00
// Clear deadlines set by HTTP server.
2024-04-04 16:32:47 +03:00
netConn . SetDeadline ( time . Time { } )
2014-06-30 06:47:15 +04:00
2014-04-18 01:45:40 +04:00
if u . HandshakeTimeout > 0 {
2024-04-04 16:32:47 +03:00
netConn . SetWriteDeadline ( time . Now ( ) . Add ( u . HandshakeTimeout ) )
2014-04-18 01:45:40 +04:00
}
if _ , err = netConn . Write ( p ) ; err != nil {
2024-04-04 16:32:47 +03:00
netConn . Close ( )
2024-03-11 10:06:55 +03:00
return nil , err
2014-04-18 01:45:40 +04:00
}
2014-04-21 17:28:28 +04:00
if u . HandshakeTimeout > 0 {
2024-04-04 16:32:47 +03:00
netConn . SetWriteDeadline ( time . Time { } )
2014-04-21 17:28:28 +04:00
}
2014-04-18 01:45:40 +04:00
return c , nil
}
2017-07-07 21:47:14 +03:00
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
2014-04-20 22:38:35 +04:00
//
2017-07-18 23:21:30 +03:00
// Deprecated: Use websocket.Upgrader instead.
//
2017-07-10 17:21:27 +03:00
// Upgrade does not perform origin checking. The application is responsible for
// checking the Origin header before calling Upgrade. An example implementation
// of the same origin policy check is:
2013-10-16 20:41:47 +04:00
//
// if req.Header.Get("Origin") != "http://"+req.Host {
2018-02-26 06:30:15 +03:00
// http.Error(w, "Origin not allowed", http.StatusForbidden)
2013-10-16 20:41:47 +04:00
// return
// }
//
2013-12-21 02:54:49 +04:00
// If the endpoint supports subprotocols, then the application is responsible
// for negotiating the protocol used on the connection. Use the Subprotocols()
// function to get the subprotocols requested by the client. Use the
// Sec-Websocket-Protocol response header to specify the subprotocol selected
// by the application.
2013-10-27 19:34:33 +04:00
//
2013-12-21 02:54:49 +04:00
// The responseHeader is included in the response to the client's upgrade
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
// negotiated subprotocol (Sec-Websocket-Protocol).
//
// The connection buffers IO to the underlying network connection. The
// readBufSize and writeBufSize parameters specify the size of the buffers to
// use. Messages can be larger than the buffers.
2013-10-27 19:34:33 +04:00
//
// If the request is not a valid WebSocket handshake, then Upgrade returns an
// error of type HandshakeError. Applications should handle this error by
// replying to the client with an HTTP error response.
2013-10-16 20:41:47 +04:00
func Upgrade ( w http . ResponseWriter , r * http . Request , responseHeader http . Header , readBufSize , writeBufSize int ) ( * Conn , error ) {
2014-04-16 19:25:17 +04:00
u := Upgrader { ReadBufferSize : readBufSize , WriteBufferSize : writeBufSize }
u . Error = func ( w http . ResponseWriter , r * http . Request , status int , reason error ) {
// don't return errors to maintain backwards compatibility
2013-12-14 19:48:43 +04:00
}
2014-04-18 02:07:36 +04:00
u . CheckOrigin = func ( r * http . Request ) bool {
// allow all connections by default
return true
}
2014-04-16 19:25:17 +04:00
return u . Upgrade ( w , r , responseHeader )
2013-10-16 20:41:47 +04:00
}
2013-10-26 17:52:30 +04:00
// Subprotocols returns the subprotocols requested by the client in the
// Sec-Websocket-Protocol header.
func Subprotocols ( r * http . Request ) [ ] string {
h := strings . TrimSpace ( r . Header . Get ( "Sec-Websocket-Protocol" ) )
if h == "" {
return nil
}
protocols := strings . Split ( h , "," )
for i := range protocols {
protocols [ i ] = strings . TrimSpace ( protocols [ i ] )
}
return protocols
}
2016-03-09 21:36:44 +03:00
// IsWebSocketUpgrade returns true if the client requested upgrade to the
// WebSocket protocol.
func IsWebSocketUpgrade ( r * http . Request ) bool {
return tokenListContainsValue ( r . Header , "Connection" , "upgrade" ) &&
tokenListContainsValue ( r . Header , "Upgrade" , "websocket" )
}
2018-08-18 05:50:34 +03:00
2018-08-25 00:03:26 +03:00
// bufioReaderSize size returns the size of a bufio.Reader.
2018-08-18 05:50:34 +03:00
func bufioReaderSize ( originalReader io . Reader , br * bufio . Reader ) int {
// This code assumes that peek on a reset reader returns
// bufio.Reader.buf[:0].
// TODO: Use bufio.Reader.Size() after Go 1.10
br . Reset ( originalReader )
if p , err := br . Peek ( 0 ) ; err == nil {
return cap ( p )
}
return 0
}
// writeHook is an io.Writer that records the last slice passed to it vio
// io.Writer.Write.
type writeHook struct {
p [ ] byte
}
func ( wh * writeHook ) Write ( p [ ] byte ) ( int , error ) {
wh . p = p
return len ( p ) , nil
}
// bufioWriterBuffer grabs the buffer from a bufio.Writer.
func bufioWriterBuffer ( originalWriter io . Writer , bw * bufio . Writer ) [ ] byte {
// This code assumes that bufio.Writer.buf[:1] is passed to the
// bufio.Writer's underlying writer.
var wh writeHook
bw . Reset ( & wh )
2024-04-04 16:32:47 +03:00
bw . WriteByte ( 0 )
bw . Flush ( )
2018-08-18 05:50:34 +03:00
bw . Reset ( originalWriter )
return wh . p [ : cap ( wh . p ) ]
}