{"action":"created","comment":{"url":"https://api.github.com/repos/dotnet/corefx/pulls/comments/43896594","id":43896594,"diff_hunk":"@@ -0,0 +1,231 @@\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+#include \"pal_config.h\"\n+#include \"pal_utilities.h\"\n+#include \"pal_console.h\"\n+\n+#include <assert.h>\n+#include <errno.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <termios.h>\n+#include <signal.h>\n+#include <pthread.h>\n+#include <sys/ioctl.h>\n+\n+extern \"C\" int32_t GetWindowSize(WinSize* windowSize)\n+{\n+    assert(windowSize != nullptr);\n+\n+#if HAVE_IOCTL && HAVE_TIOCGWINSZ\n+    int error = ioctl(STDOUT_FILENO, TIOCGWINSZ, windowSize);\n+\n+    if (error != 0)\n+    {\n+        *windowSize = {}; // managed out param must be initialized\n+    }\n+\n+    return error;\n+#else\n+    errno = ENOTSUP;\n+    return -1;\n+#endif\n+}\n+\n+extern \"C\" int32_t IsATty(int fd)\n+{\n+    return isatty(fd);\n+}\n+\n+extern \"C\" int32_t ReadStdinUnbuffered(void* buffer, int32_t bufferSize)\n+{\n+    assert(buffer != nullptr || bufferSize == 0);\n+    assert(bufferSize >= 0);\n+\n+    if (bufferSize < 0)\n+    {\n+        errno = EINVAL;\n+        return -1;\n+    }\n+\n+#if HAVE_TCGETATTR && HAVE_TCSETATTR && HAVE_ECHO && HAVE_ICANON && HAVE_TCSANOW\n+    struct termios oldtermios = {};\n+    struct termios newtermios = {};\n+\n+    if (tcgetattr(STDIN_FILENO, &oldtermios) < 0)\n+        return -1;\n+\n+    newtermios = oldtermios;\n+    newtermios.c_lflag &= static_cast<uint32_t>(~(ECHO | ICANON));\n+    newtermios.c_cc[VMIN] = 1;\n+    newtermios.c_cc[VTIME] = 0;\n+    if (tcsetattr(STDIN_FILENO, TCSANOW, &newtermios) < 0)\n+        return -1;\n+    ssize_t count = read(STDIN_FILENO, buffer, UnsignedCast(bufferSize));\n+    tcsetattr(STDIN_FILENO, TCSANOW, &oldtermios);\n+    return static_cast<int32_t>(count);\n+#else\n+    errno = ENOTSUP;\n+    return -1;\n+#endif\n+}\n+\n+static volatile CtrlCallback g_ctrlCallback; // Callback invoked for SIGINT/SIGQUIT \n+static struct sigaction g_previousSigIntAction;  // Prev signal handler for SIGINT\n+static struct sigaction g_previousSigQuitAction; // Prev signal handler for SIGQUIT\n+static int g_signalPipe[2] = { 0, 0 }; // Pipe used between signal handler and worker\n+\n+// Signal handler for SIGINT / SIGQUIT.\n+static void sigintquit_handler(int code)\n+{\n+    // Write the signal code to the pipe\n+    uint8_t signalCodeByte = static_cast<uint8_t>(code);\n+    ssize_t writtenBytes;\n+    do\n+    {\n+        writtenBytes = write(g_signalPipe[1], &signalCodeByte, 1);\n+    } while ((writtenBytes == -1) && (errno == EINTR));\n+\n+    if (writtenBytes != 1)\n+    {\n+        abort(); // fatal error\n+    }\n+}\n+\n+// Ctrl-handling worker thread entry point.\n+void* CtrlHandleLoop(void* arg)\n+{\n+    // Passed in argument is a ptr to the file descriptor \n+    // for the read end of the pipe.\n+    assert(arg != nullptr);\n+    int pipeFd = *reinterpret_cast<int*>(arg); \n+    free(arg);\n+    assert(pipeFd >= 0);\n+\n+    // Continually read a signal code from the signal pipe and process it,\n+    // until the pipe is closed.\n+    while (true)\n+    {\n+        // Read the next signal, trying again if we were interrupted\n+        uint8_t signalCode;\n+        ssize_t bytesRead;\n+        do\n+        {\n+            bytesRead = read(pipeFd, &signalCode, 1);\n+        } while (bytesRead == -1 && errno == EINTR);\n+\n+        if (bytesRead <= 0)\n+        {\n+            // Write end of pipe was closed or another error occurred.\n+            // Regardless, no more data is available, so we close the read\n+            // end of the pipe and exit.\n+            close(pipeFd);\n+            return nullptr;\n+        }\n+\n+        // Invoke the callback.  We take the lock while calling it so as to prevent\n+        // it from being removed while we're still using it.\n+        CtrlCallback callback = g_ctrlCallback;\n+        int rv = callback != nullptr ?  callback(signalCode == SIGQUIT ? Break : Interrupt) : 0;\n+        if (rv == 0) // callback removed or was invoked and didn't handle the signal\n+        {\n+             // The proper behavior for unhandled SIGINT/SIGQUIT is to set the signal \n+             // handler to the default and then send the signal to self and let the \n+             // default handler do its work.\n+             struct sigaction resetAction = {\n+                 .sa_handler = SIG_DFL,\n+                 .sa_flags = 0\n+             };\n+             sigemptyset(&resetAction.sa_mask);\n+\n+             sigaction(SIGINT, &resetAction, nullptr);\n+             sigaction(SIGQUIT, &resetAction, nullptr);\n+\n+             kill(getpid(), signalCode);\n+         }\n+    }\n+}\n+\n+extern \"C\" int32_t RegisterForCtrl(CtrlCallback callback)\n+{\n+    assert(callback != nullptr);\n+    assert(g_ctrlCallback == nullptr);\n+\n+    // Create a pipe we'll use to communicate with our worker\n+    // thread.  We can't do anything interesting in the signal handler,\n+    // so we instead send a message to another thread that'll do\n+    // the handling work.\n+    if (pipe(g_signalPipe) != 0)\n+    {\n+        return 0;\n+    }\n+    assert(g_signalPipe[0] >= 0);\n+    assert(g_signalPipe[1] >= 0); \n+\n+    // Create a small object to pass the read end of the pipe to the worker.\n+    int* readFdPtr = reinterpret_cast<int*>(malloc(sizeof(int)));\n+    if (readFdPtr == nullptr)\n+    {\n+        close(g_signalPipe[0]);\n+        close(g_signalPipe[1]);\n+        errno = ENOMEM;\n+        return 0;\n+    }\n+    *readFdPtr = g_signalPipe[0];\n+\n+    // The pipe is created.  Create the worker thread.\n+    pthread_t handlerThread;\n+    if (pthread_create(&handlerThread, nullptr, CtrlHandleLoop, readFdPtr) != 0)\n+    {\n+        int err = errno;\n+        free(readFdPtr);\n+        close(g_signalPipe[0]);\n+        close(g_signalPipe[1]);\n+        errno = err;\n+        return 0;\n+    }\n+\n+    // Now that all of the state is configured, store our callback.\n+    // This must be set prior to calling sigaction.\n+    g_ctrlCallback = callback;\n+\n+    // Finally, register the signal handlers for SIGINT and SIGQUIT.\n+    struct sigaction newAction = {\n+        .sa_flags = SA_RESTART,\n+        .sa_handler = &sigintquit_handler,\n+    };\n+    sigemptyset(&newAction.sa_mask);\n+\n+    int rv = sigaction(SIGINT, &newAction, &g_previousSigIntAction);\n+    assert(rv == 0);\n+    rv = sigaction(SIGQUIT, &newAction, &g_previousSigQuitAction);\n+    assert(rv == 0);\n+\n+    // Success\n+    return 1;\n+}\n+\n+extern \"C\" void UnregisterForCtrl()","path":"src/Native/System.Native/pal_console.cpp","position":210,"original_position":210,"commit_id":"8a11cbbc29132a5d66f25a5ad2294732c4103733","original_commit_id":"8a11cbbc29132a5d66f25a5ad2294732c4103733","user":{"login":"stephentoub","id":2642209,"avatar_url":"https://avatars.githubusercontent.com/u/2642209?v=3","gravatar_id":"","url":"https://api.github.com/users/stephentoub","html_url":"https://github.com/stephentoub","followers_url":"https://api.github.com/users/stephentoub/followers","following_url":"https://api.github.com/users/stephentoub/following{/other_user}","gists_url":"https://api.github.com/users/stephentoub/gists{/gist_id}","starred_url":"https://api.github.com/users/stephentoub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephentoub/subscriptions","organizations_url":"https://api.github.com/users/stephentoub/orgs","repos_url":"https://api.github.com/users/stephentoub/repos","events_url":"https://api.github.com/users/stephentoub/events{/privacy}","received_events_url":"https://api.github.com/users/stephentoub/received_events","type":"User","site_admin":false},"body":"Ok, I'll remove the unregistration. If nothing else it'll simplify the code.","created_at":"2015-11-04T15:48:52Z","updated_at":"2015-11-04T15:48:52Z","html_url":"https://github.com/dotnet/corefx/pull/4313#discussion_r43896594","pull_request_url":"https://api.github.com/repos/dotnet/corefx/pulls/4313","_links":{"self":{"href":"https://api.github.com/repos/dotnet/corefx/pulls/comments/43896594"},"html":{"href":"https://github.com/dotnet/corefx/pull/4313#discussion_r43896594"},"pull_request":{"href":"https://api.github.com/repos/dotnet/corefx/pulls/4313"}}},"pull_request":{"url":"https://api.github.com/repos/dotnet/corefx/pulls/4313","id":49657070,"html_url":"https://github.com/dotnet/corefx/pull/4313","diff_url":"https://github.com/dotnet/corefx/pull/4313.diff","patch_url":"https://github.com/dotnet/corefx/pull/4313.patch","issue_url":"https://api.github.com/repos/dotnet/corefx/issues/4313","number":4313,"state":"open","locked":false,"title":"Implement ctrl-C and ctrl-\\ signal handling in System.Native","user":{"login":"stephentoub","id":2642209,"avatar_url":"https://avatars.githubusercontent.com/u/2642209?v=3","gravatar_id":"","url":"https://api.github.com/users/stephentoub","html_url":"https://github.com/stephentoub","followers_url":"https://api.github.com/users/stephentoub/followers","following_url":"https://api.github.com/users/stephentoub/following{/other_user}","gists_url":"https://api.github.com/users/stephentoub/gists{/gist_id}","starred_url":"https://api.github.com/users/stephentoub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephentoub/subscriptions","organizations_url":"https://api.github.com/users/stephentoub/orgs","repos_url":"https://api.github.com/users/stephentoub/repos","events_url":"https://api.github.com/users/stephentoub/events{/privacy}","received_events_url":"https://api.github.com/users/stephentoub/received_events","type":"User","site_admin":false},"body":"In System.Console.dll on Unix, we currently rely on libcoreclr's signal handling to support Console.CancelKeyPress.  Such a dependency is problematic if System.Console.dll is ever to be used on another runtime.\r\n\r\nThis commit adds support for SIGINT/SIGQUIT signal handling to System.Native and then uses that support from System.Console.dll.  The functionality is implemented very similar to how libcoreclr currently does it: since we're unable to call out to managed code as part of a signal handler, a worker thread is maintained while a callback is registered, the signal handler writes to a pipe, and the worker thread reads from that pipe and dispatches the handler call to managed code.  This change largely makes libcoreclr's SetConsoleCtrlHandler defunct, as while a delegate is registered with Console.CancelKeyPress, SIGINT/SIGQUIT will not be forwarded to libcoreclr's registered handler.\r\n\r\n(In a separate commit, I also moved the other console-related functionality out of pal_io.h/cpp into pal_console.h/cpp, where I also added this logic.)\r\n\r\ncc: @ellismg, @jkotas, @janvorli, @mikem8361, @sokket ","created_at":"2015-11-04T07:40:45Z","updated_at":"2015-11-04T15:48:52Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cbfcccf1563729e40ee4c5b418d25eb45ea256bf","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/dotnet/corefx/pulls/4313/commits","review_comments_url":"https://api.github.com/repos/dotnet/corefx/pulls/4313/comments","review_comment_url":"https://api.github.com/repos/dotnet/corefx/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dotnet/corefx/issues/4313/comments","statuses_url":"https://api.github.com/repos/dotnet/corefx/statuses/8a11cbbc29132a5d66f25a5ad2294732c4103733","head":{"label":"stephentoub:ctrlc_handling","ref":"ctrlc_handling","sha":"8a11cbbc29132a5d66f25a5ad2294732c4103733","user":{"login":"stephentoub","id":2642209,"avatar_url":"https://avatars.githubusercontent.com/u/2642209?v=3","gravatar_id":"","url":"https://api.github.com/users/stephentoub","html_url":"https://github.com/stephentoub","followers_url":"https://api.github.com/users/stephentoub/followers","following_url":"https://api.github.com/users/stephentoub/following{/other_user}","gists_url":"https://api.github.com/users/stephentoub/gists{/gist_id}","starred_url":"https://api.github.com/users/stephentoub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephentoub/subscriptions","organizations_url":"https://api.github.com/users/stephentoub/orgs","repos_url":"https://api.github.com/users/stephentoub/repos","events_url":"https://api.github.com/users/stephentoub/events{/privacy}","received_events_url":"https://api.github.com/users/stephentoub/received_events","type":"User","site_admin":false},"repo":{"id":26551529,"name":"corefx","full_name":"stephentoub/corefx","owner":{"login":"stephentoub","id":2642209,"avatar_url":"https://avatars.githubusercontent.com/u/2642209?v=3","gravatar_id":"","url":"https://api.github.com/users/stephentoub","html_url":"https://github.com/stephentoub","followers_url":"https://api.github.com/users/stephentoub/followers","following_url":"https://api.github.com/users/stephentoub/following{/other_user}","gists_url":"https://api.github.com/users/stephentoub/gists{/gist_id}","starred_url":"https://api.github.com/users/stephentoub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephentoub/subscriptions","organizations_url":"https://api.github.com/users/stephentoub/orgs","repos_url":"https://api.github.com/users/stephentoub/repos","events_url":"https://api.github.com/users/stephentoub/events{/privacy}","received_events_url":"https://api.github.com/users/stephentoub/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/stephentoub/corefx","description":"This repository contains the foundational libraries that make up the .NET Core development stack.","fork":true,"url":"https://api.github.com/repos/stephentoub/corefx","forks_url":"https://api.github.com/repos/stephentoub/corefx/forks","keys_url":"https://api.github.com/repos/stephentoub/corefx/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stephentoub/corefx/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stephentoub/corefx/teams","hooks_url":"https://api.github.com/repos/stephentoub/corefx/hooks","issue_events_url":"https://api.github.com/repos/stephentoub/corefx/issues/events{/number}","events_url":"https://api.github.com/repos/stephentoub/corefx/events","assignees_url":"https://api.github.com/repos/stephentoub/corefx/assignees{/user}","branches_url":"https://api.github.com/repos/stephentoub/corefx/branches{/branch}","tags_url":"https://api.github.com/repos/stephentoub/corefx/tags","blobs_url":"https://api.github.com/repos/stephentoub/corefx/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stephentoub/corefx/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stephentoub/corefx/git/refs{/sha}","trees_url":"https://api.github.com/repos/stephentoub/corefx/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stephentoub/corefx/statuses/{sha}","languages_url":"https://api.github.com/repos/stephentoub/corefx/languages","stargazers_url":"https://api.github.com/repos/stephentoub/corefx/stargazers","contributors_url":"https://api.github.com/repos/stephentoub/corefx/contributors","subscribers_url":"https://api.github.com/repos/stephentoub/corefx/subscribers","subscription_url":"https://api.github.com/repos/stephentoub/corefx/subscription","commits_url":"https://api.github.com/repos/stephentoub/corefx/commits{/sha}","git_commits_url":"https://api.github.com/repos/stephentoub/corefx/git/commits{/sha}","comments_url":"https://api.github.com/repos/stephentoub/corefx/comments{/number}","issue_comment_url":"https://api.github.com/repos/stephentoub/corefx/issues/comments{/number}","contents_url":"https://api.github.com/repos/stephentoub/corefx/contents/{+path}","compare_url":"https://api.github.com/repos/stephentoub/corefx/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stephentoub/corefx/merges","archive_url":"https://api.github.com/repos/stephentoub/corefx/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stephentoub/corefx/downloads","issues_url":"https://api.github.com/repos/stephentoub/corefx/issues{/number}","pulls_url":"https://api.github.com/repos/stephentoub/corefx/pulls{/number}","milestones_url":"https://api.github.com/repos/stephentoub/corefx/milestones{/number}","notifications_url":"https://api.github.com/repos/stephentoub/corefx/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stephentoub/corefx/labels{/name}","releases_url":"https://api.github.com/repos/stephentoub/corefx/releases{/id}","created_at":"2014-11-12T19:06:24Z","updated_at":"2015-05-08T20:06:59Z","pushed_at":"2015-11-04T08:03:46Z","git_url":"git://github.com/stephentoub/corefx.git","ssh_url":"git@github.com:stephentoub/corefx.git","clone_url":"https://github.com/stephentoub/corefx.git","svn_url":"https://github.com/stephentoub/corefx","homepage":"","size":40642,"stargazers_count":1,"watchers_count":1,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"dotnet:master","ref":"master","sha":"3fec23b9d60b25473a752986997c77e58d28c366","user":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"repo":{"id":26295345,"name":"corefx","full_name":"dotnet/corefx","owner":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dotnet/corefx","description":"This repo contains the .NET Core foundational libraries, called CoreFX. It includes classes for collections, file systems, console, XML, async and many others.","fork":false,"url":"https://api.github.com/repos/dotnet/corefx","forks_url":"https://api.github.com/repos/dotnet/corefx/forks","keys_url":"https://api.github.com/repos/dotnet/corefx/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dotnet/corefx/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dotnet/corefx/teams","hooks_url":"https://api.github.com/repos/dotnet/corefx/hooks","issue_events_url":"https://api.github.com/repos/dotnet/corefx/issues/events{/number}","events_url":"https://api.github.com/repos/dotnet/corefx/events","assignees_url":"https://api.github.com/repos/dotnet/corefx/assignees{/user}","branches_url":"https://api.github.com/repos/dotnet/corefx/branches{/branch}","tags_url":"https://api.github.com/repos/dotnet/corefx/tags","blobs_url":"https://api.github.com/repos/dotnet/corefx/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dotnet/corefx/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dotnet/corefx/git/refs{/sha}","trees_url":"https://api.github.com/repos/dotnet/corefx/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dotnet/corefx/statuses/{sha}","languages_url":"https://api.github.com/repos/dotnet/corefx/languages","stargazers_url":"https://api.github.com/repos/dotnet/corefx/stargazers","contributors_url":"https://api.github.com/repos/dotnet/corefx/contributors","subscribers_url":"https://api.github.com/repos/dotnet/corefx/subscribers","subscription_url":"https://api.github.com/repos/dotnet/corefx/subscription","commits_url":"https://api.github.com/repos/dotnet/corefx/commits{/sha}","git_commits_url":"https://api.github.com/repos/dotnet/corefx/git/commits{/sha}","comments_url":"https://api.github.com/repos/dotnet/corefx/comments{/number}","issue_comment_url":"https://api.github.com/repos/dotnet/corefx/issues/comments{/number}","contents_url":"https://api.github.com/repos/dotnet/corefx/contents/{+path}","compare_url":"https://api.github.com/repos/dotnet/corefx/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dotnet/corefx/merges","archive_url":"https://api.github.com/repos/dotnet/corefx/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dotnet/corefx/downloads","issues_url":"https://api.github.com/repos/dotnet/corefx/issues{/number}","pulls_url":"https://api.github.com/repos/dotnet/corefx/pulls{/number}","milestones_url":"https://api.github.com/repos/dotnet/corefx/milestones{/number}","notifications_url":"https://api.github.com/repos/dotnet/corefx/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dotnet/corefx/labels{/name}","releases_url":"https://api.github.com/repos/dotnet/corefx/releases{/id}","created_at":"2014-11-06T23:42:48Z","updated_at":"2015-11-04T15:12:50Z","pushed_at":"2015-11-04T11:59:13Z","git_url":"git://github.com/dotnet/corefx.git","ssh_url":"git@github.com:dotnet/corefx.git","clone_url":"https://github.com/dotnet/corefx.git","svn_url":"https://github.com/dotnet/corefx","homepage":"http://dotnet.github.io/core/","size":89699,"stargazers_count":8525,"watchers_count":8525,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1885,"mirror_url":null,"open_issues_count":652,"forks":1885,"open_issues":652,"watchers":8525,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dotnet/corefx/pulls/4313"},"html":{"href":"https://github.com/dotnet/corefx/pull/4313"},"issue":{"href":"https://api.github.com/repos/dotnet/corefx/issues/4313"},"comments":{"href":"https://api.github.com/repos/dotnet/corefx/issues/4313/comments"},"review_comments":{"href":"https://api.github.com/repos/dotnet/corefx/pulls/4313/comments"},"review_comment":{"href":"https://api.github.com/repos/dotnet/corefx/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dotnet/corefx/pulls/4313/commits"},"statuses":{"href":"https://api.github.com/repos/dotnet/corefx/statuses/8a11cbbc29132a5d66f25a5ad2294732c4103733"}}}}