mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 18:11:28 +00:00
Add gun-lite gRPC implementation (#44)
This commit is contained in:
parent
d59d40c118
commit
de2453fce9
|
@ -78,4 +78,5 @@ type V2RayQUICOptions struct{}
|
||||||
|
|
||||||
type V2RayGRPCOptions struct {
|
type V2RayGRPCOptions struct {
|
||||||
ServiceName string `json:"service_name,omitempty"`
|
ServiceName string `json:"service_name,omitempty"`
|
||||||
|
ForceLite bool `json:"-"` // for test
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,52 @@ func TestV2RayGRPCSelf(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestV2RayGRPCLite(t *testing.T) {
|
||||||
|
t.Run("server", func(t *testing.T) {
|
||||||
|
testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeGRPC,
|
||||||
|
GRPCOptions: option.V2RayGRPCOptions{
|
||||||
|
ServiceName: "TunService",
|
||||||
|
ForceLite: true,
|
||||||
|
},
|
||||||
|
}, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeGRPC,
|
||||||
|
GRPCOptions: option.V2RayGRPCOptions{
|
||||||
|
ServiceName: "TunService",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("client", func(t *testing.T) {
|
||||||
|
testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeGRPC,
|
||||||
|
GRPCOptions: option.V2RayGRPCOptions{
|
||||||
|
ServiceName: "TunService",
|
||||||
|
},
|
||||||
|
}, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeGRPC,
|
||||||
|
GRPCOptions: option.V2RayGRPCOptions{
|
||||||
|
ServiceName: "TunService",
|
||||||
|
ForceLite: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("self", func(t *testing.T) {
|
||||||
|
testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeGRPC,
|
||||||
|
GRPCOptions: option.V2RayGRPCOptions{
|
||||||
|
ServiceName: "TunService",
|
||||||
|
ForceLite: true,
|
||||||
|
},
|
||||||
|
}, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeGRPC,
|
||||||
|
GRPCOptions: option.V2RayGRPCOptions{
|
||||||
|
ServiceName: "TunService",
|
||||||
|
ForceLite: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestV2RayWebscoketSelf(t *testing.T) {
|
func TestV2RayWebscoketSelf(t *testing.T) {
|
||||||
t.Run("basic", func(t *testing.T) {
|
t.Run("basic", func(t *testing.T) {
|
||||||
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
|
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
|
||||||
|
@ -48,6 +94,9 @@ func TestV2RayWebscoketSelf(t *testing.T) {
|
||||||
func TestV2RayHTTPSelf(t *testing.T) {
|
func TestV2RayHTTPSelf(t *testing.T) {
|
||||||
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
|
testV2RayTransportSelf(t, &option.V2RayTransportOptions{
|
||||||
Type: C.V2RayTransportTypeHTTP,
|
Type: C.V2RayTransportTypeHTTP,
|
||||||
|
HTTPOptions: option.V2RayHTTPOptions{
|
||||||
|
Method: "POST",
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,15 +107,19 @@ func TestV2RayHTTPPlainSelf(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testV2RayTransportSelf(t *testing.T, transport *option.V2RayTransportOptions) {
|
func testV2RayTransportSelf(t *testing.T, transport *option.V2RayTransportOptions) {
|
||||||
|
testV2RayTransportSelfWith(t, transport, transport)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testV2RayTransportSelfWith(t *testing.T, server, client *option.V2RayTransportOptions) {
|
||||||
t.Run("vmess", func(t *testing.T) {
|
t.Run("vmess", func(t *testing.T) {
|
||||||
testVMessTransportSelf(t, transport)
|
testVMessTransportSelf(t, server, client)
|
||||||
})
|
})
|
||||||
t.Run("trojan", func(t *testing.T) {
|
t.Run("trojan", func(t *testing.T) {
|
||||||
testTrojanTransportSelf(t, transport)
|
testTrojanTransportSelf(t, server, client)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testVMessTransportSelf(t *testing.T, transport *option.V2RayTransportOptions) {
|
func testVMessTransportSelf(t *testing.T, server *option.V2RayTransportOptions, client *option.V2RayTransportOptions) {
|
||||||
user, err := uuid.DefaultGenerator.NewV4()
|
user, err := uuid.DefaultGenerator.NewV4()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||||
|
@ -104,7 +157,7 @@ func testVMessTransportSelf(t *testing.T, transport *option.V2RayTransportOption
|
||||||
CertificatePath: certPem,
|
CertificatePath: certPem,
|
||||||
KeyPath: keyPem,
|
KeyPath: keyPem,
|
||||||
},
|
},
|
||||||
Transport: transport,
|
Transport: server,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -127,7 +180,7 @@ func testVMessTransportSelf(t *testing.T, transport *option.V2RayTransportOption
|
||||||
ServerName: "example.org",
|
ServerName: "example.org",
|
||||||
CertificatePath: certPem,
|
CertificatePath: certPem,
|
||||||
},
|
},
|
||||||
Transport: transport,
|
Transport: client,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -145,7 +198,7 @@ func testVMessTransportSelf(t *testing.T, transport *option.V2RayTransportOption
|
||||||
testSuit(t, clientPort, testPort)
|
testSuit(t, clientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrojanTransportSelf(t *testing.T, transport *option.V2RayTransportOptions) {
|
func testTrojanTransportSelf(t *testing.T, server *option.V2RayTransportOptions, client *option.V2RayTransportOptions) {
|
||||||
user, err := uuid.DefaultGenerator.NewV4()
|
user, err := uuid.DefaultGenerator.NewV4()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||||
|
@ -183,7 +236,7 @@ func testTrojanTransportSelf(t *testing.T, transport *option.V2RayTransportOptio
|
||||||
CertificatePath: certPem,
|
CertificatePath: certPem,
|
||||||
KeyPath: keyPem,
|
KeyPath: keyPem,
|
||||||
},
|
},
|
||||||
Transport: transport,
|
Transport: server,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -205,7 +258,7 @@ func testTrojanTransportSelf(t *testing.T, transport *option.V2RayTransportOptio
|
||||||
ServerName: "example.org",
|
ServerName: "example.org",
|
||||||
CertificatePath: certPem,
|
CertificatePath: certPem,
|
||||||
},
|
},
|
||||||
Transport: transport,
|
Transport: client,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,14 +9,22 @@ import (
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/v2raygrpc"
|
"github.com/sagernet/sing-box/transport/v2raygrpc"
|
||||||
|
"github.com/sagernet/sing-box/transport/v2raygrpclite"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler) (adapter.V2RayServerTransport, error) {
|
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||||
|
if options.ForceLite {
|
||||||
|
return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler, errorHandler), nil
|
||||||
|
}
|
||||||
return v2raygrpc.NewServer(ctx, options, tlsConfig, handler), nil
|
return v2raygrpc.NewServer(ctx, options, tlsConfig, handler), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
|
func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
|
||||||
|
if options.ForceLite {
|
||||||
|
return v2raygrpclite.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
|
||||||
|
}
|
||||||
return v2raygrpc.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
|
return v2raygrpc.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,16 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/transport/v2raygrpclite"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errGRPCNotIncluded = E.New("gRPC is not included in this build, rebuild with -tags with_grpc")
|
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||||
|
return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler, errorHandler), nil
|
||||||
func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler) (adapter.V2RayServerTransport, error) {
|
|
||||||
return nil, errGRPCNotIncluded
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
|
func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
|
||||||
return nil, errGRPCNotIncluded
|
return v2raygrpclite.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
|
||||||
}
|
}
|
|
@ -29,7 +29,7 @@ func NewServerTransport(ctx context.Context, options option.V2RayTransportOption
|
||||||
}
|
}
|
||||||
return NewQUICServer(ctx, options.QUICOptions, tlsConfig, handler, errorHandler)
|
return NewQUICServer(ctx, options.QUICOptions, tlsConfig, handler, errorHandler)
|
||||||
case C.V2RayTransportTypeGRPC:
|
case C.V2RayTransportTypeGRPC:
|
||||||
return NewGRPCServer(ctx, options.GRPCOptions, tlsConfig, handler)
|
return NewGRPCServer(ctx, options.GRPCOptions, tlsConfig, handler, errorHandler)
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown transport type: " + options.Type)
|
return nil, E.New("unknown transport type: " + options.Type)
|
||||||
}
|
}
|
||||||
|
|
77
transport/v2raygrpclite/client.go
Normal file
77
transport/v2raygrpclite/client.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package v2raygrpclite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.V2RayClientTransport = (*Client)(nil)
|
||||||
|
|
||||||
|
var defaultClientHeader = http.Header{
|
||||||
|
"Content-Type": []string{"application/grpc"},
|
||||||
|
"User-Agent": []string{"grpc-go/1.48.0"},
|
||||||
|
"TE": []string{"trailers"},
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
ctx context.Context
|
||||||
|
dialer N.Dialer
|
||||||
|
serverAddr M.Socksaddr
|
||||||
|
transport *http.Transport
|
||||||
|
options option.V2RayGRPCOptions
|
||||||
|
url *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
|
||||||
|
return &Client{
|
||||||
|
ctx: ctx,
|
||||||
|
dialer: dialer,
|
||||||
|
serverAddr: serverAddr,
|
||||||
|
options: options,
|
||||||
|
transport: &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||||
|
},
|
||||||
|
ForceAttemptHTTP2: true,
|
||||||
|
TLSClientConfig: tlsConfig,
|
||||||
|
},
|
||||||
|
url: &url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: serverAddr.String(),
|
||||||
|
Path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||||
|
pipeInReader, pipeInWriter := io.Pipe()
|
||||||
|
request := &http.Request{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Body: pipeInReader,
|
||||||
|
URL: c.url,
|
||||||
|
Proto: "HTTP/2",
|
||||||
|
ProtoMajor: 2,
|
||||||
|
Header: defaultClientHeader,
|
||||||
|
}
|
||||||
|
request = request.WithContext(ctx)
|
||||||
|
conn := newLateGunConn(pipeInWriter)
|
||||||
|
go func() {
|
||||||
|
response, err := c.transport.RoundTrip(request)
|
||||||
|
if err == nil {
|
||||||
|
conn.setup(response.Body, nil)
|
||||||
|
} else {
|
||||||
|
conn.setup(nil, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return conn, nil
|
||||||
|
}
|
168
transport/v2raygrpclite/conn.go
Normal file
168
transport/v2raygrpclite/conn.go
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
package v2raygrpclite
|
||||||
|
|
||||||
|
import (
|
||||||
|
std_bufio "bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/bufio"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
)
|
||||||
|
|
||||||
|
// kanged from: https://github.com/Qv2ray/gun-lite
|
||||||
|
|
||||||
|
var _ net.Conn = (*GunConn)(nil)
|
||||||
|
|
||||||
|
type GunConn struct {
|
||||||
|
reader *std_bufio.Reader
|
||||||
|
writer io.Writer
|
||||||
|
flusher http.Flusher
|
||||||
|
create chan struct{}
|
||||||
|
err error
|
||||||
|
readRemaining int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGunConn(reader io.Reader, writer io.Writer, flusher http.Flusher) *GunConn {
|
||||||
|
return &GunConn{
|
||||||
|
reader: std_bufio.NewReader(reader),
|
||||||
|
writer: writer,
|
||||||
|
flusher: flusher,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLateGunConn(writer io.Writer) *GunConn {
|
||||||
|
return &GunConn{
|
||||||
|
create: make(chan struct{}),
|
||||||
|
writer: writer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) setup(reader io.Reader, err error) {
|
||||||
|
c.reader = std_bufio.NewReader(reader)
|
||||||
|
c.err = err
|
||||||
|
close(c.create)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) Read(b []byte) (n int, err error) {
|
||||||
|
n, err = c.read(b)
|
||||||
|
return n, wrapError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) read(b []byte) (n int, err error) {
|
||||||
|
if c.reader == nil {
|
||||||
|
<-c.create
|
||||||
|
if c.err != nil {
|
||||||
|
return 0, c.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.readRemaining > 0 {
|
||||||
|
if len(b) > c.readRemaining {
|
||||||
|
b = b[:c.readRemaining]
|
||||||
|
}
|
||||||
|
n, err = c.reader.Read(b)
|
||||||
|
c.readRemaining -= n
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.reader.Discard(6)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLen, err := binary.ReadUvarint(c.reader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
readLen := int(dataLen)
|
||||||
|
c.readRemaining = readLen
|
||||||
|
if len(b) > readLen {
|
||||||
|
b = b[:readLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = c.reader.Read(b)
|
||||||
|
c.readRemaining -= n
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) Write(b []byte) (n int, err error) {
|
||||||
|
protobufHeader := [1 + binary.MaxVarintLen64]byte{0x0A}
|
||||||
|
varuintLen := binary.PutUvarint(protobufHeader[1:], uint64(len(b)))
|
||||||
|
grpcHeader := buf.Get(5)
|
||||||
|
grpcPayloadLen := uint32(1 + varuintLen + len(b))
|
||||||
|
binary.BigEndian.PutUint32(grpcHeader[1:5], grpcPayloadLen)
|
||||||
|
_, err = bufio.Copy(c.writer, io.MultiReader(bytes.NewReader(grpcHeader), bytes.NewReader(protobufHeader[:varuintLen+1]), bytes.NewReader(b)))
|
||||||
|
buf.Put(grpcHeader)
|
||||||
|
if f, ok := c.writer.(http.Flusher); ok {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
return len(b), wrapError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func uLen(x uint64) int {
|
||||||
|
i := 0
|
||||||
|
for x >= 0x80 {
|
||||||
|
x >>= 7
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
|
defer buffer.Release()
|
||||||
|
dataLen := buffer.Len()
|
||||||
|
varLen := uLen(uint64(dataLen))
|
||||||
|
header := buffer.ExtendHeader(6 + varLen)
|
||||||
|
binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen))
|
||||||
|
header[5] = 0x0A
|
||||||
|
binary.PutUvarint(header[6:], uint64(dataLen))
|
||||||
|
err := rw.WriteBytes(c.writer, buffer.Bytes())
|
||||||
|
if c.flusher != nil {
|
||||||
|
c.flusher.Flush()
|
||||||
|
}
|
||||||
|
return wrapError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) FrontHeadroom() int {
|
||||||
|
return 6 + binary.MaxVarintLen64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) Close() error {
|
||||||
|
return common.Close(c.reader, c.writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) LocalAddr() net.Addr {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) RemoteAddr() net.Addr {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) SetDeadline(t time.Time) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GunConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapError(err error) error {
|
||||||
|
if E.IsMulti(err, io.ErrUnexpectedEOF) {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
96
transport/v2raygrpclite/server.go
Normal file
96
transport/v2raygrpclite/server.go
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package v2raygrpclite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
sHttp "github.com/sagernet/sing/protocol/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.V2RayServerTransport = (*Server)(nil)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
handler N.TCPConnectionHandler
|
||||||
|
errorHandler E.Handler
|
||||||
|
httpServer *http.Server
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Network() []string {
|
||||||
|
return []string{N.NetworkTCP}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) *Server {
|
||||||
|
server := &Server{
|
||||||
|
handler: handler,
|
||||||
|
errorHandler: errorHandler,
|
||||||
|
path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
|
||||||
|
}
|
||||||
|
if tlsConfig != nil {
|
||||||
|
if !common.Contains(tlsConfig.NextProtos, "h2") {
|
||||||
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server.httpServer = &http.Server{
|
||||||
|
Handler: server,
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
}
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if request.URL.Path != s.path {
|
||||||
|
writer.WriteHeader(http.StatusNotFound)
|
||||||
|
s.badRequest(request, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if request.Method != http.MethodPost {
|
||||||
|
writer.WriteHeader(http.StatusNotFound)
|
||||||
|
s.badRequest(request, E.New("bad method: ", request.Method))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ct := request.Header.Get("Content-Type"); !strings.HasPrefix(ct, "application/grpc") {
|
||||||
|
writer.WriteHeader(http.StatusNotFound)
|
||||||
|
s.badRequest(request, E.New("bad content type: ", ct))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Header().Set("Content-Type", "application/grpc")
|
||||||
|
writer.Header().Set("TE", "trailers")
|
||||||
|
writer.WriteHeader(http.StatusOK)
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = sHttp.SourceAddress(request)
|
||||||
|
conn := newGunConn(request.Body, writer, writer.(http.Flusher))
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) badRequest(request *http.Request, err error) {
|
||||||
|
s.errorHandler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(listener net.Listener) error {
|
||||||
|
if s.httpServer.TLSConfig == nil {
|
||||||
|
return s.httpServer.Serve(listener)
|
||||||
|
} else {
|
||||||
|
return s.httpServer.ServeTLS(listener, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServePacket(listener net.PacketConn) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(s.httpServer))
|
||||||
|
}
|
|
@ -141,6 +141,7 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if response.StatusCode != 200 {
|
if response.StatusCode != 200 {
|
||||||
|
pipeInWriter.Close()
|
||||||
return nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status)
|
return nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status)
|
||||||
}
|
}
|
||||||
return &HTTPConn{
|
return &HTTPConn{
|
||||||
|
|
Loading…
Reference in a new issue