{"action":"created","comment":{"url":"https://api.github.com/repos/grpc/grpc-java/pulls/comments/53707162","id":53707162,"diff_hunk":"@@ -0,0 +1,907 @@\n+/*\n+ * Copyright 2016, Google Inc. All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or without\n+ * modification, are permitted provided that the following conditions are\n+ * met:\n+ *\n+ *    * Redistributions of source code must retain the above copyright\n+ * notice, this list of conditions and the following disclaimer.\n+ *    * Redistributions in binary form must reproduce the above\n+ * copyright notice, this list of conditions and the following disclaimer\n+ * in the documentation and/or other materials provided with the\n+ * distribution.\n+ *\n+ *    * Neither the name of Google Inc. nor the names of its\n+ * contributors may be used to endorse or promote products derived from\n+ * this software without specific prior written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package io.grpc.internal.testing;\n+\n+import static com.google.common.base.Charsets.UTF_8;\n+import static org.junit.Assert.assertEquals;\n+import static org.junit.Assert.assertFalse;\n+import static org.junit.Assert.assertNotEquals;\n+import static org.junit.Assert.assertTrue;\n+import static org.junit.Assert.fail;\n+import static org.junit.Assume.assumeTrue;\n+import static org.mockito.Matchers.any;\n+import static org.mockito.Mockito.doAnswer;\n+import static org.mockito.Mockito.inOrder;\n+import static org.mockito.Mockito.mock;\n+import static org.mockito.Mockito.never;\n+import static org.mockito.Mockito.reset;\n+import static org.mockito.Mockito.timeout;\n+import static org.mockito.Mockito.verify;\n+\n+import com.google.common.collect.Lists;\n+import com.google.common.io.ByteStreams;\n+import com.google.common.util.concurrent.MoreExecutors;\n+import com.google.common.util.concurrent.SettableFuture;\n+\n+import io.grpc.Metadata;\n+import io.grpc.MethodDescriptor;\n+import io.grpc.Status;\n+import io.grpc.internal.ClientStream;\n+import io.grpc.internal.ClientStreamListener;\n+import io.grpc.internal.ClientTransport;\n+import io.grpc.internal.ManagedClientTransport;\n+import io.grpc.internal.Server;\n+import io.grpc.internal.ServerListener;\n+import io.grpc.internal.ServerStream;\n+import io.grpc.internal.ServerStreamListener;\n+import io.grpc.internal.ServerTransport;\n+import io.grpc.internal.ServerTransportListener;\n+\n+import org.junit.After;\n+import org.junit.Before;\n+import org.junit.Rule;\n+import org.junit.Test;\n+import org.junit.rules.ExpectedException;\n+import org.junit.runner.RunWith;\n+import org.junit.runners.JUnit4;\n+import org.mockito.ArgumentCaptor;\n+import org.mockito.InOrder;\n+import org.mockito.Matchers;\n+import org.mockito.invocation.InvocationOnMock;\n+import org.mockito.stubbing.Answer;\n+\n+import java.io.ByteArrayInputStream;\n+import java.io.IOException;\n+import java.io.InputStream;\n+import java.util.concurrent.BlockingQueue;\n+import java.util.concurrent.ExecutionException;\n+import java.util.concurrent.Future;\n+import java.util.concurrent.LinkedBlockingQueue;\n+import java.util.concurrent.TimeUnit;\n+import java.util.concurrent.TimeoutException;\n+\n+/** Standard unit tests for {@link ClientTransport}s and {@link ServerTransport}s. */\n+@RunWith(JUnit4.class)\n+public abstract class AbstractTransportTest {\n+  /**\n+   * Returns a new server that when started will be able to be connected to from the client. Each\n+   * returned instance should be new and yet be accessible by new client transports. This\n+   * effectively means that each instance should listen on the same port, or similar.\n+   */\n+  protected abstract Server newServer();\n+\n+  /**\n+   * Returns a new transport that when started will be able to connect to the server.\n+   */\n+  protected abstract ManagedClientTransport newClientTransport();\n+\n+  /**\n+   * When non-null, will be shut down during tearDown(). However, it _must_ have been started with\n+   * {@code serverListener}, otherwise tearDown() can't wait for shutdown which can put following\n+   * tests in an indeterminate state.\n+   */\n+  private Server server;\n+  private ServerTransport serverTransport;\n+  private ManagedClientTransport client;\n+  private MethodDescriptor<String, String> methodDescriptor = MethodDescriptor.create(\n+      MethodDescriptor.MethodType.UNKNOWN, \"service/method\", StringMarshaller.INSTANCE,\n+      StringMarshaller.INSTANCE);\n+  private Metadata.Key<String> asciiKey = Metadata.Key.of(\n+      \"ascii-key\", Metadata.ASCII_STRING_MARSHALLER);\n+  private Metadata.Key<String> binaryKey = Metadata.Key.of(\n+      \"key-bin\", StringBinaryMarshaller.INSTANCE);\n+\n+  private ManagedClientTransport.Listener mockClientTransportListener\n+      = mock(ManagedClientTransport.Listener.class);\n+  private ClientStreamListener mockClientStreamListener = mock(ClientStreamListener.class);\n+  private MockServerListener serverListener = new MockServerListener();\n+  private ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class);\n+  private ArgumentCaptor<Throwable> throwableCaptor = ArgumentCaptor.forClass(Throwable.class);\n+  private ArgumentCaptor<Metadata> metadataCaptor = ArgumentCaptor.forClass(Metadata.class);\n+  private ArgumentCaptor<InputStream> inputStreamCaptor\n+      = ArgumentCaptor.forClass(InputStream.class);\n+  @Rule\n+  public ExpectedException thrown = ExpectedException.none();\n+\n+  @Before\n+  public void setUp() {\n+    server = newServer();\n+    client = newClientTransport();\n+  }\n+\n+  @After\n+  public void tearDown() throws InterruptedException {\n+    if (client != null) {\n+      // TODO(ejona): swap to shutdownNow\n+      client.shutdown();\n+    }\n+    if (serverTransport != null) {\n+      // TODO(ejona): swap to shutdownNow\n+      serverTransport.shutdown();\n+    }\n+    if (server != null) {\n+      server.shutdown();\n+      assertTrue(serverListener.waitForShutdown(1000, TimeUnit.MILLISECONDS));\n+    }\n+  }\n+\n+  // TODO(ejona):\n+  //   multiple streams on same transport\n+  //   multiple client transports to same server\n+  //   halfClose to trigger flush (client and server)\n+  //   flow control pushes back (client and server)\n+  //   flow control provides precisely number of messages requested (client and server)\n+  //   onReady called when buffer drained (on server and client)\n+  //   test no start reentrancy (esp. during failure) (transport and call)\n+  //   multiple requests/responses (verifying contents received)\n+  //   server transport shutdown triggers client shutdown (via GOAWAY)\n+  //   queued message InputStreams are closed on stream cancel\n+  //     (and maybe exceptions handled)\n+\n+  @Test\n+  public void serverNotListening() {\n+    server = null;\n+    InOrder inOrder = inOrder(mockClientTransportListener);\n+    client.start(mockClientTransportListener);\n+    verify(mockClientTransportListener, timeout(1000)).transportTerminated();\n+    inOrder.verify(mockClientTransportListener).transportShutdown(statusCaptor.capture());\n+    assertCodeEquals(Status.UNAVAILABLE, statusCaptor.getValue());\n+    inOrder.verify(mockClientTransportListener).transportTerminated();\n+    verify(mockClientTransportListener, never()).transportReady();\n+  }\n+\n+  @Test\n+  public void clientStartStop() throws Exception {\n+    server.start(serverListener);\n+    InOrder inOrder = inOrder(mockClientTransportListener);\n+    client.start(mockClientTransportListener);\n+    client.shutdown();\n+    verify(mockClientTransportListener, timeout(1000)).transportTerminated();\n+    inOrder.verify(mockClientTransportListener).transportShutdown(any(Status.class));\n+    inOrder.verify(mockClientTransportListener).transportTerminated();\n+    server.shutdown();\n+    assertTrue(serverListener.waitForShutdown(1000, TimeUnit.MILLISECONDS));\n+    server = null;\n+  }\n+\n+  @Test\n+  public void clientStartAndStopOnceConnected() throws Exception {\n+    server.start(serverListener);\n+    InOrder inOrder = inOrder(mockClientTransportListener);\n+    client.start(mockClientTransportListener);\n+    verify(mockClientTransportListener, timeout(1000)).transportReady();\n+    MockServerTransportListener serverTransportListener\n+        = serverListener.takeListenerOrFail(1000, TimeUnit.MILLISECONDS);\n+    client.shutdown();\n+    verify(mockClientTransportListener, timeout(1000)).transportTerminated();\n+    inOrder.verify(mockClientTransportListener).transportShutdown(any(Status.class));\n+    inOrder.verify(mockClientTransportListener).transportTerminated();\n+    assertTrue(serverTransportListener.waitForTermination(1000, TimeUnit.MILLISECONDS));\n+    server.shutdown();\n+    assertTrue(serverListener.waitForShutdown(1000, TimeUnit.MILLISECONDS));\n+    server = null;\n+  }\n+\n+  @Test\n+  public void serverAlreadyListening() throws Exception {\n+    client = null;\n+    server.start(serverListener);\n+    Server server2 = newServer();\n+    thrown.expect(IOException.class);\n+    server2.start(new MockServerListener());\n+  }\n+\n+  @Test\n+  public void openStreamPreventsTermination() throws Exception {\n+    server.start(serverListener);\n+    client.start(mockClientTransportListener);\n+    MockServerTransportListener serverTransportListener\n+        = serverListener.takeListenerOrFail(1000, TimeUnit.MILLISECONDS);\n+    serverTransport = serverTransportListener.transport;\n+\n+    ClientStream clientStream = client.newStream(methodDescriptor, new Metadata());\n+    clientStream.start(mockClientStreamListener);\n+    StreamCreation serverStreamCreation\n+        = serverTransportListener.takeStreamOrFail(1000, TimeUnit.MILLISECONDS);\n+    ServerStream serverStream = serverStreamCreation.stream;\n+    ServerStreamListener mockServerStreamListener = serverStreamCreation.listener;\n+\n+    client.shutdown();\n+    client = null;\n+    server.shutdown();\n+    server = null;\n+    serverTransport.shutdown();\n+    serverTransport = null;\n+\n+    verify(mockClientTransportListener, timeout(1000)).transportShutdown(any(Status.class));\n+    assertTrue(serverListener.waitForShutdown(1000, TimeUnit.MILLISECONDS));\n+\n+    // A new server should be able to start listening, since the current server has given up\n+    // resources. There may be cases this is impossible in the future, but for now it is a useful\n+    // property.\n+    serverListener = new MockServerListener();\n+    server = newServer();\n+    server.start(serverListener);\n+\n+    // Try to \"flush\" out any listener notifications on client and server. This also ensures that\n+    // the stream still functions.\n+    serverStream.writeHeaders(new Metadata());\n+    clientStream.halfClose();\n+    verify(mockClientStreamListener, timeout(1000)).headersRead(any(Metadata.class));\n+    verify(mockServerStreamListener, timeout(1000)).halfClosed();\n+\n+    verify(mockClientTransportListener, never()).transportTerminated();\n+    assertFalse(serverTransportListener.isTerminated());\n+\n+    clientStream.cancel(Status.CANCELLED);\n+\n+    verify(mockClientTransportListener, timeout(1000)).transportTerminated();\n+    assertTrue(serverTransportListener.waitForTermination(1000, TimeUnit.MILLISECONDS));\n+  }\n+\n+  @Test\n+  public void ping() throws Exception {\n+    server.start(serverListener);\n+    client.start(mockClientTransportListener);\n+    ClientTransport.PingCallback mockPingCallback = mock(ClientTransport.PingCallback.class);\n+    try {\n+      client.ping(mockPingCallback, MoreExecutors.directExecutor());\n+    } catch (UnsupportedOperationException ex) {\n+      assumeTrue(false);\n+    }\n+    verify(mockPingCallback, timeout(1000)).onSuccess(Matchers.anyInt());\n+  }\n+\n+  @Test\n+  public void ping_duringShutdown() throws Exception {\n+    server.start(serverListener);\n+    client.start(mockClientTransportListener);\n+    // Stream prevents termination\n+    ClientStream stream = client.newStream(methodDescriptor, new Metadata());\n+    stream.start(mockClientStreamListener);\n+    client.shutdown();\n+    ClientTransport.PingCallback mockPingCallback = mock(ClientTransport.PingCallback.class);\n+    try {\n+      client.ping(mockPingCallback, MoreExecutors.directExecutor());\n+    } catch (UnsupportedOperationException ex) {\n+      assumeTrue(false);\n+    }\n+    verify(mockPingCallback, timeout(1000)).onSuccess(Matchers.anyInt());\n+    stream.cancel(Status.CANCELLED);\n+  }\n+\n+  @Test\n+  public void ping_afterTermination() throws Exception {\n+    server.start(serverListener);\n+    client.start(mockClientTransportListener);\n+    client.shutdown();\n+    ClientTransport.PingCallback mockPingCallback = mock(ClientTransport.PingCallback.class);\n+    try {\n+      client.ping(mockPingCallback, MoreExecutors.directExecutor());\n+    } catch (UnsupportedOperationException ex) {\n+      assumeTrue(false);\n+    }\n+    verify(mockPingCallback, timeout(1000)).onFailure(throwableCaptor.capture());\n+    Status status = Status.fromThrowable(throwableCaptor.getValue());\n+    assertCodeEquals(Status.UNAVAILABLE, status);\n+  }\n+\n+  @Test\n+  public void newStream_duringShutdown() throws Exception {\n+    server.start(serverListener);\n+    client.start(mockClientTransportListener);\n+    // Stream prevents termination\n+    ClientStream stream = client.newStream(methodDescriptor, new Metadata());\n+    stream.start(mockClientStreamListener);\n+    client.shutdown();\n+    ClientStream stream2 = client.newStream(methodDescriptor, new Metadata());\n+    ClientStreamListener mockClientStreamListener2 = mock(ClientStreamListener.class);\n+    stream2.start(mockClientStreamListener2);\n+    verify(mockClientStreamListener2, timeout(1000))\n+        .closed(statusCaptor.capture(), any(Metadata.class));\n+    assertCodeEquals(Status.UNAVAILABLE, statusCaptor.getValue());\n+\n+    // Make sure earlier stream works.\n+    MockServerTransportListener serverTransportListener\n+        = serverListener.takeListenerOrFail(1000, TimeUnit.MILLISECONDS);\n+    serverTransport = serverTransportListener.transport;\n+    StreamCreation serverStreamCreation\n+        = serverTransportListener.takeStreamOrFail(1000, TimeUnit.MILLISECONDS);\n+    serverStreamCreation.stream.close(Status.OK, new Metadata());\n+    verify(mockClientStreamListener, timeout(1000))\n+        .closed(statusCaptor.capture(), any(Metadata.class));\n+    assertCodeEquals(Status.OK, statusCaptor.getValue());\n+  }\n+\n+  @Test\n+  public void basicStream() throws Exception {\n+    server.start(serverListener);\n+    client.start(mockClientTransportListener);\n+    MockServerTransportListener serverTransportListener\n+        = serverListener.takeListenerOrFail(1000, TimeUnit.MILLISECONDS);\n+    serverTransport = serverTransportListener.transport;\n+\n+    Metadata clientHeaders = new Metadata();\n+    clientHeaders.put(asciiKey, \"client\");\n+    clientHeaders.put(asciiKey, \"dupvalue\");\n+    clientHeaders.put(asciiKey, \"dupvalue\");\n+    clientHeaders.put(binaryKey, \"äbinaryclient\");\n+    ClientStream clientStream = client.newStream(methodDescriptor, clientHeaders);\n+    clientStream.start(mockClientStreamListener);\n+    StreamCreation serverStreamCreation\n+        = serverTransportListener.takeStreamOrFail(1000, TimeUnit.MILLISECONDS);\n+    assertEquals(methodDescriptor.getFullMethodName(), serverStreamCreation.method);\n+    assertEquals(Lists.newArrayList(clientHeaders.getAll(asciiKey)),\n+        Lists.newArrayList(serverStreamCreation.headers.getAll(asciiKey)));\n+    assertEquals(Lists.newArrayList(clientHeaders.getAll(binaryKey)),\n+        Lists.newArrayList(serverStreamCreation.headers.getAll(binaryKey)));\n+    ServerStream serverStream = serverStreamCreation.stream;\n+    ServerStreamListener mockServerStreamListener = serverStreamCreation.listener;\n+\n+    serverStream.request(1);\n+    verify(mockClientStreamListener, timeout(1000)).onReady();\n+    assertTrue(clientStream.isReady());\n+    clientStream.writeMessage(methodDescriptor.streamRequest(\"Hello!\"));\n+    clientStream.flush();\n+    verify(mockServerStreamListener, timeout(1000)).messageRead(inputStreamCaptor.capture());\n+    assertEquals(\"Hello!\", methodDescriptor.parseRequest(inputStreamCaptor.getValue()));\n+    inputStreamCaptor.getValue().close();\n+\n+    clientStream.halfClose();\n+    verify(mockServerStreamListener, timeout(1000)).halfClosed();\n+\n+    Metadata serverHeaders = new Metadata();\n+    serverHeaders.put(asciiKey, \"server\");\n+    serverHeaders.put(asciiKey, \"dupvalue\");\n+    serverHeaders.put(asciiKey, \"dupvalue\");\n+    serverHeaders.put(binaryKey, \"äbinaryserver\");\n+    serverStream.writeHeaders(serverHeaders);\n+    verify(mockClientStreamListener, timeout(1000)).headersRead(metadataCaptor.capture());\n+    assertEquals(Lists.newArrayList(serverHeaders.getAll(asciiKey)),\n+        Lists.newArrayList(metadataCaptor.getValue().getAll(asciiKey)));\n+    assertEquals(Lists.newArrayList(serverHeaders.getAll(binaryKey)),\n+        Lists.newArrayList(metadataCaptor.getValue().getAll(binaryKey)));\n+\n+    clientStream.request(1);\n+    verify(mockServerStreamListener, timeout(1000)).onReady();","path":"testing/src/main/java/io/grpc/internal/testing/AbstractTransportTest.java","position":395,"original_position":395,"commit_id":"58d6c6b7a729ef6a25492b8c95939015d09b27ec","original_commit_id":"58d6c6b7a729ef6a25492b8c95939015d09b27ec","user":{"login":"nmittler","id":6493296,"avatar_url":"https://avatars.githubusercontent.com/u/6493296?v=3","gravatar_id":"","url":"https://api.github.com/users/nmittler","html_url":"https://github.com/nmittler","followers_url":"https://api.github.com/users/nmittler/followers","following_url":"https://api.github.com/users/nmittler/following{/other_user}","gists_url":"https://api.github.com/users/nmittler/gists{/gist_id}","starred_url":"https://api.github.com/users/nmittler/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nmittler/subscriptions","organizations_url":"https://api.github.com/users/nmittler/orgs","repos_url":"https://api.github.com/users/nmittler/repos","events_url":"https://api.github.com/users/nmittler/events{/privacy}","received_events_url":"https://api.github.com/users/nmittler/received_events","type":"User","site_admin":false},"body":"To help reduce the verbosity, you could make a static method `verifyWithTimeout(obj)` that automatically adds the `timeout(1000)` ... WDYT?","created_at":"2016-02-22T22:46:41Z","updated_at":"2016-02-22T22:46:41Z","html_url":"https://github.com/grpc/grpc-java/pull/1462#discussion_r53707162","pull_request_url":"https://api.github.com/repos/grpc/grpc-java/pulls/1462","_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/comments/53707162"},"html":{"href":"https://github.com/grpc/grpc-java/pull/1462#discussion_r53707162"},"pull_request":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/1462"}}},"pull_request":{"url":"https://api.github.com/repos/grpc/grpc-java/pulls/1462","id":60114558,"html_url":"https://github.com/grpc/grpc-java/pull/1462","diff_url":"https://github.com/grpc/grpc-java/pull/1462.diff","patch_url":"https://github.com/grpc/grpc-java/pull/1462.patch","issue_url":"https://api.github.com/repos/grpc/grpc-java/issues/1462","number":1462,"state":"open","locked":false,"title":"Create new AbstractTransportTest for Transports","user":{"login":"ejona86","id":2811396,"avatar_url":"https://avatars.githubusercontent.com/u/2811396?v=3","gravatar_id":"","url":"https://api.github.com/users/ejona86","html_url":"https://github.com/ejona86","followers_url":"https://api.github.com/users/ejona86/followers","following_url":"https://api.github.com/users/ejona86/following{/other_user}","gists_url":"https://api.github.com/users/ejona86/gists{/gist_id}","starred_url":"https://api.github.com/users/ejona86/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ejona86/subscriptions","organizations_url":"https://api.github.com/users/ejona86/orgs","repos_url":"https://api.github.com/users/ejona86/repos","events_url":"https://api.github.com/users/ejona86/events{/privacy}","received_events_url":"https://api.github.com/users/ejona86/received_events","type":"User","site_admin":false},"body":"This is lower-level than the existing AbstractTransportTest. It should\r\nbe used by all transports, but InProcessTransport is the only one as\r\npart of this commit.\r\n\r\nThis is the main commit of #1397. Since #1397 is so large covering so many detailed areas, I'm splitting this out. After it is in, I will create separate PRs for the okhttp and netty changes.\r\n\r\n@nmittler and @zhangkun83, no real rush on reviewing this, but it would be good to get in as it is a good place for regression tests and more precise than interop-testing's AbstractTransportTest..","created_at":"2016-02-22T01:32:30Z","updated_at":"2016-02-22T22:46:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dab942f902e3ba044560866d14e29d83c642f8a8","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/grpc/grpc-java/pulls/1462/commits","review_comments_url":"https://api.github.com/repos/grpc/grpc-java/pulls/1462/comments","review_comment_url":"https://api.github.com/repos/grpc/grpc-java/pulls/comments{/number}","comments_url":"https://api.github.com/repos/grpc/grpc-java/issues/1462/comments","statuses_url":"https://api.github.com/repos/grpc/grpc-java/statuses/58d6c6b7a729ef6a25492b8c95939015d09b27ec","head":{"label":"ejona86:inprocess-test","ref":"inprocess-test","sha":"58d6c6b7a729ef6a25492b8c95939015d09b27ec","user":{"login":"ejona86","id":2811396,"avatar_url":"https://avatars.githubusercontent.com/u/2811396?v=3","gravatar_id":"","url":"https://api.github.com/users/ejona86","html_url":"https://github.com/ejona86","followers_url":"https://api.github.com/users/ejona86/followers","following_url":"https://api.github.com/users/ejona86/following{/other_user}","gists_url":"https://api.github.com/users/ejona86/gists{/gist_id}","starred_url":"https://api.github.com/users/ejona86/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ejona86/subscriptions","organizations_url":"https://api.github.com/users/ejona86/orgs","repos_url":"https://api.github.com/users/ejona86/repos","events_url":"https://api.github.com/users/ejona86/events{/privacy}","received_events_url":"https://api.github.com/users/ejona86/received_events","type":"User","site_admin":false},"repo":{"id":31378968,"name":"grpc-java","full_name":"ejona86/grpc-java","owner":{"login":"ejona86","id":2811396,"avatar_url":"https://avatars.githubusercontent.com/u/2811396?v=3","gravatar_id":"","url":"https://api.github.com/users/ejona86","html_url":"https://github.com/ejona86","followers_url":"https://api.github.com/users/ejona86/followers","following_url":"https://api.github.com/users/ejona86/following{/other_user}","gists_url":"https://api.github.com/users/ejona86/gists{/gist_id}","starred_url":"https://api.github.com/users/ejona86/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ejona86/subscriptions","organizations_url":"https://api.github.com/users/ejona86/orgs","repos_url":"https://api.github.com/users/ejona86/repos","events_url":"https://api.github.com/users/ejona86/events{/privacy}","received_events_url":"https://api.github.com/users/ejona86/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ejona86/grpc-java","description":"The Java gRPC implementation. HTTP/2 based RPC","fork":true,"url":"https://api.github.com/repos/ejona86/grpc-java","forks_url":"https://api.github.com/repos/ejona86/grpc-java/forks","keys_url":"https://api.github.com/repos/ejona86/grpc-java/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ejona86/grpc-java/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ejona86/grpc-java/teams","hooks_url":"https://api.github.com/repos/ejona86/grpc-java/hooks","issue_events_url":"https://api.github.com/repos/ejona86/grpc-java/issues/events{/number}","events_url":"https://api.github.com/repos/ejona86/grpc-java/events","assignees_url":"https://api.github.com/repos/ejona86/grpc-java/assignees{/user}","branches_url":"https://api.github.com/repos/ejona86/grpc-java/branches{/branch}","tags_url":"https://api.github.com/repos/ejona86/grpc-java/tags","blobs_url":"https://api.github.com/repos/ejona86/grpc-java/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ejona86/grpc-java/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ejona86/grpc-java/git/refs{/sha}","trees_url":"https://api.github.com/repos/ejona86/grpc-java/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ejona86/grpc-java/statuses/{sha}","languages_url":"https://api.github.com/repos/ejona86/grpc-java/languages","stargazers_url":"https://api.github.com/repos/ejona86/grpc-java/stargazers","contributors_url":"https://api.github.com/repos/ejona86/grpc-java/contributors","subscribers_url":"https://api.github.com/repos/ejona86/grpc-java/subscribers","subscription_url":"https://api.github.com/repos/ejona86/grpc-java/subscription","commits_url":"https://api.github.com/repos/ejona86/grpc-java/commits{/sha}","git_commits_url":"https://api.github.com/repos/ejona86/grpc-java/git/commits{/sha}","comments_url":"https://api.github.com/repos/ejona86/grpc-java/comments{/number}","issue_comment_url":"https://api.github.com/repos/ejona86/grpc-java/issues/comments{/number}","contents_url":"https://api.github.com/repos/ejona86/grpc-java/contents/{+path}","compare_url":"https://api.github.com/repos/ejona86/grpc-java/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ejona86/grpc-java/merges","archive_url":"https://api.github.com/repos/ejona86/grpc-java/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ejona86/grpc-java/downloads","issues_url":"https://api.github.com/repos/ejona86/grpc-java/issues{/number}","pulls_url":"https://api.github.com/repos/ejona86/grpc-java/pulls{/number}","milestones_url":"https://api.github.com/repos/ejona86/grpc-java/milestones{/number}","notifications_url":"https://api.github.com/repos/ejona86/grpc-java/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ejona86/grpc-java/labels{/name}","releases_url":"https://api.github.com/repos/ejona86/grpc-java/releases{/id}","deployments_url":"https://api.github.com/repos/ejona86/grpc-java/deployments","created_at":"2015-02-26T17:23:03Z","updated_at":"2016-01-12T00:09:03Z","pushed_at":"2016-02-22T02:03:23Z","git_url":"git://github.com/ejona86/grpc-java.git","ssh_url":"git@github.com:ejona86/grpc-java.git","clone_url":"https://github.com/ejona86/grpc-java.git","svn_url":"https://github.com/ejona86/grpc-java","homepage":"","size":5295,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":false,"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":"grpc:master","ref":"master","sha":"56dca036db679f217baa580a241d2a26694f0dd8","user":{"login":"grpc","id":7802525,"avatar_url":"https://avatars.githubusercontent.com/u/7802525?v=3","gravatar_id":"","url":"https://api.github.com/users/grpc","html_url":"https://github.com/grpc","followers_url":"https://api.github.com/users/grpc/followers","following_url":"https://api.github.com/users/grpc/following{/other_user}","gists_url":"https://api.github.com/users/grpc/gists{/gist_id}","starred_url":"https://api.github.com/users/grpc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grpc/subscriptions","organizations_url":"https://api.github.com/users/grpc/orgs","repos_url":"https://api.github.com/users/grpc/repos","events_url":"https://api.github.com/users/grpc/events{/privacy}","received_events_url":"https://api.github.com/users/grpc/received_events","type":"Organization","site_admin":false},"repo":{"id":27729926,"name":"grpc-java","full_name":"grpc/grpc-java","owner":{"login":"grpc","id":7802525,"avatar_url":"https://avatars.githubusercontent.com/u/7802525?v=3","gravatar_id":"","url":"https://api.github.com/users/grpc","html_url":"https://github.com/grpc","followers_url":"https://api.github.com/users/grpc/followers","following_url":"https://api.github.com/users/grpc/following{/other_user}","gists_url":"https://api.github.com/users/grpc/gists{/gist_id}","starred_url":"https://api.github.com/users/grpc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grpc/subscriptions","organizations_url":"https://api.github.com/users/grpc/orgs","repos_url":"https://api.github.com/users/grpc/repos","events_url":"https://api.github.com/users/grpc/events{/privacy}","received_events_url":"https://api.github.com/users/grpc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/grpc/grpc-java","description":"The Java gRPC implementation. HTTP/2 based RPC","fork":false,"url":"https://api.github.com/repos/grpc/grpc-java","forks_url":"https://api.github.com/repos/grpc/grpc-java/forks","keys_url":"https://api.github.com/repos/grpc/grpc-java/keys{/key_id}","collaborators_url":"https://api.github.com/repos/grpc/grpc-java/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/grpc/grpc-java/teams","hooks_url":"https://api.github.com/repos/grpc/grpc-java/hooks","issue_events_url":"https://api.github.com/repos/grpc/grpc-java/issues/events{/number}","events_url":"https://api.github.com/repos/grpc/grpc-java/events","assignees_url":"https://api.github.com/repos/grpc/grpc-java/assignees{/user}","branches_url":"https://api.github.com/repos/grpc/grpc-java/branches{/branch}","tags_url":"https://api.github.com/repos/grpc/grpc-java/tags","blobs_url":"https://api.github.com/repos/grpc/grpc-java/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/grpc/grpc-java/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/grpc/grpc-java/git/refs{/sha}","trees_url":"https://api.github.com/repos/grpc/grpc-java/git/trees{/sha}","statuses_url":"https://api.github.com/repos/grpc/grpc-java/statuses/{sha}","languages_url":"https://api.github.com/repos/grpc/grpc-java/languages","stargazers_url":"https://api.github.com/repos/grpc/grpc-java/stargazers","contributors_url":"https://api.github.com/repos/grpc/grpc-java/contributors","subscribers_url":"https://api.github.com/repos/grpc/grpc-java/subscribers","subscription_url":"https://api.github.com/repos/grpc/grpc-java/subscription","commits_url":"https://api.github.com/repos/grpc/grpc-java/commits{/sha}","git_commits_url":"https://api.github.com/repos/grpc/grpc-java/git/commits{/sha}","comments_url":"https://api.github.com/repos/grpc/grpc-java/comments{/number}","issue_comment_url":"https://api.github.com/repos/grpc/grpc-java/issues/comments{/number}","contents_url":"https://api.github.com/repos/grpc/grpc-java/contents/{+path}","compare_url":"https://api.github.com/repos/grpc/grpc-java/compare/{base}...{head}","merges_url":"https://api.github.com/repos/grpc/grpc-java/merges","archive_url":"https://api.github.com/repos/grpc/grpc-java/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/grpc/grpc-java/downloads","issues_url":"https://api.github.com/repos/grpc/grpc-java/issues{/number}","pulls_url":"https://api.github.com/repos/grpc/grpc-java/pulls{/number}","milestones_url":"https://api.github.com/repos/grpc/grpc-java/milestones{/number}","notifications_url":"https://api.github.com/repos/grpc/grpc-java/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/grpc/grpc-java/labels{/name}","releases_url":"https://api.github.com/repos/grpc/grpc-java/releases{/id}","deployments_url":"https://api.github.com/repos/grpc/grpc-java/deployments","created_at":"2014-12-08T19:00:00Z","updated_at":"2016-02-22T15:42:47Z","pushed_at":"2016-02-22T21:57:25Z","git_url":"git://github.com/grpc/grpc-java.git","ssh_url":"git@github.com:grpc/grpc-java.git","clone_url":"https://github.com/grpc/grpc-java.git","svn_url":"https://github.com/grpc/grpc-java","homepage":"http://grpc.io","size":20806,"stargazers_count":1154,"watchers_count":1154,"language":"Java","has_issues":true,"has_downloads":false,"has_wiki":false,"has_pages":true,"forks_count":299,"mirror_url":null,"open_issues_count":122,"forks":299,"open_issues":122,"watchers":1154,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/1462"},"html":{"href":"https://github.com/grpc/grpc-java/pull/1462"},"issue":{"href":"https://api.github.com/repos/grpc/grpc-java/issues/1462"},"comments":{"href":"https://api.github.com/repos/grpc/grpc-java/issues/1462/comments"},"review_comments":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/1462/comments"},"review_comment":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/1462/commits"},"statuses":{"href":"https://api.github.com/repos/grpc/grpc-java/statuses/58d6c6b7a729ef6a25492b8c95939015d09b27ec"}}}}