diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..47f5c749c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,45 @@ +name: Build + +on: + push: + paths: + - '.github/workflows/build.yml' + - 'src/**' + workflow_dispatch: + inputs: + publish: + description: 'Publish package' + default: false + required: false + type: boolean + +jobs: + build: + env: + BUILD_CONFIG: release + name: Build + runs-on: ubuntu-latest + steps: + + - name: Check out repository + uses: actions/checkout@v3 + + - name: Set up .NET + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 6.0.x + + - name: Install dependencies + run: dotnet restore + + - name: Build + run: dotnet build -c ${{ env.BUILD_CONFIG }} --no-restore -p:ContinuousIntegrationBuild=true + + #- name: Test + # run: dotnet test -c ${{ env.BUILD_CONFIG }} --no-restore + + - name: Publish + if: ${{ inputs.publish }} + run: | + dotnet pack -c ${{ env.BUILD_CONFIG }} --no-build + dotnet nuget push build_output/packages/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json diff --git a/DotNetty.sln b/DotNetty.sln index f2809a025..c9dc8be92 100644 --- a/DotNetty.sln +++ b/DotNetty.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2024 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32819.101 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{F5B1CA65-5852-41C6-9D6F-184A3889237B}" ProjectSection(SolutionItems) = preProject @@ -10,8 +10,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{F5B1CA65 build.sh = build.sh EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F716F1EF-81EF-4020-914A-5422A13A9E13}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{036F5A7D-2B05-4DE5-9AD8-45162E2F0645}" ProjectSection(SolutionItems) = preProject shared\dotnetty.com.pfx = shared\dotnetty.com.pfx @@ -63,228 +61,164 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Transport.Tests", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Transport.Tests.Performance", "test\DotNetty.Transport.Tests.Performance\DotNetty.Transport.Tests.Performance.csproj", "{3DA49C12-3614-40F5-B5CE-8B95F872EFC1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discard.Client", "examples\Discard.Client\Discard.Client.csproj", "{9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Common", "examples\Examples.Common\Examples.Common.csproj", "{66491D47-2BFB-45CF-A582-C11860219512}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discard.Server", "examples\Discard.Server\Discard.Server.csproj", "{664ECD06-26EB-4F8F-8D88-5444A5E766E0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Echo.Client", "examples\Echo.Client\Echo.Client.csproj", "{EAE5000B-9A97-4308-B791-0B71DE0A8219}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Echo.Server", "examples\Echo.Server\Echo.Server.csproj", "{E854F61B-548A-4100-A766-35B972B9EE11}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Factorial", "examples\Factorial\Factorial.csproj", "{22C53CFB-B54A-438E-820F-42C94D557345}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Factorial.Client", "examples\Factorial.Client\Factorial.Client.csproj", "{A4E85E94-383F-40EA-9478-0545070E52F5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Factorial.Server", "examples\Factorial.Server\Factorial.Server.csproj", "{928090A9-AAC7-496C-A8E3-D242D2D8BC74}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuoteOfTheMoment.Client", "examples\QuoteOfTheMoment.Client\QuoteOfTheMoment.Client.csproj", "{D69B9924-5C2E-41BC-9354-A12DA7D03FDF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuoteOfTheMoment.Server", "examples\QuoteOfTheMoment.Server\QuoteOfTheMoment.Server.csproj", "{ED307D87-E1BF-49B6-9591-5250D431C758}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SecureChat.Client", "examples\SecureChat.Client\SecureChat.Client.csproj", "{0E4B622A-063B-4A39-87CF-F18AEACBDDC5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SecureChat.Server", "examples\SecureChat.Server\SecureChat.Server.csproj", "{E2BEAAAE-497B-49F4-AD27-03BE20924311}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telnet.Client", "examples\Telnet.Client\Telnet.Client.csproj", "{F2490822-51F7-4B65-8B21-EE0082B76745}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telnet.Server", "examples\Telnet.Server\Telnet.Server.csproj", "{07C97A77-61B6-4BB5-9436-3A7FC379B75E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Microbench", "test\DotNetty.Microbench\DotNetty.Microbench.csproj", "{7155D1E6-00CE-4081-B922-E6C5524EE600}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Transport.Libuv", "src\DotNetty.Transport.Libuv\DotNetty.Transport.Libuv.csproj", "{9FE6A783-C20D-4097-9988-4178E2C4CE75}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Transport.Libuv.Tests", "test\DotNetty.Transport.Libuv.Tests\DotNetty.Transport.Libuv.Tests.csproj", "{1012C962-7F6D-4EC5-A0EC-0741A95BAD6B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpServer", "examples\HttpServer\HttpServer.csproj", "{A7CACAE7-66E7-43DA-948B-28EB0DDDB582}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Codecs.Http", "src\DotNetty.Codecs.Http\DotNetty.Codecs.Http.csproj", "{5F68A5B1-7907-4B16-8AFE-326E9DD7D65B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Codecs.Http.Tests", "test\DotNetty.Codecs.Http.Tests\DotNetty.Codecs.Http.Tests.csproj", "{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSockets.Server", "examples\WebSockets.Server\WebSockets.Server.csproj", "{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSockets.Client", "examples\WebSockets.Client\WebSockets.Client.csproj", "{3326DB6E-023E-483F-9A1C-5905D3091B57}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Package|Any CPU = Package|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Package|Any CPU.ActiveCfg = Package|Any CPU + {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Package|Any CPU.Build.0 = Package|Any CPU {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Release|Any CPU.Build.0 = Release|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Package|Any CPU.ActiveCfg = Package|Any CPU + {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Package|Any CPU.Build.0 = Package|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Release|Any CPU.ActiveCfg = Release|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Release|Any CPU.Build.0 = Release|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82796E9E-1331-4858-90C3-8E74BA4CC383}.Package|Any CPU.ActiveCfg = Package|Any CPU + {82796E9E-1331-4858-90C3-8E74BA4CC383}.Package|Any CPU.Build.0 = Package|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Release|Any CPU.ActiveCfg = Release|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Release|Any CPU.Build.0 = Release|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Package|Any CPU.ActiveCfg = Package|Any CPU + {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Package|Any CPU.Build.0 = Package|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Release|Any CPU.Build.0 = Release|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12DCCEFD-623B-46CC-979C-407FA265E239}.Package|Any CPU.ActiveCfg = Package|Any CPU + {12DCCEFD-623B-46CC-979C-407FA265E239}.Package|Any CPU.Build.0 = Package|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Release|Any CPU.ActiveCfg = Release|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Release|Any CPU.Build.0 = Release|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Package|Any CPU.ActiveCfg = Package|Any CPU + {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Package|Any CPU.Build.0 = Package|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Release|Any CPU.ActiveCfg = Release|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Release|Any CPU.Build.0 = Release|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D284C2BF-E06E-481B-B301-503A9D477B0E}.Package|Any CPU.ActiveCfg = Package|Any CPU + {D284C2BF-E06E-481B-B301-503A9D477B0E}.Package|Any CPU.Build.0 = Package|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Release|Any CPU.ActiveCfg = Release|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Release|Any CPU.Build.0 = Release|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Package|Any CPU.ActiveCfg = Package|Any CPU + {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Package|Any CPU.Build.0 = Package|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Release|Any CPU.ActiveCfg = Release|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Release|Any CPU.Build.0 = Release|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F442118-A665-4891-B056-FE9E54C5B049}.Package|Any CPU.ActiveCfg = Package|Any CPU + {1F442118-A665-4891-B056-FE9E54C5B049}.Package|Any CPU.Build.0 = Package|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Release|Any CPU.Build.0 = Release|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {572E1914-489F-402D-944F-71EE0632E5D8}.Package|Any CPU.ActiveCfg = Package|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Release|Any CPU.Build.0 = Release|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Package|Any CPU.ActiveCfg = Package|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Release|Any CPU.Build.0 = Release|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C92F76-F804-4300-BFF1-459420D9EF0B}.Package|Any CPU.ActiveCfg = Package|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Release|Any CPU.Build.0 = Release|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Package|Any CPU.ActiveCfg = Package|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Release|Any CPU.ActiveCfg = Release|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Release|Any CPU.Build.0 = Release|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Package|Any CPU.ActiveCfg = Package|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Release|Any CPU.ActiveCfg = Release|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Release|Any CPU.Build.0 = Release|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Package|Any CPU.ActiveCfg = Package|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Release|Any CPU.Build.0 = Release|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08C19033-23B2-47D7-8332-86273AE287BC}.Package|Any CPU.ActiveCfg = Package|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Release|Any CPU.ActiveCfg = Release|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Release|Any CPU.Build.0 = Release|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Package|Any CPU.ActiveCfg = Package|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Release|Any CPU.Build.0 = Release|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Package|Any CPU.ActiveCfg = Package|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Release|Any CPU.Build.0 = Release|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Package|Any CPU.ActiveCfg = Package|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Release|Any CPU.Build.0 = Release|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDA099C5-E435-49DF-9922-58D63E11B764}.Package|Any CPU.ActiveCfg = Package|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Release|Any CPU.ActiveCfg = Release|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Release|Any CPU.Build.0 = Release|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Package|Any CPU.ActiveCfg = Package|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Release|Any CPU.ActiveCfg = Release|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Release|Any CPU.Build.0 = Release|Any CPU - {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Release|Any CPU.Build.0 = Release|Any CPU - {66491D47-2BFB-45CF-A582-C11860219512}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66491D47-2BFB-45CF-A582-C11860219512}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66491D47-2BFB-45CF-A582-C11860219512}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66491D47-2BFB-45CF-A582-C11860219512}.Release|Any CPU.Build.0 = Release|Any CPU - {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Release|Any CPU.Build.0 = Release|Any CPU - {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Release|Any CPU.Build.0 = Release|Any CPU - {E854F61B-548A-4100-A766-35B972B9EE11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E854F61B-548A-4100-A766-35B972B9EE11}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E854F61B-548A-4100-A766-35B972B9EE11}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E854F61B-548A-4100-A766-35B972B9EE11}.Release|Any CPU.Build.0 = Release|Any CPU - {22C53CFB-B54A-438E-820F-42C94D557345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {22C53CFB-B54A-438E-820F-42C94D557345}.Debug|Any CPU.Build.0 = Debug|Any CPU - {22C53CFB-B54A-438E-820F-42C94D557345}.Release|Any CPU.ActiveCfg = Release|Any CPU - {22C53CFB-B54A-438E-820F-42C94D557345}.Release|Any CPU.Build.0 = Release|Any CPU - {A4E85E94-383F-40EA-9478-0545070E52F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4E85E94-383F-40EA-9478-0545070E52F5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4E85E94-383F-40EA-9478-0545070E52F5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4E85E94-383F-40EA-9478-0545070E52F5}.Release|Any CPU.Build.0 = Release|Any CPU - {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Release|Any CPU.Build.0 = Release|Any CPU - {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Release|Any CPU.Build.0 = Release|Any CPU - {ED307D87-E1BF-49B6-9591-5250D431C758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED307D87-E1BF-49B6-9591-5250D431C758}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ED307D87-E1BF-49B6-9591-5250D431C758}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ED307D87-E1BF-49B6-9591-5250D431C758}.Release|Any CPU.Build.0 = Release|Any CPU - {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Release|Any CPU.Build.0 = Release|Any CPU - {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Release|Any CPU.Build.0 = Release|Any CPU - {F2490822-51F7-4B65-8B21-EE0082B76745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F2490822-51F7-4B65-8B21-EE0082B76745}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F2490822-51F7-4B65-8B21-EE0082B76745}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F2490822-51F7-4B65-8B21-EE0082B76745}.Release|Any CPU.Build.0 = Release|Any CPU - {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Release|Any CPU.Build.0 = Release|Any CPU {7155D1E6-00CE-4081-B922-E6C5524EE600}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7155D1E6-00CE-4081-B922-E6C5524EE600}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7155D1E6-00CE-4081-B922-E6C5524EE600}.Package|Any CPU.ActiveCfg = Package|Any CPU {7155D1E6-00CE-4081-B922-E6C5524EE600}.Release|Any CPU.ActiveCfg = Release|Any CPU {7155D1E6-00CE-4081-B922-E6C5524EE600}.Release|Any CPU.Build.0 = Release|Any CPU {9FE6A783-C20D-4097-9988-4178E2C4CE75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9FE6A783-C20D-4097-9988-4178E2C4CE75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FE6A783-C20D-4097-9988-4178E2C4CE75}.Package|Any CPU.ActiveCfg = Package|Any CPU + {9FE6A783-C20D-4097-9988-4178E2C4CE75}.Package|Any CPU.Build.0 = Package|Any CPU {9FE6A783-C20D-4097-9988-4178E2C4CE75}.Release|Any CPU.ActiveCfg = Release|Any CPU {9FE6A783-C20D-4097-9988-4178E2C4CE75}.Release|Any CPU.Build.0 = Release|Any CPU {1012C962-7F6D-4EC5-A0EC-0741A95BAD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1012C962-7F6D-4EC5-A0EC-0741A95BAD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1012C962-7F6D-4EC5-A0EC-0741A95BAD6B}.Package|Any CPU.ActiveCfg = Package|Any CPU {1012C962-7F6D-4EC5-A0EC-0741A95BAD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU {1012C962-7F6D-4EC5-A0EC-0741A95BAD6B}.Release|Any CPU.Build.0 = Release|Any CPU - {A7CACAE7-66E7-43DA-948B-28EB0DDDB582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A7CACAE7-66E7-43DA-948B-28EB0DDDB582}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A7CACAE7-66E7-43DA-948B-28EB0DDDB582}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A7CACAE7-66E7-43DA-948B-28EB0DDDB582}.Release|Any CPU.Build.0 = Release|Any CPU {5F68A5B1-7907-4B16-8AFE-326E9DD7D65B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5F68A5B1-7907-4B16-8AFE-326E9DD7D65B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F68A5B1-7907-4B16-8AFE-326E9DD7D65B}.Package|Any CPU.ActiveCfg = Package|Any CPU + {5F68A5B1-7907-4B16-8AFE-326E9DD7D65B}.Package|Any CPU.Build.0 = Package|Any CPU {5F68A5B1-7907-4B16-8AFE-326E9DD7D65B}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F68A5B1-7907-4B16-8AFE-326E9DD7D65B}.Release|Any CPU.Build.0 = Release|Any CPU {16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Package|Any CPU.ActiveCfg = Package|Any CPU {16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Release|Any CPU.Build.0 = Release|Any CPU - {EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Release|Any CPU.Build.0 = Release|Any CPU - {3326DB6E-023E-483F-9A1C-5905D3091B57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3326DB6E-023E-483F-9A1C-5905D3091B57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3326DB6E-023E-483F-9A1C-5905D3091B57}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3326DB6E-023E-483F-9A1C-5905D3091B57}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -311,31 +245,14 @@ Global {5D5A3137-F118-4F6C-ABE7-2523184A3A2D} = {541093F6-616E-43D9-B671-FCD1F9C0A181} {BDA099C5-E435-49DF-9922-58D63E11B764} = {541093F6-616E-43D9-B671-FCD1F9C0A181} {3DA49C12-3614-40F5-B5CE-8B95F872EFC1} = {541093F6-616E-43D9-B671-FCD1F9C0A181} - {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {66491D47-2BFB-45CF-A582-C11860219512} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {664ECD06-26EB-4F8F-8D88-5444A5E766E0} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {EAE5000B-9A97-4308-B791-0B71DE0A8219} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {E854F61B-548A-4100-A766-35B972B9EE11} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {22C53CFB-B54A-438E-820F-42C94D557345} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {A4E85E94-383F-40EA-9478-0545070E52F5} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {928090A9-AAC7-496C-A8E3-D242D2D8BC74} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {D69B9924-5C2E-41BC-9354-A12DA7D03FDF} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {ED307D87-E1BF-49B6-9591-5250D431C758} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {0E4B622A-063B-4A39-87CF-F18AEACBDDC5} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {E2BEAAAE-497B-49F4-AD27-03BE20924311} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {F2490822-51F7-4B65-8B21-EE0082B76745} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {07C97A77-61B6-4BB5-9436-3A7FC379B75E} = {F716F1EF-81EF-4020-914A-5422A13A9E13} {7155D1E6-00CE-4081-B922-E6C5524EE600} = {541093F6-616E-43D9-B671-FCD1F9C0A181} {9FE6A783-C20D-4097-9988-4178E2C4CE75} = {126EA539-4B28-4B07-8B5D-D1D7F794D189} {1012C962-7F6D-4EC5-A0EC-0741A95BAD6B} = {541093F6-616E-43D9-B671-FCD1F9C0A181} - {A7CACAE7-66E7-43DA-948B-28EB0DDDB582} = {F716F1EF-81EF-4020-914A-5422A13A9E13} {5F68A5B1-7907-4B16-8AFE-326E9DD7D65B} = {126EA539-4B28-4B07-8B5D-D1D7F794D189} {16C89E7C-1575-4685-8DFA-8E7E2C6101BF} = {541093F6-616E-43D9-B671-FCD1F9C0A181} - {EA387B4B-DAD0-4E34-B8A3-79EA4616726A} = {F716F1EF-81EF-4020-914A-5422A13A9E13} - {3326DB6E-023E-483F-9A1C-5905D3091B57} = {F716F1EF-81EF-4020-914A-5422A13A9E13} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - {9FE6A783-C20D-4097-9988-4178E2C4CE75} = {126EA539-4B28-4B07-8B5D-D1D7F794D189} SolutionGuid = {1FBD8DF1-D90A-4F21-8EB6-DA17B9431FE3} + {9FE6A783-C20D-4097-9988-4178E2C4CE75} = {126EA539-4B28-4B07-8B5D-D1D7F794D189} EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 728f69116..defa18602 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,5 @@ We gladly accept community contributions. * Issues: Please report bugs using the Issues section of GitHub * Source Code Contributions: - * Please follow the [Contribution Guidelines for Microsoft Azure](http://azure.github.io/guidelines.html) open source that details information on onboarding as a contributor + * Please follow the [Microsoft Azure Projects Contribution Guidelines](https://opensource.microsoft.com/collaborate) open source that details information on onboarding as a contributor * See [C# Coding Style](https://github.com/Azure/DotNetty/wiki/C%23-Coding-Style) for reference on coding style. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b99a9b86f..70ced068f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,14 @@ +#### 0.7.5 August 31, 2022 +- Fix infinite loop caused by TlsHandler + +#### 0.7.4 August 04, 2022 +- target fix: net5 -> net5.0, net6 -> net6.0 + +#### 0.7.3 July 28, 2022 +- target net5 and net6 +- Adopted TlsHandler for the case when Handshake completion callback is dispatched asynchronously to thread pool +- Introduced SingleThreadedEmbededChannel for TlsHandler and SniHandler tests + #### 0.7.2 February 14, 2022 - Start threads as background in HashedWheelTimer, LoopExecutor, ThreadDeathWatcher - Google.Protobuf 3.19.4 (latest) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..869fdfe2b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/build.cake b/build.cake index 92448f42b..b6b829de6 100644 --- a/build.cake +++ b/build.cake @@ -25,6 +25,8 @@ var output = Directory("build"); var outputBinaries = output + Directory("binaries"); var outputBinariesNet = outputBinaries + Directory("net472"); var outputBinariesNetStandard = outputBinaries + Directory("netstandard2.0"); +var outputBinariesNet5 = outputBinaries + Directory("net5.0"); +var outputBinariesNet6 = outputBinaries + Directory("net6.0"); var outputPackages = output + Directory("packages"); var outputNuGet = output + Directory("nuget"); var outputPerfResults = Directory("perfResults"); @@ -37,7 +39,8 @@ Task("Clean") // Clean artifact directories. CleanDirectories(new DirectoryPath[] { output, outputBinaries, outputPackages, outputNuGet, - outputBinariesNet, outputBinariesNetStandard + outputBinariesNet, outputBinariesNetStandard, + outputBinariesNet5, outputBinariesNet6 }); if(!skipClean) { diff --git a/build.ps1 b/build.ps1 index 291b9efdc..b843a9f5e 100644 --- a/build.ps1 +++ b/build.ps1 @@ -33,7 +33,7 @@ Param( $CakeVersion = "0.27.1" $DotNetChannel = "Current"; -$DotNetVersion = "3.1.411"; +$DotNetVersion = "6.0.302"; $DotNetInstallerUri = "https://dot.net/v1/dotnet-install.ps1"; $NugetUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" # Temporarily skip verification of addins. diff --git a/examples/Discard.Client/Discard.Client.csproj b/examples/Discard.Client/Discard.Client.csproj index 96c39faba..63cb815ba 100644 --- a/examples/Discard.Client/Discard.Client.csproj +++ b/examples/Discard.Client/Discard.Client.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Discard.Server/Discard.Server.csproj b/examples/Discard.Server/Discard.Server.csproj index 96c39faba..63cb815ba 100644 --- a/examples/Discard.Server/Discard.Server.csproj +++ b/examples/Discard.Server/Discard.Server.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Echo.Client/Echo.Client.csproj b/examples/Echo.Client/Echo.Client.csproj index 96c39faba..63cb815ba 100644 --- a/examples/Echo.Client/Echo.Client.csproj +++ b/examples/Echo.Client/Echo.Client.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Echo.Server/Echo.Server.csproj b/examples/Echo.Server/Echo.Server.csproj index defec1f72..dfe7ea312 100644 --- a/examples/Echo.Server/Echo.Server.csproj +++ b/examples/Echo.Server/Echo.Server.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Examples.Common/ExampleHelper.cs b/examples/Examples.Common/ExampleHelper.cs index 535170274..f2b775210 100644 --- a/examples/Examples.Common/ExampleHelper.cs +++ b/examples/Examples.Common/ExampleHelper.cs @@ -22,7 +22,7 @@ public static string ProcessDirectory { get { -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER return AppContext.BaseDirectory; #else return AppDomain.CurrentDomain.BaseDirectory; diff --git a/examples/Examples.Common/Examples.Common.csproj b/examples/Examples.Common/Examples.Common.csproj index 8e186c734..06abb948a 100644 --- a/examples/Examples.Common/Examples.Common.csproj +++ b/examples/Examples.Common/Examples.Common.csproj @@ -1,8 +1,10 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU diff --git a/examples/Factorial.Client/Factorial.Client.csproj b/examples/Factorial.Client/Factorial.Client.csproj index f5a342a29..493334d23 100644 --- a/examples/Factorial.Client/Factorial.Client.csproj +++ b/examples/Factorial.Client/Factorial.Client.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Factorial.Server/Factorial.Server.csproj b/examples/Factorial.Server/Factorial.Server.csproj index f5a342a29..493334d23 100644 --- a/examples/Factorial.Server/Factorial.Server.csproj +++ b/examples/Factorial.Server/Factorial.Server.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Factorial/Factorial.csproj b/examples/Factorial/Factorial.csproj index d2ccbbd2f..dd5d00f26 100644 --- a/examples/Factorial/Factorial.csproj +++ b/examples/Factorial/Factorial.csproj @@ -1,8 +1,10 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU diff --git a/examples/HttpServer/HttpServer.csproj b/examples/HttpServer/HttpServer.csproj index 9e580ec6f..ecf15b92a 100644 --- a/examples/HttpServer/HttpServer.csproj +++ b/examples/HttpServer/HttpServer.csproj @@ -1,10 +1,12 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false true + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/QuoteOfTheMoment.Client/QuoteOfTheMoment.Client.csproj b/examples/QuoteOfTheMoment.Client/QuoteOfTheMoment.Client.csproj index 96c39faba..63cb815ba 100644 --- a/examples/QuoteOfTheMoment.Client/QuoteOfTheMoment.Client.csproj +++ b/examples/QuoteOfTheMoment.Client/QuoteOfTheMoment.Client.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/QuoteOfTheMoment.Server/QuoteOfTheMoment.Server.csproj b/examples/QuoteOfTheMoment.Server/QuoteOfTheMoment.Server.csproj index 96c39faba..63cb815ba 100644 --- a/examples/QuoteOfTheMoment.Server/QuoteOfTheMoment.Server.csproj +++ b/examples/QuoteOfTheMoment.Server/QuoteOfTheMoment.Server.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/SecureChat.Client/SecureChat.Client.csproj b/examples/SecureChat.Client/SecureChat.Client.csproj index 96c39faba..63cb815ba 100644 --- a/examples/SecureChat.Client/SecureChat.Client.csproj +++ b/examples/SecureChat.Client/SecureChat.Client.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/SecureChat.Server/SecureChat.Server.csproj b/examples/SecureChat.Server/SecureChat.Server.csproj index 96c39faba..63cb815ba 100644 --- a/examples/SecureChat.Server/SecureChat.Server.csproj +++ b/examples/SecureChat.Server/SecureChat.Server.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Telnet.Client/Telnet.Client.csproj b/examples/Telnet.Client/Telnet.Client.csproj index 96c39faba..63cb815ba 100644 --- a/examples/Telnet.Client/Telnet.Client.csproj +++ b/examples/Telnet.Client/Telnet.Client.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Telnet.Server/Telnet.Server.csproj b/examples/Telnet.Server/Telnet.Server.csproj index 96c39faba..63cb815ba 100644 --- a/examples/Telnet.Server/Telnet.Server.csproj +++ b/examples/Telnet.Server/Telnet.Server.csproj @@ -1,9 +1,11 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/WebSockets.Client/WebSocketClientHandler.cs b/examples/WebSockets.Client/WebSocketClientHandler.cs index 6219b1b3a..04b2f7c6f 100644 --- a/examples/WebSockets.Client/WebSocketClientHandler.cs +++ b/examples/WebSockets.Client/WebSocketClientHandler.cs @@ -11,6 +11,7 @@ namespace WebSockets.Client using DotNetty.Common.Concurrency; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public class WebSocketClientHandler : SimpleChannelInboundHandler { diff --git a/examples/WebSockets.Client/WebSockets.Client.csproj b/examples/WebSockets.Client/WebSockets.Client.csproj index 1beb17f74..b9a7c762a 100644 --- a/examples/WebSockets.Client/WebSockets.Client.csproj +++ b/examples/WebSockets.Client/WebSockets.Client.csproj @@ -2,9 +2,11 @@ Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/WebSockets.Server/WebSockets.Server.csproj b/examples/WebSockets.Server/WebSockets.Server.csproj index 86c509dbf..55ab19721 100644 --- a/examples/WebSockets.Server/WebSockets.Server.csproj +++ b/examples/WebSockets.Server/WebSockets.Server.csproj @@ -2,10 +2,12 @@ Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 2.0.3 false true + Debug;Release;Package + AnyCPU win-x64 diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 000000000..a250448a0 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,35 @@ + + + + + Debug;Release;Package + AnyCPU + true + + + + pdbonly + true + $(SolutionDir)build_output\packages + True + true + + 1.0.1 + $(PackageVersion) + + + + Nethermind, Microsoft + Demerzel Solutions Limited, Microsoft Corporation + true + MIT + https://github.com/nethermindeth/dotnetty + git + https://github.com/nethermindeth/dotnetty + true + snupkg + + + diff --git a/src/DotNetty.Buffers/DotNetty.Buffers.csproj b/src/DotNetty.Buffers/DotNetty.Buffers.csproj index ba7da0fb5..987b41201 100644 --- a/src/DotNetty.Buffers/DotNetty.Buffers.csproj +++ b/src/DotNetty.Buffers/DotNetty.Buffers.csproj @@ -1,13 +1,13 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - Buffer management in DotNetty - © Microsoft Corporation. All rights reserved. + DotNetty: buffer management en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 True false @@ -16,18 +16,29 @@ ../../DotNetty.snk true true - DotNetty.Buffers + 2.0.3 + + + Buffer management in DotNetty + Nethermind.DotNetty.Buffers + socket tcp protocol netty dotnetty network + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/DotNetty.Buffers/IByteBuffer.cs b/src/DotNetty.Buffers/IByteBuffer.cs index 88bf34455..498630b0e 100644 --- a/src/DotNetty.Buffers/IByteBuffer.cs +++ b/src/DotNetty.Buffers/IByteBuffer.cs @@ -959,7 +959,7 @@ public interface IByteBuffer : IReferenceCounted, IComparable, IEqu /// /// Transfers bytes from this buffer's data into the specified destination buffer - /// starting at the curent until the destination becomes + /// starting at the current until the destination becomes /// non-writable and increases the by the number of transferred bytes. /// /// diff --git a/src/DotNetty.Buffers/IPoolChunkListMetric.cs b/src/DotNetty.Buffers/IPoolChunkListMetric.cs index fe5596f8e..163d190bb 100644 --- a/src/DotNetty.Buffers/IPoolChunkListMetric.cs +++ b/src/DotNetty.Buffers/IPoolChunkListMetric.cs @@ -7,10 +7,10 @@ namespace DotNetty.Buffers public interface IPoolChunkListMetric : IEnumerable { - /// Return the minum usage of the chunk list before which chunks are promoted to the previous list. + /// Return the minimum usage of the chunk list before which chunks are promoted to the previous list. int MinUsage { get; } - /// Return the minum usage of the chunk list after which chunks are promoted to the next list. + /// Return the maximum usage of the chunk list after which chunks are promoted to the next list. int MaxUsage { get; } } } \ No newline at end of file diff --git a/src/DotNetty.Buffers/UnsafeByteBufferUtil.cs b/src/DotNetty.Buffers/UnsafeByteBufferUtil.cs index 61680802b..a9c2fdef9 100644 --- a/src/DotNetty.Buffers/UnsafeByteBufferUtil.cs +++ b/src/DotNetty.Buffers/UnsafeByteBufferUtil.cs @@ -386,7 +386,7 @@ internal static void SetZero(byte* addr, int length) internal static string GetString(byte* src, int length, Encoding encoding) { -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER return encoding.GetString(src, length); #else int charCount = encoding.GetCharCount(src, length); diff --git a/src/DotNetty.Codecs.Http/DotNetty.Codecs.Http.csproj b/src/DotNetty.Codecs.Http/DotNetty.Codecs.Http.csproj index 567db2273..b8d411268 100644 --- a/src/DotNetty.Codecs.Http/DotNetty.Codecs.Http.csproj +++ b/src/DotNetty.Codecs.Http/DotNetty.Codecs.Http.csproj @@ -1,15 +1,15 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Codecs.Http + DotNetty: Http codec en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 true false @@ -18,18 +18,29 @@ ../../DotNetty.snk true true - socket;tcp;protocol;netty;dotnetty;network;http + 2.0.3 + + + Http codec for DotNetty + Nethermind.DotNetty.Codecs.Http + socket tcp protocol netty dotnetty network http + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/DotNetty.Codecs.Http/WebSockets/WebSocketClientHandshaker.cs b/src/DotNetty.Codecs.Http/WebSockets/WebSocketClientHandshaker.cs index a2b32ff67..279612c96 100644 --- a/src/DotNetty.Codecs.Http/WebSockets/WebSocketClientHandshaker.cs +++ b/src/DotNetty.Codecs.Http/WebSockets/WebSocketClientHandshaker.cs @@ -11,6 +11,7 @@ namespace DotNetty.Codecs.Http.WebSockets using DotNetty.Common.Concurrency; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public abstract class WebSocketClientHandshaker { diff --git a/src/DotNetty.Codecs.Http/WebSockets/WebSocketServerHandshaker.cs b/src/DotNetty.Codecs.Http/WebSockets/WebSocketServerHandshaker.cs index e5ec847d8..5449282eb 100644 --- a/src/DotNetty.Codecs.Http/WebSockets/WebSocketServerHandshaker.cs +++ b/src/DotNetty.Codecs.Http/WebSockets/WebSocketServerHandshaker.cs @@ -15,6 +15,7 @@ namespace DotNetty.Codecs.Http.WebSockets using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public abstract class WebSocketServerHandshaker { diff --git a/src/DotNetty.Codecs.Mqtt/DotNetty.Codecs.Mqtt.csproj b/src/DotNetty.Codecs.Mqtt/DotNetty.Codecs.Mqtt.csproj index 7e14a1ed5..58e1f7a27 100644 --- a/src/DotNetty.Codecs.Mqtt/DotNetty.Codecs.Mqtt.csproj +++ b/src/DotNetty.Codecs.Mqtt/DotNetty.Codecs.Mqtt.csproj @@ -1,14 +1,15 @@ - + + - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Codecs.Mqtt + DotNetty: MQTT codec en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 false true @@ -16,17 +17,31 @@ ../../DotNetty.snk true true - socket;tcp;protocol;netty;dotnetty;network;mqtt + 2.0.3 + + + MQTT codec for DotNetty + Nethermind.DotNetty.Codecs.Mqtt + socket tcp protocol netty dotnetty network mqtt + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/DotNetty.Codecs.Protobuf/DotNetty.Codecs.Protobuf.csproj b/src/DotNetty.Codecs.Protobuf/DotNetty.Codecs.Protobuf.csproj index 4c4a6e2ff..c027351ef 100644 --- a/src/DotNetty.Codecs.Protobuf/DotNetty.Codecs.Protobuf.csproj +++ b/src/DotNetty.Codecs.Protobuf/DotNetty.Codecs.Protobuf.csproj @@ -1,14 +1,15 @@ - + + - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Codecs.Protobuf + DotNetty: Protobuf Proto3 codec en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 false true @@ -16,14 +17,21 @@ ../../DotNetty.snk true true - socket;tcp;protocol;netty;dotnetty;network;Protobuf + 2.0.3 + + + Protobuf Proto3 codec for DotNetty + Nethermind.DotNetty.Codecs.Protobuf + socket tcp protocol netty dotnetty network protobuf + + @@ -35,5 +43,9 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/src/DotNetty.Codecs.ProtocolBuffers/DotNetty.Codecs.ProtocolBuffers.csproj b/src/DotNetty.Codecs.ProtocolBuffers/DotNetty.Codecs.ProtocolBuffers.csproj index 884946bde..82a269086 100644 --- a/src/DotNetty.Codecs.ProtocolBuffers/DotNetty.Codecs.ProtocolBuffers.csproj +++ b/src/DotNetty.Codecs.ProtocolBuffers/DotNetty.Codecs.ProtocolBuffers.csproj @@ -1,15 +1,15 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Codecs.ProtocolBuffers + DotNetty: ProtocolBuffers Proto2 codec en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 false true @@ -17,14 +17,21 @@ ../../DotNetty.snk true true - socket;tcp;protocol;netty;dotnetty;network;ProtocolBuffers + 2.0.3 + + + ProtocolBuffers Proto2 codec for DotNetty + Nethermind.DotNetty.Codecs.ProtocolBuffers + socket tcp protocol netty dotnetty network protocolbuffers + + @@ -36,5 +43,9 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/src/DotNetty.Codecs.Redis/DotNetty.Codecs.Redis.csproj b/src/DotNetty.Codecs.Redis/DotNetty.Codecs.Redis.csproj index 586377cdb..2202de528 100644 --- a/src/DotNetty.Codecs.Redis/DotNetty.Codecs.Redis.csproj +++ b/src/DotNetty.Codecs.Redis/DotNetty.Codecs.Redis.csproj @@ -1,14 +1,14 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Codecs.Redis + DotNetty: Redis codec en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 false true @@ -16,17 +16,31 @@ ../../DotNetty.snk true true - socket;tcp;protocol;netty;dotnetty;network;redis + 2.0.3 + + + Redis codec for DotNetty + Nethermind.DotNetty.Codecs.Redis + socket tcp protocol netty dotnetty network redis + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.csproj b/src/DotNetty.Codecs/DotNetty.Codecs.csproj index b89c9edb6..15ebda181 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.csproj +++ b/src/DotNetty.Codecs/DotNetty.Codecs.csproj @@ -1,14 +1,14 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Codecs + DotNetty: codecs en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 true false @@ -17,19 +17,30 @@ ../../DotNetty.snk true true - DotNetty.Codecs + 2.0.3 + + + General purpose codecs for DotNetty + Nethermind.DotNetty.Codecs + socket tcp protocol netty dotnetty network codec + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/DotNetty.Common/DotNetty.Common.csproj b/src/DotNetty.Common/DotNetty.Common.csproj index 13632d123..d0b302135 100644 --- a/src/DotNetty.Common/DotNetty.Common.csproj +++ b/src/DotNetty.Common/DotNetty.Common.csproj @@ -1,15 +1,15 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Common + DotNetty: common routines en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 True false @@ -18,19 +18,30 @@ ../../DotNetty.snk true true - socket;tcp;protocol;netty;dotnetty;network + 2.0.3 + + + DotNetty common routines + Nethermind.DotNetty.Common + socket tcp protocol netty dotnetty network + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/src/DotNetty.Common/ThreadDeathWatcher.cs b/src/DotNetty.Common/ThreadDeathWatcher.cs index f043db778..516a457a4 100644 --- a/src/DotNetty.Common/ThreadDeathWatcher.cs +++ b/src/DotNetty.Common/ThreadDeathWatcher.cs @@ -59,10 +59,18 @@ static void Schedule(Thread thread, Action task, bool isWatch) if (Interlocked.CompareExchange(ref started, 1, 0) == 0) { - var watcherThread = new Thread(s => ((IRunnable)s).Run()); - watcherThread.IsBackground = true; - watcherThread.Start(watcher); - ThreadDeathWatcher.watcherThread = watcherThread; + try + { + var watcherThread = new Thread(s => ((IRunnable)s).Run()); + watcherThread.IsBackground = true; + watcherThread.Start(watcher); + ThreadDeathWatcher.watcherThread = watcherThread; + } + catch (Exception e) + { + bool stopped = !watcherThread.IsAlive && Interlocked.CompareExchange(ref started, 0, 1) == 1; + Logger.Warn($"Thread death watcher stopped: {stopped} and raised an exception while trying to start the thread:", e); + } } } @@ -95,48 +103,57 @@ sealed class Watcher : IRunnable public void Run() { - for (;;) + try { - this.FetchWatchees(); - this.NotifyWatchees(); + for (;;) + { + this.FetchWatchees(); + this.NotifyWatchees(); - // Try once again just in case notifyWatchees() triggered watch() or unwatch(). - this.FetchWatchees(); - this.NotifyWatchees(); + // Try once again just in case notifyWatchees() triggered watch() or unwatch(). + this.FetchWatchees(); + this.NotifyWatchees(); - Thread.Sleep(1000); + Thread.Sleep(1000); - if (this.watchees.Count == 0 && PendingEntries.IsEmpty) - { - // Mark the current worker thread as stopped. - // The following CAS must always success and must be uncontended, - // because only one watcher thread should be running at the same time. - bool stopped = Interlocked.CompareExchange(ref started, 0, 1) == 1; - Contract.Assert(stopped); - - // Check if there are pending entries added by watch() while we do CAS above. - if (PendingEntries.IsEmpty) + if (this.watchees.Count == 0 && PendingEntries.IsEmpty) { - // A) watch() was not invoked and thus there's nothing to handle - // -> safe to terminate because there's nothing left to do - // B) a new watcher thread started and handled them all - // -> safe to terminate the new watcher thread will take care the rest - break; + // Mark the current worker thread as stopped. + // The following CAS must always success and must be uncontended, + // because only one watcher thread should be running at the same time. + bool stopped = Interlocked.CompareExchange(ref started, 0, 1) == 1; + Contract.Assert(stopped); + + // Check if there are pending entries added by watch() while we do CAS above. + if (PendingEntries.IsEmpty) + { + // A) watch() was not invoked and thus there's nothing to handle + // -> safe to terminate because there's nothing left to do + // B) a new watcher thread started and handled them all + // -> safe to terminate the new watcher thread will take care the rest + break; + } + + // There are pending entries again, added by watch() + if (Interlocked.CompareExchange(ref started, 1, 0) != 0) + { + // watch() started a new watcher thread and set 'started' to true. + // -> terminate this thread so that the new watcher reads from pendingEntries exclusively. + break; + } + + // watch() added an entry, but this worker was faster to set 'started' to true. + // i.e. a new watcher thread was not started + // -> keep this thread alive to handle the newly added entries. } - - // There are pending entries again, added by watch() - if (Interlocked.CompareExchange(ref started, 1, 0) != 0) - { - // watch() started a new watcher thread and set 'started' to true. - // -> terminate this thread so that the new watcher reads from pendingEntries exclusively. - break; - } - - // watch() added an entry, but this worker was faster to set 'started' to true. - // i.e. a new watcher thread was not started - // -> keep this thread alive to handle the newly added entries. } } + catch (Exception e) + { + // unexpected event, try mark thread as stopped + bool stopped = Interlocked.CompareExchange(ref started, 0, 1) == 1; + Logger.Error($"Thread death watcher was stopped: {stopped} and raised an fatal exception while processing:", e); + } } void FetchWatchees() diff --git a/src/DotNetty.Common/ThreadLocalPool.cs b/src/DotNetty.Common/ThreadLocalPool.cs index 327cb15e9..a643101cf 100644 --- a/src/DotNetty.Common/ThreadLocalPool.cs +++ b/src/DotNetty.Common/ThreadLocalPool.cs @@ -56,6 +56,7 @@ public override void Release(T value) { throw new InvalidOperationException("recycled already"); } + stack.Push(this); } } @@ -107,6 +108,7 @@ internal Head(StrongBox availableSharedCapacity, StrongBox weakTableCo { Interlocked.Decrement(ref this.weakTableCounter.Value); } + if (this.availableSharedCapacity == null) { return; @@ -138,13 +140,14 @@ internal bool ReserveSpace(int space) internal static bool ReserveSpace(StrongBox availableSharedCapacity, int space) { Debug.Assert(space >= 0); - for (; ; ) + for (;;) { int available = Volatile.Read(ref availableSharedCapacity.Value); if (available < space) { return false; } + if (Interlocked.CompareExchange(ref availableSharedCapacity.Value, available - space, available) == available) { return true; @@ -155,7 +158,9 @@ internal static bool ReserveSpace(StrongBox availableSharedCapacity, int sp // chain of data items readonly Head head; + Link tail; + // pointer to another queue of delayed items for the same stack internal WeakOrderQueue next; internal readonly WeakReference owner; @@ -220,10 +225,12 @@ internal void Add(DefaultHandle handle) // Drop it. return; } + // We allocate a Link so reserve the space this.tail = tail = tail.next = new Link(); writeIndex = tail.WriteIndex; } + tail.elements[writeIndex] = handle; handle.Stack = null; // we lazy set to ensure that setting stack to null appears before we unnull it in the owning thread; @@ -236,7 +243,7 @@ internal void Add(DefaultHandle handle) // transfer as many items as we can from this queue to the stack, returning true if any were transferred internal bool Transfer(Stack dst) { - Link head = this.head.link; + Link head = this.head?.link; if (head == null) { return false; @@ -248,6 +255,7 @@ internal bool Transfer(Stack dst) { return false; } + this.head.link = head = head.next; } @@ -259,6 +267,11 @@ internal bool Transfer(Stack dst) return false; } + if (dst?.elements == null) + { + return false; + } + int dstSize = dst.size; int expectedCapacity = dstSize + srcSize; @@ -273,9 +286,19 @@ internal bool Transfer(Stack dst) DefaultHandle[] srcElems = head.elements; DefaultHandle[] dstElems = dst.elements; int newDstSize = dstSize; + if (head.elements == null) + { + return false; + } + for (int i = srcStart; i < srcEnd; i++) { DefaultHandle element = srcElems[i]; + if (element == null) + { + return false; + } + if (element.recycleId == 0) { element.recycleId = element.lastRecycledId; @@ -284,6 +307,7 @@ internal bool Transfer(Stack dst) { throw new InvalidOperationException("recycled already"); } + srcElems[i] = null; if (dst.DropHandle(element)) @@ -291,6 +315,7 @@ internal bool Transfer(Stack dst) // Drop the object. continue; } + element.Stack = dst; dstElems[newDstSize++] = element; } @@ -307,6 +332,7 @@ internal bool Transfer(Stack dst) { return false; } + dst.size = newDstSize; return true; } @@ -344,8 +370,13 @@ protected sealed class Stack WeakOrderQueue cursorQueue, prevQueue; volatile WeakOrderQueue headQueue; - internal Stack(ThreadLocalPool parent, Thread thread, int maxCapacity, int maxSharedCapacityFactor, - int ratioMask, int maxDelayedQueues) + internal Stack( + ThreadLocalPool parent, + Thread thread, + int maxCapacity, + int maxSharedCapacityFactor, + int ratioMask, + int maxDelayedQueues) { this.parent = parent; this.threadRef = new WeakReference(thread); @@ -410,6 +441,7 @@ void PushNow(DefaultHandle item) { throw new InvalidOperationException("released already"); } + item.recycleId = item.lastRecycledId = ownThreadId; int size = this.size; @@ -418,6 +450,7 @@ void PushNow(DefaultHandle item) // Hit the maximum capacity - drop the possibly youngest object. return; } + if (size == this.elements.Length) { Array.Resize(ref this.elements, Math.Min(size << 1, this.maxCapacity)); @@ -443,12 +476,14 @@ void PushLater(DefaultHandle item, Thread thread) delayedRecycled.Add(this, WeakOrderQueue.Dummy); return; } + // Check if we already reached the maximum number of delayed queues and if we can allocate at all. if ((queue = WeakOrderQueue.Allocate(this, thread, countedWeakTable)) == null) { // drop object return; } + delayedRecycled.Add(this, queue); } else if (queue == WeakOrderQueue.Dummy) @@ -469,8 +504,10 @@ internal bool DropHandle(DefaultHandle handle) // Drop the object. return true; } + handle.hasBeenRecycled = true; } + return false; } @@ -486,8 +523,10 @@ internal bool TryPop(out DefaultHandle item) item = null; return false; } + size = this.size; } + size--; DefaultHandle ret = this.elements[size]; elements[size] = null; @@ -495,6 +534,7 @@ internal bool TryPop(out DefaultHandle item) { throw new InvalidOperationException("recycled multiple times"); } + ret.recycleId = 0; ret.lastRecycledId = 0; this.size = size; @@ -564,6 +604,7 @@ bool ScavengeSome() } } } + if (prev != null) { prev.Next = next; @@ -604,6 +645,7 @@ public class CountedWeakTable internal readonly StrongBox Counter = new StrongBox(); } + protected override CountedWeakTable GetInitialValue() => new CountedWeakTable(); } @@ -612,8 +654,9 @@ static ThreadLocalPool() // In the future, we might have different maxCapacity for different object types. // e.g. io.netty.recycler.maxCapacity.writeTask // io.netty.recycler.maxCapacity.outboundBuffer - int maxCapacityPerThread = SystemPropertyUtil.GetInt("io.netty.recycler.maxCapacityPerThread", - SystemPropertyUtil.GetInt("io.netty.recycler.maxCapacity", DefaultInitialMaxCapacityPerThread)); + int maxCapacityPerThread = SystemPropertyUtil.GetInt( + "io.netty.recycler.maxCapacityPerThread", + SystemPropertyUtil.GetInt("io.netty.recycler.maxCapacity", DefaultInitialMaxCapacityPerThread)); if (maxCapacityPerThread < 0) { maxCapacityPerThread = DefaultInitialMaxCapacityPerThread; @@ -621,17 +664,21 @@ static ThreadLocalPool() DefaultMaxCapacityPerThread = maxCapacityPerThread; - DefaultMaxSharedCapacityFactor = Math.Max(2, - SystemPropertyUtil.GetInt("io.netty.recycler.maxSharedCapacityFactor", - 2)); + DefaultMaxSharedCapacityFactor = Math.Max( + 2, + SystemPropertyUtil.GetInt( + "io.netty.recycler.maxSharedCapacityFactor", + 2)); - DefaultMaxDelayedQueuesPerThread = Math.Max(0, - SystemPropertyUtil.GetInt("io.netty.recycler.maxDelayedQueuesPerThread", - // We use the same value as default EventLoop number - Environment.ProcessorCount * 2)); + DefaultMaxDelayedQueuesPerThread = Math.Max( + 0, + SystemPropertyUtil.GetInt( + "io.netty.recycler.maxDelayedQueuesPerThread", + // We use the same value as default EventLoop number + Environment.ProcessorCount * 2)); LinkCapacity = MathUtil.SafeFindNextPositivePowerOfTwo( - Math.Max(SystemPropertyUtil.GetInt("io.netty.recycler.linkCapacity", 16), 16)); + Math.Max(SystemPropertyUtil.GetInt("io.netty.recycler.linkCapacity", 16), 16)); // By default we allow one push to a Recycler for each 8th try on handles that were never recycled before. // This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation @@ -663,12 +710,15 @@ static ThreadLocalPool() } public ThreadLocalPool(int maxCapacityPerThread) - : this (maxCapacityPerThread, DefaultMaxSharedCapacityFactor, DefaultRatio, DefaultMaxDelayedQueuesPerThread) + : this(maxCapacityPerThread, DefaultMaxSharedCapacityFactor, DefaultRatio, DefaultMaxDelayedQueuesPerThread) { } - public ThreadLocalPool(int maxCapacityPerThread, int maxSharedCapacityFactor, - int ratio, int maxDelayedQueuesPerThread) + public ThreadLocalPool( + int maxCapacityPerThread, + int maxSharedCapacityFactor, + int ratio, + int maxDelayedQueuesPerThread) { this.ratioMask = MathUtil.SafeFindNextPositivePowerOfTwo(ratio) - 1; if (maxCapacityPerThread <= 0) @@ -718,8 +768,13 @@ public ThreadLocalPool(Func valueFactory, int maxCapacityPerThread, i { } - public ThreadLocalPool(Func valueFactory, int maxCapacityPerThread, int maxSharedCapacityFactor, - int ratio, int maxDelayedQueuesPerThread, bool preCreate = false) + public ThreadLocalPool( + Func valueFactory, + int maxCapacityPerThread, + int maxSharedCapacityFactor, + int ratio, + int maxDelayedQueuesPerThread, + bool preCreate = false) : base(maxCapacityPerThread, maxSharedCapacityFactor, ratio, maxDelayedQueuesPerThread) { Contract.Requires(valueFactory != null); @@ -743,6 +798,7 @@ public T Take() { handle = CreateValue(stack); } + return (T)handle.Value; } @@ -768,8 +824,13 @@ public ThreadLocalStack(ThreadLocalPool owner) protected override Stack GetInitialValue() { - var stack = new Stack(this.owner, Thread.CurrentThread, this.owner.maxCapacityPerThread, - this.owner.maxSharedCapacityFactor, this.owner.ratioMask, this.owner.maxDelayedQueuesPerThread); + var stack = new Stack( + this.owner, + Thread.CurrentThread, + this.owner.maxCapacityPerThread, + this.owner.maxSharedCapacityFactor, + this.owner.ratioMask, + this.owner.maxDelayedQueuesPerThread); if (this.owner.preCreate) { for (int i = 0; i < this.owner.maxCapacityPerThread; i++) @@ -777,6 +838,7 @@ protected override Stack GetInitialValue() stack.Push(this.owner.CreateValue(stack)); } } + return stack; } diff --git a/src/DotNetty.Common/Utilities/HashedWheelTimer.cs b/src/DotNetty.Common/Utilities/HashedWheelTimer.cs index a1dd13782..7322dc9e6 100644 --- a/src/DotNetty.Common/Utilities/HashedWheelTimer.cs +++ b/src/DotNetty.Common/Utilities/HashedWheelTimer.cs @@ -15,6 +15,7 @@ namespace DotNetty.Common.Utilities using DotNetty.Common.Concurrency; using DotNetty.Common.Internal; using DotNetty.Common.Internal.Logging; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public sealed class HashedWheelTimer : ITimer { diff --git a/src/DotNetty.Common/Utilities/TaskEx.cs b/src/DotNetty.Common/Utilities/TaskEx.cs index d0093a97d..8c2c72d98 100644 --- a/src/DotNetty.Common/Utilities/TaskEx.cs +++ b/src/DotNetty.Common/Utilities/TaskEx.cs @@ -6,6 +6,7 @@ namespace DotNetty.Common.Utilities using System; using System.Threading.Tasks; using DotNetty.Common.Concurrency; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public static class TaskEx { diff --git a/src/DotNetty.Handlers/DotNetty.Handlers.csproj b/src/DotNetty.Handlers/DotNetty.Handlers.csproj index 83fed99c9..f0d58587a 100644 --- a/src/DotNetty.Handlers/DotNetty.Handlers.csproj +++ b/src/DotNetty.Handlers/DotNetty.Handlers.csproj @@ -1,15 +1,15 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Handlers + DotNetty: handlers en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 false true @@ -17,17 +17,31 @@ ../../DotNetty.snk true true - socket;tcp;protocol;netty;dotnetty;network;tls;ssl + 2.0.3 + + + Application handlers for DotNetty + Nethermind.DotNetty.Handlers + socket tcp protocol netty dotnetty network tls ssl + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/DotNetty.Handlers/Streams/ChunkedWriteHandler.cs b/src/DotNetty.Handlers/Streams/ChunkedWriteHandler.cs index 438856ae5..f31bbbdf5 100644 --- a/src/DotNetty.Handlers/Streams/ChunkedWriteHandler.cs +++ b/src/DotNetty.Handlers/Streams/ChunkedWriteHandler.cs @@ -11,6 +11,7 @@ namespace DotNetty.Handlers.Streams using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public class ChunkedWriteHandler : ChannelDuplexHandler { diff --git a/src/DotNetty.Handlers/Tls/SniHandler.cs b/src/DotNetty.Handlers/Tls/SniHandler.cs index 4f7b48827..c7148ef15 100644 --- a/src/DotNetty.Handlers/Tls/SniHandler.cs +++ b/src/DotNetty.Handlers/Tls/SniHandler.cs @@ -214,7 +214,7 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input }; hostname = idn.GetAscii(hostname); -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER // TODO: netcore does not have culture sensitive tolower() hostname = hostname.ToLowerInvariant(); #else diff --git a/src/DotNetty.Handlers/Tls/TlsHandler.MediationStream.Net.cs b/src/DotNetty.Handlers/Tls/TlsHandler.MediationStream.Net.cs new file mode 100644 index 000000000..b8e48babd --- /dev/null +++ b/src/DotNetty.Handlers/Tls/TlsHandler.MediationStream.Net.cs @@ -0,0 +1,302 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if NET5_0_OR_GREATER +namespace DotNetty.Handlers.Tls +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Threading; + using System.Threading.Tasks; + + partial class TlsHandler + { + sealed class MediationStreamNet : MediationStreamBase + { + readonly CompositeSource source = new(); + TaskCompletionSource readCompletionSource; + Memory sslOwnedMemory; + int readByteCount; + + public MediationStreamNet(TlsHandler owner) + : base(owner) + { + } + + public override bool SourceIsReadable => this.source.IsReadable; + public override int SourceReadableBytes => this.source.GetTotalReadableBytes(); + + public override void SetSource(byte[] source, int offset) => this.source.AddSource(source, offset); + public override void ResetSource() => this.source.ResetSources(); + + public override void ExpandSource(int count) + { + this.source.Expand(count); + + Memory sslMemory = this.sslOwnedMemory; + if (sslMemory.IsEmpty) + { + return; + } + + this.sslOwnedMemory = default; + + this.readByteCount = this.ReadFromInput(sslMemory); + // hack: this tricks SslStream's continuation to run synchronously instead of dispatching to TP. Remove once Begin/EndRead are available. + new Task( + ms => + { + var self = (MediationStreamNet)ms; + TaskCompletionSource p = self.readCompletionSource; + self.readCompletionSource = null; + p.TrySetResult(self.readByteCount); + }, + this) + .RunSynchronously(TaskScheduler.Default); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + => this.owner.capturedContext.Executor.InEventLoop + ? this.InLoopReadAsync(buffer, cancellationToken) + : new ValueTask(this.OutOfLoopReadAsync(buffer, cancellationToken)); + + ValueTask InLoopReadAsync(Memory buffer, CancellationToken cancellationToken) + { + if (this.SourceIsReadable) + { + // we have the bytes available upfront - write out synchronously + int read = this.ReadFromInput(buffer); + return new ValueTask(read); + } + + Contract.Assert(this.sslOwnedMemory.IsEmpty); + // take note of buffer - we will pass bytes there once available + this.sslOwnedMemory = buffer; + this.readCompletionSource = new TaskCompletionSource(); + return new ValueTask(this.readCompletionSource.Task); + } + + Task OutOfLoopReadAsync(Memory buffer, CancellationToken cancellationToken) + { + return this.owner.capturedContext.Executor.SubmitAsync( + () => + { + if (this.SourceIsReadable) + { + // we have the bytes available upfront - write out synchronously + int read = this.ReadFromInput(buffer); + return Task.FromResult(read); + } + + Contract.Assert(this.sslOwnedMemory.IsEmpty); + // take note of buffer - we will pass bytes there once available + this.sslOwnedMemory = buffer; + this.readCompletionSource = new TaskCompletionSource(); + return this.readCompletionSource.Task; + }, + cancellationToken).Unwrap(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (this.owner.capturedContext.Executor.InEventLoop) + { + this.owner.FinishWrap(buffer, offset, count); + } + else + { + this.owner.capturedContext.Executor.Execute(() => this.owner.FinishWrap(buffer, offset, count)); + } + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (this.owner.capturedContext.Executor.InEventLoop) + { + return this.owner.FinishWrapNonAppDataAsync(buffer, offset, count); + } + + return this.owner.capturedContext.Executor.SubmitAsync( + () => this.owner.FinishWrapNonAppDataAsync(buffer, offset, count), + cancellationToken + ).Unwrap(); + } + + public override void Flush() + { + // NOOP: called on SslStream.Close + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + TaskCompletionSource p = this.readCompletionSource; + if (p != null) + { + this.readCompletionSource = null; + p.TrySetResult(0); + } + } + } + + int ReadFromInput(Memory destination) => this.source.Read(destination); + + #region Source + + sealed class Source + { + byte[] input; + int startOffset; + int offset; + int length; + bool retained; + + public Source(byte[] input, int offset) + { + this.input = input; + this.startOffset = offset; + this.offset = 0; + this.length = 0; + } + + public int ReadableBytes => this.length - this.offset; + + public bool IsReadable => this.ReadableBytes > 0; + + public void Expand(int count) + { + Contract.Assert(!this.retained); // Retained source is not expected to be Expanded + this.length += count; + Contract.Assert(this.length <= this.input.Length); + } + + public int Read(Memory destination) + { + int len = Math.Min(this.ReadableBytes, destination.Length); + new ReadOnlySpan(this.input, this.startOffset + this.offset, len).CopyTo(destination.Span); + this.offset += len; + return len; + } + + // This is to avoid input bytes to be discarded by ref counting mechanism + public void Retain() + { + int readableBytes = this.ReadableBytes; + if (this.retained || readableBytes <= 0) + { + return; + } + + // todo: is there a way to not discard those bytes till they are read??? If not, then use context.Allocator??? + + // Copy only readable bytes to a new buffer + byte[] copy = new byte[readableBytes]; + Buffer.BlockCopy(this.input, this.startOffset + this.offset, copy, 0, readableBytes); + this.input = copy; + + // Set both offsets to 0 and length to readableBytes (so that this.ReadableBytes stays the same) + this.startOffset = 0; + this.offset = 0; + this.length = readableBytes; + + this.retained = true; + } + } + + sealed class CompositeSource + { + // Why not List? + // 1. It's unlikely this list to grow more than 10 nodes. In fact in most cases it'll have one element only + // 2. Cleanup removes from head, so it's cheaper compared to List which shifts elements in this case. + readonly LinkedList sources = new LinkedList(); + + public bool IsReadable + { + get + { + // The composite source is readable if any readable sources, so + // it's enough to check on last one as we always AddLast + LinkedListNode last = this.sources.Last; + return last != null && last.Value.IsReadable; + } + } + + public void AddSource(byte[] input, int offset) + { + // Always add to the tail + this.sources.AddLast(new Source(input, offset)); + } + + public void Expand(int count) + { + Contract.Assert(this.sources.Last != null); // AddSource is always called before + + // Always expand the last added source + this.sources.Last.Value.Expand(count); + } + + public int GetTotalReadableBytes() + { + int count = 0; + + LinkedListNode node = this.sources.First; + while (node != null) + { + count += node.Value.ReadableBytes; + node = node.Next; + } + + return count; + } + + // Read from all readable sources to the destination starting from head (oldest) + public int Read(Memory destination) + { + int totalRead = 0; + + LinkedListNode node = this.sources.First; + while (node != null && totalRead < destination.Length) + { + Source source = node.Value; + int read = source.Read(destination.Slice(totalRead, destination.Length - totalRead)); + totalRead += read; + + if (!source.IsReadable) + { + node = node.Next; + // Do not remove the node here as it can be expanded. Instead, + // remove in the CleanUp method below + } + } + + return totalRead; + } + + // Remove all not readable sources and retain readable. Start from first as it's the oldest + public void ResetSources() + { + LinkedListNode node = this.sources.First; + while (node != null) + { + if (!node.Value.IsReadable) + { + this.sources.RemoveFirst(); + node = this.sources.First; + } + else + { + node.Value.Retain(); + node = node.Next; + } + } + } + } + + #endregion + } + } +} +#endif \ No newline at end of file diff --git a/src/DotNetty.Handlers/Tls/TlsHandler.MediationStream.cs b/src/DotNetty.Handlers/Tls/TlsHandler.MediationStream.cs new file mode 100644 index 000000000..580292152 --- /dev/null +++ b/src/DotNetty.Handlers/Tls/TlsHandler.MediationStream.cs @@ -0,0 +1,338 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace DotNetty.Handlers.Tls +{ + using System; + using System.Diagnostics; + using System.Diagnostics.Contracts; + using System.IO; + using System.Runtime.ExceptionServices; + using System.Threading; + using System.Threading.Tasks; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; + + partial class TlsHandler + { + sealed class MediationStream : MediationStreamBase + { + byte[] input; + int inputStartOffset; + int inputOffset; + int inputLength; + TaskCompletionSource readCompletionSource; + ArraySegment sslOwnedBuffer; +#if NETSTANDARD2_0 || NETCOREAPP3_1 + int readByteCount; +#else + SynchronousAsyncResult syncReadResult; + AsyncCallback readCallback; + TaskCompletionSource writeCompletion; + AsyncCallback writeCallback; +#endif + + public MediationStream(TlsHandler owner) + : base(owner) + { + } + + public override int SourceReadableBytes => this.inputLength - this.inputOffset; + + public override bool SourceIsReadable => this.SourceReadableBytes > 0; + + public override void SetSource(byte[] source, int offset) + { + this.input = source; + this.inputStartOffset = offset; + this.inputOffset = 0; + this.inputLength = 0; + } + + public override void ResetSource() + { + this.input = null; + this.inputLength = 0; + this.inputOffset = 0; + } + + public override void ExpandSource(int count) + { + Contract.Assert(this.input != null); + + this.inputLength += count; + + ArraySegment sslBuffer = this.sslOwnedBuffer; + if (sslBuffer.Array == null) + { + // there is no pending read operation - keep for future + return; + } + + this.sslOwnedBuffer = default(ArraySegment); + +#if NETSTANDARD2_0 || NETCOREAPP3_1 + this.readByteCount = this.ReadFromInput(sslBuffer.Array, sslBuffer.Offset, sslBuffer.Count); + // hack: this tricks SslStream's continuation to run synchronously instead of dispatching to TP. Remove once Begin/EndRead are available. + new Task( + ms => + { + var self = (MediationStream)ms; + TaskCompletionSource p = self.readCompletionSource; + self.readCompletionSource = null; + p.TrySetResult(self.readByteCount); + }, + this) + .RunSynchronously(TaskScheduler.Default); +#else + int read = this.ReadFromInput(sslBuffer.Array, sslBuffer.Offset, sslBuffer.Count); + + TaskCompletionSource promise = this.readCompletionSource; + this.readCompletionSource = null; + promise.TrySetResult(read); + + AsyncCallback callback = this.readCallback; + this.readCallback = null; + callback?.Invoke(promise.Task); +#endif + } + +#if NETSTANDARD2_0 || NETCOREAPP3_1 + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (this.SourceReadableBytes > 0) + { + // we have the bytes available upfront - write out synchronously + int read = this.ReadFromInput(buffer, offset, count); + return Task.FromResult(read); + } + + Contract.Assert(this.sslOwnedBuffer.Array == null); + // take note of buffer - we will pass bytes there once available + this.sslOwnedBuffer = new ArraySegment(buffer, offset, count); + this.readCompletionSource = new TaskCompletionSource(); + return this.readCompletionSource.Task; + } +#else + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + if (this.SourceReadableBytes > 0) + { + // we have the bytes available upfront - write out synchronously + int read = this.ReadFromInput(buffer, offset, count); + var res = this.PrepareSyncReadResult(read, state); + callback?.Invoke(res); + return res; + } + + Contract.Assert(this.sslOwnedBuffer.Array == null); + // take note of buffer - we will pass bytes there once available + this.sslOwnedBuffer = new ArraySegment(buffer, offset, count); + this.readCompletionSource = new TaskCompletionSource(state); + this.readCallback = callback; + return this.readCompletionSource.Task; + } + + public override int EndRead(IAsyncResult asyncResult) + { + SynchronousAsyncResult syncResult = this.syncReadResult; + if (ReferenceEquals(asyncResult, syncResult)) + { + return syncResult.Result; + } + + Debug.Assert(this.readCompletionSource == null || this.readCompletionSource.Task == asyncResult); + Contract.Assert(!((Task)asyncResult).IsCanceled); + + try + { + return ((Task)asyncResult).Result; + } + catch (AggregateException ex) + { + ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); + throw; // unreachable + } + } + + IAsyncResult PrepareSyncReadResult(int readBytes, object state) + { + // it is safe to reuse sync result object as it can't lead to leak (no way to attach to it via handle) + SynchronousAsyncResult result = this.syncReadResult ?? (this.syncReadResult = new SynchronousAsyncResult()); + result.Result = readBytes; + result.AsyncState = state; + return result; + } +#endif + + public override void Write(byte[] buffer, int offset, int count) => this.owner.FinishWrap(buffer, offset, count); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => this.owner.FinishWrapNonAppDataAsync(buffer, offset, count); + +#if !(NETSTANDARD2_0 || NETCOREAPP3_1) + static readonly Action WriteCompleteCallback = HandleChannelWriteComplete; + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + Task task = this.WriteAsync(buffer, offset, count); + switch (task.Status) + { + case TaskStatus.RanToCompletion: + // write+flush completed synchronously (and successfully) + var result = new SynchronousAsyncResult(); + result.AsyncState = state; + callback?.Invoke(result); + return result; + default: + if (callback != null || state != task.AsyncState) + { + Contract.Assert(this.writeCompletion == null); + this.writeCallback = callback; + var tcs = new TaskCompletionSource(state); + this.writeCompletion = tcs; + task.ContinueWith(WriteCompleteCallback, this, TaskContinuationOptions.ExecuteSynchronously); + return tcs.Task; + } + else + { + return task; + } + } + } + + static void HandleChannelWriteComplete(Task writeTask, object state) + { + var self = (MediationStream)state; + + AsyncCallback callback = self.writeCallback; + self.writeCallback = null; + + var promise = self.writeCompletion; + self.writeCompletion = null; + + switch (writeTask.Status) + { + case TaskStatus.RanToCompletion: + promise.TryComplete(); + break; + case TaskStatus.Canceled: + promise.TrySetCanceled(); + break; + case TaskStatus.Faulted: + promise.TrySetException(writeTask.Exception); + break; + default: + throw new ArgumentOutOfRangeException("Unexpected task status: " + writeTask.Status); + } + + callback?.Invoke(promise.Task); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + if (asyncResult is SynchronousAsyncResult) + { + return; + } + + try + { + ((Task)asyncResult).Wait(); + } + catch (AggregateException ex) + { + ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); + throw; + } + } +#endif + + int ReadFromInput(byte[] destination, int destinationOffset, int destinationCapacity) + { + Contract.Assert(destination != null); + + byte[] source = this.input; + int readableBytes = this.SourceReadableBytes; + int length = Math.Min(readableBytes, destinationCapacity); + Buffer.BlockCopy(source, this.inputStartOffset + this.inputOffset, destination, destinationOffset, length); + this.inputOffset += length; + return length; + } + + public override void Flush() + { + // NOOP: called on SslStream.Close + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + TaskCompletionSource p = this.readCompletionSource; + if (p != null) + { + this.readCompletionSource = null; + p.TrySetResult(0); + } + } + } + + #region plumbing + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + #endregion + + #region sync result + + sealed class SynchronousAsyncResult : IAsyncResult + { + public T Result { get; set; } + + public bool IsCompleted => true; + + public WaitHandle AsyncWaitHandle + { + get { throw new InvalidOperationException("Cannot wait on a synchronous result."); } + } + + public object AsyncState { get; set; } + + public bool CompletedSynchronously => true; + } + + #endregion + } + } +} \ No newline at end of file diff --git a/src/DotNetty.Handlers/Tls/TlsHandler.MediationStreamBase.cs b/src/DotNetty.Handlers/Tls/TlsHandler.MediationStreamBase.cs new file mode 100644 index 000000000..1e0c6a085 --- /dev/null +++ b/src/DotNetty.Handlers/Tls/TlsHandler.MediationStreamBase.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace DotNetty.Handlers.Tls +{ + using System; + using System.IO; + + partial class TlsHandler + { + abstract class MediationStreamBase : Stream + { + protected readonly TlsHandler owner; + + public MediationStreamBase(TlsHandler owner) + { + this.owner = owner; + } + + public static MediationStreamBase Create(TlsHandler owner) + { +#if NET5_0_OR_GREATER + return new TlsHandler.MediationStreamNet(owner); +#else + return new MediationStream(owner); +#endif + } + + public abstract bool SourceIsReadable { get; } + public abstract int SourceReadableBytes { get; } + + public abstract void SetSource(byte[] source, int offset); + public abstract void ExpandSource(int count); + public abstract void ResetSource(); + + #region plumbing + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => true; + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + #endregion + } + } +} \ No newline at end of file diff --git a/src/DotNetty.Handlers/Tls/TlsHandler.cs b/src/DotNetty.Handlers/Tls/TlsHandler.cs index e46d8c32f..082fa0c0b 100644 --- a/src/DotNetty.Handlers/Tls/TlsHandler.cs +++ b/src/DotNetty.Handlers/Tls/TlsHandler.cs @@ -5,21 +5,19 @@ namespace DotNetty.Handlers.Tls { using System; using System.Collections.Generic; - using System.Diagnostics; using System.Diagnostics.Contracts; using System.IO; using System.Net.Security; - using System.Runtime.ExceptionServices; using System.Security.Cryptography.X509Certificates; - using System.Threading; using System.Threading.Tasks; using DotNetty.Buffers; using DotNetty.Codecs; - using DotNetty.Common.Concurrency; + using DotNetty.Common; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; - public sealed class TlsHandler : ByteToMessageDecoder + public sealed partial class TlsHandler : ByteToMessageDecoder { readonly TlsSettings settings; const int FallbackReadBufferSize = 256; @@ -29,11 +27,12 @@ public sealed class TlsHandler : ByteToMessageDecoder static readonly Action HandshakeCompletionCallback = new Action(HandleHandshakeCompleted); readonly SslStream sslStream; - readonly MediationStream mediationStream; + readonly MediationStreamBase mediationStream; readonly TaskCompletionSource closeFuture; TlsHandlerState state; - int packetLength; + (int packetLength, byte packetContentType) packetInfo; + List<(int packetLength, byte packetContentType)> pendingDataPackets; volatile IChannelHandlerContext capturedContext; BatchingPendingWriteQueue pendingUnencryptedWrites; Task lastContextWriteTask; @@ -53,7 +52,7 @@ public TlsHandler(Func sslStreamFactory, TlsSettings settings this.settings = settings; this.closeFuture = new TaskCompletionSource(); - this.mediationStream = new MediationStream(this); + this.mediationStream = MediationStreamBase.Create(this); this.sslStream = sslStreamFactory(this.mediationStream); } @@ -118,38 +117,74 @@ bool IgnoreException(Exception t) static void HandleHandshakeCompleted(Task task, object state) { var self = (TlsHandler)state; + if (self.capturedContext.Executor.InEventLoop) + { + HandleHandshakeCompletedInternal(task, self); + } + else + { + self.capturedContext.Executor.Execute(() => HandleHandshakeCompletedInternal(task, self)); + } + } + + static void HandleHandshakeCompletedInternal(Task task, TlsHandler self) + { switch (task.Status) { case TaskStatus.RanToCompletion: - { - TlsHandlerState oldState = self.state; - - Contract.Assert(!oldState.HasAny(TlsHandlerState.AuthenticationCompleted)); - self.state = (oldState | TlsHandlerState.Authenticated) & ~(TlsHandlerState.Authenticating | TlsHandlerState.FlushedBeforeHandshake); + { + TlsHandlerState oldState = self.state; - self.capturedContext.FireUserEventTriggered(TlsHandshakeCompletionEvent.Success); + Contract.Assert(!oldState.HasAny(TlsHandlerState.AuthenticationCompleted)); + self.state = (oldState | TlsHandlerState.Authenticated) & ~(TlsHandlerState.Authenticating | TlsHandlerState.FlushedBeforeHandshake); - if (oldState.Has(TlsHandlerState.ReadRequestedBeforeAuthenticated) && !self.capturedContext.Channel.Configuration.AutoRead) + self.capturedContext.FireUserEventTriggered(TlsHandshakeCompletionEvent.Success); + + // Due to possible async execution of HandleHandshakeCompleted continuation, we need to + // Unwrap any pending app data packets in case, when read completed and no more messages in the channel. + if (self.pendingDataPackets != null && self.pendingDataPackets.Count > 0) + { + ThreadLocalObjectList output = ThreadLocalObjectList.NewInstance(); + try { - self.capturedContext.Read(); + self.Unwrap(self.capturedContext, Unpooled.Empty, 0, 0, new List<(int packetLength, byte packetContentType)>(0), output); + for (int i = 0; i < output.Count; i++) + { + self.capturedContext.FireChannelRead(output[i]); + } } - - if (oldState.Has(TlsHandlerState.FlushedBeforeHandshake)) + catch (Exception ex) { - self.Wrap(self.capturedContext); - self.capturedContext.Flush(); + throw new DecoderException(ex); + } + finally + { + output.Return(); } - break; } - case TaskStatus.Canceled: - case TaskStatus.Faulted: + + if (oldState.Has(TlsHandlerState.ReadRequestedBeforeAuthenticated) && !self.capturedContext.Channel.Configuration.AutoRead) { - // ReSharper disable once AssignNullToNotNullAttribute -- task.Exception will be present as task is faulted - TlsHandlerState oldState = self.state; - Contract.Assert(!oldState.HasAny(TlsHandlerState.Authenticated)); - self.HandleFailure(task.Exception); - break; + self.capturedContext.Read(); + } + + if (oldState.Has(TlsHandlerState.FlushedBeforeHandshake)) + { + self.Wrap(self.capturedContext); + self.capturedContext.Flush(); } + + break; + } + case TaskStatus.Canceled: + case TaskStatus.Faulted: + { + // ReSharper disable once AssignNullToNotNullAttribute -- task.Exception will be present as task is faulted + TlsHandlerState oldState = self.state; + Contract.Assert(!oldState.HasAny(TlsHandlerState.Authenticated)); + self.HandleFailure(task.Exception); + break; + } default: throw new ArgumentOutOfRangeException(nameof(task), "Unexpected task status: " + task.Status); } @@ -183,27 +218,27 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input int offset = startOffset; int totalLength = 0; - List packetLengths; + List<(int, byte)> packetInfos; // if we calculated the length of the current SSL record before, use that information. - if (this.packetLength > 0) + if (this.packetInfo.packetLength > 0) { - if (endOffset - startOffset < this.packetLength) + if (endOffset - startOffset < this.packetInfo.packetLength) { // input does not contain a single complete SSL record return; } else { - packetLengths = new List(4); - packetLengths.Add(this.packetLength); - offset += this.packetLength; - totalLength = this.packetLength; - this.packetLength = 0; + packetInfos = new List<(int, byte)>(4); + packetInfos.Add(this.packetInfo); + offset += this.packetInfo.packetLength; + totalLength = this.packetInfo.packetLength; + this.packetInfo = default; } } else { - packetLengths = new List(4); + packetInfos = new List<(int, byte)>(4); } bool nonSslRecord = false; @@ -216,7 +251,7 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input break; } - int encryptedPacketLength = TlsUtils.GetEncryptedPacketLength(input, offset); + int encryptedPacketLength = TlsUtils.GetEncryptedPacketLength(input, offset, out byte contentType); if (encryptedPacketLength == -1) { nonSslRecord = true; @@ -228,7 +263,7 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input if (encryptedPacketLength > readableBytes) { // wait until the whole packet can be read - this.packetLength = encryptedPacketLength; + this.packetInfo = (encryptedPacketLength, contentType); break; } @@ -244,7 +279,7 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input // We have a whole packet. // Increment the offset to handle the next packet. - packetLengths.Add(encryptedPacketLength); + packetInfos.Add((encryptedPacketLength, contentType)); offset += encryptedPacketLength; totalLength = newTotalLength; } @@ -263,7 +298,7 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input // See https://github.com/netty/netty/issues/1534 input.SkipBytes(totalLength); - this.Unwrap(context, input, startOffset, totalLength, packetLengths, output); + this.Unwrap(context, input, startOffset, totalLength, packetInfos, output); if (!this.firedChannelRead) { @@ -305,11 +340,18 @@ void ReadIfNeeded(IChannelHandlerContext ctx) ctx.Read(); } } + /// Unwraps inbound SSL records. - void Unwrap(IChannelHandlerContext ctx, IByteBuffer packet, int offset, int length, List packetLengths, List output) + void Unwrap( + IChannelHandlerContext ctx, + IByteBuffer packet, + int offset, + int length, + List<(int packetLength, byte packetContentType)> packetInfos, + List output) { - Contract.Requires(packetLengths.Count > 0); + Contract.Requires(packetInfos.Count > 0 || this.pendingDataPackets != null); //bool notifyClosure = false; // todo: netty/issues/137 bool pending = false; @@ -318,17 +360,30 @@ void Unwrap(IChannelHandlerContext ctx, IByteBuffer packet, int offset, int leng try { - ArraySegment inputIoBuffer = packet.GetIoBuffer(offset, length); - this.mediationStream.SetSource(inputIoBuffer.Array, inputIoBuffer.Offset); - int packetIndex = 0; - while (!this.EnsureAuthenticated()) + if (packetInfos.Count > 0) { - this.mediationStream.ExpandSource(packetLengths[packetIndex]); - if (++packetIndex == packetLengths.Count) + ArraySegment inputIoBuffer = packet.GetIoBuffer(offset, length); + this.mediationStream.SetSource(inputIoBuffer.Array, inputIoBuffer.Offset); + + while (!this.EnsureAuthenticated()) { - return; + (int packetLength, byte type) = packetInfos[packetIndex]; + this.mediationStream.ExpandSource(packetLength); + + if (type == TlsUtils.SSL_CONTENT_TYPE_APPLICATION_DATA) + { + // Due to SslStream's implementation, it's possible that we expand after handshake completed. Hence, we + // need to make sure we call ReadFromSslStreamAsync for these packets later + this.pendingDataPackets = this.pendingDataPackets ?? new List<(int packetLength, byte packetContentType)>(8); + this.pendingDataPackets.Add((packetLength, type)); + } + + if (++packetIndex == packetInfos.Count) + { + return; + } } } @@ -353,10 +408,33 @@ void Unwrap(IChannelHandlerContext ctx, IByteBuffer packet, int offset, int leng } // go through packets one by one (because SslStream does not consume more than 1 packet at a time) - for (; packetIndex < packetLengths.Count; packetIndex++) + // account pendingDataPackets + int skipExpandPacketCount = 0; + if (this.pendingDataPackets != null) { - int currentPacketLength = packetLengths[packetIndex]; - this.mediationStream.ExpandSource(currentPacketLength); + // We already expanded the source for all pendingDataPackets, so skip expand further below + skipExpandPacketCount = this.pendingDataPackets.Count; + + // add packetLengths to pending except already processed + for (int i = packetIndex; i < packetInfos.Count; i++) + { + this.pendingDataPackets.Add(packetInfos[i]); + } + + packetInfos = this.pendingDataPackets; + this.pendingDataPackets = null; + packetIndex = 0; + } + + for (; packetIndex < packetInfos.Count; packetIndex++) + { + int currentPacketLength = packetInfos[packetIndex].packetLength; + + if (--skipExpandPacketCount < 0) + { + // For pending packets we already expended, so skip expand + this.mediationStream.ExpandSource(currentPacketLength); + } if (currentReadFuture != null) { @@ -365,12 +443,10 @@ void Unwrap(IChannelHandlerContext ctx, IByteBuffer packet, int offset, int leng if (!currentReadFuture.IsCompleted) { // we did feed the whole current packet to SslStream yet it did not produce any result -> move to the next packet in input - continue; } int read = currentReadFuture.Result; - if (read == 0) { //Stream closed @@ -382,7 +458,7 @@ void Unwrap(IChannelHandlerContext ctx, IByteBuffer packet, int offset, int leng currentReadFuture = null; outputBuffer = null; - if (this.mediationStream.SourceReadableBytes == 0) + if (!this.mediationStream.SourceIsReadable) { // we just made a frame available for reading but there was already pending read so SslStream read it out to make further progress there @@ -428,9 +504,18 @@ void Unwrap(IChannelHandlerContext ctx, IByteBuffer packet, int offset, int leng { break; } + int read = currentReadFuture.Result; + + if (read == 0) + { + //Stream closed + return; + } + AddBufferToOutput(outputBuffer, read, output); } + outputBuffer = ctx.Allocator.Buffer(FallbackReadBufferSize); currentReadFuture = this.ReadFromSslStreamAsync(outputBuffer, FallbackReadBufferSize); } @@ -514,6 +599,7 @@ public override Task WriteAsync(IChannelHandlerContext context, object message) { return TaskEx.FromException(new UnsupportedMessageTypeException(message, typeof(IByteBuffer))); } + return this.pendingUnencryptedWrites.Add(message); } @@ -569,6 +655,7 @@ void Wrap(IChannelHandlerContext context) buffer.Release(); } } + buf.ReadBytes(this.sslStream, buf.ReadableBytes); // this leads to FinishWrap being called 0+ times buf.Release(); @@ -627,7 +714,6 @@ void HandleFailure(Exception cause) { // Release all resources such as internal buffers that SSLEngine // is managing. - this.mediationStream.Dispose(); try { @@ -664,323 +750,6 @@ void NotifyHandshakeFailure(Exception cause) this.CloseAsync(this.capturedContext); } } - - sealed class MediationStream : Stream - { - readonly TlsHandler owner; - byte[] input; - int inputStartOffset; - int inputOffset; - int inputLength; - TaskCompletionSource readCompletionSource; - ArraySegment sslOwnedBuffer; -#if NETSTANDARD2_0 - int readByteCount; -#else - SynchronousAsyncResult syncReadResult; - AsyncCallback readCallback; - TaskCompletionSource writeCompletion; - AsyncCallback writeCallback; -#endif - - public MediationStream(TlsHandler owner) - { - this.owner = owner; - } - - public int SourceReadableBytes => this.inputLength - this.inputOffset; - - public void SetSource(byte[] source, int offset) - { - this.input = source; - this.inputStartOffset = offset; - this.inputOffset = 0; - this.inputLength = 0; - } - - public void ResetSource() - { - this.input = null; - this.inputLength = 0; - } - - public void ExpandSource(int count) - { - Contract.Assert(this.input != null); - - this.inputLength += count; - - ArraySegment sslBuffer = this.sslOwnedBuffer; - if (sslBuffer.Array == null) - { - // there is no pending read operation - keep for future - return; - } - this.sslOwnedBuffer = default(ArraySegment); - -#if NETSTANDARD2_0 - this.readByteCount = this.ReadFromInput(sslBuffer.Array, sslBuffer.Offset, sslBuffer.Count); - // hack: this tricks SslStream's continuation to run synchronously instead of dispatching to TP. Remove once Begin/EndRead are available. - new Task( - ms => - { - var self = (MediationStream)ms; - TaskCompletionSource p = self.readCompletionSource; - self.readCompletionSource = null; - p.TrySetResult(self.readByteCount); - }, - this) - .RunSynchronously(TaskScheduler.Default); -#else - int read = this.ReadFromInput(sslBuffer.Array, sslBuffer.Offset, sslBuffer.Count); - - TaskCompletionSource promise = this.readCompletionSource; - this.readCompletionSource = null; - promise.TrySetResult(read); - - AsyncCallback callback = this.readCallback; - this.readCallback = null; - callback?.Invoke(promise.Task); -#endif - } - -#if NETSTANDARD2_0 - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (this.SourceReadableBytes > 0) - { - // we have the bytes available upfront - write out synchronously - int read = this.ReadFromInput(buffer, offset, count); - return Task.FromResult(read); - } - - Contract.Assert(this.sslOwnedBuffer.Array == null); - // take note of buffer - we will pass bytes there once available - this.sslOwnedBuffer = new ArraySegment(buffer, offset, count); - this.readCompletionSource = new TaskCompletionSource(); - return this.readCompletionSource.Task; - } -#else - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - if (this.SourceReadableBytes > 0) - { - // we have the bytes available upfront - write out synchronously - int read = this.ReadFromInput(buffer, offset, count); - var res = this.PrepareSyncReadResult(read, state); - callback?.Invoke(res); - return res; - } - - Contract.Assert(this.sslOwnedBuffer.Array == null); - // take note of buffer - we will pass bytes there once available - this.sslOwnedBuffer = new ArraySegment(buffer, offset, count); - this.readCompletionSource = new TaskCompletionSource(state); - this.readCallback = callback; - return this.readCompletionSource.Task; - } - - public override int EndRead(IAsyncResult asyncResult) - { - SynchronousAsyncResult syncResult = this.syncReadResult; - if (ReferenceEquals(asyncResult, syncResult)) - { - return syncResult.Result; - } - - Debug.Assert(this.readCompletionSource == null || this.readCompletionSource.Task == asyncResult); - Contract.Assert(!((Task)asyncResult).IsCanceled); - - try - { - return ((Task)asyncResult).Result; - } - catch (AggregateException ex) - { - ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); - throw; // unreachable - } - } - - IAsyncResult PrepareSyncReadResult(int readBytes, object state) - { - // it is safe to reuse sync result object as it can't lead to leak (no way to attach to it via handle) - SynchronousAsyncResult result = this.syncReadResult ?? (this.syncReadResult = new SynchronousAsyncResult()); - result.Result = readBytes; - result.AsyncState = state; - return result; - } -#endif - - public override void Write(byte[] buffer, int offset, int count) => this.owner.FinishWrap(buffer, offset, count); - - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - => this.owner.FinishWrapNonAppDataAsync(buffer, offset, count); - -#if !NETSTANDARD2_0 - static readonly Action WriteCompleteCallback = HandleChannelWriteComplete; - - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - Task task = this.WriteAsync(buffer, offset, count); - switch (task.Status) - { - case TaskStatus.RanToCompletion: - // write+flush completed synchronously (and successfully) - var result = new SynchronousAsyncResult(); - result.AsyncState = state; - callback?.Invoke(result); - return result; - default: - if (callback != null || state != task.AsyncState) - { - Contract.Assert(this.writeCompletion == null); - this.writeCallback = callback; - var tcs = new TaskCompletionSource(state); - this.writeCompletion = tcs; - task.ContinueWith(WriteCompleteCallback, this, TaskContinuationOptions.ExecuteSynchronously); - return tcs.Task; - } - else - { - return task; - } - } - } - - static void HandleChannelWriteComplete(Task writeTask, object state) - { - var self = (MediationStream)state; - - AsyncCallback callback = self.writeCallback; - self.writeCallback = null; - - var promise = self.writeCompletion; - self.writeCompletion = null; - - switch (writeTask.Status) - { - case TaskStatus.RanToCompletion: - promise.TryComplete(); - break; - case TaskStatus.Canceled: - promise.TrySetCanceled(); - break; - case TaskStatus.Faulted: - promise.TrySetException(writeTask.Exception); - break; - default: - throw new ArgumentOutOfRangeException("Unexpected task status: " + writeTask.Status); - } - - callback?.Invoke(promise.Task); - } - - public override void EndWrite(IAsyncResult asyncResult) - { - if (asyncResult is SynchronousAsyncResult) - { - return; - } - - try - { - ((Task)asyncResult).Wait(); - } - catch (AggregateException ex) - { - ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); - throw; - } - } -#endif - - int ReadFromInput(byte[] destination, int destinationOffset, int destinationCapacity) - { - Contract.Assert(destination != null); - - byte[] source = this.input; - int readableBytes = this.SourceReadableBytes; - int length = Math.Min(readableBytes, destinationCapacity); - Buffer.BlockCopy(source, this.inputStartOffset + this.inputOffset, destination, destinationOffset, length); - this.inputOffset += length; - return length; - } - - public override void Flush() - { - // NOOP: called on SslStream.Close - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - TaskCompletionSource p = this.readCompletionSource; - if (p != null) - { - this.readCompletionSource = null; - p.TrySetResult(0); - } - } - } - -#region plumbing - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => true; - - public override long Length - { - get { throw new NotSupportedException(); } - } - - public override long Position - { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } - } - -#endregion - -#region sync result - sealed class SynchronousAsyncResult : IAsyncResult - { - public T Result { get; set; } - - public bool IsCompleted => true; - - public WaitHandle AsyncWaitHandle - { - get { throw new InvalidOperationException("Cannot wait on a synchronous result."); } - } - - public object AsyncState { get; set; } - - public bool CompletedSynchronously => true; - } - -#endregion - } } [Flags] @@ -1001,4 +770,4 @@ static class TlsHandlerStateExtensions public static bool HasAny(this TlsHandlerState value, TlsHandlerState testValue) => (value & testValue) != 0; } -} \ No newline at end of file +} diff --git a/src/DotNetty.Handlers/Tls/TlsUtils.cs b/src/DotNetty.Handlers/Tls/TlsUtils.cs index c271c823e..bfc8d802b 100644 --- a/src/DotNetty.Handlers/Tls/TlsUtils.cs +++ b/src/DotNetty.Handlers/Tls/TlsUtils.cs @@ -53,12 +53,13 @@ static class TlsUtils /// The length of the encrypted packet that is included in the buffer. This will /// return -1 if the given is not encrypted at all. /// - public static int GetEncryptedPacketLength(IByteBuffer buffer, int offset) + public static int GetEncryptedPacketLength(IByteBuffer buffer, int offset, out byte contentType) { int packetLength = 0; // SSLv3 or TLS - Check ContentType - switch (buffer.GetByte(offset)) + contentType = buffer.GetByte(offset); + switch (contentType) { case SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC: case SSL_CONTENT_TYPE_ALERT: @@ -90,6 +91,11 @@ public static int GetEncryptedPacketLength(IByteBuffer buffer, int offset) return packetLength; } + public static int GetEncryptedPacketLength(IByteBuffer buffer, int offset) + { + return GetEncryptedPacketLength(buffer, offset, out _); + } + public static void NotifyHandshakeFailure(IChannelHandlerContext ctx, Exception cause) { // We have may haven written some parts of data before an exception was thrown so ensure we always flush. @@ -98,5 +104,23 @@ public static void NotifyHandshakeFailure(IChannelHandlerContext ctx, Exception ctx.FireUserEventTriggered(new TlsHandshakeCompletionEvent(cause)); ctx.CloseAsync(); } + + public static string FormatContentType(byte contentType) + { + switch (contentType) + { + case SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC: + return nameof(SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC); + case SSL_CONTENT_TYPE_ALERT: + return nameof(SSL_CONTENT_TYPE_ALERT); + case SSL_CONTENT_TYPE_HANDSHAKE: + return nameof(SSL_CONTENT_TYPE_HANDSHAKE); + case SSL_CONTENT_TYPE_APPLICATION_DATA: + return nameof(SSL_CONTENT_TYPE_APPLICATION_DATA); + default: + // SSLv2 or bad data + return "non-ssl"; + } + } } } \ No newline at end of file diff --git a/src/DotNetty.Transport.Libuv/DotNetty.Transport.Libuv.csproj b/src/DotNetty.Transport.Libuv/DotNetty.Transport.Libuv.csproj index b9abbe773..cde6ed7bb 100644 --- a/src/DotNetty.Transport.Libuv/DotNetty.Transport.Libuv.csproj +++ b/src/DotNetty.Transport.Libuv/DotNetty.Transport.Libuv.csproj @@ -1,15 +1,16 @@ - + + - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Transport.Libuv + DotNetty: libuv transport model Experimental en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 True false @@ -18,21 +19,31 @@ ../../DotNetty.snk true true - socket;tcp;udp;protocol;netty;dotnetty;network + 2.0.3 + + Libuv transport model in DotNetty + Nethermind.DotNetty.Transport.Libuv + socket tcp udp protocol netty dotnetty network + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/DotNetty.Transport.Libuv/LoopExecutor.cs b/src/DotNetty.Transport.Libuv/LoopExecutor.cs index 69ffa8f26..51585e41b 100644 --- a/src/DotNetty.Transport.Libuv/LoopExecutor.cs +++ b/src/DotNetty.Transport.Libuv/LoopExecutor.cs @@ -19,6 +19,7 @@ namespace DotNetty.Transport.Libuv using DotNetty.Common; using DotNetty.Transport.Libuv.Native; using Timer = Native.Timer; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; class LoopExecutor : AbstractScheduledEventExecutor { diff --git a/src/DotNetty.Transport.Libuv/Native/NativeMethods.cs b/src/DotNetty.Transport.Libuv/Native/NativeMethods.cs index 73d437b30..8ff9b1700 100644 --- a/src/DotNetty.Transport.Libuv/Native/NativeMethods.cs +++ b/src/DotNetty.Transport.Libuv/Native/NativeMethods.cs @@ -331,7 +331,7 @@ internal static IPEndPoint TcpGetSocketName(IntPtr handle) { Debug.Assert(handle != IntPtr.Zero); -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER int namelen = Marshal.SizeOf(); #else int namelen = Marshal.SizeOf(typeof(sockaddr)); @@ -344,7 +344,7 @@ internal static IPEndPoint TcpGetPeerName(IntPtr handle) { Debug.Assert(handle != IntPtr.Zero); -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER int namelen = Marshal.SizeOf(); #else int namelen = Marshal.SizeOf(typeof(sockaddr)); @@ -354,7 +354,7 @@ internal static IPEndPoint TcpGetPeerName(IntPtr handle) return sockaddr.GetIPEndPoint(); } -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER internal static IntPtr Allocate(int size) => Marshal.AllocCoTaskMem(size); internal static void FreeMemory(IntPtr ptr) => Marshal.FreeCoTaskMem(ptr); diff --git a/src/DotNetty.Transport.Libuv/Native/WindowsApi.cs b/src/DotNetty.Transport.Libuv/Native/WindowsApi.cs index 12b4307b9..0db29ea23 100644 --- a/src/DotNetty.Transport.Libuv/Native/WindowsApi.cs +++ b/src/DotNetty.Transport.Libuv/Native/WindowsApi.cs @@ -41,7 +41,7 @@ public void DetachFromIOCP(NativeHandle handle) IntPtr socket = IntPtr.Zero; NativeMethods.uv_fileno(handle.Handle, ref socket); -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER uint len = (uint)Marshal.SizeOf(); #else uint len = (uint)Marshal.SizeOf(typeof(FILE_COMPLETION_INFORMATION)); diff --git a/src/DotNetty.Transport.Libuv/Native/WriteRequest.cs b/src/DotNetty.Transport.Libuv/Native/WriteRequest.cs index 169e68d65..975b66532 100644 --- a/src/DotNetty.Transport.Libuv/Native/WriteRequest.cs +++ b/src/DotNetty.Transport.Libuv/Native/WriteRequest.cs @@ -26,7 +26,7 @@ sealed class WriteRequest : NativeRequest, ChannelOutboundBuffer.IMessageProcess static WriteRequest() { -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER BufferSize = Marshal.SizeOf(); #else BufferSize = Marshal.SizeOf(typeof(uv_buf_t)); diff --git a/src/DotNetty.Transport.Libuv/NativeChannel.cs b/src/DotNetty.Transport.Libuv/NativeChannel.cs index b77764a1a..a75ad7e9a 100644 --- a/src/DotNetty.Transport.Libuv/NativeChannel.cs +++ b/src/DotNetty.Transport.Libuv/NativeChannel.cs @@ -13,6 +13,7 @@ namespace DotNetty.Transport.Libuv using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; using DotNetty.Transport.Libuv.Native; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public abstract class NativeChannel : AbstractChannel { diff --git a/src/DotNetty.Transport.Libuv/WorkerEventLoop.cs b/src/DotNetty.Transport.Libuv/WorkerEventLoop.cs index 214818460..e891ba887 100644 --- a/src/DotNetty.Transport.Libuv/WorkerEventLoop.cs +++ b/src/DotNetty.Transport.Libuv/WorkerEventLoop.cs @@ -15,6 +15,7 @@ namespace DotNetty.Transport.Libuv using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; using DotNetty.Transport.Libuv.Native; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; sealed class WorkerEventLoop : LoopExecutor, IEventLoop { diff --git a/src/DotNetty.Transport/Bootstrapping/AbstractBootstrap.cs b/src/DotNetty.Transport/Bootstrapping/AbstractBootstrap.cs index 5dd26ddb1..7dad976a8 100644 --- a/src/DotNetty.Transport/Bootstrapping/AbstractBootstrap.cs +++ b/src/DotNetty.Transport/Bootstrapping/AbstractBootstrap.cs @@ -14,6 +14,7 @@ namespace DotNetty.Transport.Bootstrapping using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; /// /// This is a helper class that makes it easy to bootstrap an . It supports method- diff --git a/src/DotNetty.Transport/Bootstrapping/Bootstrap.cs b/src/DotNetty.Transport/Bootstrapping/Bootstrap.cs index 689afcbeb..f543a012f 100644 --- a/src/DotNetty.Transport/Bootstrapping/Bootstrap.cs +++ b/src/DotNetty.Transport/Bootstrapping/Bootstrap.cs @@ -13,6 +13,7 @@ namespace DotNetty.Transport.Bootstrapping using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; /// /// A that makes it easy to bootstrap an to use for clients. diff --git a/src/DotNetty.Transport/Channels/AbstractChannel.cs b/src/DotNetty.Transport/Channels/AbstractChannel.cs index 10f06f7a5..6e8f8aff0 100644 --- a/src/DotNetty.Transport/Channels/AbstractChannel.cs +++ b/src/DotNetty.Transport/Channels/AbstractChannel.cs @@ -13,6 +13,7 @@ namespace DotNetty.Transport.Channels using DotNetty.Common.Concurrency; using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public abstract class AbstractChannel : DefaultAttributeMap, IChannel { diff --git a/src/DotNetty.Transport/Channels/AbstractChannelHandlerContext.cs b/src/DotNetty.Transport/Channels/AbstractChannelHandlerContext.cs index 6401d5c1f..300875131 100644 --- a/src/DotNetty.Transport/Channels/AbstractChannelHandlerContext.cs +++ b/src/DotNetty.Transport/Channels/AbstractChannelHandlerContext.cs @@ -14,6 +14,7 @@ namespace DotNetty.Transport.Channels using DotNetty.Common.Concurrency; using DotNetty.Common.Internal; using DotNetty.Common.Utilities; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; abstract class AbstractChannelHandlerContext : IChannelHandlerContext, IResourceLeakHint { diff --git a/src/DotNetty.Transport/Channels/BatchingPendingWriteQueue.cs b/src/DotNetty.Transport/Channels/BatchingPendingWriteQueue.cs index 8d2fc2278..5896fbcf9 100644 --- a/src/DotNetty.Transport/Channels/BatchingPendingWriteQueue.cs +++ b/src/DotNetty.Transport/Channels/BatchingPendingWriteQueue.cs @@ -11,6 +11,7 @@ namespace DotNetty.Transport.Channels using DotNetty.Common.Concurrency; using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; /// /// A queue of write operations which are pending for later execution. It also updates the diff --git a/src/DotNetty.Transport/Channels/DefaultChannelPipeline.cs b/src/DotNetty.Transport/Channels/DefaultChannelPipeline.cs index 1390b7a01..1473e5cc0 100644 --- a/src/DotNetty.Transport/Channels/DefaultChannelPipeline.cs +++ b/src/DotNetty.Transport/Channels/DefaultChannelPipeline.cs @@ -16,6 +16,7 @@ namespace DotNetty.Transport.Channels using DotNetty.Common.Concurrency; using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; + using ReferenceEqualityComparer = DotNetty.Common.Utilities.ReferenceEqualityComparer; public class DefaultChannelPipeline : IChannelPipeline { diff --git a/src/DotNetty.Transport/Channels/Embedded/EmbeddedChannel.cs b/src/DotNetty.Transport/Channels/Embedded/EmbeddedChannel.cs index d3bd4ca07..543d0a6d9 100644 --- a/src/DotNetty.Transport/Channels/Embedded/EmbeddedChannel.cs +++ b/src/DotNetty.Transport/Channels/Embedded/EmbeddedChannel.cs @@ -14,7 +14,7 @@ namespace DotNetty.Transport.Channels.Embedded using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; - public class EmbeddedChannel : AbstractChannel + public class EmbeddedChannel : AbstractChannel, IEmbeddedChannel { static readonly EndPoint LOCAL_ADDRESS = new EmbeddedSocketAddress(); static readonly EndPoint REMOTE_ADDRESS = new EmbeddedSocketAddress(); diff --git a/src/DotNetty.Transport/Channels/Embedded/IEmbeddedChannel.cs b/src/DotNetty.Transport/Channels/Embedded/IEmbeddedChannel.cs new file mode 100644 index 000000000..5cd9917e4 --- /dev/null +++ b/src/DotNetty.Transport/Channels/Embedded/IEmbeddedChannel.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace DotNetty.Transport.Channels.Embedded +{ + public interface IEmbeddedChannel : IChannel + { + bool WriteInbound(params object[] msgs); + + bool WriteOutbound(params object[] msgs); + + T ReadInbound(); + + T ReadOutbound(); + + bool Finish(); + } +} \ No newline at end of file diff --git a/src/DotNetty.Transport/Channels/Embedded/SingleThreadedEmbeddedChannel.cs b/src/DotNetty.Transport/Channels/Embedded/SingleThreadedEmbeddedChannel.cs new file mode 100644 index 000000000..b3a51d642 --- /dev/null +++ b/src/DotNetty.Transport/Channels/Embedded/SingleThreadedEmbeddedChannel.cs @@ -0,0 +1,468 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace DotNetty.Transport.Channels.Embedded +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Diagnostics.Contracts; + using System.Net; + using System.Runtime.ExceptionServices; + using System.Threading.Tasks; + using DotNetty.Common; + using DotNetty.Common.Internal.Logging; + using DotNetty.Common.Utilities; + + public class SingleThreadedEmbeddedChannel : AbstractChannel, IEmbeddedChannel + { + static readonly EndPoint LOCAL_ADDRESS = new EmbeddedSocketAddress(); + static readonly EndPoint REMOTE_ADDRESS = new EmbeddedSocketAddress(); + + enum State + { + Open, + Active, + Closed + }; + + static readonly IChannelHandler[] EMPTY_HANDLERS = new IChannelHandler[0]; + + static readonly IInternalLogger logger = InternalLoggerFactory.GetInstance(); + + static readonly ChannelMetadata METADATA_NO_DISCONNECT = new ChannelMetadata(false); + static readonly ChannelMetadata METADATA_DISCONNECT = new ChannelMetadata(true); + + readonly IEventLoop loop = new SingleThreadEventLoop(); + + Queue inboundMessages; + Queue outboundMessages; + Exception lastException; + State state; + + /// + /// Create a new instance with an empty pipeline. + /// + public SingleThreadedEmbeddedChannel(IEventLoop eventLoop = null) + : this(EmbeddedChannelId.Instance, eventLoop, EMPTY_HANDLERS) + { + } + + /// + /// Create a new instance with an empty pipeline with the specified . + /// + /// The of this channel. + public SingleThreadedEmbeddedChannel(IChannelId channelId, IEventLoop eventLoop = null) + : this(channelId, eventLoop, EMPTY_HANDLERS) + { + } + + /// + /// Create a new instance with the pipeline initialized with the specified handlers. + /// + /// + /// The s that will be added to the + /// + public SingleThreadedEmbeddedChannel(IEventLoop eventLoop = null, params IChannelHandler[] handlers) + : this(EmbeddedChannelId.Instance, eventLoop, handlers) + { + } + + public SingleThreadedEmbeddedChannel(IChannelId id, IEventLoop eventLoop = null, params IChannelHandler[] handlers) + : this(id, false, eventLoop, handlers) + { + } + + /// Create a new instance with the pipeline initialized with the specified handlers. + /// The of this channel. + /// + /// false if this will delegate + /// to , true otherwise. + /// + /// + /// The s that will be added to the + /// + public SingleThreadedEmbeddedChannel(IChannelId id, bool hasDisconnect, IEventLoop eventLoop = null, params IChannelHandler[] handlers) + : this(id, hasDisconnect, true, eventLoop, handlers) + { } + + public SingleThreadedEmbeddedChannel(IChannelId id, bool hasDisconnect, bool register, IEventLoop eventLoop = null, params IChannelHandler[] handlers) + : base(null, id) + { + if (eventLoop != null) + { + this.loop = eventLoop; + } + + this.Metadata = GetMetadata(hasDisconnect); + this.Configuration = new DefaultChannelConfiguration(this); + this.Setup(register, handlers); + } + + public SingleThreadedEmbeddedChannel(IChannelId id, bool hasDisconnect, IChannelConfiguration config, IEventLoop eventLoop = null, + params IChannelHandler[] handlers) + : base(null, id) + { + Contract.Requires(config != null); + + if (eventLoop != null) + { + this.loop = eventLoop; + } + this.Metadata = GetMetadata(hasDisconnect); + this.Configuration = config; + this.Setup(true, handlers); + } + + static ChannelMetadata GetMetadata(bool hasDisconnect) => hasDisconnect ? METADATA_DISCONNECT : METADATA_NO_DISCONNECT; + + void Setup(bool register, params IChannelHandler[] handlers) + { + Contract.Requires(handlers != null); + + IChannelPipeline p = this.Pipeline; + p.AddLast(new ActionChannelInitializer(channel => + { + IChannelPipeline pipeline = channel.Pipeline; + foreach (IChannelHandler h in handlers) + { + if (h == null) + { + break; + + } + pipeline.AddLast(h); + } + })); + + if (register) + { + Task future = this.loop.RegisterAsync(this); + future.GetAwaiter().GetResult(); + Debug.Assert(future.IsCompleted); + } + } + + public void Register() + { + Task future = this.loop.RegisterAsync(this); + // Debug.Assert(future.IsCompleted); + this.Pipeline.AddLast(new LastInboundHandler(this.InboundMessages, this.RecordException)); + future.GetAwaiter().GetResult(); + } + + protected sealed override DefaultChannelPipeline NewChannelPipeline() => new SingleThreadedEmbeddedChannelPipeline(this); + + public override ChannelMetadata Metadata { get; } + + public override IChannelConfiguration Configuration { get; } + + /// + /// Returns the which holds all of the s that + /// were received by this . + /// + public Queue InboundMessages => this.inboundMessages ?? (this.inboundMessages = new Queue()); + + /// + /// Returns the which holds all of the s that + /// were written by this . + /// + public Queue OutboundMessages => this.outboundMessages ?? (this.outboundMessages = new Queue()); + + /// + /// Return received data from this . + /// + public T ReadInbound() => (T)Poll(this.inboundMessages); + + /// + /// Read data from the outbound. This may return null if nothing is readable. + /// + public T ReadOutbound() => (T)Poll(this.outboundMessages); + + protected override EndPoint LocalAddressInternal => this.Active ? LOCAL_ADDRESS : null; + + protected override EndPoint RemoteAddressInternal => this.Active ? REMOTE_ADDRESS : null; + + protected override IChannelUnsafe NewUnsafe() => new DefaultUnsafe(this); + + protected override bool IsCompatible(IEventLoop eventLoop) => true; + + protected override void DoBind(EndPoint localAddress) + { + //NOOP + } + + protected override void DoRegister() => this.state = State.Active; + + protected override void DoDisconnect() => this.DoClose(); + + protected override void DoClose() => this.state = State.Closed; + + protected override void DoBeginRead() + { + //NOOP + } + + protected override void DoWrite(ChannelOutboundBuffer input) + { + for (;;) + { + object msg = input.Current; + if (msg == null) + { + break; + } + + ReferenceCountUtil.Retain(msg); + this.OutboundMessages.Enqueue(msg); + input.Remove(); + } + } + + public override bool Open => this.state != State.Closed; + + public override bool Active => this.state == State.Active; + + /// + /// Write messages to the inbound of this + /// + /// The messages to be written. + /// true if the write operation did add something to the inbound buffer + public bool WriteInbound(params object[] msgs) + { + this.EnsureOpen(); + if (msgs.Length == 0) + { + return IsNotEmpty(this.inboundMessages); + } + + IChannelPipeline p = this.Pipeline; + foreach (object m in msgs) + { + p.FireChannelRead(m); + } + p.FireChannelReadComplete(); + this.CheckException(); + return IsNotEmpty(this.inboundMessages); + } + + /// + /// Write messages to the outbound of this . + /// + /// The messages to be written. + /// true if the write operation did add something to the inbound buffer + public bool WriteOutbound(params object[] msgs) + { + this.EnsureOpen(); + if (msgs.Length == 0) + { + return IsNotEmpty(this.outboundMessages); + } + + ThreadLocalObjectList futures = ThreadLocalObjectList.NewInstance(msgs.Length); + + foreach (object m in msgs) + { + if (m == null) + { + break; + } + futures.Add(this.WriteAsync(m)); + } + this.Flush(); + + int size = futures.Count; + for (int i = 0; i < size; i++) + { + var future = (Task)futures[i]; + if (future.IsCompleted) + { + this.RecordException(future); + } + else + { + // The write may be delayed to run later by runPendingTasks() + future.ContinueWith(t => this.RecordException(t)); + } + } + futures.Return(); + + this.CheckException(); + return IsNotEmpty(this.outboundMessages); + } + + void RecordException(Task future) + { + switch (future.Status) + { + case TaskStatus.Canceled: + case TaskStatus.Faulted: + this.RecordException(future.Exception); + break; + default: + break; + } + } + + void RecordException(Exception cause) + { + if (this.lastException == null) + { + this.lastException = cause; + } + else + { + logger.Warn("More than one exception was raised. " + "Will report only the first one and log others.", cause); + } + } + + /// + /// Mark this as finished. Any further try to write data to it will fail. + /// + /// bufferReadable returns true + public bool Finish() => this.Finish(false); + + /// + /// Marks this as finished and releases all pending message in the inbound and outbound + /// buffer. Any futher try to write data to it will fail. + /// + /// true if any of the used buffers has something left to read, otherwise false. + public bool FinishAndReleaseAll() => this.Finish(true); + + /// + /// Marks this as finished. Any futher attempt to write data to it will fail. + /// + /// If true, all pending messages in the inbound and outbound buffer are released. + /// true if any of the used buffers has something left to read, otherwise false. + bool Finish(bool releaseAll) + { + this.CloseSafe(); + try + { + this.CheckException(); + return IsNotEmpty(this.inboundMessages) || IsNotEmpty(this.outboundMessages); + } + finally + { + if (releaseAll) + { + ReleaseAll(this.inboundMessages); + ReleaseAll(this.outboundMessages); + } + } + } + + /// + /// Releases all buffered inbound messages. + /// + /// true if any were in the inbound buffer, otherwise false. + public bool ReleaseInbound() => ReleaseAll(this.inboundMessages); + + /// + /// Releases all buffered outbound messages. + /// + /// true if any were in the outbound buffer, otherwise false. + public bool ReleaseOutbound() => ReleaseAll(this.outboundMessages); + + static bool ReleaseAll(Queue queue) + { + if (queue != null && queue.Count > 0) + { + for (;;) + { + if (queue.Count == 0) + { + break; + } + object msg = queue.Dequeue(); + ReferenceCountUtil.Release(msg); + } + return true; + } + return false; + } + + public override Task CloseAsync() + { + // We need to call RunPendingTasks() before calling super.CloseAsync() as there may be something in the queue + // that needs to be run before the actual close takes place. + Task future = base.CloseAsync(); + return future; + } + + public override Task DisconnectAsync() + { + Task future = base.DisconnectAsync(); + return future; + } + + static bool IsNotEmpty(Queue queue) => queue != null && queue.Count > 0; + + /// + /// Check to see if there was any and rethrow if so. + /// + public void CheckException() + { + Exception e = this.lastException; + if (e == null) + { + return; + } + + this.lastException = null; + ExceptionDispatchInfo.Capture(e).Throw(); + } + + /// + /// Ensure the is open and if not throw an exception. + /// + protected void EnsureOpen() + { + if (!this.Open) + { + this.RecordException(new ClosedChannelException()); + this.CheckException(); + } + } + + static object Poll(Queue queue) => IsNotEmpty(queue) ? queue.Dequeue() : null; + + class DefaultUnsafe : AbstractUnsafe + { + public DefaultUnsafe(AbstractChannel channel) + : base(channel) + { + } + + public override Task ConnectAsync(EndPoint remoteAddress, EndPoint localAddress) => TaskEx.Completed; + } + + internal sealed class LastInboundHandler : ChannelHandlerAdapter + { + readonly Queue inboundMessages; + readonly Action recordException; + + public LastInboundHandler(Queue inboundMessages, Action recordException) + { + this.inboundMessages = inboundMessages; + this.recordException = recordException; + } + + public override void ChannelRead(IChannelHandlerContext context, object message) => this.inboundMessages.Enqueue(message); + + public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) => this.recordException(exception); + } + + sealed class SingleThreadedEmbeddedChannelPipeline : DefaultChannelPipeline + { + public SingleThreadedEmbeddedChannelPipeline(SingleThreadedEmbeddedChannel channel) + : base(channel) + { + } + + protected override void OnUnhandledInboundException(Exception cause) => ((SingleThreadedEmbeddedChannel)this.Channel).RecordException(cause); + + protected override void OnUnhandledInboundMessage(object msg) => ((SingleThreadedEmbeddedChannel)this.Channel).InboundMessages.Enqueue(msg); + } + } +} \ No newline at end of file diff --git a/src/DotNetty.Transport/Channels/Local/LocalChannel.cs b/src/DotNetty.Transport/Channels/Local/LocalChannel.cs index ac466d56c..3c1decd26 100644 --- a/src/DotNetty.Transport/Channels/Local/LocalChannel.cs +++ b/src/DotNetty.Transport/Channels/Local/LocalChannel.cs @@ -13,6 +13,7 @@ namespace DotNetty.Transport.Channels.Local using DotNetty.Common.Internal; using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; /// /// A for the local transport. diff --git a/src/DotNetty.Transport/Channels/PendingWriteQueue.cs b/src/DotNetty.Transport/Channels/PendingWriteQueue.cs index 80441bb22..568fc0f54 100644 --- a/src/DotNetty.Transport/Channels/PendingWriteQueue.cs +++ b/src/DotNetty.Transport/Channels/PendingWriteQueue.cs @@ -11,6 +11,7 @@ namespace DotNetty.Transport.Channels using DotNetty.Common.Concurrency; using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; /// /// A queue of write operations which are pending for later execution. It also updates the writability of the diff --git a/src/DotNetty.Transport/Channels/Sockets/AbstractSocketByteChannel.cs b/src/DotNetty.Transport/Channels/Sockets/AbstractSocketByteChannel.cs index e297a8b08..e42c32018 100644 --- a/src/DotNetty.Transport/Channels/Sockets/AbstractSocketByteChannel.cs +++ b/src/DotNetty.Transport/Channels/Sockets/AbstractSocketByteChannel.cs @@ -154,7 +154,7 @@ protected override void ScheduleSocketRead() { SocketChannelAsyncOperation operation = this.ReadOperation; bool pending; -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER pending = this.Socket.ReceiveAsync(operation); #else if (ExecutionContext.IsFlowSuppressed()) @@ -308,7 +308,7 @@ protected bool IncompleteWrite(bool scheduleAsync, SocketChannelAsyncOperation o this.SetState(StateFlags.WriteScheduled); bool pending; -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER pending = this.Socket.SendAsync(operation); #else if (ExecutionContext.IsFlowSuppressed()) diff --git a/src/DotNetty.Transport/Channels/Sockets/AbstractSocketChannel.cs b/src/DotNetty.Transport/Channels/Sockets/AbstractSocketChannel.cs index c5da05692..dab11317a 100644 --- a/src/DotNetty.Transport/Channels/Sockets/AbstractSocketChannel.cs +++ b/src/DotNetty.Transport/Channels/Sockets/AbstractSocketChannel.cs @@ -12,6 +12,7 @@ namespace DotNetty.Transport.Channels.Sockets using DotNetty.Common.Concurrency; using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public abstract class AbstractSocketChannel : AbstractChannel { diff --git a/src/DotNetty.Transport/Channels/Sockets/IDatagramChannel.cs b/src/DotNetty.Transport/Channels/Sockets/IDatagramChannel.cs index a8046c52f..7e5e43ea5 100644 --- a/src/DotNetty.Transport/Channels/Sockets/IDatagramChannel.cs +++ b/src/DotNetty.Transport/Channels/Sockets/IDatagramChannel.cs @@ -7,6 +7,7 @@ namespace DotNetty.Transport.Channels.Sockets using System.Net.NetworkInformation; using System.Threading.Tasks; using DotNetty.Common.Concurrency; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public interface IDatagramChannel : IChannel { diff --git a/src/DotNetty.Transport/Channels/Sockets/SocketDatagramChannel.cs b/src/DotNetty.Transport/Channels/Sockets/SocketDatagramChannel.cs index 4696a4ab2..82a49dc6c 100644 --- a/src/DotNetty.Transport/Channels/Sockets/SocketDatagramChannel.cs +++ b/src/DotNetty.Transport/Channels/Sockets/SocketDatagramChannel.cs @@ -16,6 +16,7 @@ namespace DotNetty.Transport.Channels.Sockets using DotNetty.Common.Concurrency; using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public class SocketDatagramChannel : AbstractSocketMessageChannel, IDatagramChannel { @@ -115,7 +116,7 @@ protected override void ScheduleSocketRead() operation.SetBuffer(bytes.Array, bytes.Offset, bytes.Count); bool pending; -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER pending = this.Socket.ReceiveFromAsync(operation); #else if (ExecutionContext.IsFlowSuppressed()) diff --git a/src/DotNetty.Transport/Channels/Sockets/TcpSocketChannel.cs b/src/DotNetty.Transport/Channels/Sockets/TcpSocketChannel.cs index 4e6b2fef1..8d7b843d5 100644 --- a/src/DotNetty.Transport/Channels/Sockets/TcpSocketChannel.cs +++ b/src/DotNetty.Transport/Channels/Sockets/TcpSocketChannel.cs @@ -10,6 +10,7 @@ namespace DotNetty.Transport.Channels.Sockets using System.Threading.Tasks; using DotNetty.Buffers; using DotNetty.Common.Concurrency; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; /// /// which uses Socket-based implementation. diff --git a/src/DotNetty.Transport/Channels/Util.cs b/src/DotNetty.Transport/Channels/Util.cs index 105860e87..0b7849c80 100644 --- a/src/DotNetty.Transport/Channels/Util.cs +++ b/src/DotNetty.Transport/Channels/Util.cs @@ -7,6 +7,7 @@ namespace DotNetty.Transport.Channels using System.Threading.Tasks; using DotNetty.Common.Concurrency; using DotNetty.Common.Internal.Logging; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; static class Util { diff --git a/src/DotNetty.Transport/DotNetty.Transport.csproj b/src/DotNetty.Transport/DotNetty.Transport.csproj index 6530c18db..d98489b77 100644 --- a/src/DotNetty.Transport/DotNetty.Transport.csproj +++ b/src/DotNetty.Transport/DotNetty.Transport.csproj @@ -1,15 +1,15 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5.0;net6.0 true - DotNetty.Transport + DotNetty: transport model en-US - 0.7.2 - Microsoft + $(NoWarn);CS1591 false true @@ -17,17 +17,31 @@ ../../DotNetty.snk true true - socket;tcp;udp;protocol;netty;dotnetty;network + 2.0.3 + + + Transport model in DotNetty + Nethermind.DotNetty.Transport + socket tcp udp protocol netty dotnetty network + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/shared/SharedAssemblyInfo.cs b/src/shared/SharedAssemblyInfo.cs index ed949d621..89be3dacd 100644 --- a/src/shared/SharedAssemblyInfo.cs +++ b/src/shared/SharedAssemblyInfo.cs @@ -5,9 +5,8 @@ //------------------------------------------------------------------------------ using System.Reflection; -[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyCompany("Nethermind, Microsoft")] [assembly: AssemblyProduct("DotNetty")] -[assembly: AssemblyVersion("0.7.2")] -[assembly: AssemblyFileVersion("0.7.2")] -[assembly: AssemblyCopyright("(c) Microsoft 2015 - 2021")] - +[assembly: AssemblyVersion("1.0.0")] +[assembly: AssemblyFileVersion("1.0.0")] +[assembly: AssemblyCopyright("Demerzel Solutions Limited, Microsoft Corporation")] diff --git a/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj b/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj index 94e38d7a6..23bf7ad9c 100644 --- a/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj +++ b/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Codecs.Http.Tests/DotNetty.Codecs.Http.Tests.csproj b/test/DotNetty.Codecs.Http.Tests/DotNetty.Codecs.Http.Tests.csproj index 82dcbc4d9..09e3e7bd0 100644 --- a/test/DotNetty.Codecs.Http.Tests/DotNetty.Codecs.Http.Tests.csproj +++ b/test/DotNetty.Codecs.Http.Tests/DotNetty.Codecs.Http.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Codecs.Http.Tests/HttpClientCodecTest.cs b/test/DotNetty.Codecs.Http.Tests/HttpClientCodecTest.cs index 09876ee6c..0ca42abc0 100644 --- a/test/DotNetty.Codecs.Http.Tests/HttpClientCodecTest.cs +++ b/test/DotNetty.Codecs.Http.Tests/HttpClientCodecTest.cs @@ -17,6 +17,7 @@ namespace DotNetty.Codecs.Http.Tests using Xunit; using HttpVersion = DotNetty.Codecs.Http.HttpVersion; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public sealed class HttpClientCodecTest { diff --git a/test/DotNetty.Codecs.Http.Tests/HttpServerUpgradeHandlerTest.cs b/test/DotNetty.Codecs.Http.Tests/HttpServerUpgradeHandlerTest.cs index cb8f3a6a2..9143b693f 100644 --- a/test/DotNetty.Codecs.Http.Tests/HttpServerUpgradeHandlerTest.cs +++ b/test/DotNetty.Codecs.Http.Tests/HttpServerUpgradeHandlerTest.cs @@ -13,6 +13,7 @@ namespace DotNetty.Codecs.Http.Tests using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Embedded; using Xunit; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public class HttpServerUpgradeHandlerTest { diff --git a/test/DotNetty.Codecs.Http.Tests/WebSockets/WebSocketHandshakeHandOverTest.cs b/test/DotNetty.Codecs.Http.Tests/WebSockets/WebSocketHandshakeHandOverTest.cs index cf527aad6..18a1f1731 100644 --- a/test/DotNetty.Codecs.Http.Tests/WebSockets/WebSocketHandshakeHandOverTest.cs +++ b/test/DotNetty.Codecs.Http.Tests/WebSockets/WebSocketHandshakeHandOverTest.cs @@ -12,6 +12,7 @@ namespace DotNetty.Codecs.Http.Tests.WebSockets using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Embedded; using Xunit; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public class WebSocketHandshakeHandOverTest { diff --git a/test/DotNetty.Codecs.Mqtt.Tests/DotNetty.Codecs.Mqtt.Tests.csproj b/test/DotNetty.Codecs.Mqtt.Tests/DotNetty.Codecs.Mqtt.Tests.csproj index f14e1e5e7..cb1dadae6 100644 --- a/test/DotNetty.Codecs.Mqtt.Tests/DotNetty.Codecs.Mqtt.Tests.csproj +++ b/test/DotNetty.Codecs.Mqtt.Tests/DotNetty.Codecs.Mqtt.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Codecs.Protobuf.Tests/DotNetty.Codecs.Protobuf.Tests.csproj b/test/DotNetty.Codecs.Protobuf.Tests/DotNetty.Codecs.Protobuf.Tests.csproj index 4a32e857b..599180c0f 100644 --- a/test/DotNetty.Codecs.Protobuf.Tests/DotNetty.Codecs.Protobuf.Tests.csproj +++ b/test/DotNetty.Codecs.Protobuf.Tests/DotNetty.Codecs.Protobuf.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Codecs.ProtocolBuffers.Tests/DotNetty.Codecs.ProtocolBuffers.Tests.csproj b/test/DotNetty.Codecs.ProtocolBuffers.Tests/DotNetty.Codecs.ProtocolBuffers.Tests.csproj index 58d563787..a60ee8be7 100644 --- a/test/DotNetty.Codecs.ProtocolBuffers.Tests/DotNetty.Codecs.ProtocolBuffers.Tests.csproj +++ b/test/DotNetty.Codecs.ProtocolBuffers.Tests/DotNetty.Codecs.ProtocolBuffers.Tests.csproj @@ -1,8 +1,10 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Codecs.Redis.Tests/DotNetty.Codecs.Redis.Tests.csproj b/test/DotNetty.Codecs.Redis.Tests/DotNetty.Codecs.Redis.Tests.csproj index f42b68f15..bab73a44b 100644 --- a/test/DotNetty.Codecs.Redis.Tests/DotNetty.Codecs.Redis.Tests.csproj +++ b/test/DotNetty.Codecs.Redis.Tests/DotNetty.Codecs.Redis.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Codecs.Tests/DotNetty.Codecs.Tests.csproj b/test/DotNetty.Codecs.Tests/DotNetty.Codecs.Tests.csproj index ac979f577..3e8b2cf69 100644 --- a/test/DotNetty.Codecs.Tests/DotNetty.Codecs.Tests.csproj +++ b/test/DotNetty.Codecs.Tests/DotNetty.Codecs.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Common.Tests/Concurrency/SingleThreadEventExecutorTests.cs b/test/DotNetty.Common.Tests/Concurrency/SingleThreadEventExecutorTests.cs index ae45ce3ec..e3f6812a7 100644 --- a/test/DotNetty.Common.Tests/Concurrency/SingleThreadEventExecutorTests.cs +++ b/test/DotNetty.Common.Tests/Concurrency/SingleThreadEventExecutorTests.cs @@ -13,6 +13,7 @@ namespace DotNetty.Common.Tests.Concurrency using DotNetty.Tests.Common; using Xunit; using Xunit.Abstractions; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public class SingleThreadEventExecutorTests : TestBase { diff --git a/test/DotNetty.Common.Tests/DotNetty.Common.Tests.csproj b/test/DotNetty.Common.Tests/DotNetty.Common.Tests.csproj index b6cef6261..e6de15885 100644 --- a/test/DotNetty.Common.Tests/DotNetty.Common.Tests.csproj +++ b/test/DotNetty.Common.Tests/DotNetty.Common.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Handlers.Tests/AsIsWriteStrategy.cs b/test/DotNetty.Handlers.Tests/AsIsWriteStrategy.cs index a2fd4cf66..a0477edd6 100644 --- a/test/DotNetty.Handlers.Tests/AsIsWriteStrategy.cs +++ b/test/DotNetty.Handlers.Tests/AsIsWriteStrategy.cs @@ -11,7 +11,7 @@ namespace DotNetty.Handlers.Tests class AsIsWriteStrategy : IWriteStrategy { - public Task WriteToChannelAsync(EmbeddedChannel channel, ArraySegment input) + public Task WriteToChannelAsync(IEmbeddedChannel channel, ArraySegment input) { channel.WriteInbound(Unpooled.WrappedBuffer(input.Array, input.Offset, input.Count)); return TaskEx.Completed; diff --git a/test/DotNetty.Handlers.Tests/BatchingWriteStrategy.cs b/test/DotNetty.Handlers.Tests/BatchingWriteStrategy.cs index 85211b392..fd7ebfc15 100644 --- a/test/DotNetty.Handlers.Tests/BatchingWriteStrategy.cs +++ b/test/DotNetty.Handlers.Tests/BatchingWriteStrategy.cs @@ -14,7 +14,7 @@ class BatchingWriteStrategy : IWriteStrategy readonly TimeSpan timeWindow; readonly bool forceSizing; IByteBuffer pendingBuffer; - EmbeddedChannel channel; + IEmbeddedChannel channel; public BatchingWriteStrategy(int maxBatchSize, TimeSpan timeWindow, bool forceSizing) { @@ -23,7 +23,7 @@ public BatchingWriteStrategy(int maxBatchSize, TimeSpan timeWindow, bool forceSi this.forceSizing = forceSizing; } - public async Task WriteToChannelAsync(EmbeddedChannel ch, ArraySegment input) + public async Task WriteToChannelAsync(IEmbeddedChannel ch, ArraySegment input) { this.channel = ch; diff --git a/test/DotNetty.Handlers.Tests/DotNetty.Handlers.Tests.csproj b/test/DotNetty.Handlers.Tests/DotNetty.Handlers.Tests.csproj index 0a67729c2..c394fdc74 100644 --- a/test/DotNetty.Handlers.Tests/DotNetty.Handlers.Tests.csproj +++ b/test/DotNetty.Handlers.Tests/DotNetty.Handlers.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Handlers.Tests/IWriteStrategy.cs b/test/DotNetty.Handlers.Tests/IWriteStrategy.cs index a6f183994..125062871 100644 --- a/test/DotNetty.Handlers.Tests/IWriteStrategy.cs +++ b/test/DotNetty.Handlers.Tests/IWriteStrategy.cs @@ -9,6 +9,6 @@ namespace DotNetty.Handlers.Tests public interface IWriteStrategy { - Task WriteToChannelAsync(EmbeddedChannel channel, ArraySegment input); + Task WriteToChannelAsync(IEmbeddedChannel channel, ArraySegment input); } } \ No newline at end of file diff --git a/test/DotNetty.Handlers.Tests/SniHandlerTest.cs b/test/DotNetty.Handlers.Tests/SniHandlerTest.cs index 0f4d09328..7bb1a03f4 100644 --- a/test/DotNetty.Handlers.Tests/SniHandlerTest.cs +++ b/test/DotNetty.Handlers.Tests/SniHandlerTest.cs @@ -72,13 +72,13 @@ public async Task TlsRead(int[] frameLengths, bool isClient, IWriteStrategy writ this.Output.WriteLine($"protocol: {protocol}"); this.Output.WriteLine($"targetHost: {targetHost}"); - var executor = new SingleThreadEventExecutor("test executor", TimeSpan.FromMilliseconds(10)); + var executor = new SingleThreadEventLoop("test executor", TimeSpan.FromMilliseconds(10)); try { var writeTasks = new List(); var pair = await SetupStreamAndChannelAsync(isClient, executor, writeStrategy, protocol, writeTasks, targetHost).WithTimeout(TimeSpan.FromSeconds(10)); - EmbeddedChannel ch = pair.Item1; + IEmbeddedChannel ch = pair.Item1; SslStream driverStream = pair.Item2; int randomSeed = Environment.TickCount; @@ -139,13 +139,13 @@ public async Task TlsWrite(int[] frameLengths, bool isClient, SslProtocols proto this.Output.WriteLine($"targetHost: {targetHost}"); var writeStrategy = new AsIsWriteStrategy(); - var executor = new SingleThreadEventExecutor("test executor", TimeSpan.FromMilliseconds(10)); + var executor = new SingleThreadEventLoop("test executor", TimeSpan.FromMilliseconds(10)); try { var writeTasks = new List(); var pair = await SetupStreamAndChannelAsync(isClient, executor, writeStrategy, protocol, writeTasks, targetHost); - EmbeddedChannel ch = pair.Item1; + IEmbeddedChannel ch = pair.Item1; SslStream driverStream = pair.Item2; int randomSeed = Environment.TickCount; @@ -189,7 +189,7 @@ await ReadOutboundAsync( } } - static async Task> SetupStreamAndChannelAsync(bool isClient, IEventExecutor executor, IWriteStrategy writeStrategy, SslProtocols protocol, List writeTasks, string targetHost) + static async Task> SetupStreamAndChannelAsync(bool isClient, IEventLoop executor, IWriteStrategy writeStrategy, SslProtocols protocol, List writeTasks, string targetHost) { IChannelHandler tlsHandler = isClient ? (IChannelHandler)new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => @@ -199,13 +199,19 @@ static async Task> SetupStreamAndChannelAsync( }), new ClientTlsSettings(protocol, false, new List(), targetHost)) : new SniHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ServerTlsSniSettings(CertificateSelector)); //var ch = new EmbeddedChannel(new LoggingHandler("BEFORE"), tlsHandler, new LoggingHandler("AFTER")); - var ch = new EmbeddedChannel(tlsHandler); - +#if NET5_0_OR_GREATER + IEmbeddedChannel ch = new SingleThreadedEmbeddedChannel(executor, tlsHandler); +#else + IEmbeddedChannel ch = new EmbeddedChannel(tlsHandler); +#endif if (!isClient) { // check if in the beginning snihandler exists in the pipeline, but not tls handler - Assert.NotNull(ch.Pipeline.Get()); - Assert.Null(ch.Pipeline.Get()); + await AssertEx.EventuallyAsync( + () => ch.Pipeline.Get() != null && ch.Pipeline.Get() == null, + TimeSpan.FromMilliseconds(10), + TimeSpan.FromSeconds(5) + ); } IByteBuffer readResultBuffer = Unpooled.Buffer(4 * 1024); diff --git a/test/DotNetty.Handlers.Tests/TlsHandlerTest.cs b/test/DotNetty.Handlers.Tests/TlsHandlerTest.cs index ba7748c59..34677e5ca 100644 --- a/test/DotNetty.Handlers.Tests/TlsHandlerTest.cs +++ b/test/DotNetty.Handlers.Tests/TlsHandlerTest.cs @@ -69,7 +69,6 @@ from protocol in protocols select new object[] { frameLengths, isClient, writeStrategyFactory(), protocol.Item1, protocol.Item2 }; } - [Theory] [MemberData(nameof(GetTlsReadTestData))] public async Task TlsRead(int[] frameLengths, bool isClient, IWriteStrategy writeStrategy, SslProtocols serverProtocol, SslProtocols clientProtocol) @@ -80,15 +79,15 @@ public async Task TlsRead(int[] frameLengths, bool isClient, IWriteStrategy writ this.Output.WriteLine($"serverProtocol: {serverProtocol}"); this.Output.WriteLine($"clientProtocol: {clientProtocol}"); - var executor = new SingleThreadEventExecutor("test executor", TimeSpan.FromMilliseconds(10)); + var executor = new SingleThreadEventLoop("test executor", TimeSpan.FromMilliseconds(10)); try { var writeTasks = new List(); var pair = await SetupStreamAndChannelAsync(isClient, executor, writeStrategy, serverProtocol, clientProtocol, writeTasks).WithTimeout(TimeSpan.FromSeconds(10)); - EmbeddedChannel ch = pair.Item1; + IEmbeddedChannel ch = pair.Item1; SslStream driverStream = pair.Item2; - + int randomSeed = Environment.TickCount; var random = new Random(randomSeed); IByteBuffer expectedBuffer = Unpooled.Buffer(16 * 1024); @@ -101,6 +100,7 @@ public async Task TlsRead(int[] frameLengths, bool isClient, IWriteStrategy writ } await Task.WhenAll(writeTasks).WithTimeout(TimeSpan.FromSeconds(5)); IByteBuffer finalReadBuffer = Unpooled.Buffer(16 * 1024); + await ReadOutboundAsync(async () => ch.ReadInbound(), expectedBuffer.ReadableBytes, finalReadBuffer, TestTimeout); bool isEqual = ByteBufferUtil.Equals(expectedBuffer, finalReadBuffer); if (!isEqual) @@ -160,15 +160,15 @@ public async Task TlsWrite(int[] frameLengths, bool isClient, SslProtocols serve var writeStrategy = new AsIsWriteStrategy(); this.Output.WriteLine($"writeStrategy: {writeStrategy}"); - var executor = new SingleThreadEventExecutor("test executor", TimeSpan.FromMilliseconds(10)); + var executor = new SingleThreadEventLoop("test executor", TimeSpan.FromMilliseconds(10)); try { var writeTasks = new List(); var pair = await SetupStreamAndChannelAsync(isClient, executor, writeStrategy, serverProtocol, clientProtocol, writeTasks); - EmbeddedChannel ch = pair.Item1; + IEmbeddedChannel ch = pair.Item1; SslStream driverStream = pair.Item2; - + int randomSeed = Environment.TickCount; var random = new Random(randomSeed); IByteBuffer expectedBuffer = Unpooled.Buffer(16 * 1024); @@ -182,7 +182,7 @@ public async Task TlsWrite(int[] frameLengths, bool isClient, SslProtocols serve return (object)Unpooled.WrappedBuffer(data); }).ToArray()); } - + IByteBuffer finalReadBuffer = Unpooled.Buffer(16 * 1024); var readBuffer = new byte[16 * 1024 * 10]; await ReadOutboundAsync( @@ -206,7 +206,7 @@ await ReadOutboundAsync( } } - static async Task> SetupStreamAndChannelAsync(bool isClient, IEventExecutor executor, IWriteStrategy writeStrategy, SslProtocols serverProtocol, SslProtocols clientProtocol, List writeTasks) + static async Task> SetupStreamAndChannelAsync(bool isClient, IEventLoop executor, IWriteStrategy writeStrategy, SslProtocols serverProtocol, SslProtocols clientProtocol, List writeTasks) { X509Certificate2 tlsCertificate = TestResourceHelper.GetTestCertificate(); string targetHost = tlsCertificate.GetNameInfo(X509NameType.DnsName, false); @@ -214,7 +214,12 @@ static async Task> SetupStreamAndChannelAsync( new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ClientTlsSettings(clientProtocol, false, new List(), targetHost)) : new TlsHandler(new ServerTlsSettings(tlsCertificate, false, false, serverProtocol)); //var ch = new EmbeddedChannel(new LoggingHandler("BEFORE"), tlsHandler, new LoggingHandler("AFTER")); - var ch = new EmbeddedChannel(tlsHandler); + +#if NET5_0_OR_GREATER + IEmbeddedChannel ch = new SingleThreadedEmbeddedChannel(executor, tlsHandler); +#else + IEmbeddedChannel ch = new EmbeddedChannel(tlsHandler); +#endif IByteBuffer readResultBuffer = Unpooled.Buffer(4 * 1024); Func, Task> readDataFunc = async output => @@ -228,21 +233,40 @@ static async Task> SetupStreamAndChannelAsync( if (readResultBuffer.ReadableBytes < output.Count) { if (ch.Active) - await ReadOutboundAsync(async () => ch.ReadOutbound(), output.Count - readResultBuffer.ReadableBytes, readResultBuffer, TestTimeout, readResultBuffer.ReadableBytes != 0 ? 0 : 1); + { + await ReadOutboundAsync( + async () => ch.ReadOutbound(), + output.Count - readResultBuffer.ReadableBytes, + readResultBuffer, + TestTimeout, + readResultBuffer.ReadableBytes != 0 ? 0 : 1 + ); + } } int read = Math.Min(output.Count, readResultBuffer.ReadableBytes); readResultBuffer.ReadBytes(output.Array, output.Offset, read); return read; }; - var mediationStream = new MediationStream(readDataFunc, input => - { - Task task = executor.SubmitAsync(() => writeStrategy.WriteToChannelAsync(ch, input)).Unwrap(); - writeTasks.Add(task); - return task; - }, () => - { - ch.CloseAsync(); - }); + var mediationStream = new MediationStream( + output => + { + Task task = executor.SubmitAsync( + () => readDataFunc(output) + ).Unwrap(); + return task; + }, + input => + { + Task task = executor.SubmitAsync( + () => writeStrategy.WriteToChannelAsync(ch, input) + ).Unwrap(); + writeTasks.Add(task); + return task; + }, + () => + { + ch.CloseAsync(); + }); var driverStream = new SslStream(mediationStream, true, (_1, _2, _3, _4) => true); if (isClient) diff --git a/test/DotNetty.Microbench/DotNetty.Microbench.csproj b/test/DotNetty.Microbench/DotNetty.Microbench.csproj index 8be7693c2..7f804cd78 100644 --- a/test/DotNetty.Microbench/DotNetty.Microbench.csproj +++ b/test/DotNetty.Microbench/DotNetty.Microbench.csproj @@ -2,10 +2,12 @@ Exe true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU win-x64 diff --git a/test/DotNetty.Tests.Common/DotNetty.Tests.Common.csproj b/test/DotNetty.Tests.Common/DotNetty.Tests.Common.csproj index 946f21561..8a28698f1 100644 --- a/test/DotNetty.Tests.Common/DotNetty.Tests.Common.csproj +++ b/test/DotNetty.Tests.Common/DotNetty.Tests.Common.csproj @@ -2,9 +2,11 @@ true false - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Tests.End2End/DotNetty.Tests.End2End.csproj b/test/DotNetty.Tests.End2End/DotNetty.Tests.End2End.csproj index c1a9f7130..17b5107c8 100644 --- a/test/DotNetty.Tests.End2End/DotNetty.Tests.End2End.csproj +++ b/test/DotNetty.Tests.End2End/DotNetty.Tests.End2End.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Tests.End2End/End2EndTests.cs b/test/DotNetty.Tests.End2End/End2EndTests.cs index 39473d46b..cffc672a7 100644 --- a/test/DotNetty.Tests.End2End/End2EndTests.cs +++ b/test/DotNetty.Tests.End2End/End2EndTests.cs @@ -23,6 +23,7 @@ namespace DotNetty.Tests.End2End using DotNetty.Transport.Channels.Sockets; using Xunit; using Xunit.Abstractions; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public class End2EndTests : TestBase { diff --git a/test/DotNetty.Transport.Libuv.Tests/BufReleaseTests.cs b/test/DotNetty.Transport.Libuv.Tests/BufReleaseTests.cs index e104f9f27..f9a05817b 100644 --- a/test/DotNetty.Transport.Libuv.Tests/BufReleaseTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/BufReleaseTests.cs @@ -13,6 +13,7 @@ namespace DotNetty.Transport.Libuv.Tests using Xunit; using static TestUtil; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; [Collection(LibuvTransport)] public sealed class BufReleaseTests : IDisposable diff --git a/test/DotNetty.Transport.Libuv.Tests/CompositeBufferGatheringWriteTests.cs b/test/DotNetty.Transport.Libuv.Tests/CompositeBufferGatheringWriteTests.cs index ab276ce4a..5bc8cd0af 100644 --- a/test/DotNetty.Transport.Libuv.Tests/CompositeBufferGatheringWriteTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/CompositeBufferGatheringWriteTests.cs @@ -14,6 +14,7 @@ namespace DotNetty.Transport.Libuv.Tests using Xunit; using static TestUtil; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; [Collection(LibuvTransport)] public sealed class CompositeBufferGatheringWriteTests : IDisposable diff --git a/test/DotNetty.Transport.Libuv.Tests/DetectPeerCloseWithoutReadTests.cs b/test/DotNetty.Transport.Libuv.Tests/DetectPeerCloseWithoutReadTests.cs index 3355c12b1..e24a985ec 100644 --- a/test/DotNetty.Transport.Libuv.Tests/DetectPeerCloseWithoutReadTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/DetectPeerCloseWithoutReadTests.cs @@ -14,6 +14,7 @@ namespace DotNetty.Transport.Libuv.Tests using Xunit; using static TestUtil; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; [Collection(LibuvTransport)] public sealed class DetectPeerCloseWithoutReadTests : IDisposable diff --git a/test/DotNetty.Transport.Libuv.Tests/DotNetty.Transport.Libuv.Tests.csproj b/test/DotNetty.Transport.Libuv.Tests/DotNetty.Transport.Libuv.Tests.csproj index 1bad6c058..1a3681396 100644 --- a/test/DotNetty.Transport.Libuv.Tests/DotNetty.Transport.Libuv.Tests.csproj +++ b/test/DotNetty.Transport.Libuv.Tests/DotNetty.Transport.Libuv.Tests.csproj @@ -1,10 +1,12 @@ true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU win-x64 diff --git a/test/DotNetty.Transport.Libuv.Tests/EchoTests.cs b/test/DotNetty.Transport.Libuv.Tests/EchoTests.cs index 77613e87e..eca92688b 100644 --- a/test/DotNetty.Transport.Libuv.Tests/EchoTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/EchoTests.cs @@ -14,6 +14,7 @@ namespace DotNetty.Transport.Libuv.Tests using Xunit.Abstractions; using static TestUtil; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; [Collection(LibuvTransport)] public sealed class EchoTests : IDisposable diff --git a/test/DotNetty.Transport.Libuv.Tests/EventLoopTests.cs b/test/DotNetty.Transport.Libuv.Tests/EventLoopTests.cs index 8302671f9..efd8d75e0 100644 --- a/test/DotNetty.Transport.Libuv.Tests/EventLoopTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/EventLoopTests.cs @@ -11,6 +11,7 @@ namespace DotNetty.Transport.Libuv.Tests using Xunit; using Xunit.Abstractions; using static TestUtil; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public sealed class EventLoopTests : TestBase, IDisposable { diff --git a/test/DotNetty.Transport.Libuv.Tests/ExceptionHandlingTests.cs b/test/DotNetty.Transport.Libuv.Tests/ExceptionHandlingTests.cs index 2ac71cf76..5b755130f 100644 --- a/test/DotNetty.Transport.Libuv.Tests/ExceptionHandlingTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/ExceptionHandlingTests.cs @@ -15,6 +15,7 @@ namespace DotNetty.Transport.Libuv.Tests using Xunit; using static TestUtil; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; [Collection(LibuvTransport)] public sealed class ExceptionHandlingTests : IDisposable diff --git a/test/DotNetty.Transport.Libuv.Tests/ReadPendingTests.cs b/test/DotNetty.Transport.Libuv.Tests/ReadPendingTests.cs index 0696eda56..f0f3ee186 100644 --- a/test/DotNetty.Transport.Libuv.Tests/ReadPendingTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/ReadPendingTests.cs @@ -15,6 +15,7 @@ namespace DotNetty.Transport.Libuv.Tests using Xunit; using static TestUtil; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; [Collection(LibuvTransport)] public sealed class ReadPendingTests : IDisposable diff --git a/test/DotNetty.Transport.Libuv.Tests/ResetTests.cs b/test/DotNetty.Transport.Libuv.Tests/ResetTests.cs index 301e7159b..ac94944e6 100644 --- a/test/DotNetty.Transport.Libuv.Tests/ResetTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/ResetTests.cs @@ -13,6 +13,7 @@ namespace DotNetty.Transport.Libuv.Tests using Xunit; using static TestUtil; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; [Collection(LibuvTransport)] public sealed class ResetTests : IDisposable diff --git a/test/DotNetty.Transport.Tests.Performance/DotNetty.Transport.Tests.Performance.csproj b/test/DotNetty.Transport.Tests.Performance/DotNetty.Transport.Tests.Performance.csproj index 72cb185f7..92c6f96e5 100644 --- a/test/DotNetty.Transport.Tests.Performance/DotNetty.Transport.Tests.Performance.csproj +++ b/test/DotNetty.Transport.Tests.Performance/DotNetty.Transport.Tests.Performance.csproj @@ -4,11 +4,13 @@ true - net472 + netcoreapp3.1;net472;net5.0;net6.0 false library + Debug;Release;Package + AnyCPU diff --git a/test/DotNetty.Transport.Tests.Performance/Transport/AbstractPingPongPerfSpecs.cs b/test/DotNetty.Transport.Tests.Performance/Transport/AbstractPingPongPerfSpecs.cs index 92f050f48..a2e7d0ab5 100644 --- a/test/DotNetty.Transport.Tests.Performance/Transport/AbstractPingPongPerfSpecs.cs +++ b/test/DotNetty.Transport.Tests.Performance/Transport/AbstractPingPongPerfSpecs.cs @@ -13,6 +13,7 @@ namespace DotNetty.Transport.Tests.Performance.Transport using DotNetty.Transport.Bootstrapping; using DotNetty.Transport.Channels; using NBench; + using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; public abstract class AbstractPingPongPerfSpecs where TServer : IServerChannel, new() diff --git a/test/DotNetty.Transport.Tests/DotNetty.Transport.Tests.csproj b/test/DotNetty.Transport.Tests/DotNetty.Transport.Tests.csproj index b1909e485..530e594c5 100644 --- a/test/DotNetty.Transport.Tests/DotNetty.Transport.Tests.csproj +++ b/test/DotNetty.Transport.Tests/DotNetty.Transport.Tests.csproj @@ -1,10 +1,12 @@  true - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5.0;net6.0 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU