From 9c5e47d2612176b1d8545dbf2eab9e928eea48fc Mon Sep 17 00:00:00 2001
From: mockingjay <8400684@qq.com>
Date: Sat, 4 May 2024 21:27:25 +0800
Subject: [PATCH 1/8] Add support for quic

---
 SuperSocket.sln                               |  30 ++++
 .../QuicPipeConnection.cs                     |  55 ++++++
 .../QuicPipeStream.cs                         |  66 ++++++++
 .../SuperSocket.Quic.Connection.csproj        |  12 ++
 src/SuperSocket.Quic/QuicConnectionFactory.cs |  29 ++++
 .../QuicConnectionFactoryBuilder.cs           |  13 ++
 .../QuicConnectionListener.cs                 | 157 ++++++++++++++++++
 .../QuicConnectionListenerFactory.cs          |  72 ++++++++
 .../QuicServerHostBuilderExtensions.cs        |  44 +++++
 src/SuperSocket.Quic/QuicTransportOptions.cs  |  27 +++
 src/SuperSocket.Quic/SuperSocket.Quic.csproj  |  15 ++
 test/SuperSocket.Tests/ClientTest.cs          |   3 +
 .../SuperSocket.Tests/QuicHostConfigurator.cs | 148 +++++++++++++++++
 .../SuperSocket.Tests.csproj                  |   1 +
 14 files changed, 672 insertions(+)
 create mode 100644 src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
 create mode 100644 src/SuperSocket.Quic.Connection/QuicPipeStream.cs
 create mode 100644 src/SuperSocket.Quic.Connection/SuperSocket.Quic.Connection.csproj
 create mode 100644 src/SuperSocket.Quic/QuicConnectionFactory.cs
 create mode 100644 src/SuperSocket.Quic/QuicConnectionFactoryBuilder.cs
 create mode 100644 src/SuperSocket.Quic/QuicConnectionListener.cs
 create mode 100644 src/SuperSocket.Quic/QuicConnectionListenerFactory.cs
 create mode 100644 src/SuperSocket.Quic/QuicServerHostBuilderExtensions.cs
 create mode 100644 src/SuperSocket.Quic/QuicTransportOptions.cs
 create mode 100644 src/SuperSocket.Quic/SuperSocket.Quic.csproj
 create mode 100644 test/SuperSocket.Tests/QuicHostConfigurator.cs

