github.com/cnotch/ipchub@v1.1.0/network/websocket/websocket.go (about)

     1  /**********************************************************************************
     2  * Copyright (c) 2009-2017 Misakai Ltd.
     3  * This program is free software: you can redistribute it and/or modify it under the
     4  * terms of the GNU Affero General Public License as published by the  Free Software
     5  * Foundation, either version 3 of the License, or(at your option) any later version.
     6  *
     7  * This program is distributed  in the hope that it  will be useful, but WITHOUT ANY
     8  * WARRANTY;  without even  the implied warranty of MERCHANTABILITY or FITNESS FOR A
     9  * PARTICULAR PURPOSE.  See the GNU Affero General Public License  for  more details.
    10  *
    11  * You should have  received a copy  of the  GNU Affero General Public License along
    12  * with this program. If not, see<http://www.gnu.org/licenses/>.
    13  ************************************************************************************/
    14  //
    15  // Copyright (c) 2019,CAOHONGJU All rights reserved.
    16  // Use of this source code is governed by a MIT-style
    17  // license that can be found in the LICENSE file.
    18  
    19  package websocket
    20  
    21  import (
    22  	"io"
    23  	"net"
    24  	"net/http"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/gorilla/websocket"
    29  )
    30  
    31  // Conn websocket连接
    32  type Conn interface {
    33  	net.Conn
    34  	Subprotocol() string // 获取子协议
    35  	TextTransport() Conn // 获取文本传输通道
    36  	Path() string        // 接入时的ws后的路径
    37  	Username() string    // 接入是http验证后的用户名称
    38  }
    39  
    40  type websocketConn interface {
    41  	NextReader() (messageType int, r io.Reader, err error)
    42  	NextWriter(messageType int) (io.WriteCloser, error)
    43  	Close() error
    44  	LocalAddr() net.Addr
    45  	RemoteAddr() net.Addr
    46  	SetReadDeadline(t time.Time) error
    47  	SetWriteDeadline(t time.Time) error
    48  	Subprotocol() string
    49  }
    50  
    51  // websocketConn represents a websocket connection.
    52  type websocketTransport struct {
    53  	sync.Mutex
    54  	socket   websocketConn
    55  	reader   io.Reader
    56  	closing  chan bool
    57  	path     string
    58  	username string
    59  }
    60  
    61  const (
    62  	writeWait        = 10 * time.Second    // Time allowed to write a message to the peer.
    63  	pongWait         = 60 * time.Second    // Time allowed to read the next pong message from the peer.
    64  	pingPeriod       = (pongWait * 9) / 10 // Send pings to peer with this period. Must be less than pongWait.
    65  	closeGracePeriod = 10 * time.Second    // Time to wait before force close on connection.
    66  )
    67  
    68  // The default upgrader to use
    69  var upgrader = &websocket.Upgrader{
    70  	Subprotocols: []string{"rtsp"},
    71  	CheckOrigin:  func(r *http.Request) bool { return true },
    72  	// ReadBufferSize: 64 * 1024, WriteBufferSize: 64 * 1024,
    73  }
    74  
    75  // TryUpgrade attempts to upgrade an HTTP request to rtsp/wsp over websocket.
    76  func TryUpgrade(w http.ResponseWriter, r *http.Request, path, username string) (Conn, bool) {
    77  	if w == nil || r == nil {
    78  		return nil, false
    79  	}
    80  
    81  	if ws, err := upgrader.Upgrade(w, r, nil); err == nil {
    82  		return newConn(ws, path, username), true
    83  	}
    84  
    85  	return nil, false
    86  }
    87  
    88  // newConn creates a new transport from websocket.
    89  func newConn(ws websocketConn, path, username string) Conn {
    90  	conn := &websocketTransport{
    91  		socket:   ws,
    92  		closing:  make(chan bool),
    93  		path:     path,
    94  		username: username,
    95  	}
    96  
    97  	/*ws.SetReadLimit(maxMessageSize)
    98  	ws.SetReadDeadline(time.Now().Add(pongWait))
    99  	ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
   100  
   101  	ws.SetCloseHandler(func(code int, text string) error {
   102  		return conn.Close()
   103  	})
   104  
   105  	utils.Repeat(func() {
   106  		log.Println("ping")
   107  		if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {
   108  			log.Println("ping:", err)
   109  		}
   110  	}, pingPeriod, conn.closing)*/
   111  
   112  	return conn
   113  }
   114  
   115  // Read reads data from the connection. It is possible to allow reader to time
   116  // out and return a Error with Timeout() == true after a fixed time limit by
   117  // using SetDeadline and SetReadDeadline on the websocket.
   118  func (c *websocketTransport) Read(b []byte) (n int, err error) {
   119  	var opCode int
   120  	if c.reader == nil {
   121  		// New message
   122  		var r io.Reader
   123  		for {
   124  			if opCode, r, err = c.socket.NextReader(); err != nil {
   125  				return
   126  			}
   127  
   128  			if opCode != websocket.BinaryMessage && opCode != websocket.TextMessage {
   129  				continue
   130  			}
   131  
   132  			c.reader = r
   133  			break
   134  		}
   135  	}
   136  
   137  	// Read from the reader
   138  	n, err = c.reader.Read(b)
   139  	if err != nil {
   140  		if err == io.EOF {
   141  			c.reader = nil
   142  			err = nil
   143  		}
   144  	}
   145  	return
   146  }
   147  
   148  // Write writes data to the connection. It is possible to allow writer to time
   149  // out and return a Error with Timeout() == true after a fixed time limit by
   150  // using SetDeadline and SetWriteDeadline on the websocket.
   151  func (c *websocketTransport) Write(b []byte) (n int, err error) {
   152  	// Serialize write to avoid concurrent write
   153  	c.Lock()
   154  	defer c.Unlock()
   155  
   156  	var w io.WriteCloser
   157  	if w, err = c.socket.NextWriter(websocket.BinaryMessage); err == nil {
   158  		if n, err = w.Write(b); err == nil {
   159  			err = w.Close()
   160  		}
   161  	}
   162  	return
   163  }
   164  
   165  // Close terminates the connection.
   166  func (c *websocketTransport) Close() error {
   167  	return c.socket.Close()
   168  }
   169  
   170  // LocalAddr returns the local network address.
   171  func (c *websocketTransport) LocalAddr() net.Addr {
   172  	return c.socket.LocalAddr()
   173  }
   174  
   175  // RemoteAddr returns the remote network address.
   176  func (c *websocketTransport) RemoteAddr() net.Addr {
   177  	return c.socket.RemoteAddr()
   178  }
   179  
   180  // SetDeadline sets the read and write deadlines associated
   181  // with the connection. It is equivalent to calling both
   182  // SetReadDeadline and SetWriteDeadline.
   183  func (c *websocketTransport) SetDeadline(t time.Time) (err error) {
   184  	if err = c.socket.SetReadDeadline(t); err == nil {
   185  		err = c.socket.SetWriteDeadline(t)
   186  	}
   187  	return
   188  }
   189  
   190  // SetReadDeadline sets the deadline for future Read calls
   191  // and any currently-blocked Read call.
   192  func (c *websocketTransport) SetReadDeadline(t time.Time) error {
   193  	return c.socket.SetReadDeadline(t)
   194  }
   195  
   196  // SetWriteDeadline sets the deadline for future Write calls
   197  // and any currently-blocked Write call.
   198  func (c *websocketTransport) SetWriteDeadline(t time.Time) error {
   199  	return c.socket.SetWriteDeadline(t)
   200  }
   201  
   202  // Subprotocol 获取子协议名称
   203  func (c *websocketTransport) Subprotocol() string {
   204  	return c.socket.Subprotocol()
   205  }
   206  
   207  // TextTransport 获取文本传输Conn
   208  func (c *websocketTransport) TextTransport() Conn {
   209  	return &websocketTextTransport{c}
   210  }
   211  
   212  func (c *websocketTransport) Path() string {
   213  	return c.path
   214  }
   215  
   216  func (c *websocketTransport) Username() string {
   217  	return c.username
   218  }
   219  
   220  type websocketTextTransport struct {
   221  	*websocketTransport
   222  }
   223  
   224  // Write writes data to the connection. It is possible to allow writer to time
   225  // out and return a Error with Timeout() == true after a fixed time limit by
   226  // using SetDeadline and SetWriteDeadline on the websocket.
   227  func (c *websocketTextTransport) Write(b []byte) (n int, err error) {
   228  	// Serialize write to avoid concurrent write
   229  	c.Lock()
   230  	defer c.Unlock()
   231  
   232  	var w io.WriteCloser
   233  	if w, err = c.socket.NextWriter(websocket.TextMessage); err == nil {
   234  		if n, err = w.Write(b); err == nil {
   235  			err = w.Close()
   236  		}
   237  	}
   238  	return
   239  }