From 239999e4c96f3244b70e296003f8beee40455aa7 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 7 Nov 2016 20:58:41 -0800 Subject: [PATCH] Make things actually work (#10) - Added build.cmd - Renamed the solution to match our conventions - Fixed bad doc comment references - Used strong name version of StackExchange.Redis --- .travis.yml | 32 +++++++++ ...soft.AspNetCore.Sockets.sln => SignalR.sln | 0 appveyor.yml | 13 ++++ build.cmd | 2 + build.ps1 | 67 +++++++++++++++++++ build.sh | 46 +++++++++++++ .../project.json | 2 +- .../IWebSocketConnection.cs | 38 +++++++---- .../Properties/AssemblyInfo.cs | 2 +- .../WebSocketConnection.cs | 4 +- .../project.json | 6 +- 11 files changed, 191 insertions(+), 21 deletions(-) create mode 100644 .travis.yml rename Microsoft.AspNetCore.Sockets.sln => SignalR.sln (100%) create mode 100644 appveyor.yml create mode 100644 build.cmd create mode 100644 build.ps1 create mode 100644 build.sh diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..d7636fa329 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,32 @@ +language: csharp +sudo: required +dist: trusty +addons: + apt: + packages: + - gettext + - libcurl4-openssl-dev + - libicu-dev + - libssl-dev + - libunwind8 + - zlib1g +env: + global: + - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + - DOTNET_CLI_TELEMETRY_OPTOUT: 1 +mono: + - 4.0.5 +os: + - linux + - osx +osx_image: xcode7.1 +branches: + only: + - master + - release + - dev + - /^(.*\/)?ci-.*$/ +before_install: + - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi +script: + - ./build.sh --quiet verify diff --git a/Microsoft.AspNetCore.Sockets.sln b/SignalR.sln similarity index 100% rename from Microsoft.AspNetCore.Sockets.sln rename to SignalR.sln diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..be95b88d6f --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,13 @@ +init: + - git config --global core.autocrlf true +branches: + only: + - master + - release + - dev + - /^(.*\/)?ci-.*$/ +build_script: + - build.cmd --quiet verify +clone_depth: 1 +test: off +deploy: off \ No newline at end of file diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000000..7d4894cb4a --- /dev/null +++ b/build.cmd @@ -0,0 +1,2 @@ +@ECHO OFF +PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0build.ps1' %*; exit $LASTEXITCODE" \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000000..8f2f99691a --- /dev/null +++ b/build.ps1 @@ -0,0 +1,67 @@ +$ErrorActionPreference = "Stop" + +function DownloadWithRetry([string] $url, [string] $downloadLocation, [int] $retries) +{ + while($true) + { + try + { + Invoke-WebRequest $url -OutFile $downloadLocation + break + } + catch + { + $exceptionMessage = $_.Exception.Message + Write-Host "Failed to download '$url': $exceptionMessage" + if ($retries -gt 0) { + $retries-- + Write-Host "Waiting 10 seconds before retrying. Retries left: $retries" + Start-Sleep -Seconds 10 + + } + else + { + $exception = $_.Exception + throw $exception + } + } + } +} + +cd $PSScriptRoot + +$repoFolder = $PSScriptRoot +$env:REPO_FOLDER = $repoFolder + +$koreBuildZip="https://github.com/aspnet/KoreBuild/archive/dev.zip" +if ($env:KOREBUILD_ZIP) +{ + $koreBuildZip=$env:KOREBUILD_ZIP +} + +$buildFolder = ".build" +$buildFile="$buildFolder\KoreBuild.ps1" + +if (!(Test-Path $buildFolder)) { + Write-Host "Downloading KoreBuild from $koreBuildZip" + + $tempFolder=$env:TEMP + "\KoreBuild-" + [guid]::NewGuid() + New-Item -Path "$tempFolder" -Type directory | Out-Null + + $localZipFile="$tempFolder\korebuild.zip" + + DownloadWithRetry -url $koreBuildZip -downloadLocation $localZipFile -retries 6 + + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($localZipFile, $tempFolder) + + New-Item -Path "$buildFolder" -Type directory | Out-Null + copy-item "$tempFolder\**\build\*" $buildFolder -Recurse + + # Cleanup + if (Test-Path $tempFolder) { + Remove-Item -Recurse -Force $tempFolder + } +} + +&"$buildFile" $args \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000000..f4208100eb --- /dev/null +++ b/build.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +repoFolder="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $repoFolder + +koreBuildZip="https://github.com/aspnet/KoreBuild/archive/dev.zip" +if [ ! -z $KOREBUILD_ZIP ]; then + koreBuildZip=$KOREBUILD_ZIP +fi + +buildFolder=".build" +buildFile="$buildFolder/KoreBuild.sh" + +if test ! -d $buildFolder; then + echo "Downloading KoreBuild from $koreBuildZip" + + tempFolder="/tmp/KoreBuild-$(uuidgen)" + mkdir $tempFolder + + localZipFile="$tempFolder/korebuild.zip" + + retries=6 + until (wget -O $localZipFile $koreBuildZip 2>/dev/null || curl -o $localZipFile --location $koreBuildZip 2>/dev/null) + do + echo "Failed to download '$koreBuildZip'" + if [ "$retries" -le 0 ]; then + exit 1 + fi + retries=$((retries - 1)) + echo "Waiting 10 seconds before retrying. Retries left: $retries" + sleep 10s + done + + unzip -q -d $tempFolder $localZipFile + + mkdir $buildFolder + cp -r $tempFolder/**/build/** $buildFolder + + chmod +x $buildFile + + # Cleanup + if test ! -d $tempFolder; then + rm -rf $tempFolder + fi +fi + +$buildFile -r $repoFolder "$@" \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SignalR.Redis/project.json b/src/Microsoft.AspNetCore.SignalR.Redis/project.json index 29ae356ccf..8937acd078 100644 --- a/src/Microsoft.AspNetCore.SignalR.Redis/project.json +++ b/src/Microsoft.AspNetCore.SignalR.Redis/project.json @@ -26,7 +26,7 @@ "target": "project" }, "NETStandard.Library": "1.6.1-*", - "StackExchange.Redis": "1.1.*" + "StackExchange.Redis.StrongName": "1.1.605" }, "frameworks": { diff --git a/src/Microsoft.Extensions.WebSockets.Internal/IWebSocketConnection.cs b/src/Microsoft.Extensions.WebSockets.Internal/IWebSocketConnection.cs index dfb65595d9..7f7d1b4d26 100644 --- a/src/Microsoft.Extensions.WebSockets.Internal/IWebSocketConnection.cs +++ b/src/Microsoft.Extensions.WebSockets.Internal/IWebSocketConnection.cs @@ -13,13 +13,13 @@ namespace Microsoft.Extensions.WebSockets.Internal /// /// /// Implementors of this type are generally considered thread-safe under the following condition: No two threads attempt to call either - /// or simultaneously. Different threads may call each method, but the same method + /// or simultaneously. Different threads may call each method, but the same method /// cannot be re-entered while it is being run in a different thread. However, ensure you verify that the specific implementor is /// thread-safe in this way. For example, (including the implementations returned by the /// static factory methods on that type) is thread-safe in this way. /// /// - /// The general pattern of having a single thread running and a separate thread running will + /// The general pattern of having a single thread running and a separate thread running will /// be thread-safe, as each method interacts with completely separate state. /// /// @@ -70,56 +70,63 @@ namespace Microsoft.Extensions.WebSockets.Internal /// /// Sends the specified frame. /// + /// The /// The message to send. /// A that completes when the message has been written to the outbound stream. - public static Task SendAsync(this IWebSocketConnection self, WebSocketFrame message) => self.SendAsync(message, CancellationToken.None); + public static Task SendAsync(this IWebSocketConnection connection, WebSocketFrame message) => connection.SendAsync(message, CancellationToken.None); /// /// Sends a Close frame to the other party. This does not guarantee that the client will send a responding close frame. /// + /// The /// A value to be sent to the client in the close frame. /// A that completes when the close frame has been sent - public static Task CloseAsync(this IWebSocketConnection self, WebSocketCloseStatus status) => self.CloseAsync(new WebSocketCloseResult(status), CancellationToken.None); + public static Task CloseAsync(this IWebSocketConnection connection, WebSocketCloseStatus status) => connection.CloseAsync(new WebSocketCloseResult(status), CancellationToken.None); /// /// Sends a Close frame to the other party. This does not guarantee that the client will send a responding close frame. /// + /// The /// A value to be sent to the client in the close frame. /// A textual description of the reason for closing the connection. /// A that completes when the close frame has been sent - public static Task CloseAsync(this IWebSocketConnection self, WebSocketCloseStatus status, string description) => self.CloseAsync(new WebSocketCloseResult(status, description), CancellationToken.None); + public static Task CloseAsync(this IWebSocketConnection connection, WebSocketCloseStatus status, string description) => connection.CloseAsync(new WebSocketCloseResult(status, description), CancellationToken.None); /// /// Sends a Close frame to the other party. This does not guarantee that the client will send a responding close frame. /// + /// The /// A value to be sent to the client in the close frame. /// A that indicates when/if the send is cancelled. /// A that completes when the close frame has been sent - public static Task CloseAsync(this IWebSocketConnection self, WebSocketCloseStatus status, CancellationToken cancellationToken) => self.CloseAsync(new WebSocketCloseResult(status), cancellationToken); + public static Task CloseAsync(this IWebSocketConnection connection, WebSocketCloseStatus status, CancellationToken cancellationToken) => connection.CloseAsync(new WebSocketCloseResult(status), cancellationToken); /// /// Sends a Close frame to the other party. This does not guarantee that the client will send a responding close frame. /// + /// The /// A value to be sent to the client in the close frame. /// A textual description of the reason for closing the connection. /// A that indicates when/if the send is cancelled. /// A that completes when the close frame has been sent - public static Task CloseAsync(this IWebSocketConnection self, WebSocketCloseStatus status, string description, CancellationToken cancellationToken) => self.CloseAsync(new WebSocketCloseResult(status, description), cancellationToken); + public static Task CloseAsync(this IWebSocketConnection connection, WebSocketCloseStatus status, string description, CancellationToken cancellationToken) => connection.CloseAsync(new WebSocketCloseResult(status, description), cancellationToken); /// /// Sends a Close frame to the other party. This does not guarantee that the client will send a responding close frame. /// + /// The /// A with the payload for the close frame. /// A that completes when the close frame has been sent - public static Task CloseAsync(this IWebSocketConnection self, WebSocketCloseResult result) => self.CloseAsync(result, CancellationToken.None); + public static Task CloseAsync(this IWebSocketConnection connection, WebSocketCloseResult result) => connection.CloseAsync(result, CancellationToken.None); /// /// Runs the WebSocket receive loop, using the provided message handler. /// + /// The /// The callback that will be invoked for each new frame /// A that will complete when the client has sent a close frame, or the connection has been terminated - public static Task ExecuteAsync(this IWebSocketConnection self, Action messageHandler) => - self.ExecuteAsync((frame, _) => + public static Task ExecuteAsync(this IWebSocketConnection connection, Action messageHandler) => + connection.ExecuteAsync((frame, _) => { messageHandler(frame); return Task.CompletedTask; @@ -128,10 +135,12 @@ namespace Microsoft.Extensions.WebSockets.Internal /// /// Runs the WebSocket receive loop, using the provided message handler. /// + /// The /// The callback that will be invoked for each new frame + /// The state to pass to the callback when the delegate is invoked. This may be null. /// A that will complete when the client has sent a close frame, or the connection has been terminated - public static Task ExecuteAsync(this IWebSocketConnection self, Action messageHandler, object state) => - self.ExecuteAsync((frame, s) => + public static Task ExecuteAsync(this IWebSocketConnection connection, Action messageHandler, object state) => + connection.ExecuteAsync((frame, s) => { messageHandler(frame, s); return Task.CompletedTask; @@ -140,9 +149,10 @@ namespace Microsoft.Extensions.WebSockets.Internal /// /// Runs the WebSocket receive loop, using the provided message handler. /// + /// The /// The callback that will be invoked for each new frame /// A that will complete when the client has sent a close frame, or the connection has been terminated - public static Task ExecuteAsync(this IWebSocketConnection self, Func messageHandler) => - self.ExecuteAsync((frame, _) => messageHandler(frame), null); + public static Task ExecuteAsync(this IWebSocketConnection connection, Func messageHandler) => + connection.ExecuteAsync((frame, _) => messageHandler(frame), null); } } diff --git a/src/Microsoft.Extensions.WebSockets.Internal/Properties/AssemblyInfo.cs b/src/Microsoft.Extensions.WebSockets.Internal/Properties/AssemblyInfo.cs index faeef5ffeb..74635e3701 100644 --- a/src/Microsoft.Extensions.WebSockets.Internal/Properties/AssemblyInfo.cs +++ b/src/Microsoft.Extensions.WebSockets.Internal/Properties/AssemblyInfo.cs @@ -12,4 +12,4 @@ using System.Runtime.CompilerServices; [assembly: AssemblyProduct("Microsoft.Extensions.WebSockets")] [assembly: AssemblyTrademark("")] -[assembly: InternalsVisibleTo("Microsoft.Extensions.WebSockets.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("Microsoft.Extensions.WebSockets.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Microsoft.Extensions.WebSockets.Internal/WebSocketConnection.cs b/src/Microsoft.Extensions.WebSockets.Internal/WebSocketConnection.cs index 013288658e..b775d59db2 100644 --- a/src/Microsoft.Extensions.WebSockets.Internal/WebSocketConnection.cs +++ b/src/Microsoft.Extensions.WebSockets.Internal/WebSocketConnection.cs @@ -18,11 +18,11 @@ namespace Microsoft.Extensions.WebSockets.Internal /// /// /// This type is thread-safe under the following condition: No two threads attempt to call either - /// or simultaneously. Different threads may call each method, but the same method + /// or simultaneously. Different threads may call each method, but the same method /// cannot be re-entered while it is being run in a different thread. /// /// - /// The general pattern of having a single thread running and a separate thread running will + /// The general pattern of having a single thread running and a separate thread running will /// be thread-safe, as each method interacts with completely separate state. /// /// diff --git a/src/Microsoft.Extensions.WebSockets.Internal/project.json b/src/Microsoft.Extensions.WebSockets.Internal/project.json index 4bb1ed4244..8af2e39b1f 100644 --- a/src/Microsoft.Extensions.WebSockets.Internal/project.json +++ b/src/Microsoft.Extensions.WebSockets.Internal/project.json @@ -1,6 +1,6 @@ { "version": "0.1.0-*", - "description": "WebSockets client and server components.", + "description": "Low-allocation Push-oriented WebSockets based on Channels", "packOptions": { "repository": { @@ -18,10 +18,10 @@ "nowarn": [ "CS1591" ], - "xmlDoc": true + "xmlDoc": true, + "allowUnsafe": true }, - "description": "Low-allocation Push-oriented WebSockets based on Channels", "dependencies": { "Channels": "0.2.0-beta-*", "Channels.Text.Primitives": "0.2.0-beta-*",