diff --git a/SuperSocket.sln b/SuperSocket.sln
index 834eed086..5ba66e3f4 100644
--- a/SuperSocket.sln
+++ b/SuperSocket.sln
@@ -39,6 +39,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Connection", "s
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Kestrel", "src\SuperSocket.Kestrel\SuperSocket.Kestrel.csproj", "{8C8507D6-903F-4786-8F18-ACA54257454B}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Quic", "src\SuperSocket.Quic\SuperSocket.Quic.csproj", "{AC215A13-27AE-4417-A66A-6894BB732D2B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Quic.Connection", "src\SuperSocket.Quic.Connection\SuperSocket.Quic.Connection.csproj", "{8EE55816-8775-4A04-A3C3-05CCBD21F66D}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -241,6 +245,30 @@ Global
 		{8C8507D6-903F-4786-8F18-ACA54257454B}.Release|x64.Build.0 = Release|Any CPU
 		{8C8507D6-903F-4786-8F18-ACA54257454B}.Release|x86.ActiveCfg = Release|Any CPU
 		{8C8507D6-903F-4786-8F18-ACA54257454B}.Release|x86.Build.0 = Release|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Debug|x64.Build.0 = Debug|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Debug|x86.Build.0 = Debug|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Release|x64.ActiveCfg = Release|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Release|x64.Build.0 = Release|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Release|x86.ActiveCfg = Release|Any CPU
+		{AC215A13-27AE-4417-A66A-6894BB732D2B}.Release|x86.Build.0 = Release|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Debug|x64.Build.0 = Debug|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Debug|x86.Build.0 = Debug|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Release|x64.ActiveCfg = Release|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Release|x64.Build.0 = Release|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Release|x86.ActiveCfg = Release|Any CPU
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -262,6 +290,8 @@ Global
 		{8454E8D5-777D-46CB-B050-76C5119B624B} = {DDA4741E-097F-40C3-A252-2E1E3476C1A7}
 		{FC2B529F-4AF4-4C39-BC4F-A3836CC7B37C} = {DDA4741E-097F-40C3-A252-2E1E3476C1A7}
 		{8C8507D6-903F-4786-8F18-ACA54257454B} = {DDA4741E-097F-40C3-A252-2E1E3476C1A7}
+		{AC215A13-27AE-4417-A66A-6894BB732D2B} = {DDA4741E-097F-40C3-A252-2E1E3476C1A7}
+		{8EE55816-8775-4A04-A3C3-05CCBD21F66D} = {DDA4741E-097F-40C3-A252-2E1E3476C1A7}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {ADB30AA2-A848-4CB3-8A20-488C80F1BA9E}
diff --git a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
new file mode 100644
index 000000000..88f0221f1
--- /dev/null
+++ b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
@@ -0,0 +1,55 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Quic;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+using SuperSocket.Connection;
+
+#pragma warning disable CA2252
+
+namespace SuperSocket.Quic.Connection;
+
+public class QuicPipeConnection : StreamPipeConnection
+{
+    private readonly Stream _stream;
+
+    public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, ConnectionOptions options)
+        : this(stream, remoteEndPoint, null, options)
+    {
+    }
+
+    public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, EndPoint localEndPoint, ConnectionOptions options)
+        : base(stream, remoteEndPoint, localEndPoint, options)
+    {
+        if (stream is not QuicStream or QuicPipeStream)
+            throw new NotSupportedException("QuicPipeConnection only supports QuicStream or QuicPipeStream");
+
+        _stream = stream;
+    }
+
+    protected override async Task StartInputPipeTask<TPackageInfo>(IObjectPipe<TPackageInfo> packagePipe,
+        CancellationToken cancellationToken)
+    {
+        if (_stream is QuicPipeStream quicPipeStream)
+            await quicPipeStream.OpenStreamAsync(cancellationToken);
+
+        await base.StartInputPipeTask(packagePipe, cancellationToken);
+    }
+
+    protected override bool IsIgnorableException(Exception e)
+    {
+        if (base.IsIgnorableException(e))
+            return true;
+
+        switch (e)
+        {
+            case QuicException:
+            case SocketException se when se.IsIgnorableSocketException():
+                return true;
+            default:
+                return false;
+        }
+    }
+}
diff --git a/src/SuperSocket.Quic.Connection/QuicPipeStream.cs b/src/SuperSocket.Quic.Connection/QuicPipeStream.cs
new file mode 100644
index 000000000..b1bb6ecd5
--- /dev/null
+++ b/src/SuperSocket.Quic.Connection/QuicPipeStream.cs
@@ -0,0 +1,66 @@
+using System;
+using System.IO;
+using System.Net.Quic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SuperSocket.Quic.Connection;
+
+#pragma warning disable CA2252
+
+public sealed class QuicPipeStream : Stream
+{
+    private Stream _stream;
+
+    private readonly QuicConnection _connection;
+
+    public QuicPipeStream(QuicConnection connection)
+    {
+        _connection = connection;
+    }
+
+    public override bool CanRead => _stream.CanRead;
+    public override bool CanSeek => _stream.CanSeek;
+    public override bool CanWrite => _stream.CanWrite;
+    public override long Length => _stream.Length;
+
+    public override long Position
+    {
+        get => _stream.Position;
+        set => _stream.Position = value;
+    }
+
+    public async ValueTask OpenStreamAsync(CancellationToken cancellationToken)
+    {
+        _stream = await _connection.AcceptInboundStreamAsync(cancellationToken);
+    }
+
+    public override void Flush() => _stream.Flush();
+
+    public override int Read(byte[] buffer, int offset, int count) => _stream.Read(buffer, offset, count);
+
+    public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin);
+
+    public override void SetLength(long value) => _stream.Flush();
+
+    public override void Close() => _stream.Close();
+
+    public override Task FlushAsync(CancellationToken cancellationToken) => _stream.FlushAsync(cancellationToken);
+
+    public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
+        _stream.ReadAsync(buffer, offset, count, cancellationToken);
+
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer,
+        CancellationToken cancellationToken = default) => _stream.ReadAsync(buffer, cancellationToken);
+
+    public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
+        _stream.WriteAsync(buffer, offset, count, cancellationToken);
+
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer,
+        CancellationToken cancellationToken = default) =>
+        _stream.WriteAsync(buffer, cancellationToken);
+
+    public override void Write(ReadOnlySpan<byte> buffer) => _stream.Flush();
+
+    public override void Write(byte[] buffer, int offset, int count) => _stream.Flush();
+}
\ No newline at end of file
diff --git a/src/SuperSocket.Quic.Connection/SuperSocket.Quic.Connection.csproj b/src/SuperSocket.Quic.Connection/SuperSocket.Quic.Connection.csproj
new file mode 100644
index 000000000..e1e646928
--- /dev/null
+++ b/src/SuperSocket.Quic.Connection/SuperSocket.Quic.Connection.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <Description>SuperSocket quic connection library.</Description>
+        <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\SuperSocket.Connection\SuperSocket.Connection.csproj" />
+    </ItemGroup>
+
+</Project>
diff --git a/src/SuperSocket.Quic/QuicConnectionFactory.cs b/src/SuperSocket.Quic/QuicConnectionFactory.cs
new file mode 100644
index 000000000..a59e5950c
--- /dev/null
+++ b/src/SuperSocket.Quic/QuicConnectionFactory.cs
@@ -0,0 +1,29 @@
+using System.Net.Quic;
+using System.Threading;
+using System.Threading.Tasks;
+using SuperSocket.Connection;
+using SuperSocket.Quic.Connection;
+
+#pragma warning disable CA2252
+namespace SuperSocket.Quic;
+
+internal class QuicConnectionFactory : IConnectionFactory
+{
+    private readonly ConnectionOptions _connectionOptions;
+
+    public QuicConnectionFactory(ConnectionOptions connectionOptions)
+    {
+        _connectionOptions = connectionOptions;
+    }
+
+    public Task<IConnection> CreateConnection(object connection, CancellationToken cancellationToken)
+    {
+        var quicConnection = (QuicConnection)connection;
+
+        var quicStream = new QuicPipeStream(quicConnection);
+
+        var pipeConnection = new QuicPipeConnection(quicStream, quicConnection.RemoteEndPoint, quicConnection.LocalEndPoint, _connectionOptions);
+
+        return Task.FromResult<IConnection>(pipeConnection);
+    }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.Quic/QuicConnectionFactoryBuilder.cs b/src/SuperSocket.Quic/QuicConnectionFactoryBuilder.cs
new file mode 100644
index 000000000..7ca1e740c
--- /dev/null
+++ b/src/SuperSocket.Quic/QuicConnectionFactoryBuilder.cs
@@ -0,0 +1,13 @@
+using SuperSocket.Connection;
+using SuperSocket.Server.Abstractions;
+using SuperSocket.Server.Abstractions.Connections;
+
+namespace SuperSocket.Quic;
+
+internal class QuicConnectionFactoryBuilder : IConnectionFactoryBuilder
+{
+    public IConnectionFactory Build(ListenOptions listenOptions, ConnectionOptions connectionOptions)
+    {
+        return new QuicConnectionFactory(connectionOptions);
+    }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.Quic/QuicConnectionListener.cs b/src/SuperSocket.Quic/QuicConnectionListener.cs
new file mode 100644
index 000000000..ded0514f2
--- /dev/null
+++ b/src/SuperSocket.Quic/QuicConnectionListener.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using SuperSocket.Connection;
+using SuperSocket.Server.Abstractions;
+using SuperSocket.Server.Abstractions.Connections;
+using System.Net.Quic;
+using System.Net.Security;
+
+#pragma warning disable CA2252
+namespace SuperSocket.Quic;
+
+internal sealed class QuicConnectionListener : IConnectionListener
+{
+    private readonly ILogger _logger;
+    private readonly QuicTransportOptions _quicTransportOptions;
+
+    private QuicListener _listenQuic;
+    private CancellationTokenSource _cancellationTokenSource;
+    private TaskCompletionSource<bool> _stopTaskCompletionSource;
+    public IConnectionFactory ConnectionFactory { get; }
+    public ListenOptions Options { get; }
+    public bool IsRunning { get; private set; }
+
+    public QuicConnectionListener(ListenOptions options,
+        QuicTransportOptions quicTransportOptions,
+        IConnectionFactory connectionFactory, ILogger logger)
+    {
+        Options = options;
+        ConnectionFactory = connectionFactory;
+        _logger = logger;
+        _quicTransportOptions = quicTransportOptions;
+    }
+
+    public bool Start()
+    {
+        var options = Options;
+
+        try
+        {
+            var listenEndpoint = options.ToEndPoint();
+
+            if (options.CertificateOptions == null)
+                throw new ArgumentNullException(nameof(options.CertificateOptions),"Quic requires an ssl certificate");
+            
+            if (options.CertificateOptions.Certificate == null)
+                options.CertificateOptions.EnsureCertificate();
+
+            var quicListenerOptions = new QuicListenerOptions
+            {
+                ListenBacklog = options.BackLog,
+                ListenEndPoint = listenEndpoint,
+                ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },
+                ConnectionOptionsCallback = (connection, ssl, token) => ValueTask.FromResult(
+                    new QuicServerConnectionOptions
+                    {
+                        DefaultStreamErrorCode = _quicTransportOptions.DefaultStreamErrorCode,
+                        DefaultCloseErrorCode = _quicTransportOptions.DefaultCloseErrorCode,
+                        IdleTimeout = _quicTransportOptions.IdleTimeout.HasValue
+                            ? TimeSpan.FromMicroseconds(_quicTransportOptions.IdleTimeout.Value)
+                            : Timeout.InfiniteTimeSpan,
+                        MaxInboundBidirectionalStreams = _quicTransportOptions.MaxBidirectionalStreamCount,
+                        MaxInboundUnidirectionalStreams = _quicTransportOptions.MaxUnidirectionalStreamCount,
+                        ServerAuthenticationOptions = new SslServerAuthenticationOptions()
+                        {
+                            ApplicationProtocols =
+                                new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },
+                            ServerCertificate = options.CertificateOptions.Certificate,
+                            RemoteCertificateValidationCallback =
+                                options.CertificateOptions.RemoteCertificateValidationCallback,
+                        }
+                    })
+            };
+
+            var listenSocket = _listenQuic = QuicListener.ListenAsync(quicListenerOptions).GetAwaiter().GetResult();
+
+            IsRunning = true;
+
+            _cancellationTokenSource = new CancellationTokenSource();
+
+            KeepAcceptAsync(listenSocket, _cancellationTokenSource.Token).DoNotAwait();
+            return true;
+        }
+        catch (Exception e)
+        {
+            _logger.LogError(e, $"The listener[{this.ToString()}] failed to start.");
+            return false;
+        }
+    }
+
+
+    private async Task KeepAcceptAsync(QuicListener listenSocket, CancellationToken cancellationToken)
+    {
+        while (!cancellationToken.IsCancellationRequested)
+        {
+            try
+            {
+                var quicConnection =
+                    await listenSocket.AcceptConnectionAsync(cancellationToken).ConfigureAwait(false);
+                OnNewClientAccept(quicConnection, cancellationToken);
+            }
+            catch (Exception e)
+            {
+                _logger.LogError(e, $"Listener[{this.ToString()}] failed to do AcceptAsync");
+            }
+        }
+
+        _stopTaskCompletionSource.TrySetResult(true);
+    }
+
+    public event NewConnectionAcceptHandler NewConnectionAccept;
+
+    private async void OnNewClientAccept(QuicConnection quicConnection, CancellationToken cancellationToken)
+    {
+        var handler = NewConnectionAccept;
+
+        if (handler == null)
+            return;
+
+        IConnection connection = null;
+
+        try
+        {
+            using var cts = CancellationTokenSourcePool.Shared.Rent(Options.ConnectionAcceptTimeOut);
+            connection = await ConnectionFactory.CreateConnection(quicConnection, cts.Token);
+        }
+        catch (Exception e)
+        {
+            _logger.LogError(e, $"Failed to create quicConnection for {quicConnection.RemoteEndPoint}.");
+            return;
+        }
+
+        await handler.Invoke(this.Options, connection);
+    }
+
+    public async Task StopAsync()
+    {
+        var listenSocket = _listenQuic;
+
+        if (listenSocket == null)
+            return;
+
+        _stopTaskCompletionSource = new TaskCompletionSource<bool>();
+
+        _cancellationTokenSource.Cancel();
+        await _listenQuic.DisposeAsync();
+
+        await _stopTaskCompletionSource.Task;
+    }
+
+    public override string ToString()
+    {
+        return Options?.ToString();
+    }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.Quic/QuicConnectionListenerFactory.cs b/src/SuperSocket.Quic/QuicConnectionListenerFactory.cs
new file mode 100644
index 000000000..c6e5cc00f
--- /dev/null
+++ b/src/SuperSocket.Quic/QuicConnectionListenerFactory.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Quic;
+using System.Net.Security;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using SuperSocket.Connection;
+using SuperSocket.Server.Abstractions;
+using SuperSocket.Server.Abstractions.Connections;
+
+namespace SuperSocket.Quic;
+#pragma warning disable CA2252
+internal class QuicConnectionListenerFactory : IConnectionListenerFactory
+{
+    private readonly QuicTransportOptions _quicTransportOptions;
+    private readonly IConnectionFactoryBuilder _connectionFactoryBuilder;
+
+    public QuicConnectionListenerFactory(IConnectionFactoryBuilder connectionFactoryBuilder,
+        IOptions<QuicTransportOptions> options)
+    {
+        _connectionFactoryBuilder = connectionFactoryBuilder;
+        _quicTransportOptions = options.Value;
+    }
+
+    public IConnectionListener CreateConnectionListener(ListenOptions options, ConnectionOptions connectionOptions,
+        ILoggerFactory loggerFactory)
+    {
+        connectionOptions.Logger = loggerFactory.CreateLogger(nameof(IConnection));
+        var connectionFactoryLogger = loggerFactory.CreateLogger(nameof(QuicConnectionListener));
+
+        var connectionFactory = _connectionFactoryBuilder.Build(options, connectionOptions);
+
+        var listenEndpoint = options.ToEndPoint();
+
+        if (options.CertificateOptions == null)
+            throw new ArgumentNullException(nameof(options.CertificateOptions),"Quic requires an ssl certificate");
+            
+        if (options.CertificateOptions.Certificate == null)
+            options.CertificateOptions.EnsureCertificate();
+
+        var quicListenerOptions = new QuicListenerOptions
+        {
+            ListenBacklog = options.BackLog,
+            ListenEndPoint = listenEndpoint,
+            ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },
+            ConnectionOptionsCallback = (connection, ssl, token) => ValueTask.FromResult(
+                new QuicServerConnectionOptions
+                {
+                    DefaultStreamErrorCode = _quicTransportOptions.DefaultStreamErrorCode,
+                    DefaultCloseErrorCode = _quicTransportOptions.DefaultCloseErrorCode,
+                    IdleTimeout = _quicTransportOptions.IdleTimeout.HasValue
+                        ? TimeSpan.FromMicroseconds(_quicTransportOptions.IdleTimeout.Value)
+                        : Timeout.InfiniteTimeSpan,
+                    MaxInboundBidirectionalStreams = _quicTransportOptions.MaxBidirectionalStreamCount,
+                    MaxInboundUnidirectionalStreams = _quicTransportOptions.MaxUnidirectionalStreamCount,
+                    ServerAuthenticationOptions = new SslServerAuthenticationOptions()
+                    {
+                        ApplicationProtocols =
+                            new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },
+                        ServerCertificate = options.CertificateOptions.Certificate,
+                        RemoteCertificateValidationCallback =
+                            options.CertificateOptions.RemoteCertificateValidationCallback,
+                    }
+                })
+        };
+        
+        return new QuicConnectionListener(options, _quicTransportOptions, connectionFactory,
+            connectionFactoryLogger);
+    }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.Quic/QuicServerHostBuilderExtensions.cs b/src/SuperSocket.Quic/QuicServerHostBuilderExtensions.cs
new file mode 100644
index 000000000..a931c0a37
--- /dev/null
+++ b/src/SuperSocket.Quic/QuicServerHostBuilderExtensions.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Net.Quic;
+using Microsoft.Extensions.DependencyInjection;
+using SuperSocket.Server.Abstractions.Connections;
+using SuperSocket.Server.Abstractions.Host;
+using SuperSocket.Quic;
+
+#pragma warning disable CA2252
+
+namespace SuperSocket.Server;
+
+public static class QuicServerHostBuilderExtensions
+{
+    public static ISuperSocketHostBuilder UseQuic(this ISuperSocketHostBuilder hostBuilder)
+    {
+        return hostBuilder.UseQuic(o => { });
+    }
+
+    public static ISuperSocketHostBuilder<TReceivePackage> UseQuic<TReceivePackage>(
+        this ISuperSocketHostBuilder<TReceivePackage> hostBuilder)
+    {
+        return (hostBuilder as ISuperSocketHostBuilder).UseQuic(o => { }) as
+            ISuperSocketHostBuilder<TReceivePackage>;
+    }
+
+    public static ISuperSocketHostBuilder<TReceivePackage> UseQuic<TReceivePackage>(this ISuperSocketHostBuilder<TReceivePackage> hostBuilder, Action<QuicTransportOptions> globalConfigure)
+    {
+        return (hostBuilder as ISuperSocketHostBuilder).UseQuic(globalConfigure) as
+            ISuperSocketHostBuilder<TReceivePackage>;
+    }
+
+    public static ISuperSocketHostBuilder UseQuic(this ISuperSocketHostBuilder hostBuilder, Action<QuicTransportOptions> globalConfigure)
+    {
+        if (!QuicListener.IsSupported)
+            throw new PlatformNotSupportedException("System.Net.Quic is not supported on this platform.");
+
+        return hostBuilder.ConfigureServices((_, services) =>
+        {
+            services.Configure(globalConfigure);
+            services.AddSingleton<IConnectionListenerFactory, QuicConnectionListenerFactory>();
+            services.AddSingleton<IConnectionFactoryBuilder, QuicConnectionFactoryBuilder>();
+        }) as ISuperSocketHostBuilder;
+    }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.Quic/QuicTransportOptions.cs b/src/SuperSocket.Quic/QuicTransportOptions.cs
new file mode 100644
index 000000000..6793c8f58
--- /dev/null
+++ b/src/SuperSocket.Quic/QuicTransportOptions.cs
@@ -0,0 +1,27 @@
+namespace SuperSocket.Quic;
+
+public sealed class QuicTransportOptions
+{
+    public int MaxBidirectionalStreamCount { get; set; } = 100;
+
+    /// <summary>
+    /// The maximum number of concurrent inbound uni-directional streams per connection.
+    /// </summary>
+    public int MaxUnidirectionalStreamCount { get; set; } = 10;
+    
+    /// <summary>
+    /// Error code used when the stream needs to abort the read or write side of the stream internally.
+    /// </summary>
+    public long DefaultStreamErrorCode { get; set; }
+
+    /// <summary>
+    /// Error code used when an open connection is disposed
+    /// </summary>
+    public long DefaultCloseErrorCode { get; set; }
+
+    /// <summary>Gets or sets the idle timeout for connections. The idle timeout is the time after which the connection will be closed.
+    /// Default <see cref="F:System.TimeSpan.Zero" /> means underlying implementation default idle timeout.</summary>
+    /// <returns>The idle timeout for connections. The default is <see cref="F:System.TimeSpan.Zero" />, which means that the default idle timeout of the underlying implementation is used.</returns>
+    public int? IdleTimeout { get; set; } 
+    
+}
\ No newline at end of file
diff --git a/src/SuperSocket.Quic/SuperSocket.Quic.csproj b/src/SuperSocket.Quic/SuperSocket.Quic.csproj
new file mode 100644
index 000000000..0be6201ae
--- /dev/null
+++ b/src/SuperSocket.Quic/SuperSocket.Quic.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <Description>SuperSocket quic library.</Description>
+        <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
+        <EnablePreviewFeatures>True</EnablePreviewFeatures>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\SuperSocket.Quic.Connection\SuperSocket.Quic.Connection.csproj" />
+        <ProjectReference Include="..\SuperSocket.Server.Abstractions\SuperSocket.Server.Abstractions.csproj" />
+        <ProjectReference Include="..\SuperSocket.Server\SuperSocket.Server.csproj" />
+    </ItemGroup>
+
+</Project>
diff --git a/test/SuperSocket.Tests/ClientTest.cs b/test/SuperSocket.Tests/ClientTest.cs
index b36cff6e2..0f1103425 100644
--- a/test/SuperSocket.Tests/ClientTest.cs
+++ b/test/SuperSocket.Tests/ClientTest.cs
@@ -37,6 +37,7 @@ public ClientTest(ITestOutputHelper outputHelper)
         [InlineData(typeof(GzipHostConfigurator), false)]
         [InlineData(typeof(GzipSecureHostConfigurator), false)]
         [InlineData(typeof(RegularHostConfigurator), true)]
+        [InlineData(typeof(QuicHostConfigurator), false)]
         public async Task TestEcho(Type hostConfiguratorType, bool clientReadAsDemand)
         {
             var serverSessionEvent = new AutoResetEvent(false);
@@ -99,6 +100,7 @@ public async Task TestEcho(Type hostConfiguratorType, bool clientReadAsDemand)
         [Theory]
         [InlineData(typeof(RegularHostConfigurator))]
         [InlineData(typeof(GzipHostConfigurator))]
+        [InlineData(typeof(QuicHostConfigurator))]
         [Trait("Category", "Client.TestBindLocalEndPoint")]
         public async Task TestBindLocalEndPoint(Type hostConfiguratorType)
         {
@@ -197,6 +199,7 @@ public void TestCancellationTokenIsBeingUsedWhenConnecting()
         [InlineData(typeof(SecureHostConfigurator))]
         [InlineData(typeof(GzipSecureHostConfigurator))]
         [InlineData(typeof(GzipHostConfigurator))]
+        [InlineData(typeof(QuicHostConfigurator))]
         public async Task TestCommandLine(Type hostConfiguratorType)
         {
             var packageEvent = new AutoResetEvent(false);
diff --git a/test/SuperSocket.Tests/QuicHostConfigurator.cs b/test/SuperSocket.Tests/QuicHostConfigurator.cs
new file mode 100644
index 000000000..9fb08ffaf
--- /dev/null
+++ b/test/SuperSocket.Tests/QuicHostConfigurator.cs
@@ -0,0 +1,148 @@
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Quic;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using SuperSocket.Connection;
+using SuperSocket.Server;
+using SuperSocket.Server.Abstractions;
+using SuperSocket.Server.Abstractions.Host;
+using SuperSocket.Client;
+using SuperSocket.ProtoBase;
+using SuperSocket.Quic;
+using SuperSocket.Quic.Connection;
+
+#pragma warning disable CA2252
+namespace SuperSocket.Tests
+{
+    public class QuicHostConfigurator : IHostConfigurator
+    {
+        private static readonly ArrayPool<byte> _bufferPool = ArrayPool<byte>.Shared;
+
+        public string WebSocketSchema => "ws";
+
+        public bool IsSecure => false;
+
+        public ListenOptions Listener { get; private set; }
+
+        public IEasyClient<TPackageInfo> ConfigureEasyClient<TPackageInfo>(IPipelineFilter<TPackageInfo> pipelineFilter,
+            ConnectionOptions options) where TPackageInfo : class
+        {
+            return new QuicClient<TPackageInfo>(pipelineFilter, options);
+        }
+
+        private static Random _rd = new Random();
+
+        public void Configure(ISuperSocketHostBuilder hostBuilder)
+        {
+            hostBuilder
+                .UseQuic()
+                .ConfigureServices((ctx, services) =>
+                    {
+                        services.Configure<ServerOptions>((options) =>
+                        {
+                            var listener = options.Listeners[0];
+                            listener.CertificateOptions = new CertificateOptions
+                            {
+                                FilePath = "supersocket.pfx",
+                                Password = "supersocket"
+                            };
+                            Listener = listener;
+                        });
+                    }
+                );
+        }
+
+        public TextReader GetStreamReader(Stream stream, Encoding encoding)
+        {
+            throw new NotImplementedException();
+        }
+
+        public ValueTask<Stream> GetClientStream(Socket socket)
+        {
+            throw new NotImplementedException();
+        }
+
+        private IPipelineFilter<TextPackageInfo> GetPipelineFilter()
+        {
+            return new TerminatorPipelineFilter<TextPackageInfo>(new[] { (byte)'\r', (byte)'\n' })
+            {
+                Decoder = new UdpPackageDecoder()
+            };
+        }
+
+        class UdpPackageDecoder : IPackageDecoder<TextPackageInfo>
+        {
+            public TextPackageInfo Decode(ref ReadOnlySequence<byte> buffer, object context)
+            {
+                return new TextPackageInfo { Text = buffer.GetString(Encoding.UTF8) };
+            }
+        }
+
+        class QuicClient<TReceivePackage> : EasyClient<TReceivePackage>
+            where TReceivePackage : class
+        {
+            public QuicClient(IPipelineFilter<TReceivePackage> pipelineFilter, ConnectionOptions options)
+                : base(pipelineFilter, options)
+            {
+            }
+
+            protected override async ValueTask<bool> ConnectAsync(EndPoint remoteEndPoint,
+                CancellationToken cancellationToken)
+            {
+                var quicConnection = await QuicConnection.ConnectAsync(
+                    cancellationToken: cancellationToken,
+                    options: new QuicClientConnectionOptions
+                    {
+                        DefaultCloseErrorCode = 0,
+                        DefaultStreamErrorCode = 0,
+                        RemoteEndPoint = remoteEndPoint,
+                        LocalEndPoint = LocalEndPoint,
+                        IdleTimeout = TimeSpan.FromSeconds(10),
+                        ClientAuthenticationOptions = new SslClientAuthenticationOptions
+                        {
+                            ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 },
+                            RemoteCertificateValidationCallback = (sender, certificate, chain, errors) =>
+                            {
+                                return true;
+                            }
+                        }
+                    });
+
+                if (cancellationToken.IsCancellationRequested)
+                {
+                    OnError($"The connection to {remoteEndPoint} was cancelled.");
+                    return false;
+                }
+
+                var quicStream =
+                    await quicConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional, cancellationToken);
+                
+                var connection = new QuicPipeConnection(quicStream, quicConnection.RemoteEndPoint,
+                    quicConnection.LocalEndPoint, Options);
+                SetupConnection(connection);
+
+                return true;
+            }
+        }
+
+        public async ValueTask KeepSequence()
+        {
+            await Task.Delay(200);
+        }
+
+        public Socket CreateClient()
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/test/SuperSocket.Tests/SuperSocket.Tests.csproj b/test/SuperSocket.Tests/SuperSocket.Tests.csproj
index 2f292b952..084867a20 100755
--- a/test/SuperSocket.Tests/SuperSocket.Tests.csproj
+++ b/test/SuperSocket.Tests/SuperSocket.Tests.csproj
@@ -13,6 +13,7 @@
     <ProjectReference Include="../../src/SuperSocket.Http/SuperSocket.Http.csproj" />
     <ProjectReference Include="../../src/SuperSocket.Kestrel/SuperSocket.Kestrel.csproj" />
     <ProjectReference Include="../SuperSocket.Tests.Command/SuperSocket.Tests.Command.csproj" />
+    <ProjectReference Include="..\..\src\SuperSocket.Quic\SuperSocket.Quic.csproj" />
   </ItemGroup>
   <ItemGroup>  
     <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />  

From 78193efd8c90ce4406b663b7177bcaa2deaf8f7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=90=B4=E4=BF=8A?= <8400684@qq.com>
Date: Sat, 4 May 2024 21:55:18 +0800
Subject: [PATCH 2/8] fix bugs

---
 src/SuperSocket.Quic.Connection/QuicPipeConnection.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
index 88f0221f1..20d3fadb0 100644
--- a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
+++ b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
@@ -23,7 +23,7 @@ public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, ConnectionOpti
     public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, EndPoint localEndPoint, ConnectionOptions options)
         : base(stream, remoteEndPoint, localEndPoint, options)
     {
-        if (stream is not QuicStream or QuicPipeStream)
+        if (stream is not QuicStream && stream is not QuicPipeStream)
             throw new NotSupportedException("QuicPipeConnection only supports QuicStream or QuicPipeStream");
 
         _stream = stream;

From 209c614801aaf8c53c6cdfa9cec648d53bcb970f Mon Sep 17 00:00:00 2001
From: mockingjay <8400684@qq.com>
Date: Mon, 6 May 2024 11:25:38 +0800
Subject: [PATCH 3/8] fix bugs

---
 src/SuperSocket.Quic/QuicConnectionListener.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/SuperSocket.Quic/QuicConnectionListener.cs b/src/SuperSocket.Quic/QuicConnectionListener.cs
index ded0514f2..f8639b96c 100644
--- a/src/SuperSocket.Quic/QuicConnectionListener.cs
+++ b/src/SuperSocket.Quic/QuicConnectionListener.cs
@@ -99,7 +99,7 @@ private async Task KeepAcceptAsync(QuicListener listenSocket, CancellationToken
             {
                 var quicConnection =
                     await listenSocket.AcceptConnectionAsync(cancellationToken).ConfigureAwait(false);
-                OnNewClientAccept(quicConnection, cancellationToken);
+                OnNewClientAccept(quicConnection);
             }
             catch (Exception e)
             {
@@ -112,7 +112,7 @@ private async Task KeepAcceptAsync(QuicListener listenSocket, CancellationToken
 
     public event NewConnectionAcceptHandler NewConnectionAccept;
 
-    private async void OnNewClientAccept(QuicConnection quicConnection, CancellationToken cancellationToken)
+    private async void OnNewClientAccept(QuicConnection quicConnection)
     {
         var handler = NewConnectionAccept;
 

From 26fd35c23d8f9e877783da2ce024728747bb4b8f Mon Sep 17 00:00:00 2001
From: mockingjay <8400684@qq.com>
Date: Sat, 11 May 2024 10:12:20 +0800
Subject: [PATCH 4/8] fix bugs

---
 .../PipeConnectionBase.cs                     |  7 +------
 .../QuicPipeConnection.cs                     | 19 ++-----------------
 src/SuperSocket.Quic/QuicConnectionFactory.cs |  9 ++++-----
 3 files changed, 7 insertions(+), 28 deletions(-)

diff --git a/src/SuperSocket.Connection/PipeConnectionBase.cs b/src/SuperSocket.Connection/PipeConnectionBase.cs
index cba380d7a..d390ddc15 100644
--- a/src/SuperSocket.Connection/PipeConnectionBase.cs
+++ b/src/SuperSocket.Connection/PipeConnectionBase.cs
@@ -82,12 +82,7 @@ public async override IAsyncEnumerable<TPackageInfo> RunAsync<TPackageInfo>(IPip
             {
                 var package = await packagePipe.ReadAsync().ConfigureAwait(false);
 
-                if (package == null)
-                {
-                    yield break;
-                }
-
-                yield return package;
+            e
             }
 
             //How do empty a pipe?
diff --git a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
index 20d3fadb0..91a224edf 100644
--- a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
+++ b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
@@ -13,29 +13,14 @@ namespace SuperSocket.Quic.Connection;
 
 public class QuicPipeConnection : StreamPipeConnection
 {
-    private readonly Stream _stream;
-
-    public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, ConnectionOptions options)
+    public QuicPipeConnection(QuicStream stream, EndPoint remoteEndPoint, ConnectionOptions options)
         : this(stream, remoteEndPoint, null, options)
     {
     }
 
-    public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, EndPoint localEndPoint, ConnectionOptions options)
+    public QuicPipeConnection(QuicStream stream, EndPoint remoteEndPoint, EndPoint localEndPoint, ConnectionOptions options)
         : base(stream, remoteEndPoint, localEndPoint, options)
     {
-        if (stream is not QuicStream && stream is not QuicPipeStream)
-            throw new NotSupportedException("QuicPipeConnection only supports QuicStream or QuicPipeStream");
-
-        _stream = stream;
-    }
-
-    protected override async Task StartInputPipeTask<TPackageInfo>(IObjectPipe<TPackageInfo> packagePipe,
-        CancellationToken cancellationToken)
-    {
-        if (_stream is QuicPipeStream quicPipeStream)
-            await quicPipeStream.OpenStreamAsync(cancellationToken);
-
-        await base.StartInputPipeTask(packagePipe, cancellationToken);
     }
 
     protected override bool IsIgnorableException(Exception e)
diff --git a/src/SuperSocket.Quic/QuicConnectionFactory.cs b/src/SuperSocket.Quic/QuicConnectionFactory.cs
index a59e5950c..e2b69ade9 100644
--- a/src/SuperSocket.Quic/QuicConnectionFactory.cs
+++ b/src/SuperSocket.Quic/QuicConnectionFactory.cs
@@ -16,14 +16,13 @@ public QuicConnectionFactory(ConnectionOptions connectionOptions)
         _connectionOptions = connectionOptions;
     }
 
-    public Task<IConnection> CreateConnection(object connection, CancellationToken cancellationToken)
+    public async Task<IConnection> CreateConnection(object connection, CancellationToken cancellationToken)
     {
         var quicConnection = (QuicConnection)connection;
 
-        var quicStream = new QuicPipeStream(quicConnection);
+        var stream = await quicConnection.AcceptInboundStreamAsync(cancellationToken);
 
-        var pipeConnection = new QuicPipeConnection(quicStream, quicConnection.RemoteEndPoint, quicConnection.LocalEndPoint, _connectionOptions);
-
-        return Task.FromResult<IConnection>(pipeConnection);
+        return new QuicPipeConnection(stream, quicConnection.RemoteEndPoint,
+            quicConnection.LocalEndPoint, _connectionOptions);
     }
 }
\ No newline at end of file

From 6b11ba8286576d0a99dffc39a66b6efd925c604d Mon Sep 17 00:00:00 2001
From: mockingjay <8400684@qq.com>
Date: Sat, 11 May 2024 10:13:16 +0800
Subject: [PATCH 5/8] fix bus

---
 src/SuperSocket.Connection/PipeConnectionBase.cs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/SuperSocket.Connection/PipeConnectionBase.cs b/src/SuperSocket.Connection/PipeConnectionBase.cs
index d390ddc15..cba380d7a 100644
--- a/src/SuperSocket.Connection/PipeConnectionBase.cs
+++ b/src/SuperSocket.Connection/PipeConnectionBase.cs
@@ -82,7 +82,12 @@ public async override IAsyncEnumerable<TPackageInfo> RunAsync<TPackageInfo>(IPip
             {
                 var package = await packagePipe.ReadAsync().ConfigureAwait(false);
 
-            e
+                if (package == null)
+                {
+                    yield break;
+                }
+
+                yield return package;
             }
 
             //How do empty a pipe?

From f9894eb40663d211765df4c7e2bb3a52e9bf0a38 Mon Sep 17 00:00:00 2001
From: mockingjay <8400684@qq.com>
Date: Sat, 11 May 2024 10:27:47 +0800
Subject: [PATCH 6/8] Revert "fix bugs"

This reverts commit 26fd35c23d8f9e877783da2ce024728747bb4b8f.
---
 .../QuicPipeConnection.cs                     | 19 +++++++++++++++++--
 src/SuperSocket.Quic/QuicConnectionFactory.cs |  9 +++++----
 2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
index 91a224edf..20d3fadb0 100644
--- a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
+++ b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
@@ -13,14 +13,29 @@ namespace SuperSocket.Quic.Connection;
 
 public class QuicPipeConnection : StreamPipeConnection
 {
-    public QuicPipeConnection(QuicStream stream, EndPoint remoteEndPoint, ConnectionOptions options)
+    private readonly Stream _stream;
+
+    public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, ConnectionOptions options)
         : this(stream, remoteEndPoint, null, options)
     {
     }
 
-    public QuicPipeConnection(QuicStream stream, EndPoint remoteEndPoint, EndPoint localEndPoint, ConnectionOptions options)
+    public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, EndPoint localEndPoint, ConnectionOptions options)
         : base(stream, remoteEndPoint, localEndPoint, options)
     {
+        if (stream is not QuicStream && stream is not QuicPipeStream)
+            throw new NotSupportedException("QuicPipeConnection only supports QuicStream or QuicPipeStream");
+
+        _stream = stream;
+    }
+
+    protected override async Task StartInputPipeTask<TPackageInfo>(IObjectPipe<TPackageInfo> packagePipe,
+        CancellationToken cancellationToken)
+    {
+        if (_stream is QuicPipeStream quicPipeStream)
+            await quicPipeStream.OpenStreamAsync(cancellationToken);
+
+        await base.StartInputPipeTask(packagePipe, cancellationToken);
     }
 
     protected override bool IsIgnorableException(Exception e)
diff --git a/src/SuperSocket.Quic/QuicConnectionFactory.cs b/src/SuperSocket.Quic/QuicConnectionFactory.cs
index e2b69ade9..a59e5950c 100644
--- a/src/SuperSocket.Quic/QuicConnectionFactory.cs
+++ b/src/SuperSocket.Quic/QuicConnectionFactory.cs
@@ -16,13 +16,14 @@ public QuicConnectionFactory(ConnectionOptions connectionOptions)
         _connectionOptions = connectionOptions;
     }
 
-    public async Task<IConnection> CreateConnection(object connection, CancellationToken cancellationToken)
+    public Task<IConnection> CreateConnection(object connection, CancellationToken cancellationToken)
     {
         var quicConnection = (QuicConnection)connection;
 
-        var stream = await quicConnection.AcceptInboundStreamAsync(cancellationToken);
+        var quicStream = new QuicPipeStream(quicConnection);
 
-        return new QuicPipeConnection(stream, quicConnection.RemoteEndPoint,
-            quicConnection.LocalEndPoint, _connectionOptions);
+        var pipeConnection = new QuicPipeConnection(quicStream, quicConnection.RemoteEndPoint, quicConnection.LocalEndPoint, _connectionOptions);
+
+        return Task.FromResult<IConnection>(pipeConnection);
     }
 }
\ No newline at end of file

From 263e4a7e7d25ffba6491125ab7f15a85c8361bbe Mon Sep 17 00:00:00 2001
From: mockingjay <8400684@qq.com>
Date: Sun, 12 May 2024 09:47:47 +0800
Subject: [PATCH 7/8] try test

---
 src/SuperSocket.Quic.Connection/QuicPipeStream.cs | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/SuperSocket.Quic.Connection/QuicPipeStream.cs b/src/SuperSocket.Quic.Connection/QuicPipeStream.cs
index b1bb6ecd5..df8556217 100644
--- a/src/SuperSocket.Quic.Connection/QuicPipeStream.cs
+++ b/src/SuperSocket.Quic.Connection/QuicPipeStream.cs
@@ -23,7 +23,11 @@ public QuicPipeStream(QuicConnection connection)
     public override bool CanSeek => _stream.CanSeek;
     public override bool CanWrite => _stream.CanWrite;
     public override long Length => _stream.Length;
-
+    public override int ReadTimeout 
+    {
+        get => _stream.ReadTimeout;
+        set => _stream.ReadTimeout = value;
+    }
     public override long Position
     {
         get => _stream.Position;

From 41513a0ea172a9b7cafe5f45627bcb12de3dbb2c Mon Sep 17 00:00:00 2001
From: mockingjay <8400684@qq.com>
Date: Sun, 12 May 2024 12:28:18 +0800
Subject: [PATCH 8/8] try test

---
 .../QuicPipeConnection.cs                        | 16 ++++++++--------
 src/SuperSocket.Quic/QuicConnectionFactory.cs    | 16 ++++++++++------
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
index 20d3fadb0..39a3daf4b 100644
--- a/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
+++ b/src/SuperSocket.Quic.Connection/QuicPipeConnection.cs
@@ -29,14 +29,14 @@ public QuicPipeConnection(Stream stream, EndPoint remoteEndPoint, EndPoint local
         _stream = stream;
     }
 
-    protected override async Task StartInputPipeTask<TPackageInfo>(IObjectPipe<TPackageInfo> packagePipe,
-        CancellationToken cancellationToken)
-    {
-        if (_stream is QuicPipeStream quicPipeStream)
-            await quicPipeStream.OpenStreamAsync(cancellationToken);
-
-        await base.StartInputPipeTask(packagePipe, cancellationToken);
-    }
+    // protected override async Task StartInputPipeTask<TPackageInfo>(IObjectPipe<TPackageInfo> packagePipe,
+    //     CancellationToken cancellationToken)
+    // {
+    //     if (_stream is QuicPipeStream quicPipeStream)
+    //         await quicPipeStream.OpenStreamAsync(cancellationToken);
+    //
+    //     await base.StartInputPipeTask(packagePipe, cancellationToken);
+    // }
 
     protected override bool IsIgnorableException(Exception e)
     {
diff --git a/src/SuperSocket.Quic/QuicConnectionFactory.cs b/src/SuperSocket.Quic/QuicConnectionFactory.cs
index a59e5950c..e7db55565 100644
--- a/src/SuperSocket.Quic/QuicConnectionFactory.cs
+++ b/src/SuperSocket.Quic/QuicConnectionFactory.cs
@@ -16,14 +16,18 @@ public QuicConnectionFactory(ConnectionOptions connectionOptions)
         _connectionOptions = connectionOptions;
     }
 
-    public Task<IConnection> CreateConnection(object connection, CancellationToken cancellationToken)
+    public async Task<IConnection> CreateConnection(object connection, CancellationToken cancellationToken)
     {
         var quicConnection = (QuicConnection)connection;
 
-        var quicStream = new QuicPipeStream(quicConnection);
-
-        var pipeConnection = new QuicPipeConnection(quicStream, quicConnection.RemoteEndPoint, quicConnection.LocalEndPoint, _connectionOptions);
-
-        return Task.FromResult<IConnection>(pipeConnection);
+        var quicStream = await quicConnection.AcceptInboundStreamAsync(CancellationToken.None);
+        
+        return new QuicPipeConnection(quicStream, quicConnection.RemoteEndPoint, quicConnection.LocalEndPoint, _connectionOptions);
+        
+        // var quicStream = new QuicPipeStream(quicConnection);
+        //
+        // var pipeConnection = new QuicPipeConnection(quicStream, quicConnection.RemoteEndPoint, quicConnection.LocalEndPoint, _connectionOptions);
+        //
+        // return Task.FromResult<IConnection>(pipeConnection);
     }
 }
\ No newline at end of file