github.com/v2fly/v2ray-core/v4@v4.45.2/transport/internet/websocket/connection.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package websocket
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  	"net"
    10  	"time"
    11  
    12  	"github.com/gorilla/websocket"
    13  
    14  	"github.com/v2fly/v2ray-core/v4/common/buf"
    15  	"github.com/v2fly/v2ray-core/v4/common/errors"
    16  	"github.com/v2fly/v2ray-core/v4/common/serial"
    17  )
    18  
    19  var _ buf.Writer = (*connection)(nil)
    20  
    21  // connection is a wrapper for net.Conn over WebSocket connection.
    22  type connection struct {
    23  	conn       *websocket.Conn
    24  	reader     io.Reader
    25  	remoteAddr net.Addr
    26  
    27  	shouldWait        bool
    28  	delayedDialFinish context.Context
    29  	finishedDial      context.CancelFunc
    30  	dialer            DelayedDialer
    31  }
    32  
    33  type DelayedDialer interface {
    34  	Dial(earlyData []byte) (*websocket.Conn, error)
    35  }
    36  
    37  func newConnection(conn *websocket.Conn, remoteAddr net.Addr) *connection {
    38  	return &connection{
    39  		conn:       conn,
    40  		remoteAddr: remoteAddr,
    41  	}
    42  }
    43  
    44  func newConnectionWithEarlyData(conn *websocket.Conn, remoteAddr net.Addr, earlyData io.Reader) *connection {
    45  	return &connection{
    46  		conn:       conn,
    47  		remoteAddr: remoteAddr,
    48  		reader:     earlyData,
    49  	}
    50  }
    51  
    52  func newConnectionWithDelayedDial(dialer DelayedDialer) *connection {
    53  	delayedDialContext, CancellFunc := context.WithCancel(context.Background())
    54  	return &connection{
    55  		shouldWait:        true,
    56  		delayedDialFinish: delayedDialContext,
    57  		finishedDial:      CancellFunc,
    58  		dialer:            dialer,
    59  	}
    60  }
    61  
    62  func newRelayedConnectionWithDelayedDial(dialer DelayedDialerForwarded) *connectionForwarder {
    63  	delayedDialContext, CancellFunc := context.WithCancel(context.Background())
    64  	return &connectionForwarder{
    65  		shouldWait:        true,
    66  		delayedDialFinish: delayedDialContext,
    67  		finishedDial:      CancellFunc,
    68  		dialer:            dialer,
    69  	}
    70  }
    71  
    72  func newRelayedConnection(conn io.ReadWriteCloser) *connectionForwarder {
    73  	return &connectionForwarder{
    74  		ReadWriteCloser: conn,
    75  		shouldWait:      false,
    76  	}
    77  }
    78  
    79  // Read implements net.Conn.Read()
    80  func (c *connection) Read(b []byte) (int, error) {
    81  	for {
    82  		reader, err := c.getReader()
    83  		if err != nil {
    84  			return 0, err
    85  		}
    86  
    87  		nBytes, err := reader.Read(b)
    88  		if errors.Cause(err) == io.EOF {
    89  			c.reader = nil
    90  			continue
    91  		}
    92  		return nBytes, err
    93  	}
    94  }
    95  
    96  func (c *connection) getReader() (io.Reader, error) {
    97  	if c.shouldWait {
    98  		<-c.delayedDialFinish.Done()
    99  		if c.conn == nil {
   100  			return nil, newError("unable to read delayed dial websocket connection as it do not exist")
   101  		}
   102  	}
   103  	if c.reader != nil {
   104  		return c.reader, nil
   105  	}
   106  
   107  	_, reader, err := c.conn.NextReader()
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	c.reader = reader
   112  	return reader, nil
   113  }
   114  
   115  // Write implements io.Writer.
   116  func (c *connection) Write(b []byte) (int, error) {
   117  	if c.shouldWait {
   118  		var err error
   119  		c.conn, err = c.dialer.Dial(b)
   120  		c.finishedDial()
   121  		if err != nil {
   122  			return 0, newError("Unable to proceed with delayed write").Base(err)
   123  		}
   124  		c.remoteAddr = c.conn.RemoteAddr()
   125  		c.shouldWait = false
   126  		return len(b), nil
   127  	}
   128  	if err := c.conn.WriteMessage(websocket.BinaryMessage, b); err != nil {
   129  		return 0, err
   130  	}
   131  	return len(b), nil
   132  }
   133  
   134  func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error {
   135  	mb = buf.Compact(mb)
   136  	mb, err := buf.WriteMultiBuffer(c, mb)
   137  	buf.ReleaseMulti(mb)
   138  	return err
   139  }
   140  
   141  func (c *connection) Close() error {
   142  	if c.shouldWait {
   143  		<-c.delayedDialFinish.Done()
   144  		if c.conn == nil {
   145  			return newError("unable to close delayed dial websocket connection as it do not exist")
   146  		}
   147  	}
   148  	var errors []interface{}
   149  	if err := c.conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(time.Second*5)); err != nil {
   150  		errors = append(errors, err)
   151  	}
   152  	if err := c.conn.Close(); err != nil {
   153  		errors = append(errors, err)
   154  	}
   155  	if len(errors) > 0 {
   156  		return newError("failed to close connection").Base(newError(serial.Concat(errors...)))
   157  	}
   158  	return nil
   159  }
   160  
   161  func (c *connection) LocalAddr() net.Addr {
   162  	if c.shouldWait {
   163  		<-c.delayedDialFinish.Done()
   164  		if c.conn == nil {
   165  			newError("websocket transport is not materialized when LocalAddr() is called").AtWarning().WriteToLog()
   166  			return &net.UnixAddr{
   167  				Name: "@placeholder",
   168  				Net:  "unix",
   169  			}
   170  		}
   171  	}
   172  	return c.conn.LocalAddr()
   173  }
   174  
   175  func (c *connection) RemoteAddr() net.Addr {
   176  	return c.remoteAddr
   177  }
   178  
   179  func (c *connection) SetDeadline(t time.Time) error {
   180  	if err := c.SetReadDeadline(t); err != nil {
   181  		return err
   182  	}
   183  	return c.SetWriteDeadline(t)
   184  }
   185  
   186  func (c *connection) SetReadDeadline(t time.Time) error {
   187  	if c.shouldWait {
   188  		<-c.delayedDialFinish.Done()
   189  		if c.conn == nil {
   190  			newError("websocket transport is not materialized when SetReadDeadline() is called").AtWarning().WriteToLog()
   191  			return nil
   192  		}
   193  	}
   194  	return c.conn.SetReadDeadline(t)
   195  }
   196  
   197  func (c *connection) SetWriteDeadline(t time.Time) error {
   198  	if c.shouldWait {
   199  		<-c.delayedDialFinish.Done()
   200  		if c.conn == nil {
   201  			newError("websocket transport is not materialized when SetWriteDeadline() is called").AtWarning().WriteToLog()
   202  			return nil
   203  		}
   204  	}
   205  	return c.conn.SetWriteDeadline(t)
   206  }