mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-05-13 20:44:12 +08:00
Merge d218a5e8c5e45ab21d68b00fdd9306bbeb50a9a1 into 72170d8b6be0d83af1624f6d31b81cf73f1a189c
This commit is contained in:
commit
a1f47b97c4
@ -1,11 +1,13 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChunkSizeDecoder is a utility class to decode size value from bytes.
|
// ChunkSizeDecoder is a utility class to decode size value from bytes.
|
||||||
@ -117,6 +119,7 @@ func (r *ChunkStreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
}
|
}
|
||||||
r.leftOverSize = size
|
r.leftOverSize = size
|
||||||
|
|
||||||
|
errors.LogInfo(context.Background(), "StreamReader read ", size)
|
||||||
mb, err := r.reader.ReadAtMost(size)
|
mb, err := r.reader.ReadAtMost(size)
|
||||||
if !mb.IsEmpty() {
|
if !mb.IsEmpty() {
|
||||||
r.leftOverSize -= mb.Len()
|
r.leftOverSize -= mb.Len()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package mux
|
package mux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
@ -33,6 +34,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size, err := serial.ReadUint16(r.reader)
|
size, err := serial.ReadUint16(r.reader)
|
||||||
|
errors.LogInfo(context.Background(), "PacketReader read ", size, r.dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,8 @@ func TestRegressionOutboundLeak(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
b := buf.FromBytes([]byte("hello"))
|
b := buf.New()
|
||||||
|
b.Write([]byte("hello"))
|
||||||
common.Must(muxClientDownlink.Writer.WriteMultiBuffer(buf.MultiBuffer{b}))
|
common.Must(muxClientDownlink.Writer.WriteMultiBuffer(buf.MultiBuffer{b}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +103,8 @@ func TestRegressionOutboundLeak(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
b := buf.FromBytes([]byte("world"))
|
b := buf.New()
|
||||||
|
b.Write([]byte("world"))
|
||||||
common.Must(websiteUplink.Writer.WriteMultiBuffer(buf.MultiBuffer{b}))
|
common.Must(websiteUplink.Writer.WriteMultiBuffer(buf.MultiBuffer{b}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package mux
|
package mux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
@ -75,10 +78,10 @@ func writeMetaWithFrame(writer buf.Writer, meta FrameMetadata, data buf.MultiBuf
|
|||||||
if _, err := serial.WriteUint16(frame, uint16(data.Len())); err != nil {
|
if _, err := serial.WriteUint16(frame, uint16(data.Len())); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mb2 := make(buf.MultiBuffer, 0, len(data)+1)
|
mb2 := make(buf.MultiBuffer, 0, len(data)+1)
|
||||||
mb2 = append(mb2, frame)
|
mb2 = append(mb2, frame)
|
||||||
mb2 = append(mb2, data...)
|
mb2 = append(mb2, data...)
|
||||||
|
mb2 = buf.Compact(mb2)
|
||||||
return writer.WriteMultiBuffer(mb2)
|
return writer.WriteMultiBuffer(mb2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +109,7 @@ func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
mb = mb2
|
mb = mb2
|
||||||
chunk = buf.MultiBuffer{b}
|
chunk = buf.MultiBuffer{b}
|
||||||
}
|
}
|
||||||
|
errors.LogInfo(context.Background(), "MuxWriter write ", chunk.Len(), w.dest)
|
||||||
if err := w.writeData(chunk); err != nil {
|
if err := w.writeData(chunk); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/httpupgrade"
|
"github.com/xtls/xray-core/transport/internet/httpupgrade"
|
||||||
"github.com/xtls/xray-core/transport/internet/kcp"
|
"github.com/xtls/xray-core/transport/internet/kcp"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/quic"
|
||||||
"github.com/xtls/xray-core/transport/internet/reality"
|
"github.com/xtls/xray-core/transport/internet/reality"
|
||||||
"github.com/xtls/xray-core/transport/internet/splithttp"
|
"github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
"github.com/xtls/xray-core/transport/internet/tcp"
|
"github.com/xtls/xray-core/transport/internet/tcp"
|
||||||
@ -332,6 +333,22 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QUICConfig struct {
|
||||||
|
// Header json.RawMessage `json:"header"`
|
||||||
|
// Security string `json:"security"`
|
||||||
|
// Key string `json:"key"`
|
||||||
|
|
||||||
|
Fec bool `json:"fec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build implements Buildable.
|
||||||
|
func (c *QUICConfig) Build() (proto.Message, error) {
|
||||||
|
config := &quic.Config{
|
||||||
|
Fec: c.Fec,
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
func readFileOrString(f string, s []string) ([]byte, error) {
|
func readFileOrString(f string, s []string) ([]byte, error) {
|
||||||
if len(f) > 0 {
|
if len(f) > 0 {
|
||||||
return filesystem.ReadCert(f)
|
return filesystem.ReadCert(f)
|
||||||
@ -683,8 +700,8 @@ func (p TransportProtocol) Build() (string, error) {
|
|||||||
return "httpupgrade", nil
|
return "httpupgrade", nil
|
||||||
case "h2", "h3", "http":
|
case "h2", "h3", "http":
|
||||||
return "", errors.PrintRemovedFeatureError("HTTP transport (without header padding, etc.)", "XHTTP stream-one H2 & H3")
|
return "", errors.PrintRemovedFeatureError("HTTP transport (without header padding, etc.)", "XHTTP stream-one H2 & H3")
|
||||||
case "quic":
|
case "quic", "datagram":
|
||||||
return "", errors.PrintRemovedFeatureError("QUIC transport (without web service, etc.)", "XHTTP stream-one H3")
|
return "quic", nil
|
||||||
default:
|
default:
|
||||||
return "", errors.New("Config: unknown transport protocol: ", p)
|
return "", errors.New("Config: unknown transport protocol: ", p)
|
||||||
}
|
}
|
||||||
@ -843,6 +860,7 @@ type StreamConfig struct {
|
|||||||
XHTTPSettings *SplitHTTPConfig `json:"xhttpSettings"`
|
XHTTPSettings *SplitHTTPConfig `json:"xhttpSettings"`
|
||||||
SplitHTTPSettings *SplitHTTPConfig `json:"splithttpSettings"`
|
SplitHTTPSettings *SplitHTTPConfig `json:"splithttpSettings"`
|
||||||
KCPSettings *KCPConfig `json:"kcpSettings"`
|
KCPSettings *KCPConfig `json:"kcpSettings"`
|
||||||
|
QUICSettings *QUICConfig `json:"quicSettings"`
|
||||||
GRPCSettings *GRPCConfig `json:"grpcSettings"`
|
GRPCSettings *GRPCConfig `json:"grpcSettings"`
|
||||||
WSSettings *WebSocketConfig `json:"wsSettings"`
|
WSSettings *WebSocketConfig `json:"wsSettings"`
|
||||||
HTTPUPGRADESettings *HttpUpgradeConfig `json:"httpupgradeSettings"`
|
HTTPUPGRADESettings *HttpUpgradeConfig `json:"httpupgradeSettings"`
|
||||||
@ -934,6 +952,16 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
Settings: serial.ToTypedMessage(ts),
|
Settings: serial.ToTypedMessage(ts),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if c.QUICSettings != nil {
|
||||||
|
qs, err := c.QUICSettings.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Failed to build QUIC config").Base(err)
|
||||||
|
}
|
||||||
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
||||||
|
ProtocolName: "quic",
|
||||||
|
Settings: serial.ToTypedMessage(qs),
|
||||||
|
})
|
||||||
|
}
|
||||||
if c.GRPCSettings != nil {
|
if c.GRPCSettings != nil {
|
||||||
gs, err := c.GRPCSettings.Build()
|
gs, err := c.GRPCSettings.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -53,6 +53,7 @@ import (
|
|||||||
_ "github.com/xtls/xray-core/transport/internet/grpc"
|
_ "github.com/xtls/xray-core/transport/internet/grpc"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/httpupgrade"
|
_ "github.com/xtls/xray-core/transport/internet/httpupgrade"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/kcp"
|
_ "github.com/xtls/xray-core/transport/internet/kcp"
|
||||||
|
_ "github.com/xtls/xray-core/transport/internet/quic"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/reality"
|
_ "github.com/xtls/xray-core/transport/internet/reality"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/splithttp"
|
_ "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/tcp"
|
_ "github.com/xtls/xray-core/transport/internet/tcp"
|
||||||
|
137
transport/internet/quic/config.pb.go
Normal file
137
transport/internet/quic/config.pb.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.35.1
|
||||||
|
// protoc v5.28.2
|
||||||
|
// source: transport/internet/quic/config.proto
|
||||||
|
|
||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// string key = 1;
|
||||||
|
// xray.common.protocol.SecurityConfig security = 2;
|
||||||
|
// xray.common.serial.TypedMessage header = 3;
|
||||||
|
Fec bool `protobuf:"varint,4,opt,name=fec,proto3" json:"fec,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_quic_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_quic_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_quic_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetFec() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Fec
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_quic_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_transport_internet_quic_config_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x24, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||||
|
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x71, 0x75, 0x69, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||||
|
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||||
|
0x71, 0x75, 0x69, 0x63, 0x22, 0x1a, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10,
|
||||||
|
0x0a, 0x03, 0x66, 0x65, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x66, 0x65, 0x63,
|
||||||
|
0x42, 0x76, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||||
|
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||||
|
0x71, 0x75, 0x69, 0x63, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
|
||||||
|
0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||||
|
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x71, 0x75, 0x69, 0x63, 0xaa, 0x02, 0x1c, 0x58, 0x72, 0x61, 0x79,
|
||||||
|
0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72,
|
||||||
|
0x6e, 0x65, 0x74, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_quic_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_quic_config_proto_rawDescData = file_transport_internet_quic_config_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_quic_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_quic_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_quic_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_quic_config_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_transport_internet_quic_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_quic_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_quic_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.quic.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_quic_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_quic_config_proto_init() }
|
||||||
|
func file_transport_internet_quic_config_proto_init() {
|
||||||
|
if File_transport_internet_quic_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_transport_internet_quic_config_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_quic_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_quic_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_quic_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_quic_config_proto = out.File
|
||||||
|
file_transport_internet_quic_config_proto_rawDesc = nil
|
||||||
|
file_transport_internet_quic_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_quic_config_proto_depIdxs = nil
|
||||||
|
}
|
14
transport/internet/quic/config.proto
Normal file
14
transport/internet/quic/config.proto
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.quic;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Quic";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/quic";
|
||||||
|
option java_package = "com.xray.transport.internet.quic";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
// string key = 1;
|
||||||
|
// xray.common.protocol.SecurityConfig security = 2;
|
||||||
|
// xray.common.serial.TypedMessage header = 3;
|
||||||
|
bool fec = 4;
|
||||||
|
}
|
247
transport/internet/quic/conn.go
Normal file
247
transport/internet/quic/conn.go
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/mux"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/serial"
|
||||||
|
"github.com/xtls/xray-core/common/signal/done"
|
||||||
|
)
|
||||||
|
|
||||||
|
var MaxIncomingStreams = 16
|
||||||
|
var currentStream = 0
|
||||||
|
|
||||||
|
type interConn struct {
|
||||||
|
ctx context.Context
|
||||||
|
quicConn quic.Connection // small udp packet can be sent with Datagram directly
|
||||||
|
streams []quic.Stream // other packets can be sent via steam, it offer mux, reliability, fragmentation and ordering
|
||||||
|
readChannel chan readResult
|
||||||
|
reader buf.MultiBufferContainer
|
||||||
|
done *done.Instance
|
||||||
|
local net.Addr
|
||||||
|
remote net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
type readResult struct {
|
||||||
|
buffer []byte
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnInitReader(ctx context.Context, quicConn quic.Connection, done *done.Instance, remote net.Addr) *interConn {
|
||||||
|
c := &interConn{
|
||||||
|
ctx: ctx,
|
||||||
|
quicConn: quicConn,
|
||||||
|
readChannel: make(chan readResult),
|
||||||
|
reader: buf.MultiBufferContainer{},
|
||||||
|
done: done,
|
||||||
|
local: quicConn.LocalAddr(),
|
||||||
|
remote: remote,
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
received, e := c.quicConn.ReceiveDatagram(c.ctx)
|
||||||
|
errors.LogInfo(c.ctx, "Read ReceiveDatagram ", len(received))
|
||||||
|
c.readChannel <- readResult{buffer: received, err: e}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go c.acceptStreams()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) acceptStreams() {
|
||||||
|
for {
|
||||||
|
stream, err := c.quicConn.AcceptStream(context.Background())
|
||||||
|
errors.LogInfo(c.ctx, "Read AcceptStream ", err)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "failed to accept stream")
|
||||||
|
select {
|
||||||
|
case <-c.quicConn.Context().Done():
|
||||||
|
return
|
||||||
|
case <-c.done.Wait():
|
||||||
|
if err := c.quicConn.CloseWithError(0, ""); err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "failed to close connection")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go c.readMuxCoolPacket(stream)
|
||||||
|
c.streams = append(c.streams, stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) readMuxCoolPacket(stream quic.Stream) {
|
||||||
|
for {
|
||||||
|
received := make([]byte, buf.Size)
|
||||||
|
i, e := stream.Read(received)
|
||||||
|
if e != nil {
|
||||||
|
errors.LogErrorInner(c.ctx, e, "Error read stream, drop this buffer ", i)
|
||||||
|
c.readChannel <- readResult{buffer: nil, err: e}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
errors.LogInfo(c.ctx, "Read stream ", i)
|
||||||
|
|
||||||
|
buffer := buf.New()
|
||||||
|
buffer.Write(received[:i])
|
||||||
|
muxCoolReader := &buf.MultiBufferContainer{}
|
||||||
|
muxCoolReader.MultiBuffer = append(muxCoolReader.MultiBuffer, buffer)
|
||||||
|
var meta mux.FrameMetadata
|
||||||
|
err := meta.Unmarshal(muxCoolReader)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfo(c.ctx, "Not a Mux Cool packet beginning, copy directly ", i)
|
||||||
|
buf.ReleaseMulti(muxCoolReader.MultiBuffer)
|
||||||
|
c.readChannel <- readResult{buffer: received[:i], err: e}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !meta.Option.Has(mux.OptionData) {
|
||||||
|
errors.LogInfo(c.ctx, "No option data, copy directly ", i)
|
||||||
|
buf.ReleaseMulti(muxCoolReader.MultiBuffer)
|
||||||
|
c.readChannel <- readResult{buffer: received[:i], err: e}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size, err := serial.ReadUint16(muxCoolReader)
|
||||||
|
remaining := uint16(muxCoolReader.MultiBuffer.Len())
|
||||||
|
errors.LogInfo(c.ctx, "Read stream ", i, " option size ", size, " remaining size ", remaining)
|
||||||
|
if err != nil || size <= remaining || size > remaining + 1500 {
|
||||||
|
errors.LogInfo(c.ctx, "do not wait for second part of UDP packet ", i)
|
||||||
|
buf.ReleaseMulti(muxCoolReader.MultiBuffer)
|
||||||
|
c.readChannel <- readResult{buffer: received[:i], err: e}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2, e := stream.Read(received[i:])
|
||||||
|
if e != nil {
|
||||||
|
errors.LogErrorInner(c.ctx, e, "Error read stream, drop this buffer ", i2)
|
||||||
|
buf.ReleaseMulti(muxCoolReader.MultiBuffer)
|
||||||
|
c.readChannel <- readResult{buffer: nil, err: e}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
errors.LogInfo(c.ctx, "Read stream i2 size ", i2)
|
||||||
|
buf.ReleaseMulti(muxCoolReader.MultiBuffer)
|
||||||
|
c.readChannel <- readResult{buffer: received[:(i + i2)], err: e}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) Read(b []byte) (int, error) {
|
||||||
|
if c.reader.MultiBuffer.Len() > 0 {
|
||||||
|
return c.reader.Read(b)
|
||||||
|
}
|
||||||
|
received := <- c.readChannel
|
||||||
|
if received.err != nil {
|
||||||
|
return 0, received.err
|
||||||
|
}
|
||||||
|
buffer := buf.New()
|
||||||
|
buffer.Write(received.buffer)
|
||||||
|
c.reader.MultiBuffer = append(c.reader.MultiBuffer, buffer)
|
||||||
|
errors.LogInfo(c.ctx, "Read copy ", len(received.buffer))
|
||||||
|
return c.reader.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
|
mb = buf.Compact(mb)
|
||||||
|
mb, err := buf.WriteMultiBuffer(c, mb)
|
||||||
|
buf.ReleaseMulti(mb)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) Write(b []byte) (int, error) {
|
||||||
|
if len(b) > 1240 { // TODO: why quic-go increase internal MTU causing packet loss?
|
||||||
|
if len(c.streams) < MaxIncomingStreams {
|
||||||
|
stream, err := c.quicConn.OpenStream()
|
||||||
|
errors.LogInfo(c.ctx, "Write OpenStream ", err)
|
||||||
|
if err == nil {
|
||||||
|
c.streams = append(c.streams, stream)
|
||||||
|
} else {
|
||||||
|
errors.LogInfoInner(c.ctx, err, "failed to openStream: ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentStream++;
|
||||||
|
if currentStream > len(c.streams) - 1 {
|
||||||
|
currentStream = 0;
|
||||||
|
}
|
||||||
|
errors.LogInfo(c.ctx, "Write stream ", len(b), currentStream, len(c.streams))
|
||||||
|
return c.streams[currentStream].Write(b)
|
||||||
|
}
|
||||||
|
var err = c.quicConn.SendDatagram(b)
|
||||||
|
errors.LogInfo(c.ctx, "Write SendDatagram ", len(b), err)
|
||||||
|
if _, ok := err.(*quic.DatagramTooLargeError); ok {
|
||||||
|
if len(c.streams) < MaxIncomingStreams {
|
||||||
|
stream, err := c.quicConn.OpenStream()
|
||||||
|
errors.LogInfo(c.ctx, "Write OpenStream ", err)
|
||||||
|
if err == nil {
|
||||||
|
c.streams = append(c.streams, stream)
|
||||||
|
} else {
|
||||||
|
errors.LogInfoInner(c.ctx, err, "failed to openStream: ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentStream++;
|
||||||
|
if currentStream > len(c.streams) - 1 {
|
||||||
|
currentStream = 0;
|
||||||
|
}
|
||||||
|
errors.LogInfo(c.ctx, "Write stream ", len(b), currentStream, len(c.streams))
|
||||||
|
return c.streams[currentStream].Write(b)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) Close() error {
|
||||||
|
var err error
|
||||||
|
for _, s := range c.streams {
|
||||||
|
e := s.Close()
|
||||||
|
if e != nil {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) LocalAddr() net.Addr {
|
||||||
|
return c.local
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) RemoteAddr() net.Addr {
|
||||||
|
return c.remote
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) SetDeadline(t time.Time) error {
|
||||||
|
var err error
|
||||||
|
for _, s := range c.streams {
|
||||||
|
e := s.SetDeadline(t)
|
||||||
|
if e != nil {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) SetReadDeadline(t time.Time) error {
|
||||||
|
var err error
|
||||||
|
for _, s := range c.streams {
|
||||||
|
e := s.SetReadDeadline(t)
|
||||||
|
if e != nil {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *interConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
var err error
|
||||||
|
for _, s := range c.streams {
|
||||||
|
e := s.SetWriteDeadline(t)
|
||||||
|
if e != nil {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
95
transport/internet/quic/dialer.go
Normal file
95
transport/internet/quic/dialer.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/signal/done"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
||||||
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
|
if tlsConfig == nil {
|
||||||
|
tlsConfig = &tls.Config{
|
||||||
|
ServerName: internalDomain,
|
||||||
|
AllowInsecure: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var destAddr *net.UDPAddr
|
||||||
|
if dest.Address.Family().IsIP() {
|
||||||
|
destAddr = &net.UDPAddr{
|
||||||
|
IP: dest.Address.IP(),
|
||||||
|
Port: int(dest.Port),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dialerIp := internet.DestIpAddress()
|
||||||
|
if dialerIp != nil {
|
||||||
|
destAddr = &net.UDPAddr{
|
||||||
|
IP: dialerIp,
|
||||||
|
Port: int(dest.Port),
|
||||||
|
}
|
||||||
|
errors.LogInfo(ctx, "quic Dial use dialer dest addr: ", destAddr)
|
||||||
|
} else {
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", dest.NetAddr())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
destAddr = addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
|
||||||
|
return openConnection(ctx, destAddr, config, tlsConfig, streamSettings.SocketSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func openConnection(ctx context.Context, destAddr net.Addr, config *Config, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (stat.Connection, error) {
|
||||||
|
dest := net.DestinationFromAddr(destAddr)
|
||||||
|
errors.LogInfo(ctx, "dialing quic to ", dest)
|
||||||
|
rawConn, err := internet.DialSystem(ctx, dest, sockopt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
quicConfig := &quic.Config{
|
||||||
|
KeepAlivePeriod: 0,
|
||||||
|
HandshakeIdleTimeout: time.Second * 8,
|
||||||
|
MaxIdleTimeout: time.Second * 300,
|
||||||
|
EnableDatagrams: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var udpConn *net.UDPConn
|
||||||
|
switch conn := rawConn.(type) {
|
||||||
|
case *net.UDPConn:
|
||||||
|
udpConn = conn
|
||||||
|
case *internet.PacketConnWrapper:
|
||||||
|
udpConn = conn.Conn.(*net.UDPConn)
|
||||||
|
default:
|
||||||
|
rawConn.Close()
|
||||||
|
return nil, errors.New("QUIC with sockopt is unsupported").AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := quic.Transport{
|
||||||
|
ConnectionIDLength: 12,
|
||||||
|
Conn: udpConn,
|
||||||
|
}
|
||||||
|
conn, err := tr.Dial(context.Background(), destAddr, tlsConfig.GetTLSConfig(tls.WithDestination(dest)), quicConfig)
|
||||||
|
if err != nil {
|
||||||
|
udpConn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewConnInitReader(ctx, conn, done.New(), destAddr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
|
||||||
|
}
|
108
transport/internet/quic/hub.go
Normal file
108
transport/internet/quic/hub.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||||
|
"github.com/xtls/xray-core/common/signal/done"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listener is an internet.Listener that listens for TCP connections.
|
||||||
|
type Listener struct {
|
||||||
|
rawConn *net.UDPConn
|
||||||
|
listener *quic.Listener
|
||||||
|
done *done.Instance
|
||||||
|
addConn internet.ConnHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) keepAccepting(ctx context.Context) {
|
||||||
|
for {
|
||||||
|
conn, err := l.listener.Accept(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "failed to accept QUIC connection")
|
||||||
|
if l.done.Done() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
l.addConn(NewConnInitReader(ctx, conn, l.done, conn.RemoteAddr()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr implements internet.Listener.Addr.
|
||||||
|
func (l *Listener) Addr() net.Addr {
|
||||||
|
return l.listener.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements internet.Listener.Close.
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
l.done.Close()
|
||||||
|
l.listener.Close()
|
||||||
|
l.rawConn.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen creates a new Listener based on configurations.
|
||||||
|
func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
|
||||||
|
if address.Family().IsDomain() {
|
||||||
|
return nil, errors.New("domain address is not allows for listening quic")
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
|
if tlsConfig == nil {
|
||||||
|
tlsConfig = &tls.Config{
|
||||||
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.DNSNames(internalDomain), cert.CommonName(internalDomain)))},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//config := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
|
||||||
|
IP: address.IP(),
|
||||||
|
Port: int(port),
|
||||||
|
}, streamSettings.SocketSettings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
quicConfig := &quic.Config{
|
||||||
|
KeepAlivePeriod: 0,
|
||||||
|
HandshakeIdleTimeout: time.Second * 8,
|
||||||
|
MaxIdleTimeout: time.Second * 300,
|
||||||
|
MaxIncomingStreams: 16,
|
||||||
|
MaxIncomingUniStreams: -1,
|
||||||
|
EnableDatagrams: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := quic.Transport{
|
||||||
|
ConnectionIDLength: 12,
|
||||||
|
Conn: rawConn.(*net.UDPConn),
|
||||||
|
}
|
||||||
|
qListener, err := tr.Listen(tlsConfig.GetTLSConfig(), quicConfig)
|
||||||
|
if err != nil {
|
||||||
|
rawConn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listener := &Listener{
|
||||||
|
done: done.New(),
|
||||||
|
rawConn: rawConn.(*net.UDPConn),
|
||||||
|
listener: qListener,
|
||||||
|
addConn: handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
go listener.keepAccepting(ctx)
|
||||||
|
|
||||||
|
return listener, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(internet.RegisterTransportListener(protocolName, Listen))
|
||||||
|
}
|
17
transport/internet/quic/quic.go
Normal file
17
transport/internet/quic/quic.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
protocolName = "quic"
|
||||||
|
internalDomain = "quic.internal.example.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
|
||||||
|
return new(Config)
|
||||||
|
}))
|
||||||
|
}
|
105
transport/internet/quic/quic_test.go
Normal file
105
transport/internet/quic/quic_test.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package quic_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||||
|
"github.com/xtls/xray-core/testing/servers/udp"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/quic"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShortQuicConnection(t *testing.T) {
|
||||||
|
testQuicConnection(t, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAroundMTUQuicConnection(t *testing.T) {
|
||||||
|
testQuicConnection(t, 1247)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLongQuicConnection(t *testing.T) {
|
||||||
|
testQuicConnection(t, 1500)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testQuicConnection(t *testing.T, dataLen int32) {
|
||||||
|
port := udp.PickPort()
|
||||||
|
|
||||||
|
listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "quic",
|
||||||
|
ProtocolSettings: &quic.Config{},
|
||||||
|
SecurityType: "tls",
|
||||||
|
SecuritySettings: &tls.Config{
|
||||||
|
Certificate: []*tls.Certificate{
|
||||||
|
tls.ParseCertificate(
|
||||||
|
cert.MustGenerate(nil,
|
||||||
|
cert.DNSNames("www.example.com"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, func(conn stat.Connection) {
|
||||||
|
go func() {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
b := buf.New()
|
||||||
|
defer b.Release()
|
||||||
|
|
||||||
|
for {
|
||||||
|
b.Clear()
|
||||||
|
if _, err := b.ReadFrom(conn); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.Must2(conn.Write(b.Bytes()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
dctx := context.Background()
|
||||||
|
conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "quic",
|
||||||
|
ProtocolSettings: &quic.Config{},
|
||||||
|
SecurityType: "tls",
|
||||||
|
SecuritySettings: &tls.Config{
|
||||||
|
ServerName: "www.example.com",
|
||||||
|
AllowInsecure: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
b1 := make([]byte, dataLen)
|
||||||
|
common.Must2(rand.Read(b1))
|
||||||
|
b2 := buf.New()
|
||||||
|
|
||||||
|
common.Must2(conn.Write(b1))
|
||||||
|
|
||||||
|
b2.Clear()
|
||||||
|
common.Must2(b2.ReadFullFrom(conn, dataLen))
|
||||||
|
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
|
||||||
|
t.Error(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
|
||||||
|
common.Must2(conn.Write(b1))
|
||||||
|
|
||||||
|
b2.Clear()
|
||||||
|
common.Must2(b2.ReadFullFrom(conn, dataLen))
|
||||||
|
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
|
||||||
|
t.Error(r)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user