{"action":"created","comment":{"url":"https://api.github.com/repos/netty/netty/pulls/comments/29300254","id":29300254,"diff_hunk":"@@ -0,0 +1,354 @@\n+/*\n+ * Copyright 2015 The Netty Project\n+ *\n+ * The Netty Project licenses this file to you under the Apache License,\n+ * version 2.0 (the \"License\"); you may not use this file except in compliance\n+ * with the License. You may obtain a copy of the License at:\n+ *\n+ *   http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n+ * License for the specific language governing permissions and limitations\n+ * under the License.\n+ */\n+package io.netty.channel.pool;\n+\n+import io.netty.bootstrap.Bootstrap;\n+import io.netty.channel.Channel;\n+import io.netty.util.concurrent.EventExecutor;\n+import io.netty.util.concurrent.Future;\n+import io.netty.util.concurrent.FutureListener;\n+import io.netty.util.concurrent.Promise;\n+import io.netty.util.internal.EmptyArrays;\n+import io.netty.util.internal.OneTimeTask;\n+\n+import java.util.ArrayDeque;\n+import java.util.Queue;\n+import java.util.concurrent.ScheduledFuture;\n+import java.util.concurrent.TimeUnit;\n+import java.util.concurrent.TimeoutException;\n+\n+/**\n+ * {@link ChannelPool} implementation that takes another {@link ChannelPool} implementation and enfore a maximum\n+ * number of concurrent connections.\n+ */\n+public final class FixedChannelPool extends SimpleChannelPool {\n+    private static final IllegalStateException FULL_EXCEPTION =\n+            new IllegalStateException(\"Too many outstanding acquire operations\");\n+    private static final TimeoutException TIMEOUT_EXCEPTION =\n+            new TimeoutException(\"Acquire operation took longer then configured maximum time\");\n+    private static final long START_TIME = System.nanoTime();\n+\n+    static {\n+        FULL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);\n+        TIMEOUT_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);\n+    }\n+\n+    enum AcquireTimeoutAction {\n+        /**\n+         * Create a new connection when the timeout is detected.\n+         */\n+        NewConnection,\n+\n+        /**\n+         * Fail the {@link Future} of the acquire call with a {@link TimeoutException}.\n+         */\n+        Fail\n+    }\n+\n+    private final EventExecutor executor;\n+    private final long acquireTimeoutNanos;\n+    private final Runnable timeoutTask;\n+\n+    // There is no need to worry about synchronization as everything that modified the queue or counts is done\n+    // by the above EventExecutor.\n+    private final Queue<AcquireTask> pendingAcquireQueue = new ArrayDeque<AcquireTask>();\n+    private final int maxConnections;\n+    private final int maxPendingAcquires;\n+    private int acquiredChannelCount;\n+    private int pendingAcquireCount;\n+\n+    /**\n+     * Creates a new instance using the {@link ChannelHealthChecker#ACTIVE}.\n+     *\n+     * @param bootstrap         the {@link Bootstrap} that is used for connections\n+     * @param handler           the {@link ChannelPoolHandler} that will be notified for the different pool actions\n+     * @param maxConnections    the numnber of maximal active connections, once this is reached new tries to acquire\n+     *                          a {@link Channel} will be delayed until a connection is returned to the pool again.\n+     */\n+    public FixedChannelPool(Bootstrap bootstrap,\n+                            ChannelPoolHandler handler, int maxConnections) {\n+        this(bootstrap, handler, maxConnections, Integer.MAX_VALUE);\n+    }\n+\n+    /**\n+     * Creates a new instance using the {@link ChannelHealthChecker#ACTIVE}.\n+     *\n+     * @param bootstrap             the {@link Bootstrap} that is used for connections\n+     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions\n+     * @param maxConnections        the numnber of maximal active connections, once this is reached new tries to\n+     *                              acquire a {@link Channel} will be delayed until a connection is returned to the\n+     *                              pool again.\n+     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will\n+     *                              be failed.\n+     */\n+    public FixedChannelPool(Bootstrap bootstrap,\n+                            ChannelPoolHandler handler, int maxConnections, int maxPendingAcquires) {\n+        super(bootstrap, handler);\n+        executor = bootstrap.group().next();\n+        timeoutTask = null;\n+        acquireTimeoutNanos = -1;\n+        this.maxConnections = maxConnections;\n+        this.maxPendingAcquires = maxPendingAcquires;\n+    }\n+\n+    /**\n+     * Creates a new instance.\n+     *\n+     * @param bootstrap             the {@link Bootstrap} that is used for connections\n+     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions\n+     * @param healthCheck           the {@link ChannelHealthChecker} that will be used to check if a {@link Channel} is\n+     *                              still healty when obtain from the {@link ChannelPool}\n+     * @param action                the {@link AcquireTimeoutAction} to use or {@code null} if non should be used.\n+     *                              In this case {@param acquireTimeoutMillis} must be {@code -1}.\n+     * @param acquireTimeoutMillis  the time (in milliseconds) after which an pending acquire must complete or\n+     *                              the {@link AcquireTimeoutAction} takes place.\n+     * @param maxConnections        the numnber of maximal active connections, once this is reached new tries to\n+     *                              acquire a {@link Channel} will be delayed until a connection is returned to the\n+     *                              pool again.\n+     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will\n+     *                              be failed.\n+     */\n+    public FixedChannelPool(Bootstrap bootstrap,\n+                            ChannelPoolHandler  handler,\n+                            ChannelHealthChecker healthCheck, AcquireTimeoutAction action,\n+                            final long acquireTimeoutMillis,\n+                            int maxConnections, int maxPendingAcquires) {\n+        super(bootstrap, handler, healthCheck);\n+        if (maxConnections < 1) {\n+            throw new IllegalArgumentException(\"maxConnections: \" + maxConnections + \" (expected: >= 1)\");\n+        }\n+        if (maxPendingAcquires < 1) {\n+            throw new IllegalArgumentException(\"maxPendingAcquires: \" + maxPendingAcquires + \" (expected: >= 1)\");\n+        }\n+        if (action == null && acquireTimeoutMillis == -1) {\n+            timeoutTask = null;\n+            acquireTimeoutNanos = -1;\n+        } else if (action == null && acquireTimeoutMillis != -1) {\n+            throw new NullPointerException(\"action\");\n+        } else if (action != null && acquireTimeoutMillis < 0) {\n+            throw new IllegalArgumentException(\"acquireTimeoutMillis: \" + acquireTimeoutMillis + \" (expected: >= 1)\");\n+        } else {\n+            acquireTimeoutNanos = TimeUnit.MILLISECONDS.toNanos(acquireTimeoutMillis);\n+            switch (action) {\n+            case Fail:\n+                timeoutTask = new TimeoutTask() {\n+                    @Override\n+                    public void onTimeout(AcquireTask task) {\n+                        // Fail the promise as we timed out.\n+                        task.promise.setFailure(TIMEOUT_EXCEPTION);\n+                    }\n+                };\n+                break;\n+            case NewConnection:\n+                timeoutTask = new TimeoutTask() {\n+                    @Override\n+                    public void onTimeout(AcquireTask task) {\n+                        // Increment the acquire count and delegate to super to actually acquire a Channel which will\n+                        // create a new connetion.\n+                        ++acquiredChannelCount;\n+\n+                        FixedChannelPool.super.acquire(task.promise);\n+                    }\n+                };\n+                break;\n+            default:\n+                throw new Error();\n+            }\n+        }\n+        executor = bootstrap.group().next();\n+        this.maxConnections = maxConnections;\n+        this.maxPendingAcquires = maxPendingAcquires;\n+    }\n+\n+    private static long nanoTime() {\n+        return System.nanoTime() - START_TIME;\n+    }\n+\n+    @Override\n+    public Future<Channel> acquire(final Promise<Channel> promise) {\n+        try {\n+            if (executor.inEventLoop()) {\n+                acquire0(promise);\n+            } else {\n+                executor.execute(new OneTimeTask() {\n+                    @Override\n+                    public void run() {\n+                        acquire0(promise);\n+                    }\n+                });\n+            }\n+        } catch (Throwable cause) {\n+            promise.setFailure(cause);\n+        }\n+        return promise;\n+    }\n+\n+    private void acquire0(final Promise<Channel> promise) {\n+        assert executor.inEventLoop();\n+\n+        if (acquiredChannelCount < maxConnections) {\n+            ++acquiredChannelCount;\n+\n+            assert acquiredChannelCount > 0;\n+\n+            // We need to create a new promise as we need to ensure the AcquireListener runs in the correct\n+            // EventLoop\n+            Promise<Channel> p = executor.newPromise();\n+            p.addListener(new AcquireListener(promise));\n+            super.acquire(p);\n+        } else {\n+            if (pendingAcquireCount >= maxPendingAcquires) {\n+                promise.setFailure(FULL_EXCEPTION);\n+            } else {\n+                AcquireTask task = new AcquireTask(promise);\n+                if (pendingAcquireQueue.offer(task)) {\n+                    ++pendingAcquireCount;\n+\n+                    if (timeoutTask != null) {\n+                        task.timeoutFuture = executor.schedule(timeoutTask, acquireTimeoutNanos, TimeUnit.NANOSECONDS);\n+                    }\n+                } else {\n+                    promise.setFailure(FULL_EXCEPTION);\n+                }\n+            }\n+\n+            assert pendingAcquireCount > 0;\n+        }\n+    }\n+\n+    @Override\n+    public Future<Void> release(final Channel channel, final Promise<Void> promise) {\n+        try {\n+            final Promise<Void> p = executor.newPromise();\n+            super.release(channel, p.addListener(new FutureListener<Void>() {\n+\n+                @Override\n+                public void operationComplete(Future<Void> future) throws Exception {\n+                    assert executor.inEventLoop();\n+\n+                    if (future.isSuccess()) {\n+                        --acquiredChannelCount;\n+\n+                        // We should never have a negative value.\n+                        assert acquiredChannelCount >= 0;\n+\n+                        // Run the pending acquire tasks before notify the original promise so if the user would\n+                        // try to acquire again from the ChannelFutureListener and the pendingAcquireCount is >=\n+                        // maxPendingAcquires we may be able to run some pending tasks first and so allow to add\n+                        // more.\n+                        runTaskQueue();\n+                        promise.setSuccess(null);\n+                    } else {\n+                        promise.setFailure(future.cause());\n+                    }\n+                }\n+            }));\n+            return p;\n+        } catch (Throwable cause) {\n+            promise.setFailure(cause);","path":"transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java","position":261,"original_position":261,"commit_id":"462ee32b9f5b649777cfe5a39ab0d83a972f2d75","original_commit_id":"462ee32b9f5b649777cfe5a39ab0d83a972f2d75","user":{"login":"Scottmitch","id":7562868,"avatar_url":"https://avatars.githubusercontent.com/u/7562868?v=3","gravatar_id":"","url":"https://api.github.com/users/Scottmitch","html_url":"https://github.com/Scottmitch","followers_url":"https://api.github.com/users/Scottmitch/followers","following_url":"https://api.github.com/users/Scottmitch/following{/other_user}","gists_url":"https://api.github.com/users/Scottmitch/gists{/gist_id}","starred_url":"https://api.github.com/users/Scottmitch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Scottmitch/subscriptions","organizations_url":"https://api.github.com/users/Scottmitch/orgs","repos_url":"https://api.github.com/users/Scottmitch/repos","events_url":"https://api.github.com/users/Scottmitch/events{/privacy}","received_events_url":"https://api.github.com/users/Scottmitch/received_events","type":"User","site_admin":false},"body":"What should with our state if an exception is thrown?  Should we decrement our acquired count, `runTaskQueue()`, etc...?","created_at":"2015-04-28T23:51:27Z","updated_at":"2015-04-28T23:51:27Z","html_url":"https://github.com/netty/netty/pull/3607#discussion_r29300254","pull_request_url":"https://api.github.com/repos/netty/netty/pulls/3607","_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/comments/29300254"},"html":{"href":"https://github.com/netty/netty/pull/3607#discussion_r29300254"},"pull_request":{"href":"https://api.github.com/repos/netty/netty/pulls/3607"}}},"pull_request":{"url":"https://api.github.com/repos/netty/netty/pulls/3607","id":33074956,"html_url":"https://github.com/netty/netty/pull/3607","diff_url":"https://github.com/netty/netty/pull/3607.diff","patch_url":"https://github.com/netty/netty/pull/3607.patch","issue_url":"https://api.github.com/repos/netty/netty/issues/3607","number":3607,"state":"open","locked":false,"title":"[#3218] Add ChannelPool abstraction and implementations","user":{"login":"normanmaurer","id":439362,"avatar_url":"https://avatars.githubusercontent.com/u/439362?v=3","gravatar_id":"","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","site_admin":false},"body":"Motivation:\r\n\r\nMany projects need some kind a Channel/Connection pool implementation. While the protocols are different many things can be shared, so we should provide a generic API and implementation.\r\n\r\nModifications:\r\n\r\nAdd ChannelPool API and implementations.\r\n\r\nResult:\r\n\r\nReusable / Generic pool implementation that users can use.","created_at":"2015-04-10T19:13:44Z","updated_at":"2015-04-28T23:51:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4af8b328065ebc4be542bf9e5493c3b81773f618","assignee":{"login":"normanmaurer","id":439362,"avatar_url":"https://avatars.githubusercontent.com/u/439362?v=3","gravatar_id":"","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/netty/netty/milestones/115","html_url":"https://github.com/netty/netty/milestones/4.0.28.Final","labels_url":"https://api.github.com/repos/netty/netty/milestones/115/labels","id":1050260,"number":115,"title":"4.0.28.Final","description":"","creator":{"login":"normanmaurer","id":439362,"avatar_url":"https://avatars.githubusercontent.com/u/439362?v=3","gravatar_id":"","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","site_admin":false},"open_issues":60,"closed_issues":37,"state":"open","created_at":"2015-04-02T12:45:12Z","updated_at":"2015-04-27T18:05:54Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/netty/netty/pulls/3607/commits","review_comments_url":"https://api.github.com/repos/netty/netty/pulls/3607/comments","review_comment_url":"https://api.github.com/repos/netty/netty/pulls/comments{/number}","comments_url":"https://api.github.com/repos/netty/netty/issues/3607/comments","statuses_url":"https://api.github.com/repos/netty/netty/statuses/462ee32b9f5b649777cfe5a39ab0d83a972f2d75","head":{"label":"netty:pool","ref":"pool","sha":"462ee32b9f5b649777cfe5a39ab0d83a972f2d75","user":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=3","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"repo":{"id":1064563,"name":"netty","full_name":"netty/netty","owner":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=3","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/netty/netty","description":"Netty project - an event-driven asynchronous network application framework","fork":false,"url":"https://api.github.com/repos/netty/netty","forks_url":"https://api.github.com/repos/netty/netty/forks","keys_url":"https://api.github.com/repos/netty/netty/keys{/key_id}","collaborators_url":"https://api.github.com/repos/netty/netty/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/netty/netty/teams","hooks_url":"https://api.github.com/repos/netty/netty/hooks","issue_events_url":"https://api.github.com/repos/netty/netty/issues/events{/number}","events_url":"https://api.github.com/repos/netty/netty/events","assignees_url":"https://api.github.com/repos/netty/netty/assignees{/user}","branches_url":"https://api.github.com/repos/netty/netty/branches{/branch}","tags_url":"https://api.github.com/repos/netty/netty/tags","blobs_url":"https://api.github.com/repos/netty/netty/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/netty/netty/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/netty/netty/git/refs{/sha}","trees_url":"https://api.github.com/repos/netty/netty/git/trees{/sha}","statuses_url":"https://api.github.com/repos/netty/netty/statuses/{sha}","languages_url":"https://api.github.com/repos/netty/netty/languages","stargazers_url":"https://api.github.com/repos/netty/netty/stargazers","contributors_url":"https://api.github.com/repos/netty/netty/contributors","subscribers_url":"https://api.github.com/repos/netty/netty/subscribers","subscription_url":"https://api.github.com/repos/netty/netty/subscription","commits_url":"https://api.github.com/repos/netty/netty/commits{/sha}","git_commits_url":"https://api.github.com/repos/netty/netty/git/commits{/sha}","comments_url":"https://api.github.com/repos/netty/netty/comments{/number}","issue_comment_url":"https://api.github.com/repos/netty/netty/issues/comments{/number}","contents_url":"https://api.github.com/repos/netty/netty/contents/{+path}","compare_url":"https://api.github.com/repos/netty/netty/compare/{base}...{head}","merges_url":"https://api.github.com/repos/netty/netty/merges","archive_url":"https://api.github.com/repos/netty/netty/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/netty/netty/downloads","issues_url":"https://api.github.com/repos/netty/netty/issues{/number}","pulls_url":"https://api.github.com/repos/netty/netty/pulls{/number}","milestones_url":"https://api.github.com/repos/netty/netty/milestones{/number}","notifications_url":"https://api.github.com/repos/netty/netty/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/netty/netty/labels{/name}","releases_url":"https://api.github.com/repos/netty/netty/releases{/id}","created_at":"2010-11-09T09:22:21Z","updated_at":"2015-04-28T17:18:48Z","pushed_at":"2015-04-28T23:33:03Z","git_url":"git://github.com/netty/netty.git","ssh_url":"git@github.com:netty/netty.git","clone_url":"https://github.com/netty/netty.git","svn_url":"https://github.com/netty/netty","homepage":"http://netty.io/","size":119772,"stargazers_count":4501,"watchers_count":4501,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2213,"mirror_url":null,"open_issues_count":257,"forks":2213,"open_issues":257,"watchers":4501,"default_branch":"master"}},"base":{"label":"netty:4.0","ref":"4.0","sha":"c72c71abbba3473d3176d068286eee96c8f6582a","user":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=3","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"repo":{"id":1064563,"name":"netty","full_name":"netty/netty","owner":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=3","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/netty/netty","description":"Netty project - an event-driven asynchronous network application framework","fork":false,"url":"https://api.github.com/repos/netty/netty","forks_url":"https://api.github.com/repos/netty/netty/forks","keys_url":"https://api.github.com/repos/netty/netty/keys{/key_id}","collaborators_url":"https://api.github.com/repos/netty/netty/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/netty/netty/teams","hooks_url":"https://api.github.com/repos/netty/netty/hooks","issue_events_url":"https://api.github.com/repos/netty/netty/issues/events{/number}","events_url":"https://api.github.com/repos/netty/netty/events","assignees_url":"https://api.github.com/repos/netty/netty/assignees{/user}","branches_url":"https://api.github.com/repos/netty/netty/branches{/branch}","tags_url":"https://api.github.com/repos/netty/netty/tags","blobs_url":"https://api.github.com/repos/netty/netty/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/netty/netty/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/netty/netty/git/refs{/sha}","trees_url":"https://api.github.com/repos/netty/netty/git/trees{/sha}","statuses_url":"https://api.github.com/repos/netty/netty/statuses/{sha}","languages_url":"https://api.github.com/repos/netty/netty/languages","stargazers_url":"https://api.github.com/repos/netty/netty/stargazers","contributors_url":"https://api.github.com/repos/netty/netty/contributors","subscribers_url":"https://api.github.com/repos/netty/netty/subscribers","subscription_url":"https://api.github.com/repos/netty/netty/subscription","commits_url":"https://api.github.com/repos/netty/netty/commits{/sha}","git_commits_url":"https://api.github.com/repos/netty/netty/git/commits{/sha}","comments_url":"https://api.github.com/repos/netty/netty/comments{/number}","issue_comment_url":"https://api.github.com/repos/netty/netty/issues/comments{/number}","contents_url":"https://api.github.com/repos/netty/netty/contents/{+path}","compare_url":"https://api.github.com/repos/netty/netty/compare/{base}...{head}","merges_url":"https://api.github.com/repos/netty/netty/merges","archive_url":"https://api.github.com/repos/netty/netty/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/netty/netty/downloads","issues_url":"https://api.github.com/repos/netty/netty/issues{/number}","pulls_url":"https://api.github.com/repos/netty/netty/pulls{/number}","milestones_url":"https://api.github.com/repos/netty/netty/milestones{/number}","notifications_url":"https://api.github.com/repos/netty/netty/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/netty/netty/labels{/name}","releases_url":"https://api.github.com/repos/netty/netty/releases{/id}","created_at":"2010-11-09T09:22:21Z","updated_at":"2015-04-28T17:18:48Z","pushed_at":"2015-04-28T23:33:03Z","git_url":"git://github.com/netty/netty.git","ssh_url":"git@github.com:netty/netty.git","clone_url":"https://github.com/netty/netty.git","svn_url":"https://github.com/netty/netty","homepage":"http://netty.io/","size":119772,"stargazers_count":4501,"watchers_count":4501,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2213,"mirror_url":null,"open_issues_count":257,"forks":2213,"open_issues":257,"watchers":4501,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/3607"},"html":{"href":"https://github.com/netty/netty/pull/3607"},"issue":{"href":"https://api.github.com/repos/netty/netty/issues/3607"},"comments":{"href":"https://api.github.com/repos/netty/netty/issues/3607/comments"},"review_comments":{"href":"https://api.github.com/repos/netty/netty/pulls/3607/comments"},"review_comment":{"href":"https://api.github.com/repos/netty/netty/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/netty/netty/pulls/3607/commits"},"statuses":{"href":"https://api.github.com/repos/netty/netty/statuses/462ee32b9f5b649777cfe5a39ab0d83a972f2d75"}}}}