{"action":"created","comment":{"url":"https://api.github.com/repos/Azure/azure-signalr/pulls/comments/210786732","pull_request_review_id":147077163,"id":210786732,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxMDc4NjczMg==","diff_hunk":"@@ -0,0 +1,135 @@\n+﻿// Copyright (c) Microsoft. All rights reserved.\n+// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n+\n+using System;\n+using System.Collections.Generic;\n+using System.Diagnostics;\n+using System.IO;\n+using System.Linq;\n+using System.Security.Claims;\n+using System.Web;\n+using Microsoft.AspNet.SignalR;\n+using Microsoft.AspNet.SignalR.Hosting;\n+using Microsoft.AspNet.SignalR.Hubs;\n+using Microsoft.Azure.SignalR.Protocol;\n+using Microsoft.Owin;\n+\n+namespace Microsoft.Azure.SignalR.AspNet\n+{\n+    internal class ClientConnectionManager : IClientConnectionManager\n+    {\n+        private static readonly ClaimsPrincipal EmptyPrincipal = new ClaimsPrincipal(new ClaimsIdentity());\n+\n+        private static readonly string[] SystemClaims =\n+        {\n+            \"aud\", // Audience claim, used by service to make sure token is matched with target resource.\n+            \"exp\", // Expiration time claims. A token is valid only before its expiration time.\n+            \"iat\", // Issued At claim. Added by default. It is not validated by service.\n+            \"nbf\"  // Not Before claim. Added by default. It is not validated by service.\n+        };\n+\n+        private readonly HubConfiguration _configuration;\n+\n+        public ClientConnectionManager(HubConfiguration configuration)\n+        {\n+            _configuration = configuration;\n+        }\n+\n+        public AzureTransport CreateConnection(OpenConnectionMessage message)\n+        {\n+            var dispatcher = new HubDispatcher(_configuration);\n+            dispatcher.Initialize(_configuration.Resolver);\n+\n+            var connectionId = message.ConnectionId;\n+\n+            var responseStream = new MemoryStream();\n+            var hostContext = GetHostContext(message, responseStream);\n+\n+            if (dispatcher.Authorize(hostContext.Request))\n+            {\n+                // ProcessRequest checks if the connectionToken matches \"{connectionid}:{userName}\" format with context.User\n+                _ = dispatcher.ProcessRequest(hostContext);\n+\n+                // TODO: check for errors written to the response\n+                if (hostContext.Response.StatusCode != 200)\n+                {\n+                    Debug.Fail(\"Response StatusCode is \" + hostContext.Response.StatusCode);\n+                    var errorResponse = GetMemoryString(responseStream);\n+                    throw new InvalidOperationException(errorResponse);\n+                }\n+                return (AzureTransport)hostContext.Environment[Constants.Context.AzureSignalRTransportKey];\n+            }\n+\n+            // This happens when hub is not found\n+            Debug.Fail(\"Unauthorized\");\n+            throw new InvalidOperationException(\"Unable to authorize request\");\n+        }\n+\n+        internal HostContext GetHostContext(OpenConnectionMessage message, Stream responseStream)\n+        {\n+            var connectionId = message.ConnectionId;\n+            var context = new OwinContext();\n+            var response = context.Response;\n+            var request = context.Request;\n+\n+            response.Body = responseStream;\n+\n+            var user = request.User = GetUserPrincipal(message);\n+\n+            request.Path = new PathString(\"/\");\n+\n+            var userToken = string.IsNullOrEmpty(user.Identity.Name) ? string.Empty : \":\" + user.Identity.Name;\n+\n+            var queryCollection = HttpUtility.ParseQueryString(message.QueryString ?? string.Empty);\n+            queryCollection[Constants.QueryString.ConnectionToken] = $\"{connectionId}{userToken}\";\n+\n+            request.QueryString = new QueryString(queryCollection.ToString());\n+\n+            if (message.Headers != null)\n+            {\n+                foreach (var pair in message.Headers)\n+                {\n+                    request.Headers.Add(pair.Key, pair.Value);\n+                }\n+            }\n+\n+            context.Environment[Constants.Context.AzureServiceConnectionKey] = this;\n+            return new HostContext(context.Environment);\n+        }\n+\n+        internal static ClaimsPrincipal GetUserPrincipal(OpenConnectionMessage message)\n+        {\n+            if (message.Claims == null || message.Claims.Length == 0)\n+            {\n+                return EmptyPrincipal;\n+            }\n+\n+            var claims = new List<Claim>();\n+            var authenticationType = \"Bearer\";\n+\n+            foreach (var claim in message.Claims)\n+            {\n+                // TODO: Add prefix \"azure.signalr.user.\" to user claims instead of guessing them?\n+                if (claim.Type == Constants.ClaimType.AuthenticationType)\n+                {\n+                    authenticationType = claim.Value;\n+                }\n+                else if (!SystemClaims.Contains(claim.Type) && !claim.Type.StartsWith(Constants.ClaimType.AzureSignalRSysPrefix))\n+                {\n+                    claims.Add(claim);\n+                }\n+            }\n+\n+            return new ClaimsPrincipal(new ClaimsIdentity(claims, authenticationType));\n+        }\n+\n+        internal static string GetMemoryString(MemoryStream stream)\n+        {\n+            stream.Seek(0, SeekOrigin.Begin);\n+            using (var reader = new StreamReader(stream))","path":"src/Microsoft.Azure.SignalR.AspNet/Infrastructure/ClientConnectionManager.cs","position":129,"original_position":129,"commit_id":"63510c98ed8d8a0bf5c539745d148ec0d6a05311","original_commit_id":"63510c98ed8d8a0bf5c539745d148ec0d6a05311","user":{"login":"vwxyzh","id":2705828,"node_id":"MDQ6VXNlcjI3MDU4Mjg=","avatar_url":"https://avatars2.githubusercontent.com/u/2705828?v=4","gravatar_id":"","url":"https://api.github.com/users/vwxyzh","html_url":"https://github.com/vwxyzh","followers_url":"https://api.github.com/users/vwxyzh/followers","following_url":"https://api.github.com/users/vwxyzh/following{/other_user}","gists_url":"https://api.github.com/users/vwxyzh/gists{/gist_id}","starred_url":"https://api.github.com/users/vwxyzh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vwxyzh/subscriptions","organizations_url":"https://api.github.com/users/vwxyzh/orgs","repos_url":"https://api.github.com/users/vwxyzh/repos","events_url":"https://api.github.com/users/vwxyzh/events{/privacy}","received_events_url":"https://api.github.com/users/vwxyzh/received_events","type":"User","site_admin":false},"body":"using `StreamReader` will close the stream, but from the method name, I haven't realized it.","created_at":"2018-08-17T01:47:11Z","updated_at":"2018-08-17T01:47:11Z","html_url":"https://github.com/Azure/azure-signalr/pull/159#discussion_r210786732","pull_request_url":"https://api.github.com/repos/Azure/azure-signalr/pulls/159","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/Azure/azure-signalr/pulls/comments/210786732"},"html":{"href":"https://github.com/Azure/azure-signalr/pull/159#discussion_r210786732"},"pull_request":{"href":"https://api.github.com/repos/Azure/azure-signalr/pulls/159"}}},"pull_request":{"url":"https://api.github.com/repos/Azure/azure-signalr/pulls/159","id":207923479,"node_id":"MDExOlB1bGxSZXF1ZXN0MjA3OTIzNDc5","html_url":"https://github.com/Azure/azure-signalr/pull/159","diff_url":"https://github.com/Azure/azure-signalr/pull/159.diff","patch_url":"https://github.com/Azure/azure-signalr/pull/159.patch","issue_url":"https://api.github.com/repos/Azure/azure-signalr/issues/159","number":159,"state":"open","locked":false,"title":"Implement ServiceConnection for Azure AspNet Service","user":{"login":"vicancy","id":668244,"node_id":"MDQ6VXNlcjY2ODI0NA==","avatar_url":"https://avatars0.githubusercontent.com/u/668244?v=4","gravatar_id":"","url":"https://api.github.com/users/vicancy","html_url":"https://github.com/vicancy","followers_url":"https://api.github.com/users/vicancy/followers","following_url":"https://api.github.com/users/vicancy/following{/other_user}","gists_url":"https://api.github.com/users/vicancy/gists{/gist_id}","starred_url":"https://api.github.com/users/vicancy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vicancy/subscriptions","organizations_url":"https://api.github.com/users/vicancy/orgs","repos_url":"https://api.github.com/users/vicancy/repos","events_url":"https://api.github.com/users/vicancy/events{/privacy}","received_events_url":"https://api.github.com/users/vicancy/received_events","type":"User","site_admin":false},"body":"Work on #101 \r\n\r\n1. Use `ClientConnectionManager` to create client connections, unlike the ASP.NET Core one, there is no need to maintain a globally available client connections here, because *heartbeat* is managed by Service side, so there is only *service<->server* and *service<->client* heartbeat, but no *server<->client* heartbeat.\r\n2. Use `ServiceConnection` for the underlying *service<->server* connection, as similar to the ASP.NET Core one, it is based on `ServiceConnectionBase`\r\n3. Add unit tests for them. The one for `ServiceConnection` is not completed, needs integration when `ServiceMessageBus` is implemented.\r\n","created_at":"2018-08-13T09:48:10Z","updated_at":"2018-08-17T01:47:11Z","closed_at":null,"merged_at":null,"merge_commit_sha":"03d69c638aeaf67c5de51dae4f118a74cac8f8ad","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/Azure/azure-signalr/pulls/159/commits","review_comments_url":"https://api.github.com/repos/Azure/azure-signalr/pulls/159/comments","review_comment_url":"https://api.github.com/repos/Azure/azure-signalr/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Azure/azure-signalr/issues/159/comments","statuses_url":"https://api.github.com/repos/Azure/azure-signalr/statuses/63510c98ed8d8a0bf5c539745d148ec0d6a05311","head":{"label":"vicancy:svc","ref":"svc","sha":"63510c98ed8d8a0bf5c539745d148ec0d6a05311","user":{"login":"vicancy","id":668244,"node_id":"MDQ6VXNlcjY2ODI0NA==","avatar_url":"https://avatars0.githubusercontent.com/u/668244?v=4","gravatar_id":"","url":"https://api.github.com/users/vicancy","html_url":"https://github.com/vicancy","followers_url":"https://api.github.com/users/vicancy/followers","following_url":"https://api.github.com/users/vicancy/following{/other_user}","gists_url":"https://api.github.com/users/vicancy/gists{/gist_id}","starred_url":"https://api.github.com/users/vicancy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vicancy/subscriptions","organizations_url":"https://api.github.com/users/vicancy/orgs","repos_url":"https://api.github.com/users/vicancy/repos","events_url":"https://api.github.com/users/vicancy/events{/privacy}","received_events_url":"https://api.github.com/users/vicancy/received_events","type":"User","site_admin":false},"repo":{"id":136438646,"node_id":"MDEwOlJlcG9zaXRvcnkxMzY0Mzg2NDY=","name":"azure-signalr","full_name":"vicancy/azure-signalr","owner":{"login":"vicancy","id":668244,"node_id":"MDQ6VXNlcjY2ODI0NA==","avatar_url":"https://avatars0.githubusercontent.com/u/668244?v=4","gravatar_id":"","url":"https://api.github.com/users/vicancy","html_url":"https://github.com/vicancy","followers_url":"https://api.github.com/users/vicancy/followers","following_url":"https://api.github.com/users/vicancy/following{/other_user}","gists_url":"https://api.github.com/users/vicancy/gists{/gist_id}","starred_url":"https://api.github.com/users/vicancy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vicancy/subscriptions","organizations_url":"https://api.github.com/users/vicancy/orgs","repos_url":"https://api.github.com/users/vicancy/repos","events_url":"https://api.github.com/users/vicancy/events{/privacy}","received_events_url":"https://api.github.com/users/vicancy/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/vicancy/azure-signalr","description":"Microsoft Azure SignalR SDK for .NET","fork":true,"url":"https://api.github.com/repos/vicancy/azure-signalr","forks_url":"https://api.github.com/repos/vicancy/azure-signalr/forks","keys_url":"https://api.github.com/repos/vicancy/azure-signalr/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vicancy/azure-signalr/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vicancy/azure-signalr/teams","hooks_url":"https://api.github.com/repos/vicancy/azure-signalr/hooks","issue_events_url":"https://api.github.com/repos/vicancy/azure-signalr/issues/events{/number}","events_url":"https://api.github.com/repos/vicancy/azure-signalr/events","assignees_url":"https://api.github.com/repos/vicancy/azure-signalr/assignees{/user}","branches_url":"https://api.github.com/repos/vicancy/azure-signalr/branches{/branch}","tags_url":"https://api.github.com/repos/vicancy/azure-signalr/tags","blobs_url":"https://api.github.com/repos/vicancy/azure-signalr/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vicancy/azure-signalr/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vicancy/azure-signalr/git/refs{/sha}","trees_url":"https://api.github.com/repos/vicancy/azure-signalr/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vicancy/azure-signalr/statuses/{sha}","languages_url":"https://api.github.com/repos/vicancy/azure-signalr/languages","stargazers_url":"https://api.github.com/repos/vicancy/azure-signalr/stargazers","contributors_url":"https://api.github.com/repos/vicancy/azure-signalr/contributors","subscribers_url":"https://api.github.com/repos/vicancy/azure-signalr/subscribers","subscription_url":"https://api.github.com/repos/vicancy/azure-signalr/subscription","commits_url":"https://api.github.com/repos/vicancy/azure-signalr/commits{/sha}","git_commits_url":"https://api.github.com/repos/vicancy/azure-signalr/git/commits{/sha}","comments_url":"https://api.github.com/repos/vicancy/azure-signalr/comments{/number}","issue_comment_url":"https://api.github.com/repos/vicancy/azure-signalr/issues/comments{/number}","contents_url":"https://api.github.com/repos/vicancy/azure-signalr/contents/{+path}","compare_url":"https://api.github.com/repos/vicancy/azure-signalr/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vicancy/azure-signalr/merges","archive_url":"https://api.github.com/repos/vicancy/azure-signalr/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vicancy/azure-signalr/downloads","issues_url":"https://api.github.com/repos/vicancy/azure-signalr/issues{/number}","pulls_url":"https://api.github.com/repos/vicancy/azure-signalr/pulls{/number}","milestones_url":"https://api.github.com/repos/vicancy/azure-signalr/milestones{/number}","notifications_url":"https://api.github.com/repos/vicancy/azure-signalr/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vicancy/azure-signalr/labels{/name}","releases_url":"https://api.github.com/repos/vicancy/azure-signalr/releases{/id}","deployments_url":"https://api.github.com/repos/vicancy/azure-signalr/deployments","created_at":"2018-06-07T07:25:18Z","updated_at":"2018-08-09T03:40:48Z","pushed_at":"2018-08-16T07:23:49Z","git_url":"git://github.com/vicancy/azure-signalr.git","ssh_url":"git@github.com:vicancy/azure-signalr.git","clone_url":"https://github.com/vicancy/azure-signalr.git","svn_url":"https://github.com/vicancy/azure-signalr","homepage":"","size":1389,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"dev"}},"base":{"label":"Azure:preview/aspnetsignalr","ref":"preview/aspnetsignalr","sha":"3294c8ff1a89ad19829b4b0c4e248a9edcebf46f","user":{"login":"Azure","id":6844498,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY4NDQ0OTg=","avatar_url":"https://avatars0.githubusercontent.com/u/6844498?v=4","gravatar_id":"","url":"https://api.github.com/users/Azure","html_url":"https://github.com/Azure","followers_url":"https://api.github.com/users/Azure/followers","following_url":"https://api.github.com/users/Azure/following{/other_user}","gists_url":"https://api.github.com/users/Azure/gists{/gist_id}","starred_url":"https://api.github.com/users/Azure/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Azure/subscriptions","organizations_url":"https://api.github.com/users/Azure/orgs","repos_url":"https://api.github.com/users/Azure/repos","events_url":"https://api.github.com/users/Azure/events{/privacy}","received_events_url":"https://api.github.com/users/Azure/received_events","type":"Organization","site_admin":false},"repo":{"id":123069751,"node_id":"MDEwOlJlcG9zaXRvcnkxMjMwNjk3NTE=","name":"azure-signalr","full_name":"Azure/azure-signalr","owner":{"login":"Azure","id":6844498,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY4NDQ0OTg=","avatar_url":"https://avatars0.githubusercontent.com/u/6844498?v=4","gravatar_id":"","url":"https://api.github.com/users/Azure","html_url":"https://github.com/Azure","followers_url":"https://api.github.com/users/Azure/followers","following_url":"https://api.github.com/users/Azure/following{/other_user}","gists_url":"https://api.github.com/users/Azure/gists{/gist_id}","starred_url":"https://api.github.com/users/Azure/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Azure/subscriptions","organizations_url":"https://api.github.com/users/Azure/orgs","repos_url":"https://api.github.com/users/Azure/repos","events_url":"https://api.github.com/users/Azure/events{/privacy}","received_events_url":"https://api.github.com/users/Azure/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Azure/azure-signalr","description":"Azure SignalR Service SDK for .NET","fork":false,"url":"https://api.github.com/repos/Azure/azure-signalr","forks_url":"https://api.github.com/repos/Azure/azure-signalr/forks","keys_url":"https://api.github.com/repos/Azure/azure-signalr/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Azure/azure-signalr/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Azure/azure-signalr/teams","hooks_url":"https://api.github.com/repos/Azure/azure-signalr/hooks","issue_events_url":"https://api.github.com/repos/Azure/azure-signalr/issues/events{/number}","events_url":"https://api.github.com/repos/Azure/azure-signalr/events","assignees_url":"https://api.github.com/repos/Azure/azure-signalr/assignees{/user}","branches_url":"https://api.github.com/repos/Azure/azure-signalr/branches{/branch}","tags_url":"https://api.github.com/repos/Azure/azure-signalr/tags","blobs_url":"https://api.github.com/repos/Azure/azure-signalr/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Azure/azure-signalr/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Azure/azure-signalr/git/refs{/sha}","trees_url":"https://api.github.com/repos/Azure/azure-signalr/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Azure/azure-signalr/statuses/{sha}","languages_url":"https://api.github.com/repos/Azure/azure-signalr/languages","stargazers_url":"https://api.github.com/repos/Azure/azure-signalr/stargazers","contributors_url":"https://api.github.com/repos/Azure/azure-signalr/contributors","subscribers_url":"https://api.github.com/repos/Azure/azure-signalr/subscribers","subscription_url":"https://api.github.com/repos/Azure/azure-signalr/subscription","commits_url":"https://api.github.com/repos/Azure/azure-signalr/commits{/sha}","git_commits_url":"https://api.github.com/repos/Azure/azure-signalr/git/commits{/sha}","comments_url":"https://api.github.com/repos/Azure/azure-signalr/comments{/number}","issue_comment_url":"https://api.github.com/repos/Azure/azure-signalr/issues/comments{/number}","contents_url":"https://api.github.com/repos/Azure/azure-signalr/contents/{+path}","compare_url":"https://api.github.com/repos/Azure/azure-signalr/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Azure/azure-signalr/merges","archive_url":"https://api.github.com/repos/Azure/azure-signalr/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Azure/azure-signalr/downloads","issues_url":"https://api.github.com/repos/Azure/azure-signalr/issues{/number}","pulls_url":"https://api.github.com/repos/Azure/azure-signalr/pulls{/number}","milestones_url":"https://api.github.com/repos/Azure/azure-signalr/milestones{/number}","notifications_url":"https://api.github.com/repos/Azure/azure-signalr/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Azure/azure-signalr/labels{/name}","releases_url":"https://api.github.com/repos/Azure/azure-signalr/releases{/id}","deployments_url":"https://api.github.com/repos/Azure/azure-signalr/deployments","created_at":"2018-02-27T03:52:57Z","updated_at":"2018-08-16T03:40:33Z","pushed_at":"2018-08-16T08:51:17Z","git_url":"git://github.com/Azure/azure-signalr.git","ssh_url":"git@github.com:Azure/azure-signalr.git","clone_url":"https://github.com/Azure/azure-signalr.git","svn_url":"https://github.com/Azure/azure-signalr","homepage":"https://aka.ms/signalr-service","size":927,"stargazers_count":26,"watchers_count":26,"language":"C#","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":8,"mirror_url":null,"archived":false,"open_issues_count":21,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":8,"open_issues":21,"watchers":26,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/Azure/azure-signalr/pulls/159"},"html":{"href":"https://github.com/Azure/azure-signalr/pull/159"},"issue":{"href":"https://api.github.com/repos/Azure/azure-signalr/issues/159"},"comments":{"href":"https://api.github.com/repos/Azure/azure-signalr/issues/159/comments"},"review_comments":{"href":"https://api.github.com/repos/Azure/azure-signalr/pulls/159/comments"},"review_comment":{"href":"https://api.github.com/repos/Azure/azure-signalr/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Azure/azure-signalr/pulls/159/commits"},"statuses":{"href":"https://api.github.com/repos/Azure/azure-signalr/statuses/63510c98ed8d8a0bf5c539745d148ec0d6a05311"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"vwxyzh"}}