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"
2024-06-24 01:22:38 +03:00
"net"
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" ) {
2024-05-26 06:16:00 +03:00
w . Header ( ) . Set ( "Upgrade" , "websocket" )
return u . returnError ( w , r , http . StatusUpgradeRequired , badHandshake + "'websocket' token not found in 'Upgrade' header" )
2017-12-01 03:45:44 +03:00
}
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
}
}
2023-11-22 22:47:04 +03:00
netConn , brw , err := http . NewResponseController ( w ) . Hijack ( )
2023-12-13 23:28:24 +03:00
if err != nil {
2024-06-13 07:15:45 +03:00
return u . returnError ( w , r , http . StatusInternalServerError ,
"websocket: hijack: " + err . Error ( ) )
2014-08-16 07:25:38 +04:00
}
2014-04-18 01:45:40 +04:00
2024-07-05 20:35:59 +03:00
// Close the network connection when returning an error. The variable
// netConn is set to nil before the success return at the end of the
// function.
defer func ( ) {
if netConn != nil {
// It's safe to ignore the error from Close() because this code is
// only executed when returning a more important error to the
// application.
_ = netConn . Close ( )
}
} ( )
2018-08-18 05:50:34 +03:00
var br * bufio . Reader
2024-06-24 01:22:38 +03:00
if u . ReadBufferSize == 0 && brw . Reader . Size ( ) > 256 {
// Use hijacked buffered reader as the connection reader.
2018-08-18 05:50:34 +03:00
br = brw . Reader
2024-06-24 01:22:38 +03:00
} else if brw . Reader . Buffered ( ) > 0 {
// Wrap the network connection to read buffered data in brw.Reader
// before reading from the network connection. This should be rare
// because a client must not send message data before receiving the
// handshake response.
netConn = & brNetConn { br : brw . Reader , Conn : netConn }
2018-08-18 05:50:34 +03:00
}
2024-06-24 01:22:38 +03:00
buf := brw . Writer . AvailableBuffer ( )
2018-08-18 05:50:34 +03:00
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" ... )
if u . HandshakeTimeout > 0 {
2024-07-05 20:35:59 +03:00
if err := netConn . SetWriteDeadline ( time . Now ( ) . Add ( u . HandshakeTimeout ) ) ; err != nil {
return nil , err
}
} else {
// Clear deadlines set by HTTP server.
if err := netConn . SetDeadline ( time . Time { } ) ; err != nil {
return nil , err
}
2014-04-18 01:45:40 +04:00
}
2024-07-05 20:35:59 +03:00
2014-04-18 01:45:40 +04:00
if _ , err = netConn . Write ( p ) ; err != nil {
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-07-05 20:35:59 +03:00
if err := netConn . SetWriteDeadline ( time . Time { } ) ; err != nil {
return nil , err
}
2014-04-21 17:28:28 +04:00
}
2014-04-18 01:45:40 +04:00
2024-07-05 20:35:59 +03:00
// Success! Set netConn to nil to stop the deferred function above from
// closing the network connection.
netConn = nil
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
2024-06-24 01:22:38 +03:00
type brNetConn struct {
br * bufio . Reader
net . Conn
2018-08-18 05:50:34 +03:00
}
2024-06-24 01:22:38 +03:00
func ( b * brNetConn ) Read ( p [ ] byte ) ( n int , err error ) {
if b . br != nil {
// Limit read to buferred data.
if n := b . br . Buffered ( ) ; len ( p ) > n {
p = p [ : n ]
}
n , err = b . br . Read ( p )
if b . br . Buffered ( ) == 0 {
b . br = nil
}
return n , err
}
return b . Conn . Read ( p )
2018-08-18 05:50:34 +03:00
}
2024-06-24 01:22:38 +03:00
// NetConn returns the underlying connection that is wrapped by b.
func ( b * brNetConn ) NetConn ( ) net . Conn {
return b . Conn
2018-08-18 05:50:34 +03:00
}