mirror of
https://github.com/apernet/hysteria.git
synced 2025-04-03 04:27:39 +03:00
230 lines
3.9 KiB
Go
230 lines
3.9 KiB
Go
package outbounds
|
|
|
|
import (
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/database64128/tfo-go/v2"
|
|
)
|
|
|
|
type fastOpenDialer struct {
|
|
dialer *tfo.Dialer
|
|
}
|
|
|
|
func newFastOpenDialer(netDialer *net.Dialer) *fastOpenDialer {
|
|
return &fastOpenDialer{
|
|
dialer: &tfo.Dialer{
|
|
Dialer: *netDialer,
|
|
Fallback: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Dial returns immediately without actually establishing a connection.
|
|
// The connection will be established by the first Write() call.
|
|
func (d *fastOpenDialer) Dial(network, address string) (net.Conn, error) {
|
|
return &fastOpenConn{
|
|
dialer: d.dialer,
|
|
network: network,
|
|
address: address,
|
|
readyChan: make(chan struct{}),
|
|
}, nil
|
|
}
|
|
|
|
type fastOpenConn struct {
|
|
dialer *tfo.Dialer
|
|
network string
|
|
address string
|
|
|
|
conn net.Conn
|
|
connLock sync.RWMutex
|
|
readyChan chan struct{}
|
|
|
|
// States before connection ready
|
|
deadline *time.Time
|
|
readDeadline *time.Time
|
|
writeDeadline *time.Time
|
|
}
|
|
|
|
func (c *fastOpenConn) Read(b []byte) (n int, err error) {
|
|
c.connLock.RLock()
|
|
conn := c.conn
|
|
c.connLock.RUnlock()
|
|
|
|
if conn != nil {
|
|
return conn.Read(b)
|
|
}
|
|
|
|
// Wait until the connection is ready or closed
|
|
<-c.readyChan
|
|
|
|
if c.conn == nil {
|
|
// This is equivalent to isClosedBeforeReady() == true
|
|
return 0, net.ErrClosed
|
|
}
|
|
|
|
return c.conn.Read(b)
|
|
}
|
|
|
|
func (c *fastOpenConn) Write(b []byte) (n int, err error) {
|
|
c.connLock.RLock()
|
|
conn := c.conn
|
|
c.connLock.RUnlock()
|
|
|
|
if conn != nil {
|
|
return conn.Write(b)
|
|
}
|
|
|
|
c.connLock.RLock()
|
|
closed := c.isClosedBeforeReady()
|
|
c.connLock.RUnlock()
|
|
|
|
if closed {
|
|
return 0, net.ErrClosed
|
|
}
|
|
|
|
c.connLock.Lock()
|
|
defer c.connLock.Unlock()
|
|
|
|
if c.isClosedBeforeReady() {
|
|
// Closed by other goroutine
|
|
return 0, net.ErrClosed
|
|
}
|
|
|
|
conn = c.conn
|
|
if conn != nil {
|
|
// Established by other goroutine
|
|
return conn.Write(b)
|
|
}
|
|
|
|
conn, err = c.dialer.Dial(c.network, c.address, b)
|
|
if err != nil {
|
|
close(c.readyChan)
|
|
return 0, err
|
|
}
|
|
|
|
// Apply pre-set states
|
|
if c.deadline != nil {
|
|
_ = conn.SetDeadline(*c.deadline)
|
|
}
|
|
if c.readDeadline != nil {
|
|
_ = conn.SetReadDeadline(*c.readDeadline)
|
|
}
|
|
if c.writeDeadline != nil {
|
|
_ = conn.SetWriteDeadline(*c.writeDeadline)
|
|
}
|
|
|
|
c.conn = conn
|
|
close(c.readyChan)
|
|
return len(b), nil
|
|
}
|
|
|
|
func (c *fastOpenConn) Close() error {
|
|
c.connLock.RLock()
|
|
defer c.connLock.RUnlock()
|
|
|
|
if c.isClosedBeforeReady() {
|
|
return net.ErrClosed
|
|
}
|
|
|
|
if c.conn != nil {
|
|
return c.conn.Close()
|
|
}
|
|
|
|
close(c.readyChan)
|
|
return nil
|
|
}
|
|
|
|
// isClosedBeforeReady returns true if the connection is closed before the real connection is established.
|
|
// This function should be called with connLock.RLock().
|
|
func (c *fastOpenConn) isClosedBeforeReady() bool {
|
|
select {
|
|
case <-c.readyChan:
|
|
if c.conn == nil {
|
|
return true
|
|
}
|
|
default:
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *fastOpenConn) LocalAddr() net.Addr {
|
|
c.connLock.RLock()
|
|
defer c.connLock.RUnlock()
|
|
|
|
if c.conn != nil {
|
|
return c.conn.LocalAddr()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *fastOpenConn) RemoteAddr() net.Addr {
|
|
c.connLock.RLock()
|
|
conn := c.conn
|
|
c.connLock.RUnlock()
|
|
|
|
if conn != nil {
|
|
return conn.RemoteAddr()
|
|
}
|
|
|
|
addr, err := net.ResolveTCPAddr(c.network, c.address)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return addr
|
|
}
|
|
|
|
func (c *fastOpenConn) SetDeadline(t time.Time) error {
|
|
c.connLock.RLock()
|
|
defer c.connLock.RUnlock()
|
|
|
|
c.deadline = &t
|
|
|
|
if c.conn != nil {
|
|
return c.conn.SetDeadline(t)
|
|
}
|
|
|
|
if c.isClosedBeforeReady() {
|
|
return net.ErrClosed
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *fastOpenConn) SetReadDeadline(t time.Time) error {
|
|
c.connLock.RLock()
|
|
defer c.connLock.RUnlock()
|
|
|
|
c.readDeadline = &t
|
|
|
|
if c.conn != nil {
|
|
return c.conn.SetReadDeadline(t)
|
|
}
|
|
|
|
if c.isClosedBeforeReady() {
|
|
return net.ErrClosed
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *fastOpenConn) SetWriteDeadline(t time.Time) error {
|
|
c.connLock.RLock()
|
|
defer c.connLock.RUnlock()
|
|
|
|
c.writeDeadline = &t
|
|
|
|
if c.conn != nil {
|
|
return c.conn.SetWriteDeadline(t)
|
|
}
|
|
|
|
if c.isClosedBeforeReady() {
|
|
return net.ErrClosed
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var _ net.Conn = (*fastOpenConn)(nil)
|