{"action":"created","comment":{"url":"https://api.github.com/repos/grpc/grpc/pulls/comments/72649437","id":72649437,"diff_hunk":"@@ -0,0 +1,1003 @@\n+/*\n+ *\n+ * Copyright 2016, Google Inc.\n+ * 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+ *     * 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+\n+/** Implementation of the gRPC LB policy.\n+ *\n+ * This policy takes as input a set of resolved addresses {a1..an} for which the\n+ * LB set was set (it's the resolver's responsibility to ensure this). That is\n+ * to say, {a1..an} represent a collection of LB servers.\n+ *\n+ * An internal channel (\\a glb_lb_policy.lb_channel) is created over {a1..an}.\n+ * This channel behaves just like a regular channel. In particular, the\n+ * constructed URI over the addresses a1..an will use the default pick first\n+ * policy to select from this list of LB server backends.\n+ *\n+ * The first time the policy is requested a pick, a ping or to exit the idle\n+ * state, \\a query_for_backends() is called. It creates an instance of \\a\n+ * lb_client_data, an internal struct meant to contain the data associated with\n+ * the internal communication with the LB server. This instance is created via\n+ * \\a lb_client_data_create(). There, the call over lb_channel to pick-first\n+ * from {a1..an} is created, the \\a LoadBalancingRequest message is assembled\n+ * and all necessary callbacks for the progress of the internal call configured.\n+ *\n+ * Back in \\a query_for_backends(), the internal *streaming* call to the LB\n+ * server (whichever address from {a1..an} pick-first chose) is kicked off.\n+ * It'll progress over the callbacks configured in \\a lb_client_data_create()\n+ * (see the field docstrings of \\a lb_client_data for more details).\n+ *\n+ * If the call fails with UNIMPLEMENTED, the original call will also fail.\n+ * There's a misconfiguration somewhere: at least one of {a1..an} isn't a LB\n+ * server, which contradicts the LB bit being set. If the internal call times\n+ * out, the usual behavior of pick-first applies, continuing to pick from the\n+ * list {a1..an}.\n+ *\n+ * Upon sucesss, a \\a LoadBalancingResponse is expected in \\a res_rcvd_cb. An\n+ * invalid one results in the termination of the streaming call. A new streaming\n+ * call should be created if possible, failing the original call otherwise.\n+ * For a valid \\a LoadBalancingResponse, the server list of actual backends is\n+ * extracted. A Round Robin policy will be created from this list. There are two\n+ * possible scenarios:\n+ *\n+ * 1. This is the first server list received. There was no previous instance of\n+ *    the Round Robin policy. \\a rr_handover() will instantiate the RR policy\n+ *    and perform all the pending operations over it.\n+ * 2. There's already a RR policy instance active. We need to introduce the new\n+ *    one build from the new serverlist, but taking care not to disrupt the\n+ *    operations in progress over the old RR instance. This is done by\n+ *    decreasing the reference count on the old policy. The moment no more\n+ *    references are held on the old RR policy, it'll be destroyed and \\a\n+ *    rr_connectivity_changed notified with a \\a GRPC_CHANNEL_SHUTDOWN state.\n+ *    At this point we can transition to a new RR instance safely, which is done\n+ *    once again via \\a rr_handover().\n+ *\n+ *\n+ * Once a RR policy instance is in place (and getting updated as described),\n+ * calls to for a pick, a ping or a cancellation will be serviced right away by\n+ * forwarding them to the RR instance. Any time there's no RR policy available\n+ * (ie, right after the creation of the gRPCLB policy, if an empty serverlist\n+ * is received, etc), pick/ping requests are added to a list of pending\n+ * picks/pings to be flushed and serviced as part of \\a rr_handover() the moment\n+ * the RR policy instance becomes available.\n+ *\n+ * \\see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the\n+ * high level design and details. */\n+\n+/* TODO(dgq):\n+ * - Implement LB service forwarding (point 2c. in the doc's diagram).\n+ */\n+\n+#include <string.h>\n+\n+#include <grpc/byte_buffer_reader.h>\n+#include <grpc/grpc.h>\n+#include <grpc/support/alloc.h>\n+#include <grpc/support/host_port.h>\n+#include <grpc/support/string_util.h>\n+\n+#include \"src/core/ext/client_config/client_channel_factory.h\"\n+#include \"src/core/ext/client_config/lb_policy_registry.h\"\n+#include \"src/core/ext/client_config/parse_address.h\"\n+#include \"src/core/ext/lb_policy/grpclb/grpclb.h\"\n+#include \"src/core/ext/lb_policy/grpclb/load_balancer_api.h\"\n+#include \"src/core/lib/iomgr/sockaddr_utils.h\"\n+#include \"src/core/lib/support/string.h\"\n+#include \"src/core/lib/surface/call.h\"\n+#include \"src/core/lib/surface/channel.h\"\n+\n+int grpc_lb_glb_trace = 0;\n+\n+typedef struct wrapped_rr_closure_arg {\n+  /* the original closure. Usually a on_complete/notify cb for pick() and ping()\n+   * calls against the internal RR instance, respectively. */\n+  grpc_closure *wrapped_closure;\n+\n+  /* The RR instance related to the closure */\n+  grpc_lb_policy *rr_policy;\n+\n+  /* when not NULL, represents a pending_{pick,ping} node to be freed upon\n+   * closure execution */\n+  void *owning_pending_node; /* to be freed if not NULL */\n+} wrapped_rr_closure_arg;\n+\n+/* The \\a on_complete closure passed as part of the pick requires keeping a\n+ * reference to its associated round robin instance. We wrap this closure in\n+ * order to unref the round robin instance upon its invocation */\n+static void wrapped_rr_closure(grpc_exec_ctx *exec_ctx, void *arg,\n+                               grpc_error *error) {\n+  wrapped_rr_closure_arg *wc_arg = arg;\n+  if (wc_arg->rr_policy != NULL) {\n+    if (grpc_lb_glb_trace) {\n+      gpr_log(GPR_INFO, \"Unreffing RR (0x%\" PRIxPTR \")\",\n+              (intptr_t)wc_arg->rr_policy);\n+    }\n+    GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, \"wrapped_rr_closure\");\n+  }\n+  GPR_ASSERT(wc_arg->wrapped_closure != NULL);\n+  grpc_exec_ctx_sched(exec_ctx, wc_arg->wrapped_closure, error, NULL);\n+  gpr_free(wc_arg->owning_pending_node);\n+}\n+\n+/* Linked list of pending pick requests. It stores all information needed to\n+ * eventually call (Round Robin's) pick() on them. They mainly stay pending\n+ * waiting for the RR policy to be created/updated.\n+ *\n+ * One particularity is the wrapping of the user-provided \\a on_complete closure\n+ * (in \\a wrapped_on_complete and \\a wrapped_on_complete_arg). This is needed in\n+ * order to correctly unref the RR policy instance upon completion of the pick.\n+ * See \\a wrapped_rr_closure for details. */\n+typedef struct pending_pick {\n+  struct pending_pick *next;\n+\n+  /* polling entity for the pick()'s async notification */\n+  grpc_polling_entity *pollent;\n+\n+  /* the initial metadata for the pick. See grpc_lb_policy_pick() */\n+  grpc_metadata_batch *initial_metadata;\n+\n+  /* bitmask passed to pick() and used for selective cancelling. See\n+   * grpc_lb_policy_cancel_picks() */\n+  uint32_t initial_metadata_flags;\n+\n+  /* output argument where to store the pick()ed connected subchannel, or NULL\n+   * upon error. */\n+  grpc_connected_subchannel **target;\n+\n+  /* a closure wrapping the original on_complete one to be invoked once the\n+   * pick() has completed (regardless of success) */\n+  grpc_closure wrapped_on_complete;\n+\n+  /* args for wrapped_on_complete */\n+  wrapped_rr_closure_arg wrapped_on_complete_arg;\n+} pending_pick;\n+\n+/* Same as the \\a pending_pick struct but for ping operations */\n+typedef struct pending_ping {\n+  struct pending_ping *next;\n+\n+  /* a closure wrapping the original on_complete one to be invoked once the\n+   * ping() has completed (regardless of success) */\n+  grpc_closure wrapped_notify;\n+\n+  /* args for wrapped_notify */\n+  wrapped_rr_closure_arg wrapped_notify_arg;\n+} pending_ping;\n+\n+typedef struct glb_lb_policy glb_lb_policy;\n+\n+/* Used internally for the client call to the LB */\n+typedef struct lb_client_data {\n+  gpr_mu mu;\n+\n+  /* called once initial metadata's been sent */\n+  grpc_closure md_sent;\n+\n+  /* called once initial metadata's been received */\n+  grpc_closure md_rcvd;\n+\n+  /* called once the LoadBalanceRequest has been sent to the LB server. See\n+   * src/proto/grpc/.../load_balancer.proto */\n+  grpc_closure req_sent;\n+\n+  /* A response from the LB server has been received (or error). Process it */\n+  grpc_closure res_rcvd;\n+\n+  /* After the client has sent a close to the LB server */\n+  grpc_closure close_sent;\n+\n+  /* ... and the status from the LB server has been received */\n+  grpc_closure srv_status_rcvd;\n+\n+  grpc_call *lb_call;    /* streaming call to the LB server, */\n+  gpr_timespec deadline; /* for the streaming call to the LB server */\n+\n+  grpc_metadata_array initial_metadata_recv;  /* initial MD from LB server */\n+  grpc_metadata_array trailing_metadata_recv; /* trailing MD from LB server */\n+\n+  /* what's being sent to the LB server. Note that its value may vary if the LB\n+   * server indicates a redirect. */\n+  grpc_byte_buffer *request_payload;\n+\n+  /* response from the LB server, if any. Processed in res_rcvd_cb() */\n+  grpc_byte_buffer *response_payload;\n+\n+  /* the call's status and status detailset in srv_status_rcvd_cb() */\n+  grpc_status_code status;\n+  char *status_details;\n+  size_t status_details_capacity;\n+\n+  /* pointer back to the enclosing policy */\n+  glb_lb_policy *p;\n+} lb_client_data;\n+\n+/* Keeps track and reacts to changes in connectivity of the RR instance */\n+typedef struct rr_connectivity_data {\n+  grpc_closure on_change;\n+  grpc_connectivity_state state;\n+  glb_lb_policy *p;\n+} rr_connectivity_data;\n+\n+struct glb_lb_policy {\n+  /** base policy: must be first */\n+  grpc_lb_policy base;\n+\n+  /** mutex protecting remaining members */\n+  gpr_mu mu;\n+\n+  grpc_client_channel_factory *cc_factory;\n+\n+  /** for communicating with the LB server */\n+  grpc_channel *lb_channel;\n+\n+  /** the RR policy to use of the backend servers returned by the LB server */\n+  grpc_lb_policy *rr_policy;\n+\n+  bool started_picking;\n+\n+  /** our connectivity state tracker */\n+  grpc_connectivity_state_tracker state_tracker;\n+\n+  /** stores the deserialized response from the LB. May be NULL until one such\n+   * response has arrived. */\n+  grpc_grpclb_serverlist *serverlist;\n+\n+  /** list of picks that are waiting on RR's policy connectivity */\n+  pending_pick *pending_picks;\n+\n+  /** list of pings that are waiting on RR's policy connectivity */\n+  pending_ping *pending_pings;\n+\n+  /** client data associated with the LB server communication */\n+  lb_client_data *lbcd;\n+\n+  /** for tracking of the RR connectivity */\n+  rr_connectivity_data *rr_connectivity;\n+\n+  /* a wrapped (see ...) on-complete closure for readily available RR picks */","path":"src/core/ext/lb_policy/grpclb/grpclb.c","position":285,"original_position":285,"commit_id":"6061f18773364ee243dc4399f358ad857c55f83d","original_commit_id":"6061f18773364ee243dc4399f358ad857c55f83d","user":{"login":"markdroth","id":18664614,"avatar_url":"https://avatars.githubusercontent.com/u/18664614?v=3","gravatar_id":"","url":"https://api.github.com/users/markdroth","html_url":"https://github.com/markdroth","followers_url":"https://api.github.com/users/markdroth/followers","following_url":"https://api.github.com/users/markdroth/following{/other_user}","gists_url":"https://api.github.com/users/markdroth/gists{/gist_id}","starred_url":"https://api.github.com/users/markdroth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markdroth/subscriptions","organizations_url":"https://api.github.com/users/markdroth/orgs","repos_url":"https://api.github.com/users/markdroth/repos","events_url":"https://api.github.com/users/markdroth/events{/privacy}","received_events_url":"https://api.github.com/users/markdroth/received_events","type":"User","site_admin":false},"body":"What does \"see ...\" mean?","created_at":"2016-07-28T15:53:54Z","updated_at":"2016-07-28T15:53:54Z","html_url":"https://github.com/grpc/grpc/pull/6924#discussion_r72649437","pull_request_url":"https://api.github.com/repos/grpc/grpc/pulls/6924","_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc/pulls/comments/72649437"},"html":{"href":"https://github.com/grpc/grpc/pull/6924#discussion_r72649437"},"pull_request":{"href":"https://api.github.com/repos/grpc/grpc/pulls/6924"}}},"pull_request":{"url":"https://api.github.com/repos/grpc/grpc/pulls/6924","id":74025703,"html_url":"https://github.com/grpc/grpc/pull/6924","diff_url":"https://github.com/grpc/grpc/pull/6924.diff","patch_url":"https://github.com/grpc/grpc/pull/6924.patch","issue_url":"https://api.github.com/repos/grpc/grpc/issues/6924","number":6924,"state":"open","locked":false,"title":"gRPC LB policy","user":{"login":"dgquintas","id":120217,"avatar_url":"https://avatars.githubusercontent.com/u/120217?v=3","gravatar_id":"","url":"https://api.github.com/users/dgquintas","html_url":"https://github.com/dgquintas","followers_url":"https://api.github.com/users/dgquintas/followers","following_url":"https://api.github.com/users/dgquintas/following{/other_user}","gists_url":"https://api.github.com/users/dgquintas/gists{/gist_id}","starred_url":"https://api.github.com/users/dgquintas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dgquintas/subscriptions","organizations_url":"https://api.github.com/users/dgquintas/orgs","repos_url":"https://api.github.com/users/dgquintas/repos","events_url":"https://api.github.com/users/dgquintas/events{/privacy}","received_events_url":"https://api.github.com/users/dgquintas/received_events","type":"User","site_admin":false},"body":"~~note that the grpclb_test isn't working on jenkins because the enviroment needs to be able to run `./tools/codegen/core/gen_grpclb_test_response.py`, which requires:\r\n1. having the `protoc` in the path, for the python-pb-compilation of `src/proto/grpc/lb/v1/load_balancer.proto`.\r\n2. to have python proto3 installed in whichever python environment the tests run in.~~\r\n\r\n~~`./tools/codegen/core/gen_grpclb_test_response.py` is used to generate the responses the LB server in the test will use. It needs to be run dynamically from within the test because the backend server ports (which are also started as part of the test) are assigned at runtime by the port server.~~\r\n\r\n~~An alternative to the python generator would be a C++ one that'd rely on a build.yaml-driven pb-C++ compilation of `src/proto/grpc/lb/v1/load_balancer.proto`. This would have no external dependencies.~~\r\n\r\nAs of https://github.com/grpc/grpc/pull/6924/commits/aaba1313469440a887ad70be276d8b790e91146a the generation of the LB response in the test is now performed inline, after having transformed the test into a C++ one.\r\n\r\nThis PR implements https://github.com/grpc/grpc/blob/master/doc/load-balancing.md","created_at":"2016-06-16T05:53:39Z","updated_at":"2016-07-28T15:53:54Z","closed_at":null,"merged_at":null,"merge_commit_sha":"71e41684f24032d57a2095a062bac8075b656603","assignee":{"login":"ctiller","id":10120821,"avatar_url":"https://avatars.githubusercontent.com/u/10120821?v=3","gravatar_id":"","url":"https://api.github.com/users/ctiller","html_url":"https://github.com/ctiller","followers_url":"https://api.github.com/users/ctiller/followers","following_url":"https://api.github.com/users/ctiller/following{/other_user}","gists_url":"https://api.github.com/users/ctiller/gists{/gist_id}","starred_url":"https://api.github.com/users/ctiller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ctiller/subscriptions","organizations_url":"https://api.github.com/users/ctiller/orgs","repos_url":"https://api.github.com/users/ctiller/repos","events_url":"https://api.github.com/users/ctiller/events{/privacy}","received_events_url":"https://api.github.com/users/ctiller/received_events","type":"User","site_admin":false},"assignees":[{"login":"ctiller","id":10120821,"avatar_url":"https://avatars.githubusercontent.com/u/10120821?v=3","gravatar_id":"","url":"https://api.github.com/users/ctiller","html_url":"https://github.com/ctiller","followers_url":"https://api.github.com/users/ctiller/followers","following_url":"https://api.github.com/users/ctiller/following{/other_user}","gists_url":"https://api.github.com/users/ctiller/gists{/gist_id}","starred_url":"https://api.github.com/users/ctiller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ctiller/subscriptions","organizations_url":"https://api.github.com/users/ctiller/orgs","repos_url":"https://api.github.com/users/ctiller/repos","events_url":"https://api.github.com/users/ctiller/events{/privacy}","received_events_url":"https://api.github.com/users/ctiller/received_events","type":"User","site_admin":false},{"login":"markdroth","id":18664614,"avatar_url":"https://avatars.githubusercontent.com/u/18664614?v=3","gravatar_id":"","url":"https://api.github.com/users/markdroth","html_url":"https://github.com/markdroth","followers_url":"https://api.github.com/users/markdroth/followers","following_url":"https://api.github.com/users/markdroth/following{/other_user}","gists_url":"https://api.github.com/users/markdroth/gists{/gist_id}","starred_url":"https://api.github.com/users/markdroth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markdroth/subscriptions","organizations_url":"https://api.github.com/users/markdroth/orgs","repos_url":"https://api.github.com/users/markdroth/repos","events_url":"https://api.github.com/users/markdroth/events{/privacy}","received_events_url":"https://api.github.com/users/markdroth/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/grpc/grpc/pulls/6924/commits","review_comments_url":"https://api.github.com/repos/grpc/grpc/pulls/6924/comments","review_comment_url":"https://api.github.com/repos/grpc/grpc/pulls/comments{/number}","comments_url":"https://api.github.com/repos/grpc/grpc/issues/6924/comments","statuses_url":"https://api.github.com/repos/grpc/grpc/statuses/6061f18773364ee243dc4399f358ad857c55f83d","head":{"label":"dgquintas:grpclb_v0","ref":"grpclb_v0","sha":"6061f18773364ee243dc4399f358ad857c55f83d","user":{"login":"dgquintas","id":120217,"avatar_url":"https://avatars.githubusercontent.com/u/120217?v=3","gravatar_id":"","url":"https://api.github.com/users/dgquintas","html_url":"https://github.com/dgquintas","followers_url":"https://api.github.com/users/dgquintas/followers","following_url":"https://api.github.com/users/dgquintas/following{/other_user}","gists_url":"https://api.github.com/users/dgquintas/gists{/gist_id}","starred_url":"https://api.github.com/users/dgquintas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dgquintas/subscriptions","organizations_url":"https://api.github.com/users/dgquintas/orgs","repos_url":"https://api.github.com/users/dgquintas/repos","events_url":"https://api.github.com/users/dgquintas/events{/privacy}","received_events_url":"https://api.github.com/users/dgquintas/received_events","type":"User","site_admin":false},"repo":{"id":34546298,"name":"grpc","full_name":"dgquintas/grpc","owner":{"login":"dgquintas","id":120217,"avatar_url":"https://avatars.githubusercontent.com/u/120217?v=3","gravatar_id":"","url":"https://api.github.com/users/dgquintas","html_url":"https://github.com/dgquintas","followers_url":"https://api.github.com/users/dgquintas/followers","following_url":"https://api.github.com/users/dgquintas/following{/other_user}","gists_url":"https://api.github.com/users/dgquintas/gists{/gist_id}","starred_url":"https://api.github.com/users/dgquintas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dgquintas/subscriptions","organizations_url":"https://api.github.com/users/dgquintas/orgs","repos_url":"https://api.github.com/users/dgquintas/repos","events_url":"https://api.github.com/users/dgquintas/events{/privacy}","received_events_url":"https://api.github.com/users/dgquintas/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dgquintas/grpc","description":"The C based gRPC (C++, Node.js, Python, Ruby, Objective-C, PHP, C#)","fork":true,"url":"https://api.github.com/repos/dgquintas/grpc","forks_url":"https://api.github.com/repos/dgquintas/grpc/forks","keys_url":"https://api.github.com/repos/dgquintas/grpc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dgquintas/grpc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dgquintas/grpc/teams","hooks_url":"https://api.github.com/repos/dgquintas/grpc/hooks","issue_events_url":"https://api.github.com/repos/dgquintas/grpc/issues/events{/number}","events_url":"https://api.github.com/repos/dgquintas/grpc/events","assignees_url":"https://api.github.com/repos/dgquintas/grpc/assignees{/user}","branches_url":"https://api.github.com/repos/dgquintas/grpc/branches{/branch}","tags_url":"https://api.github.com/repos/dgquintas/grpc/tags","blobs_url":"https://api.github.com/repos/dgquintas/grpc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dgquintas/grpc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dgquintas/grpc/git/refs{/sha}","trees_url":"https://api.github.com/repos/dgquintas/grpc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dgquintas/grpc/statuses/{sha}","languages_url":"https://api.github.com/repos/dgquintas/grpc/languages","stargazers_url":"https://api.github.com/repos/dgquintas/grpc/stargazers","contributors_url":"https://api.github.com/repos/dgquintas/grpc/contributors","subscribers_url":"https://api.github.com/repos/dgquintas/grpc/subscribers","subscription_url":"https://api.github.com/repos/dgquintas/grpc/subscription","commits_url":"https://api.github.com/repos/dgquintas/grpc/commits{/sha}","git_commits_url":"https://api.github.com/repos/dgquintas/grpc/git/commits{/sha}","comments_url":"https://api.github.com/repos/dgquintas/grpc/comments{/number}","issue_comment_url":"https://api.github.com/repos/dgquintas/grpc/issues/comments{/number}","contents_url":"https://api.github.com/repos/dgquintas/grpc/contents/{+path}","compare_url":"https://api.github.com/repos/dgquintas/grpc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dgquintas/grpc/merges","archive_url":"https://api.github.com/repos/dgquintas/grpc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dgquintas/grpc/downloads","issues_url":"https://api.github.com/repos/dgquintas/grpc/issues{/number}","pulls_url":"https://api.github.com/repos/dgquintas/grpc/pulls{/number}","milestones_url":"https://api.github.com/repos/dgquintas/grpc/milestones{/number}","notifications_url":"https://api.github.com/repos/dgquintas/grpc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dgquintas/grpc/labels{/name}","releases_url":"https://api.github.com/repos/dgquintas/grpc/releases{/id}","deployments_url":"https://api.github.com/repos/dgquintas/grpc/deployments","created_at":"2015-04-24T23:07:11Z","updated_at":"2016-02-03T02:19:19Z","pushed_at":"2016-07-28T06:18:10Z","git_url":"git://github.com/dgquintas/grpc.git","ssh_url":"git@github.com:dgquintas/grpc.git","clone_url":"https://github.com/dgquintas/grpc.git","svn_url":"https://github.com/dgquintas/grpc","homepage":"","size":72387,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"grpc:master","ref":"master","sha":"31fd05318099c64d2bd5688a6c3cea341e4b566e","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":27729880,"name":"grpc","full_name":"grpc/grpc","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","description":"The C based gRPC (C++, Node.js, Python, Ruby, Objective-C, PHP, C#)","fork":false,"url":"https://api.github.com/repos/grpc/grpc","forks_url":"https://api.github.com/repos/grpc/grpc/forks","keys_url":"https://api.github.com/repos/grpc/grpc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/grpc/grpc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/grpc/grpc/teams","hooks_url":"https://api.github.com/repos/grpc/grpc/hooks","issue_events_url":"https://api.github.com/repos/grpc/grpc/issues/events{/number}","events_url":"https://api.github.com/repos/grpc/grpc/events","assignees_url":"https://api.github.com/repos/grpc/grpc/assignees{/user}","branches_url":"https://api.github.com/repos/grpc/grpc/branches{/branch}","tags_url":"https://api.github.com/repos/grpc/grpc/tags","blobs_url":"https://api.github.com/repos/grpc/grpc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/grpc/grpc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/grpc/grpc/git/refs{/sha}","trees_url":"https://api.github.com/repos/grpc/grpc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/grpc/grpc/statuses/{sha}","languages_url":"https://api.github.com/repos/grpc/grpc/languages","stargazers_url":"https://api.github.com/repos/grpc/grpc/stargazers","contributors_url":"https://api.github.com/repos/grpc/grpc/contributors","subscribers_url":"https://api.github.com/repos/grpc/grpc/subscribers","subscription_url":"https://api.github.com/repos/grpc/grpc/subscription","commits_url":"https://api.github.com/repos/grpc/grpc/commits{/sha}","git_commits_url":"https://api.github.com/repos/grpc/grpc/git/commits{/sha}","comments_url":"https://api.github.com/repos/grpc/grpc/comments{/number}","issue_comment_url":"https://api.github.com/repos/grpc/grpc/issues/comments{/number}","contents_url":"https://api.github.com/repos/grpc/grpc/contents/{+path}","compare_url":"https://api.github.com/repos/grpc/grpc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/grpc/grpc/merges","archive_url":"https://api.github.com/repos/grpc/grpc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/grpc/grpc/downloads","issues_url":"https://api.github.com/repos/grpc/grpc/issues{/number}","pulls_url":"https://api.github.com/repos/grpc/grpc/pulls{/number}","milestones_url":"https://api.github.com/repos/grpc/grpc/milestones{/number}","notifications_url":"https://api.github.com/repos/grpc/grpc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/grpc/grpc/labels{/name}","releases_url":"https://api.github.com/repos/grpc/grpc/releases{/id}","deployments_url":"https://api.github.com/repos/grpc/grpc/deployments","created_at":"2014-12-08T18:58:53Z","updated_at":"2016-07-28T14:38:13Z","pushed_at":"2016-07-28T15:46:48Z","git_url":"git://github.com/grpc/grpc.git","ssh_url":"git@github.com:grpc/grpc.git","clone_url":"https://github.com/grpc/grpc.git","svn_url":"https://github.com/grpc/grpc","homepage":"","size":200859,"stargazers_count":5635,"watchers_count":5635,"language":"C","has_issues":true,"has_downloads":false,"has_wiki":true,"has_pages":true,"forks_count":1043,"mirror_url":null,"open_issues_count":664,"forks":1043,"open_issues":664,"watchers":5635,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc/pulls/6924"},"html":{"href":"https://github.com/grpc/grpc/pull/6924"},"issue":{"href":"https://api.github.com/repos/grpc/grpc/issues/6924"},"comments":{"href":"https://api.github.com/repos/grpc/grpc/issues/6924/comments"},"review_comments":{"href":"https://api.github.com/repos/grpc/grpc/pulls/6924/comments"},"review_comment":{"href":"https://api.github.com/repos/grpc/grpc/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/grpc/grpc/pulls/6924/commits"},"statuses":{"href":"https://api.github.com/repos/grpc/grpc/statuses/6061f18773364ee243dc4399f358ad857c55f83d"}}}}
{"actor":{"display_login":"markdroth"}}