|
|
@@ -11,6 +11,8 @@ import (
|
|
|
"sync"
|
|
|
"time"
|
|
|
|
|
|
+ "github.com/quic-go/quic-go"
|
|
|
+ "github.com/quic-go/quic-go/http3"
|
|
|
"github.com/xtls/xray-core/common"
|
|
|
"github.com/xtls/xray-core/common/errors"
|
|
|
"github.com/xtls/xray-core/common/net"
|
|
|
@@ -233,10 +235,13 @@ func (c *httpResponseBodyWriter) Close() error {
|
|
|
|
|
|
type Listener struct {
|
|
|
sync.Mutex
|
|
|
- server http.Server
|
|
|
- listener net.Listener
|
|
|
- config *Config
|
|
|
- addConn internet.ConnHandler
|
|
|
+ server http.Server
|
|
|
+ h3server *http3.Server
|
|
|
+ listener net.Listener
|
|
|
+ h3listener *quic.EarlyListener
|
|
|
+ config *Config
|
|
|
+ addConn internet.ConnHandler
|
|
|
+ isH3 bool
|
|
|
}
|
|
|
|
|
|
func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
|
|
|
@@ -253,6 +258,17 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|
|
var listener net.Listener
|
|
|
var err error
|
|
|
var localAddr = gonet.TCPAddr{}
|
|
|
+ handler := &requestHandler{
|
|
|
+ host: shSettings.Host,
|
|
|
+ path: shSettings.GetNormalizedPath(),
|
|
|
+ ln: l,
|
|
|
+ sessionMu: &sync.Mutex{},
|
|
|
+ sessions: sync.Map{},
|
|
|
+ localAddr: localAddr,
|
|
|
+ }
|
|
|
+ tlsConfig := getTLSConfig(streamSettings)
|
|
|
+ l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3"
|
|
|
+
|
|
|
|
|
|
if port == net.Port(0) { // unix
|
|
|
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
|
|
@@ -263,6 +279,29 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|
|
return nil, errors.New("failed to listen unix domain socket(for SH) on ", address).Base(err)
|
|
|
}
|
|
|
errors.LogInfo(ctx, "listening unix domain socket(for SH) on ", address)
|
|
|
+ } else if l.isH3 { // quic
|
|
|
+ Conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
|
|
|
+ IP: address.IP(),
|
|
|
+ Port: int(port),
|
|
|
+ }, streamSettings.SocketSettings)
|
|
|
+ if err != nil {
|
|
|
+ return nil, errors.New("failed to listen UDP(for SH3) on ", address, ":", port).Base(err)
|
|
|
+ }
|
|
|
+ h3listener, err := quic.ListenEarly(Conn,tlsConfig, nil)
|
|
|
+ if err != nil {
|
|
|
+ return nil, errors.New("failed to listen QUIC(for SH3) on ", address, ":", port).Base(err)
|
|
|
+ }
|
|
|
+ l.h3listener = h3listener
|
|
|
+ errors.LogInfo(ctx, "listening QUIC(for SH3) on ", address, ":", port)
|
|
|
+
|
|
|
+ l.h3server = &http3.Server{
|
|
|
+ Handler: handler,
|
|
|
+ }
|
|
|
+ go func() {
|
|
|
+ if err := l.h3server.ServeListener(l.h3listener); err != nil {
|
|
|
+ errors.LogWarningInner(ctx, err, "failed to serve http3 for splithttp")
|
|
|
+ }
|
|
|
+ }()
|
|
|
} else { // tcp
|
|
|
localAddr = gonet.TCPAddr{
|
|
|
IP: address.IP(),
|
|
|
@@ -275,41 +314,29 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|
|
if err != nil {
|
|
|
return nil, errors.New("failed to listen TCP(for SH) on ", address, ":", port).Base(err)
|
|
|
}
|
|
|
+ l.listener = listener
|
|
|
errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port)
|
|
|
- }
|
|
|
|
|
|
+ // h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
|
|
+ h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
|
|
+ l.server = http.Server{
|
|
|
+ Handler: h2cHandler,
|
|
|
+ ReadHeaderTimeout: time.Second * 4,
|
|
|
+ MaxHeaderBytes: 8192,
|
|
|
+ }
|
|
|
+ go func() {
|
|
|
+ if err := l.server.Serve(l.listener); err != nil {
|
|
|
+ errors.LogWarningInner(ctx, err, "failed to serve http for splithttp")
|
|
|
+ }
|
|
|
+ }()
|
|
|
+ }
|
|
|
+ l.listener = listener
|
|
|
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
|
|
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
|
|
|
listener = tls.NewListener(listener, tlsConfig)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- handler := &requestHandler{
|
|
|
- host: shSettings.Host,
|
|
|
- path: shSettings.GetNormalizedPath(),
|
|
|
- ln: l,
|
|
|
- sessionMu: &sync.Mutex{},
|
|
|
- sessions: sync.Map{},
|
|
|
- localAddr: localAddr,
|
|
|
- }
|
|
|
-
|
|
|
- // h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
|
|
- h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
|
|
-
|
|
|
- l.listener = listener
|
|
|
-
|
|
|
- l.server = http.Server{
|
|
|
- Handler: h2cHandler,
|
|
|
- ReadHeaderTimeout: time.Second * 4,
|
|
|
- MaxHeaderBytes: 8192,
|
|
|
- }
|
|
|
-
|
|
|
- go func() {
|
|
|
- if err := l.server.Serve(l.listener); err != nil {
|
|
|
- errors.LogWarningInner(ctx, err, "failed to serve http for splithttp")
|
|
|
- }
|
|
|
- }()
|
|
|
-
|
|
|
return l, err
|
|
|
}
|
|
|
|
|
|
@@ -320,9 +347,22 @@ func (ln *Listener) Addr() net.Addr {
|
|
|
|
|
|
// Close implements net.Listener.Close().
|
|
|
func (ln *Listener) Close() error {
|
|
|
- return ln.listener.Close()
|
|
|
+ if ln.h3server != nil {
|
|
|
+ if err := ln.h3server.Close(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ } else if ln.listener != nil {
|
|
|
+ return ln.listener.Close()
|
|
|
+ }
|
|
|
+ return errors.New("listener does not have an HTTP/3 server or a net.listener")
|
|
|
+}
|
|
|
+func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *tls.Config {
|
|
|
+ config := v2tls.ConfigFromStreamSettings(streamSettings)
|
|
|
+ if config == nil {
|
|
|
+ return &tls.Config{}
|
|
|
+ }
|
|
|
+ return config.GetTLSConfig()
|
|
|
}
|
|
|
-
|
|
|
func init() {
|
|
|
common.Must(internet.RegisterTransportListener(protocolName, ListenSH))
|
|
|
}
|