diff --git a/WebSockets.sln b/WebSockets.sln index b755faab40..95d00d84a4 100644 --- a/WebSockets.sln +++ b/WebSockets.sln @@ -32,7 +32,11 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.WebSoc EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.WebSockets.ConformanceTest", "test\Microsoft.AspNetCore.WebSockets.ConformanceTest\Microsoft.AspNetCore.WebSockets.ConformanceTest.xproj", "{74F45408-1959-4FEE-9511-25D40F4913FD}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TestClient", "samples\TestClient\TestClient.xproj", "{8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}" +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Server.Kestrel", "..\KestrelHttpServer\src\Microsoft.AspNetCore.Server.Kestrel\Microsoft.AspNetCore.Server.Kestrel.xproj", "{F510611A-3BEE-4B88-A613-5F4A74ED82A1}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Server.Kestrel.Https", "..\KestrelHttpServer\src\Microsoft.AspNetCore.Server.Kestrel.Https\Microsoft.AspNetCore.Server.Kestrel.Https.xproj", "{5F64B3C3-0C2E-431A-B820-A81BBFC863DA}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "EchoApp", "samples\EchoApp\EchoApp.xproj", "{421954B0-5C6B-4092-8D4D-EACA4CE60AFB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -68,10 +72,18 @@ Global {74F45408-1959-4FEE-9511-25D40F4913FD}.Debug|Any CPU.Build.0 = Debug|Any CPU {74F45408-1959-4FEE-9511-25D40F4913FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {74F45408-1959-4FEE-9511-25D40F4913FD}.Release|Any CPU.Build.0 = Release|Any CPU - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9}.Release|Any CPU.Build.0 = Release|Any CPU + {F510611A-3BEE-4B88-A613-5F4A74ED82A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F510611A-3BEE-4B88-A613-5F4A74ED82A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F510611A-3BEE-4B88-A613-5F4A74ED82A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F510611A-3BEE-4B88-A613-5F4A74ED82A1}.Release|Any CPU.Build.0 = Release|Any CPU + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Release|Any CPU.Build.0 = Release|Any CPU + {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -84,6 +96,6 @@ Global {CDE16880-0374-46FA-8896-99F1B90B4B6F} = {2C7947A5-9FBD-4267-97C1-2D726D7B3BAF} {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF} = {C45106D0-76C8-4776-A140-F7DD83CA2958} {74F45408-1959-4FEE-9511-25D40F4913FD} = {C45106D0-76C8-4776-A140-F7DD83CA2958} - {8C8EAC01-DC49-4C5E-B348-E4E46FE675F9} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} + {421954B0-5C6B-4092-8D4D-EACA4CE60AFB} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} EndGlobalSection EndGlobal diff --git a/samples/EchoApp/EchoApp.xproj b/samples/EchoApp/EchoApp.xproj new file mode 100644 index 0000000000..687dae372a --- /dev/null +++ b/samples/EchoApp/EchoApp.xproj @@ -0,0 +1,25 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 421954b0-5c6b-4092-8d4d-eaca4ce60afb + EchoApp + .\obj + .\bin\ + v4.6.1 + + + + 2.0 + + + + + + + diff --git a/samples/EchoApp/Program.cs b/samples/EchoApp/Program.cs new file mode 100644 index 0000000000..2775a095ce --- /dev/null +++ b/samples/EchoApp/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; + +namespace EchoApp +{ + public class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + + host.Run(); + } + } +} diff --git a/samples/EchoApp/Properties/launchSettings.json b/samples/EchoApp/Properties/launchSettings.json new file mode 100644 index 0000000000..a9b371ff56 --- /dev/null +++ b/samples/EchoApp/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:3159/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "EchoApp": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/samples/EchoApp/Startup.cs b/samples/EchoApp/Startup.cs new file mode 100644 index 0000000000..ee3fb0ee91 --- /dev/null +++ b/samples/EchoApp/Startup.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace EchoApp +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseWebSockets(); + + app.Use(async (context, next) => + { + if (context.WebSockets.IsWebSocketRequest) + { + var webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Echo(webSocket); + } + else + { + await next(); + } + }); + + app.UseFileServer(); + } + + private async Task Echo(WebSocket webSocket) + { + var buffer = new byte[1024 * 4]; + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + while (!result.CloseStatus.HasValue) + { + // If the client send "ServerClose", then they want a server-originated close to occur + if(result.MessageType == WebSocketMessageType.Text) + { + var str = Encoding.UTF8.GetString(buffer, 0, result.Count); + if(str.Equals("ServerClose")) + { + await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing from Server", CancellationToken.None); + return; + } + } + + await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + } +} diff --git a/samples/EchoApp/project.json b/samples/EchoApp/project.json new file mode 100644 index 0000000000..5e7ef03dc7 --- /dev/null +++ b/samples/EchoApp/project.json @@ -0,0 +1,49 @@ +{ + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.0", + "type": "platform" + }, + "Microsoft.AspNetCore.Diagnostics": "1.1.0-*", + "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0-*", + "Microsoft.AspNetCore.Server.Kestrel": "1.1.0-*", + "Microsoft.Extensions.Logging.Console": "1.1.0-*", + "Microsoft.AspNetCore.StaticFiles": "1.1.0-*", + "Microsoft.AspNetCore.WebSockets": "0.2.0-*" + }, + + "tools": { + "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-*" + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": [ + "dotnet5.6", + "portable-net45+win8" + ] + } + }, + + "buildOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + + "runtimeOptions": { + "configProperties": { + "System.GC.Server": true + } + }, + + "publishOptions": { + "include": [ + "wwwroot", + "web.config" + ] + }, + + "scripts": { + "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] + } +} diff --git a/samples/EchoApp/web.config b/samples/EchoApp/web.config new file mode 100644 index 0000000000..dc0514fca5 --- /dev/null +++ b/samples/EchoApp/web.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/samples/EchoApp/wwwroot/index.html b/samples/EchoApp/wwwroot/index.html new file mode 100644 index 0000000000..d80fd3b907 --- /dev/null +++ b/samples/EchoApp/wwwroot/index.html @@ -0,0 +1,151 @@ + + + + + + + + +

WebSocket Test Page

+

Ready to connect...

+
+ + + +
+
+ + + + +
+ +

Note: When connected to the default server (i.e. the server in the address bar ;)), the message "ServerClose" will cause the server to close the connection

+ +

Communication Log

+ + + + + + + + + + +
FromToData
+ + + + \ No newline at end of file diff --git a/scripts/UpdateCoreFxCode.ps1 b/scripts/UpdateCoreFxCode.ps1 index c6e2be846a..d94e04a161 100644 --- a/scripts/UpdateCoreFxCode.ps1 +++ b/scripts/UpdateCoreFxCode.ps1 @@ -34,15 +34,7 @@ $FilesToCopy | foreach { } Write-Host "Copying $_" - # The trailing blank line is important! - $Pragmas = @" -#pragma warning disable 1572 -#pragma warning disable 1573 - -"@; - $SourceCode = [IO.File]::ReadAllText($Source) - $SourceCode = $Pragmas + $SourceCode $SourceCode = $SourceCode.Replace("Task.FromException", "CompatHelpers.FromException") $SourceCode = $SourceCode.Replace("Task.CompletedTask", "CompatHelpers.CompletedTask") $SourceCode = $SourceCode.Replace("Array.Empty", "CompatHelpers.Empty") diff --git a/src/Microsoft.AspNetCore.WebSockets/Internal/fx/src/Common/src/System/Net/WebSockets/WebSocketValidate.cs b/src/Microsoft.AspNetCore.WebSockets/Internal/fx/src/Common/src/System/Net/WebSockets/WebSocketValidate.cs index 284b9ee0b4..06e07f29dd 100644 --- a/src/Microsoft.AspNetCore.WebSockets/Internal/fx/src/Common/src/System/Net/WebSockets/WebSocketValidate.cs +++ b/src/Microsoft.AspNetCore.WebSockets/Internal/fx/src/Common/src/System/Net/WebSockets/WebSocketValidate.cs @@ -1,5 +1,3 @@ -#pragma warning disable 1572 -#pragma warning disable 1573 // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. diff --git a/src/Microsoft.AspNetCore.WebSockets/Internal/fx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/Microsoft.AspNetCore.WebSockets/Internal/fx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ManagedWebSocket.cs index 5b6f1e94da..322997f1d1 100644 --- a/src/Microsoft.AspNetCore.WebSockets/Internal/fx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/src/Microsoft.AspNetCore.WebSockets/Internal/fx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -1,5 +1,3 @@ -#pragma warning disable 1572 -#pragma warning disable 1573 // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -32,7 +30,6 @@ namespace System.Net.WebSockets /// The connected Stream. /// true if this is the server-side of the connection; false if this is the client-side of the connection. /// The agreed upon subprotocol for the connection. - /// The current state of the websocket connection. /// The interval to use for keep-alive pings. /// The buffer size to use for received data. /// The created instance. @@ -879,7 +876,7 @@ namespace System.Net.WebSockets } /// Parses a message header from the buffer. This assumes the header is in the buffer. - /// The read header. + /// The read header. /// true if a header was read; false if the header was invalid. private bool TryParseMessageHeaderFromReceiveBuffer(out MessageHeader resultHeader) { @@ -1037,7 +1034,7 @@ namespace System.Net.WebSockets /// Sends a close message to the server. /// The close status to send. - /// The close status description to send. + /// The close status description to send. /// The CancellationToken to use to cancel the websocket. private async Task SendCloseFrameAsync(WebSocketCloseStatus closeStatus, string closeStatusDescription, CancellationToken cancellationToken) { @@ -1153,9 +1150,9 @@ namespace System.Net.WebSockets /// The buffer to which the mask should be applied. /// The offset into at which the mask should start to be applied. /// The four-byte mask, stored as an Int32. - /// The index into the mas + /// The index into the mask. /// The number of bytes to mask. - /// + /// The next index into the mask to be used for future applications of the mask. private static unsafe int ApplyMask(byte[] toMask, int toMaskOffset, int mask, int maskIndex, long count) { Debug.Assert(toMaskOffset <= toMask.Length - count, $"Unexpected inputs: {toMaskOffset}, {toMask.Length}, {count}");