{"action":"created","comment":{"url":"https://api.github.com/repos/Azure/autorest/pulls/comments/61806695","id":61806695,"diff_hunk":"@@ -0,0 +1,185 @@\n+﻿// Copyright (c) Microsoft Corporation. All rights reserved.\n+// Licensed under the MIT License. See License.txt in the project root for license information.\n+\n+using System;\n+using System.Collections.Generic;\n+using System.Net.Http;\n+using System.Threading;\n+using System.Threading.Tasks;\n+\n+namespace Microsoft.Rest\n+{\n+    /// <summary>\n+    /// HTTP batch handler.\n+    /// It expects the caller to dispose HttpRequestMessage and HttpResponseMessage.\n+    /// It assumes the web service is supporting batch requests as per this MSDN article:\n+    /// https://blogs.msdn.microsoft.com/webdev/2013/11/01/introducing-batch-support-in-web-api-and-web-api-odata/\n+    /// i.e. requests are batched into a single request inside the multipart content.\n+    /// </summary>\n+    public class BatchDelegatingHandler : DelegatingHandler\n+    {\n+        /// <summary>\n+        /// HTTP method (e.g. POST or PUT) to use in issuing a batch call.\n+        /// </summary>\n+        private readonly HttpMethod batchMethod;\n+\n+        /// <summary>\n+        /// URL to issue the batch call to.\n+        /// </summary>\n+        private readonly Uri batchURL;\n+\n+        /// <summary>\n+        /// List of operations to issue in a batch.\n+        /// </summary>\n+        private List<HttpRequestMessage> requests;\n+\n+        /// <summary>\n+        /// Array of HTTP content returned by the server in response to operations that are issued in a batch.\n+        /// </summary>\n+        private HttpContent[] responses;\n+\n+        /// <summary>\n+        /// Event that tells all tasks that they can proceed.\n+        /// </summary>\n+        private ManualResetEvent batchCompleted;\n+\n+        /// <summary>\n+        /// Initializes a new instance of the <see cref=\"BatchDelegatingHandler\"/> class.\n+        /// </summary>\n+        /// <param name=\"method\">the type of method that the Batch operation is</param>\n+        /// <param name=\"url\">the URL that the Batch operation needs to hit</param>\n+        public BatchDelegatingHandler(HttpMethod method, Uri url)\n+        {\n+            this.batchMethod = method;\n+            this.batchURL = url;\n+            this.Reset();\n+        }\n+\n+        /// <summary>\n+        /// Resets the batch handler to accept a new set of batch requests.\n+        /// </summary>\n+        public void Reset()\n+        {\n+            this.requests = new List<HttpRequestMessage>();\n+            this.responses = null;\n+            this.batchCompleted = new ManualResetEvent(false);\n+        }\n+\n+        /// <summary>\n+        /// Send all the queued up requests as a single batch request to the inner handler.\n+        /// </summary>\n+        /// <param name=\"cancellationToken\">Cancellation token for the batch request</param>\n+        /// <param name=\"boundary\">Optional string which will be used as the encapsulation boundary between each request in multipart content; if not specified, a random GUID will be used</param>\n+        /// <returns>Task for the batch</returns>\n+        public async Task IssueBatch(CancellationToken cancellationToken = default(CancellationToken), string boundary = null)\n+        {\n+            // There must be at least one request to issue\n+            if (this.requests.Count == 0)\n+            {\n+                throw new Exception(\"No queued requests to issue.\");\n+            }\n+\n+            // Sanity check. There must not be any queued responses from the server.\n+            if (this.responses != null)\n+            {\n+                throw new Exception(\"This batch handler instance has been reused without calling reset first.\");\n+            }\n+\n+            // Create a request to the batch URL\n+            HttpRequestMessage batchRequest = new HttpRequestMessage(this.batchMethod, this.batchURL);\n+\n+            // Create the boundary\n+            if (string.IsNullOrEmpty(boundary))\n+            {\n+                boundary = \"batch_\" + Guid.NewGuid().ToString();\n+            }\n+\n+            // Add a multipart content to the body of the request\n+            MultipartContent batchContent = new MultipartContent(\"mixed\", boundary);\n+            batchRequest.Content = batchContent;\n+\n+            // add each request to this batch request\n+            foreach (HttpRequestMessage message in this.requests)\n+            {\n+                batchContent.Add(new HttpMessageContent(message));\n+            }\n+\n+            // send the batch request to the inner handler\n+            HttpResponseMessage batchResponse = await base.SendAsync(batchRequest, cancellationToken);\n+\n+            // throw exceptions if the response is unexpected\n+            if (batchResponse == null)\n+            {\n+                throw new Exception(\"Batch request got back a null response.\");\n+            }\n+\n+            batchResponse.EnsureSuccessStatusCode();\n+\n+            if (batchResponse.Content == null)\n+            {\n+                throw new Exception(\"Batch request got back a response with null content.\");\n+            }\n+\n+            if (!batchResponse.Content.IsMimeMultipartContent(\"mixed\"))\n+            {\n+                throw new Exception(\"Batch response returned unexpected content type.\");\n+            }\n+\n+            // pull out the responses\n+            MultipartMemoryStreamProvider responseContents = await batchResponse.Content.ReadAsMultipartAsync();\n+            if (responseContents == null)\n+            {\n+                throw new Exception(\"Failed to convert response content into multipart components.\");\n+            }\n+\n+            if (responseContents.Contents.Count != this.requests.Count)\n+            {\n+                throw new Exception(\"Batch response returned \" + responseContents.Contents.Count + \" number of responses instead of the expected \" + this.requests.Count + \".\");\n+            }\n+\n+            // copy the responses over into the array that is accessible by the tasks waiting for individual responses\n+            this.responses = new HttpContent[responseContents.Contents.Count];\n+            responseContents.Contents.CopyTo(this.responses, 0);\n+\n+            // signal tasks that they can proceed with using the response\n+            this.batchCompleted.Set();\n+        }\n+\n+        /// <summary>\n+        /// Queue an HTTP request that will be eventually sent to the inner handler when you call IssueBatch.\n+        /// DO NOT await nor access the result of the returned task until IssueBatch has been successfully awaited.\n+        /// </summary>\n+        /// <param name=\"request\">HTTP request message to send to the server</param>\n+        /// <param name=\"cancellationToken\">Not used</param>\n+        /// <returns>Task that when awaited will provide the HTTP response from the server</returns>\n+        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\n+        {\n+            // Sanity check. There must not be any queued responses from the server.\n+            if (this.responses != null)\n+            {\n+                throw new Exception(\"This batch handler instance has been reused without calling reset first.\");\n+            }\n+\n+            // what is my queue position?\n+            int position = this.requests.Count;\n+\n+            // add the request to the queue\n+            this.requests.Add(request);\n+\n+            // send back a task that will return the appropriate value\n+            Task<HttpResponseMessage> task = Task<HttpResponseMessage>.Factory.StartNew(() => this.GetResponse(position));\n+            return task;\n+        }\n+\n+        /// <summary>\n+        /// Wait for the batch call to finish and then return the requested response.\n+        /// </summary>\n+        /// <param name=\"index\">Which response to return</param>\n+        /// <returns>The HTTP response message inside the batch at the given index</returns>\n+        private HttpResponseMessage GetResponse(int index)\n+        {\n+            this.batchCompleted.WaitOne();","path":"ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/BatchDelegatingHandler.cs","position":181,"original_position":181,"commit_id":"a12679c36c4a4b073938570a474d0f6600560b51","original_commit_id":"a12679c36c4a4b073938570a474d0f6600560b51","user":{"login":"markcowl","id":1054056,"avatar_url":"https://avatars.githubusercontent.com/u/1054056?v=3","gravatar_id":"","url":"https://api.github.com/users/markcowl","html_url":"https://github.com/markcowl","followers_url":"https://api.github.com/users/markcowl/followers","following_url":"https://api.github.com/users/markcowl/following{/other_user}","gists_url":"https://api.github.com/users/markcowl/gists{/gist_id}","starred_url":"https://api.github.com/users/markcowl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markcowl/subscriptions","organizations_url":"https://api.github.com/users/markcowl/orgs","repos_url":"https://api.github.com/users/markcowl/repos","events_url":"https://api.github.com/users/markcowl/events{/privacy}","received_events_url":"https://api.github.com/users/markcowl/received_events","type":"User","site_admin":false},"body":"Should there be a configurable timeout here?","created_at":"2016-05-02T21:20:35Z","updated_at":"2016-05-02T21:20:35Z","html_url":"https://github.com/Azure/autorest/pull/914#discussion_r61806695","pull_request_url":"https://api.github.com/repos/Azure/autorest/pulls/914","_links":{"self":{"href":"https://api.github.com/repos/Azure/autorest/pulls/comments/61806695"},"html":{"href":"https://github.com/Azure/autorest/pull/914#discussion_r61806695"},"pull_request":{"href":"https://api.github.com/repos/Azure/autorest/pulls/914"}}},"pull_request":{"url":"https://api.github.com/repos/Azure/autorest/pulls/914","id":65182038,"html_url":"https://github.com/Azure/autorest/pull/914","diff_url":"https://github.com/Azure/autorest/pull/914.diff","patch_url":"https://github.com/Azure/autorest/pull/914.patch","issue_url":"https://api.github.com/repos/Azure/autorest/issues/914","number":914,"state":"open","locked":false,"title":"support for Batch API call in C# client","user":{"login":"sharadagarwal","id":5649639,"avatar_url":"https://avatars.githubusercontent.com/u/5649639?v=3","gravatar_id":"","url":"https://api.github.com/users/sharadagarwal","html_url":"https://github.com/sharadagarwal","followers_url":"https://api.github.com/users/sharadagarwal/followers","following_url":"https://api.github.com/users/sharadagarwal/following{/other_user}","gists_url":"https://api.github.com/users/sharadagarwal/gists{/gist_id}","starred_url":"https://api.github.com/users/sharadagarwal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sharadagarwal/subscriptions","organizations_url":"https://api.github.com/users/sharadagarwal/orgs","repos_url":"https://api.github.com/users/sharadagarwal/repos","events_url":"https://api.github.com/users/sharadagarwal/events{/privacy}","received_events_url":"https://api.github.com/users/sharadagarwal/received_events","type":"User","site_admin":false},"body":"Background information is in issue #837 .\r\n\r\nThis pull request includes:\r\n\r\n- BatchDelegatingHandler : C# clients can use this to pack multiple API calls into a single batch call\r\n- Documentation on how to use it\r\n- Test that verifies it is working, including changes to existing test classes to support this test\r\n\r\nPlease note that while I've used AutoRest extensively to create clients, I am new to the AutoRest code base. Thanks @devigned and @stankovski for discussions that led up to this pull request.","created_at":"2016-04-04T17:26:43Z","updated_at":"2016-05-02T21:20:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f1c7d511cda36a177f0f88a991a7b4e7336e8bf2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/Azure/autorest/pulls/914/commits","review_comments_url":"https://api.github.com/repos/Azure/autorest/pulls/914/comments","review_comment_url":"https://api.github.com/repos/Azure/autorest/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Azure/autorest/issues/914/comments","statuses_url":"https://api.github.com/repos/Azure/autorest/statuses/a12679c36c4a4b073938570a474d0f6600560b51","head":{"label":"sharadagarwal:master","ref":"master","sha":"a12679c36c4a4b073938570a474d0f6600560b51","user":{"login":"sharadagarwal","id":5649639,"avatar_url":"https://avatars.githubusercontent.com/u/5649639?v=3","gravatar_id":"","url":"https://api.github.com/users/sharadagarwal","html_url":"https://github.com/sharadagarwal","followers_url":"https://api.github.com/users/sharadagarwal/followers","following_url":"https://api.github.com/users/sharadagarwal/following{/other_user}","gists_url":"https://api.github.com/users/sharadagarwal/gists{/gist_id}","starred_url":"https://api.github.com/users/sharadagarwal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sharadagarwal/subscriptions","organizations_url":"https://api.github.com/users/sharadagarwal/orgs","repos_url":"https://api.github.com/users/sharadagarwal/repos","events_url":"https://api.github.com/users/sharadagarwal/events{/privacy}","received_events_url":"https://api.github.com/users/sharadagarwal/received_events","type":"User","site_admin":false},"repo":{"id":55171009,"name":"autorest","full_name":"sharadagarwal/autorest","owner":{"login":"sharadagarwal","id":5649639,"avatar_url":"https://avatars.githubusercontent.com/u/5649639?v=3","gravatar_id":"","url":"https://api.github.com/users/sharadagarwal","html_url":"https://github.com/sharadagarwal","followers_url":"https://api.github.com/users/sharadagarwal/followers","following_url":"https://api.github.com/users/sharadagarwal/following{/other_user}","gists_url":"https://api.github.com/users/sharadagarwal/gists{/gist_id}","starred_url":"https://api.github.com/users/sharadagarwal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sharadagarwal/subscriptions","organizations_url":"https://api.github.com/users/sharadagarwal/orgs","repos_url":"https://api.github.com/users/sharadagarwal/repos","events_url":"https://api.github.com/users/sharadagarwal/events{/privacy}","received_events_url":"https://api.github.com/users/sharadagarwal/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sharadagarwal/autorest","description":"","fork":true,"url":"https://api.github.com/repos/sharadagarwal/autorest","forks_url":"https://api.github.com/repos/sharadagarwal/autorest/forks","keys_url":"https://api.github.com/repos/sharadagarwal/autorest/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sharadagarwal/autorest/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sharadagarwal/autorest/teams","hooks_url":"https://api.github.com/repos/sharadagarwal/autorest/hooks","issue_events_url":"https://api.github.com/repos/sharadagarwal/autorest/issues/events{/number}","events_url":"https://api.github.com/repos/sharadagarwal/autorest/events","assignees_url":"https://api.github.com/repos/sharadagarwal/autorest/assignees{/user}","branches_url":"https://api.github.com/repos/sharadagarwal/autorest/branches{/branch}","tags_url":"https://api.github.com/repos/sharadagarwal/autorest/tags","blobs_url":"https://api.github.com/repos/sharadagarwal/autorest/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sharadagarwal/autorest/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sharadagarwal/autorest/git/refs{/sha}","trees_url":"https://api.github.com/repos/sharadagarwal/autorest/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sharadagarwal/autorest/statuses/{sha}","languages_url":"https://api.github.com/repos/sharadagarwal/autorest/languages","stargazers_url":"https://api.github.com/repos/sharadagarwal/autorest/stargazers","contributors_url":"https://api.github.com/repos/sharadagarwal/autorest/contributors","subscribers_url":"https://api.github.com/repos/sharadagarwal/autorest/subscribers","subscription_url":"https://api.github.com/repos/sharadagarwal/autorest/subscription","commits_url":"https://api.github.com/repos/sharadagarwal/autorest/commits{/sha}","git_commits_url":"https://api.github.com/repos/sharadagarwal/autorest/git/commits{/sha}","comments_url":"https://api.github.com/repos/sharadagarwal/autorest/comments{/number}","issue_comment_url":"https://api.github.com/repos/sharadagarwal/autorest/issues/comments{/number}","contents_url":"https://api.github.com/repos/sharadagarwal/autorest/contents/{+path}","compare_url":"https://api.github.com/repos/sharadagarwal/autorest/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sharadagarwal/autorest/merges","archive_url":"https://api.github.com/repos/sharadagarwal/autorest/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sharadagarwal/autorest/downloads","issues_url":"https://api.github.com/repos/sharadagarwal/autorest/issues{/number}","pulls_url":"https://api.github.com/repos/sharadagarwal/autorest/pulls{/number}","milestones_url":"https://api.github.com/repos/sharadagarwal/autorest/milestones{/number}","notifications_url":"https://api.github.com/repos/sharadagarwal/autorest/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sharadagarwal/autorest/labels{/name}","releases_url":"https://api.github.com/repos/sharadagarwal/autorest/releases{/id}","deployments_url":"https://api.github.com/repos/sharadagarwal/autorest/deployments","created_at":"2016-03-31T17:45:52Z","updated_at":"2016-03-31T17:46:00Z","pushed_at":"2016-04-04T17:16:05Z","git_url":"git://github.com/sharadagarwal/autorest.git","ssh_url":"git@github.com:sharadagarwal/autorest.git","clone_url":"https://github.com/sharadagarwal/autorest.git","svn_url":"https://github.com/sharadagarwal/autorest","homepage":null,"size":42232,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Azure:master","ref":"master","sha":"afd67f1955c3eeda0ba437db8b47c0437ed6145c","user":{"login":"Azure","id":6844498,"avatar_url":"https://avatars.githubusercontent.com/u/6844498?v=3","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":31089461,"name":"autorest","full_name":"Azure/autorest","owner":{"login":"Azure","id":6844498,"avatar_url":"https://avatars.githubusercontent.com/u/6844498?v=3","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/autorest","description":"","fork":false,"url":"https://api.github.com/repos/Azure/autorest","forks_url":"https://api.github.com/repos/Azure/autorest/forks","keys_url":"https://api.github.com/repos/Azure/autorest/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Azure/autorest/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Azure/autorest/teams","hooks_url":"https://api.github.com/repos/Azure/autorest/hooks","issue_events_url":"https://api.github.com/repos/Azure/autorest/issues/events{/number}","events_url":"https://api.github.com/repos/Azure/autorest/events","assignees_url":"https://api.github.com/repos/Azure/autorest/assignees{/user}","branches_url":"https://api.github.com/repos/Azure/autorest/branches{/branch}","tags_url":"https://api.github.com/repos/Azure/autorest/tags","blobs_url":"https://api.github.com/repos/Azure/autorest/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Azure/autorest/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Azure/autorest/git/refs{/sha}","trees_url":"https://api.github.com/repos/Azure/autorest/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Azure/autorest/statuses/{sha}","languages_url":"https://api.github.com/repos/Azure/autorest/languages","stargazers_url":"https://api.github.com/repos/Azure/autorest/stargazers","contributors_url":"https://api.github.com/repos/Azure/autorest/contributors","subscribers_url":"https://api.github.com/repos/Azure/autorest/subscribers","subscription_url":"https://api.github.com/repos/Azure/autorest/subscription","commits_url":"https://api.github.com/repos/Azure/autorest/commits{/sha}","git_commits_url":"https://api.github.com/repos/Azure/autorest/git/commits{/sha}","comments_url":"https://api.github.com/repos/Azure/autorest/comments{/number}","issue_comment_url":"https://api.github.com/repos/Azure/autorest/issues/comments{/number}","contents_url":"https://api.github.com/repos/Azure/autorest/contents/{+path}","compare_url":"https://api.github.com/repos/Azure/autorest/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Azure/autorest/merges","archive_url":"https://api.github.com/repos/Azure/autorest/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Azure/autorest/downloads","issues_url":"https://api.github.com/repos/Azure/autorest/issues{/number}","pulls_url":"https://api.github.com/repos/Azure/autorest/pulls{/number}","milestones_url":"https://api.github.com/repos/Azure/autorest/milestones{/number}","notifications_url":"https://api.github.com/repos/Azure/autorest/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Azure/autorest/labels{/name}","releases_url":"https://api.github.com/repos/Azure/autorest/releases{/id}","deployments_url":"https://api.github.com/repos/Azure/autorest/deployments","created_at":"2015-02-20T23:02:54Z","updated_at":"2016-05-01T12:59:52Z","pushed_at":"2016-05-02T20:42:27Z","git_url":"git://github.com/Azure/autorest.git","ssh_url":"git@github.com:Azure/autorest.git","clone_url":"https://github.com/Azure/autorest.git","svn_url":"https://github.com/Azure/autorest","homepage":null,"size":46752,"stargazers_count":359,"watchers_count":359,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":118,"mirror_url":null,"open_issues_count":108,"forks":118,"open_issues":108,"watchers":359,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Azure/autorest/pulls/914"},"html":{"href":"https://github.com/Azure/autorest/pull/914"},"issue":{"href":"https://api.github.com/repos/Azure/autorest/issues/914"},"comments":{"href":"https://api.github.com/repos/Azure/autorest/issues/914/comments"},"review_comments":{"href":"https://api.github.com/repos/Azure/autorest/pulls/914/comments"},"review_comment":{"href":"https://api.github.com/repos/Azure/autorest/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Azure/autorest/pulls/914/commits"},"statuses":{"href":"https://api.github.com/repos/Azure/autorest/statuses/a12679c36c4a4b073938570a474d0f6600560b51"}}}}