diff --git a/DotNetty.sln b/DotNetty.sln index f2809a0..5077ced 100644 --- a/DotNetty.sln +++ b/DotNetty.sln @@ -111,180 +111,235 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + Package|Any CPU = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Release|Any CPU.Build.0 = Release|Any CPU + {572E1914-489F-402D-944F-71EE0632E5D8}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Release|Any CPU.Build.0 = Release|Any CPU + {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Release|Any CPU.Build.0 = Release|Any CPU + {72C92F76-F804-4300-BFF1-459420D9EF0B}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Release|Any CPU.Build.0 = Release|Any CPU + {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Release|Any CPU.Build.0 = Release|Any CPU + {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Release|Any CPU.Build.0 = Release|Any CPU + {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Release|Any CPU.Build.0 = Release|Any CPU + {08C19033-23B2-47D7-8332-86273AE287BC}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Release|Any CPU.Build.0 = Release|Any CPU + {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Release|Any CPU.Build.0 = Release|Any CPU + {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Release|Any CPU.Build.0 = Release|Any CPU + {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Release|Any CPU.Build.0 = Release|Any CPU + {BDA099C5-E435-49DF-9922-58D63E11B764}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Release|Any CPU.Build.0 = Release|Any CPU + {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Package|Any CPU.ActiveCfg = Package|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 + {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Package|Any CPU.ActiveCfg = Package|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 + {66491D47-2BFB-45CF-A582-C11860219512}.Package|Any CPU.ActiveCfg = Package|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 + {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Package|Any CPU.ActiveCfg = Package|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 + {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Package|Any CPU.ActiveCfg = Package|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 + {E854F61B-548A-4100-A766-35B972B9EE11}.Package|Any CPU.ActiveCfg = Package|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 + {22C53CFB-B54A-438E-820F-42C94D557345}.Package|Any CPU.ActiveCfg = Package|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 + {A4E85E94-383F-40EA-9478-0545070E52F5}.Package|Any CPU.ActiveCfg = Package|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 + {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Package|Any CPU.ActiveCfg = Package|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 + {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Package|Any CPU.ActiveCfg = Package|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 + {ED307D87-E1BF-49B6-9591-5250D431C758}.Package|Any CPU.ActiveCfg = Package|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 + {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Package|Any CPU.ActiveCfg = Package|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 + {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Package|Any CPU.ActiveCfg = Package|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 + {F2490822-51F7-4B65-8B21-EE0082B76745}.Package|Any CPU.ActiveCfg = Package|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 + {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {7155D1E6-00CE-4081-B922-E6C5524EE600}.Release|Any CPU.Build.0 = Release|Any CPU + {7155D1E6-00CE-4081-B922-E6C5524EE600}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {9FE6A783-C20D-4097-9988-4178E2C4CE75}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {1012C962-7F6D-4EC5-A0EC-0741A95BAD6B}.Release|Any CPU.Build.0 = Release|Any CPU + {1012C962-7F6D-4EC5-A0EC-0741A95BAD6B}.Package|Any CPU.ActiveCfg = Package|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 + {A7CACAE7-66E7-43DA-948B-28EB0DDDB582}.Package|Any CPU.ActiveCfg = Package|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}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F68A5B1-7907-4B16-8AFE-326E9DD7D65B}.Release|Any CPU.Build.0 = Release|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 {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}.Release|Any CPU.ActiveCfg = Release|Any CPU {16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Release|Any CPU.Build.0 = Release|Any CPU + {16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Package|Any CPU.ActiveCfg = Package|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 + {EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Package|Any CPU.ActiveCfg = Package|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 + {3326DB6E-023E-483F-9A1C-5905D3091B57}.Package|Any CPU.ActiveCfg = Package|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b99a9b8..f46e235 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,8 @@ +#### 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/build.ps1 b/build.ps1 index 291b9ef..b843a9f 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 96c39fa..fc5f66a 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;net6 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 96c39fa..fc5f66a 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;net6 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 96c39fa..fc5f66a 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;net6 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 defec1f..0d2902c 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;net6 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 5351702..f2b7752 100644 --- a/examples/Examples.Common/ExampleHelper.cs +++ b/examples/Examples.Common/ExampleHelper.cs @@ -22,7 +22,7 @@ namespace Examples.Common { 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 8e186c7..3ffab8a 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;net6 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 f5a342a..e359418 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;net6 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 f5a342a..e359418 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;net6 2.0.3 false + Debug;Release;Package + AnyCPU win-x64 diff --git a/examples/Factorial/Factorial.csproj b/examples/Factorial/Factorial.csproj index d2ccbbd..157b1f5 100644 --- a/examples/Factorial/Factorial.csproj +++ b/examples/Factorial/Factorial.csproj @@ -1,8 +1,10 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 2.0.3 false + Debug;Release;Package + AnyCPU diff --git a/examples/HttpServer/HttpServer.csproj b/examples/HttpServer/HttpServer.csproj index 9e580ec..3337a67 100644 --- a/examples/HttpServer/HttpServer.csproj +++ b/examples/HttpServer/HttpServer.csproj @@ -1,10 +1,12 @@  Exe - netcoreapp3.1;net472 + netcoreapp3.1;net472;net5;net6 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 96c39fa..fc5f66a 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;net6 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 96c39fa..fc5f66a 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;net6 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 96c39fa..fc5f66a 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;net6 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 96c39fa..fc5f66a 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;net6 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 96c39fa..fc5f66a 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;net6 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 96c39fa..fc5f66a 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;net6 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 6219b1b..04b2f7c 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 1beb17f..cd4b150 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;net6 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 86c509d..597c373 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;net6 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 0000000..679f1b4 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,23 @@ + + + + + Debug;Release;Package + AnyCPU + true + pdbonly + True + true + + + pdbonly + true + $(SolutionDir)build_output\packages + True + true + + 0.7.3 + $(PackageVersion) + + + diff --git a/src/DotNetty.Buffers/DotNetty.Buffers.csproj b/src/DotNetty.Buffers/DotNetty.Buffers.csproj index ba7da0f..c6d10c9 100644 --- a/src/DotNetty.Buffers/DotNetty.Buffers.csproj +++ b/src/DotNetty.Buffers/DotNetty.Buffers.csproj @@ -1,12 +1,12 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true Buffer management in DotNetty © Microsoft Corporation. All rights reserved. DotNetty: buffer management en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 True diff --git a/src/DotNetty.Buffers/UnsafeByteBufferUtil.cs b/src/DotNetty.Buffers/UnsafeByteBufferUtil.cs index 6168080..a9c2fde 100644 --- a/src/DotNetty.Buffers/UnsafeByteBufferUtil.cs +++ b/src/DotNetty.Buffers/UnsafeByteBufferUtil.cs @@ -386,7 +386,7 @@ namespace DotNetty.Buffers 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 567db22..b6a4062 100644 --- a/src/DotNetty.Codecs.Http/DotNetty.Codecs.Http.csproj +++ b/src/DotNetty.Codecs.Http/DotNetty.Codecs.Http.csproj @@ -1,14 +1,14 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Codecs.Http Http codec for DotNetty © Microsoft Corporation. All rights reserved. DotNetty: Http codec en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 true diff --git a/src/DotNetty.Codecs.Http/WebSockets/WebSocketClientHandshaker.cs b/src/DotNetty.Codecs.Http/WebSockets/WebSocketClientHandshaker.cs index a2b32ff..279612c 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 e5ec847..5449282 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 7e14a1e..fb8cdfe 100644 --- a/src/DotNetty.Codecs.Mqtt/DotNetty.Codecs.Mqtt.csproj +++ b/src/DotNetty.Codecs.Mqtt/DotNetty.Codecs.Mqtt.csproj @@ -1,13 +1,13 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Codecs.Mqtt MQTT codec for DotNetty © Microsoft Corporation. All rights reserved. DotNetty: MQTT codec en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 false diff --git a/src/DotNetty.Codecs.Protobuf/DotNetty.Codecs.Protobuf.csproj b/src/DotNetty.Codecs.Protobuf/DotNetty.Codecs.Protobuf.csproj index 4c4a6e2..096eee8 100644 --- a/src/DotNetty.Codecs.Protobuf/DotNetty.Codecs.Protobuf.csproj +++ b/src/DotNetty.Codecs.Protobuf/DotNetty.Codecs.Protobuf.csproj @@ -1,13 +1,13 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Codecs.Protobuf Protobuf Proto3 codec for DotNetty © Microsoft Corporation. All rights reserved. DotNetty: Protobuf Proto3 codec en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 false diff --git a/src/DotNetty.Codecs.ProtocolBuffers/DotNetty.Codecs.ProtocolBuffers.csproj b/src/DotNetty.Codecs.ProtocolBuffers/DotNetty.Codecs.ProtocolBuffers.csproj index 884946b..8a00e14 100644 --- a/src/DotNetty.Codecs.ProtocolBuffers/DotNetty.Codecs.ProtocolBuffers.csproj +++ b/src/DotNetty.Codecs.ProtocolBuffers/DotNetty.Codecs.ProtocolBuffers.csproj @@ -1,14 +1,14 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Codecs.ProtocolBuffers ProtocolBuffers Proto2 codec for DotNetty © Microsoft Corporation. All rights reserved. DotNetty: ProtocolBuffers Proto2 codec en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 false diff --git a/src/DotNetty.Codecs.Redis/DotNetty.Codecs.Redis.csproj b/src/DotNetty.Codecs.Redis/DotNetty.Codecs.Redis.csproj index 586377c..ed156c6 100644 --- a/src/DotNetty.Codecs.Redis/DotNetty.Codecs.Redis.csproj +++ b/src/DotNetty.Codecs.Redis/DotNetty.Codecs.Redis.csproj @@ -1,13 +1,13 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Codecs.Redis Redis codec for DotNetty © Microsoft Corporation. All rights reserved. DotNetty: Redis codec en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 false diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.csproj b/src/DotNetty.Codecs/DotNetty.Codecs.csproj index b89c9ed..ff6f47b 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.csproj +++ b/src/DotNetty.Codecs/DotNetty.Codecs.csproj @@ -1,13 +1,13 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Codecs General purpose codecs for DotNetty © Microsoft Corporation. All rights reserved. DotNetty: codecs en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 true diff --git a/src/DotNetty.Common/DotNetty.Common.csproj b/src/DotNetty.Common/DotNetty.Common.csproj index 13632d1..8d06f5b 100644 --- a/src/DotNetty.Common/DotNetty.Common.csproj +++ b/src/DotNetty.Common/DotNetty.Common.csproj @@ -1,14 +1,14 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Common DotNetty common routines © Microsoft Corporation. All rights reserved. DotNetty: common routines en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 True diff --git a/src/DotNetty.Common/Utilities/HashedWheelTimer.cs b/src/DotNetty.Common/Utilities/HashedWheelTimer.cs index a1dd137..7322dc9 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 d0093a9..8c2c72d 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 83fed99..fe1ce05 100644 --- a/src/DotNetty.Handlers/DotNetty.Handlers.csproj +++ b/src/DotNetty.Handlers/DotNetty.Handlers.csproj @@ -1,14 +1,14 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Handlers Application handlers for DotNetty © Microsoft Corporation. All rights reserved. DotNetty: handlers en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 false diff --git a/src/DotNetty.Handlers/Streams/ChunkedWriteHandler.cs b/src/DotNetty.Handlers/Streams/ChunkedWriteHandler.cs index 438856a..f31bbbd 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 4f7b488..c7148ef 100644 --- a/src/DotNetty.Handlers/Tls/SniHandler.cs +++ b/src/DotNetty.Handlers/Tls/SniHandler.cs @@ -214,7 +214,7 @@ namespace DotNetty.Handlers.Tls }; 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 0000000..b8e48ba --- /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 0000000..5802921 --- /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 0000000..1e0c6a0 --- /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 e46d8c3..8e4749f 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 @@ namespace DotNetty.Handlers.Tls 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 @@ namespace DotNetty.Handlers.Tls 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 @@ namespace DotNetty.Handlers.Tls 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); + + 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) { - TlsHandlerState oldState = self.state; - - Contract.Assert(!oldState.HasAny(TlsHandlerState.AuthenticationCompleted)); - self.state = (oldState | TlsHandlerState.Authenticated) & ~(TlsHandlerState.Authenticating | TlsHandlerState.FlushedBeforeHandshake); - - self.capturedContext.FireUserEventTriggered(TlsHandshakeCompletionEvent.Success); - - if (oldState.Has(TlsHandlerState.ReadRequestedBeforeAuthenticated) && !self.capturedContext.Channel.Configuration.AutoRead) + 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; } + + if (oldState.Has(TlsHandlerState.ReadRequestedBeforeAuthenticated) && !self.capturedContext.Channel.Configuration.AutoRead) + { + 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; - } + { + // 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 @@ namespace DotNetty.Handlers.Tls 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 @@ namespace DotNetty.Handlers.Tls 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 @@ namespace DotNetty.Handlers.Tls if (encryptedPacketLength > readableBytes) { // wait until the whole packet can be read - this.packetLength = encryptedPacketLength; + this.packetInfo = (encryptedPacketLength, contentType); break; } @@ -244,7 +279,7 @@ namespace DotNetty.Handlers.Tls // 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 @@ namespace DotNetty.Handlers.Tls // 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 @@ namespace DotNetty.Handlers.Tls 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 @@ namespace DotNetty.Handlers.Tls 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 @@ namespace DotNetty.Handlers.Tls } // 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 @@ namespace DotNetty.Handlers.Tls 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 @@ namespace DotNetty.Handlers.Tls 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,11 @@ namespace DotNetty.Handlers.Tls { break; } + int read = currentReadFuture.Result; AddBufferToOutput(outputBuffer, read, output); } + outputBuffer = ctx.Allocator.Buffer(FallbackReadBufferSize); currentReadFuture = this.ReadFromSslStreamAsync(outputBuffer, FallbackReadBufferSize); } @@ -514,6 +592,7 @@ namespace DotNetty.Handlers.Tls { return TaskEx.FromException(new UnsupportedMessageTypeException(message, typeof(IByteBuffer))); } + return this.pendingUnencryptedWrites.Add(message); } @@ -569,6 +648,7 @@ namespace DotNetty.Handlers.Tls buffer.Release(); } } + buf.ReadBytes(this.sslStream, buf.ReadableBytes); // this leads to FinishWrap being called 0+ times buf.Release(); @@ -627,7 +707,6 @@ namespace DotNetty.Handlers.Tls { // Release all resources such as internal buffers that SSLEngine // is managing. - this.mediationStream.Dispose(); try { @@ -664,323 +743,6 @@ namespace DotNetty.Handlers.Tls 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] diff --git a/src/DotNetty.Handlers/Tls/TlsUtils.cs b/src/DotNetty.Handlers/Tls/TlsUtils.cs index c271c82..bfc8d80 100644 --- a/src/DotNetty.Handlers/Tls/TlsUtils.cs +++ b/src/DotNetty.Handlers/Tls/TlsUtils.cs @@ -53,12 +53,13 @@ namespace DotNetty.Handlers.Tls /// 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 @@ namespace DotNetty.Handlers.Tls 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 @@ namespace DotNetty.Handlers.Tls 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 b9abbe7..50241fc 100644 --- a/src/DotNetty.Transport.Libuv/DotNetty.Transport.Libuv.csproj +++ b/src/DotNetty.Transport.Libuv/DotNetty.Transport.Libuv.csproj @@ -1,14 +1,14 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Transport.Libuv Libuv transport model in DotNetty © Microsoft Corporation. All rights reserved. DotNetty: libuv transport model Experimental en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 True diff --git a/src/DotNetty.Transport.Libuv/LoopExecutor.cs b/src/DotNetty.Transport.Libuv/LoopExecutor.cs index 69ffa8f..51585e4 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 73d437b..8ff9b17 100644 --- a/src/DotNetty.Transport.Libuv/Native/NativeMethods.cs +++ b/src/DotNetty.Transport.Libuv/Native/NativeMethods.cs @@ -331,7 +331,7 @@ namespace DotNetty.Transport.Libuv.Native { 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 @@ namespace DotNetty.Transport.Libuv.Native { 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 @@ namespace DotNetty.Transport.Libuv.Native 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 12b4307..0db29ea 100644 --- a/src/DotNetty.Transport.Libuv/Native/WindowsApi.cs +++ b/src/DotNetty.Transport.Libuv/Native/WindowsApi.cs @@ -41,7 +41,7 @@ namespace DotNetty.Transport.Libuv.Native 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 169e68d..975b665 100644 --- a/src/DotNetty.Transport.Libuv/Native/WriteRequest.cs +++ b/src/DotNetty.Transport.Libuv/Native/WriteRequest.cs @@ -26,7 +26,7 @@ namespace DotNetty.Transport.Libuv.Native 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 b77764a..a75ad7e 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 2148184..e891ba8 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 5dd26dd..7dad976 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 689afcb..f543a01 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 10f06f7..6e8f8af 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 6401d5c..3008751 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 8d2fc22..5896fbc 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 1390b7a..1473e5c 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 d3bd4ca..543d0a6 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 0000000..5cd9917 --- /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 0000000..b3a51d6 --- /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 ac466d5..3c1decd 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 80441bb..568fc0f 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 e297a8b..e42c320 100644 --- a/src/DotNetty.Transport/Channels/Sockets/AbstractSocketByteChannel.cs +++ b/src/DotNetty.Transport/Channels/Sockets/AbstractSocketByteChannel.cs @@ -154,7 +154,7 @@ namespace DotNetty.Transport.Channels.Sockets { 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 @@ namespace DotNetty.Transport.Channels.Sockets 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 c5da056..dab1131 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 a8046c5..7e5e43e 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 4696a4a..82a49dc 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 @@ namespace DotNetty.Transport.Channels.Sockets 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 4e6b2fe..8d7b843 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 105860e..0b7849c 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 6530c18..e4488c9 100644 --- a/src/DotNetty.Transport/DotNetty.Transport.csproj +++ b/src/DotNetty.Transport/DotNetty.Transport.csproj @@ -1,14 +1,14 @@  - netstandard2.0;net472 + netstandard2.0;net472;net5;net6 true DotNetty.Transport Transport model in DotNetty © Microsoft Corporation. All rights reserved. DotNetty: transport model en-US - 0.7.2 + 0.7.3 Microsoft $(NoWarn);CS1591 false diff --git a/src/shared/SharedAssemblyInfo.cs b/src/shared/SharedAssemblyInfo.cs index ed949d6..7b2a9f3 100644 --- a/src/shared/SharedAssemblyInfo.cs +++ b/src/shared/SharedAssemblyInfo.cs @@ -7,7 +7,7 @@ using System.Reflection; [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("DotNetty")] -[assembly: AssemblyVersion("0.7.2")] -[assembly: AssemblyFileVersion("0.7.2")] +[assembly: AssemblyVersion("0.7.3")] +[assembly: AssemblyFileVersion("0.7.3")] [assembly: AssemblyCopyright("(c) Microsoft 2015 - 2021")] diff --git a/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj b/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj index 94e38d7..6c2b3d1 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;net6 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 82dcbc4..a266a74 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;net6 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 09876ee..0ca42ab 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 cb8f3a6..9143b69 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 cf527aa..18a1f17 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 f14e1e5..526674f 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;net6 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 4a32e85..ab2b6e2 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;net6 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 58d5637..bb4ab9a 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;net6 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 f42b68f..b8d7a57 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;net6 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 ac979f5..311bf22 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;net6 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 ae45ce3..e3f6812 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 b6cef62..401e208 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;net6 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 a2fd4cf..a0477ed 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 85211b3..fd7ebfc 100644 --- a/test/DotNetty.Handlers.Tests/BatchingWriteStrategy.cs +++ b/test/DotNetty.Handlers.Tests/BatchingWriteStrategy.cs @@ -14,7 +14,7 @@ namespace DotNetty.Handlers.Tests readonly TimeSpan timeWindow; readonly bool forceSizing; IByteBuffer pendingBuffer; - EmbeddedChannel channel; + IEmbeddedChannel channel; public BatchingWriteStrategy(int maxBatchSize, TimeSpan timeWindow, bool forceSizing) { @@ -23,7 +23,7 @@ namespace DotNetty.Handlers.Tests 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 0a67729..d8c3a3d 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;net6 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 a6f1839..1250628 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 0f4d093..7bb1a03 100644 --- a/test/DotNetty.Handlers.Tests/SniHandlerTest.cs +++ b/test/DotNetty.Handlers.Tests/SniHandlerTest.cs @@ -72,13 +72,13 @@ namespace DotNetty.Handlers.Tests 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 @@ namespace DotNetty.Handlers.Tests 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 @@ namespace DotNetty.Handlers.Tests } } - 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 @@ namespace DotNetty.Handlers.Tests }), 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 ba7748c..34677e5 100644 --- a/test/DotNetty.Handlers.Tests/TlsHandlerTest.cs +++ b/test/DotNetty.Handlers.Tests/TlsHandlerTest.cs @@ -69,7 +69,6 @@ namespace DotNetty.Handlers.Tests 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 @@ namespace DotNetty.Handlers.Tests 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 @@ namespace DotNetty.Handlers.Tests } 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 @@ namespace DotNetty.Handlers.Tests 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 @@ namespace DotNetty.Handlers.Tests 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 @@ namespace DotNetty.Handlers.Tests } } - 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 @@ namespace DotNetty.Handlers.Tests 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 @@ namespace DotNetty.Handlers.Tests 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 8be7693..f02dd32 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;net6 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 946f215..226225f 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;net6 ../../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 c1a9f71..cb732f9 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;net6 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 39473d4..cffc672 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 e104f9f..f9a0581 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 ab276ce..5bc8cd0 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 3355c12..e24a985 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 1bad6c0..df04277 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;net6 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 77613e8..eca9268 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 8302671..efd8d75 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 2ac71cf..5b75513 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 0696eda..f0f3ee1 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 301e715..ac94944 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 72cb185..49ad8cb 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;net6 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 92f050f..a2e7d0a 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 b1909e4..bdfb332 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;net6 false ../../DotNetty.snk true + Debug;Release;Package + AnyCPU