Пример #1
0
static void test_leftover(grpc_endpoint_test_config config, size_t slice_size) {
  grpc_endpoint_test_fixture f = config.create_fixture(slice_size);
  gpr_slice_buffer incoming;
  gpr_slice s =
      gpr_slice_from_copied_string("hello world 12345678900987654321");
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
  int n = 0;
  grpc_closure done_closure;
  gpr_log(GPR_INFO, "Start test left over");

  gpr_slice_buffer_init(&incoming);
  grpc_closure_init(&done_closure, inc_call_ctr, &n);
  grpc_endpoint_read(&exec_ctx, f.client_ep, &incoming, &done_closure);
  grpc_exec_ctx_finish(&exec_ctx);
  GPR_ASSERT(n == 1);
  GPR_ASSERT(incoming.count == 1);
  GPR_ASSERT(0 == gpr_slice_cmp(s, incoming.slices[0]));

  grpc_endpoint_shutdown(&exec_ctx, f.client_ep);
  grpc_endpoint_shutdown(&exec_ctx, f.server_ep);
  grpc_endpoint_destroy(&exec_ctx, f.client_ep);
  grpc_endpoint_destroy(&exec_ctx, f.server_ep);
  grpc_exec_ctx_finish(&exec_ctx);
  gpr_slice_unref(s);
  gpr_slice_buffer_destroy(&incoming);

  clean_up();
}
Пример #2
0
/* Do both reading and writing using the grpc_endpoint API.

   This also includes a test of the shutdown behavior.
 */
static void read_and_write_test(grpc_endpoint_test_config config,
                                size_t num_bytes, size_t write_size,
                                size_t slice_size, int shutdown) {
    struct read_and_write_test_state state;
    gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(20);
    grpc_endpoint_test_fixture f =
        begin_test(config, "read_and_write_test", slice_size);

    if (shutdown) {
        gpr_log(GPR_INFO, "Start read and write shutdown test");
    } else {
        gpr_log(GPR_INFO, "Start read and write test with %d bytes, slice size %d",
                num_bytes, slice_size);
    }

    state.read_ep = f.client_ep;
    state.write_ep = f.server_ep;
    state.target_bytes = num_bytes;
    state.bytes_read = 0;
    state.current_write_size = write_size;
    state.bytes_written = 0;
    state.read_done = 0;
    state.write_done = 0;
    state.current_read_data = 0;
    state.current_write_data = 0;

    /* Get started by pretending an initial write completed */
    /* NOTE: Sets up initial conditions so we can have the same write handler
       for the first iteration as for later iterations. It does the right thing
       even when bytes_written is unsigned. */
    state.bytes_written -= state.current_write_size;
    read_and_write_test_write_handler(&state, GRPC_ENDPOINT_CB_OK);

    grpc_endpoint_notify_on_read(state.read_ep, read_and_write_test_read_handler,
                                 &state);

    if (shutdown) {
        gpr_log(GPR_DEBUG, "shutdown read");
        grpc_endpoint_shutdown(state.read_ep);
        gpr_log(GPR_DEBUG, "shutdown write");
        grpc_endpoint_shutdown(state.write_ep);
    }

    gpr_mu_lock(GRPC_POLLSET_MU(g_pollset));
    while (!state.read_done || !state.write_done) {
        GPR_ASSERT(gpr_time_cmp(gpr_now(GPR_CLOCK_MONOTONIC), deadline) < 0);
        grpc_pollset_work(g_pollset, deadline);
    }
    gpr_mu_unlock(GRPC_POLLSET_MU(g_pollset));

    grpc_endpoint_destroy(state.read_ep);
    grpc_endpoint_destroy(state.write_ep);
    end_test(config);
}
Пример #3
0
// Helper function to shut down the proxy connection.
// Does NOT take ownership of a reference to error.
static void proxy_connection_failed(grpc_exec_ctx* exec_ctx,
                                    proxy_connection* conn, bool is_client,
                                    const char* prefix, grpc_error* error) {
  const char* msg = grpc_error_string(error);
  gpr_log(GPR_INFO, "%s: %s", prefix, msg);
  grpc_error_free_string(msg);
  grpc_endpoint_shutdown(exec_ctx, conn->client_endpoint);
  if (conn->server_endpoint != NULL)
    grpc_endpoint_shutdown(exec_ctx, conn->server_endpoint);
  proxy_connection_unref(exec_ctx, conn);
}
Пример #4
0
static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
  chttp2_connector *c = arg;
  gpr_mu_lock(&c->mu);
  GPR_ASSERT(c->connecting);
  c->connecting = false;
  if (error != GRPC_ERROR_NONE || c->shutdown) {
    if (error == GRPC_ERROR_NONE) {
      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown");
    } else {
      error = GRPC_ERROR_REF(error);
    }
    memset(c->result, 0, sizeof(*c->result));
    grpc_closure *notify = c->notify;
    c->notify = NULL;
    grpc_closure_sched(exec_ctx, notify, error);
    if (c->endpoint != NULL) {
      grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(error));
    }
    gpr_mu_unlock(&c->mu);
    chttp2_connector_unref(exec_ctx, arg);
  } else {
    GPR_ASSERT(c->endpoint != NULL);
    start_handshake_locked(exec_ctx, c);
    gpr_mu_unlock(&c->mu);
  }
}
Пример #5
0
// If the handshake failed or we're shutting down, clean up and invoke the
// callback with the error.
static void handshake_failed_locked(grpc_exec_ctx* exec_ctx,
                                    http_connect_handshaker* handshaker,
                                    grpc_error* error) {
  if (error == GRPC_ERROR_NONE) {
    // If we were shut down after an endpoint operation succeeded but
    // before the endpoint callback was invoked, we need to generate our
    // own error.
    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown");
  }
  if (!handshaker->shutdown) {
    // TODO(ctiller): It is currently necessary to shutdown endpoints
    // before destroying them, even if we know that there are no
    // pending read/write callbacks.  This should be fixed, at which
    // point this can be removed.
    grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint,
                           GRPC_ERROR_REF(error));
    // Not shutting down, so the handshake failed.  Clean up before
    // invoking the callback.
    cleanup_args_for_failure_locked(exec_ctx, handshaker);
    // Set shutdown to true so that subsequent calls to
    // http_connect_handshaker_shutdown() do nothing.
    handshaker->shutdown = true;
  }
  // Invoke callback.
  grpc_closure_sched(exec_ctx, handshaker->on_handshake_done, error);
}
Пример #6
0
static void run_test(const char *response_payload,
                     size_t response_payload_length,
                     grpc_status_code expected_status,
                     const char *expected_detail) {
  test_tcp_server test_server;
  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
  gpr_event ev;

  grpc_init();
  gpr_event_init(&ev);
  server_port = grpc_pick_unused_port_or_die();
  test_tcp_server_init(&test_server, on_connect, &test_server);
  test_tcp_server_start(&test_server, server_port);
  state.response_payload = response_payload;
  state.response_payload_length = response_payload_length;

  /* poll server until sending out the response */
  poll_server_until_read_done(&test_server, &ev);
  start_rpc(server_port, expected_status, expected_detail);
  gpr_event_wait(&ev, gpr_inf_future(GPR_CLOCK_REALTIME));

  /* clean up */
  grpc_endpoint_shutdown(&exec_ctx, state.tcp);
  grpc_endpoint_destroy(&exec_ctx, state.tcp);
  grpc_exec_ctx_finish(&exec_ctx);
  cleanup_rpc();
  test_tcp_server_destroy(&test_server);

  grpc_shutdown();
}
Пример #7
0
static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
  chttp2_connector *c = arg;
  gpr_mu_lock(&c->mu);
  GPR_ASSERT(c->connecting);
  c->connecting = false;
  if (error != GRPC_ERROR_NONE || c->shutdown) {
    if (error == GRPC_ERROR_NONE) {
      error = GRPC_ERROR_CREATE("connector shutdown");
    } else {
      error = GRPC_ERROR_REF(error);
    }
    memset(c->result, 0, sizeof(*c->result));
    grpc_closure *notify = c->notify;
    c->notify = NULL;
    grpc_exec_ctx_sched(exec_ctx, notify, error, NULL);
    if (c->endpoint != NULL) grpc_endpoint_shutdown(exec_ctx, c->endpoint);
    gpr_mu_unlock(&c->mu);
    chttp2_connector_unref(exec_ctx, arg);
  } else {
    GPR_ASSERT(c->endpoint != NULL);
    if (!GRPC_SLICE_IS_EMPTY(c->args.initial_connect_string)) {
      grpc_closure_init(&c->initial_string_sent, on_initial_connect_string_sent,
                        c);
      grpc_slice_buffer_init(&c->initial_string_buffer);
      grpc_slice_buffer_add(&c->initial_string_buffer,
                            c->args.initial_connect_string);
      grpc_endpoint_write(exec_ctx, c->endpoint, &c->initial_string_buffer,
                          &c->initial_string_sent);
    } else {
      start_handshake_locked(exec_ctx, c);
    }
    gpr_mu_unlock(&c->mu);
  }
}
Пример #8
0
// If the handshake failed or we're shutting down, clean up and invoke the
// callback with the error.
static void security_handshake_failed_locked(grpc_exec_ctx *exec_ctx,
                                             security_handshaker *h,
                                             grpc_error *error) {
  if (error == GRPC_ERROR_NONE) {
    // If we were shut down after the handshake succeeded but before an
    // endpoint callback was invoked, we need to generate our own error.
    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown");
  }
  const char *msg = grpc_error_string(error);
  gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg);

  if (!h->shutdown) {
    // TODO(ctiller): It is currently necessary to shutdown endpoints
    // before destroying them, even if we know that there are no
    // pending read/write callbacks.  This should be fixed, at which
    // point this can be removed.
    grpc_endpoint_shutdown(exec_ctx, h->args->endpoint, GRPC_ERROR_REF(error));
    // Not shutting down, so the write failed.  Clean up before
    // invoking the callback.
    cleanup_args_for_failure_locked(exec_ctx, h);
    // Set shutdown to true so that subsequent calls to
    // security_handshaker_shutdown() do nothing.
    h->shutdown = true;
  }
  // Invoke callback.
  GRPC_CLOSURE_SCHED(exec_ctx, h->on_handshake_done, error);
}
Пример #9
0
/* connection callback: tcp is either valid, or null on error */
static void on_connect(void *rp, grpc_endpoint *tcp) {
  request *r = rp;

  if (!grpc_client_setup_request_should_continue(r->cs_request, "on_connect")) {
    if (tcp) {
      grpc_endpoint_shutdown(tcp);
      grpc_endpoint_destroy(tcp);
    }
    done(r, 0);
    return;
  }

  if (!tcp) {
    if (!maybe_try_next_resolved(r)) {
      done(r, 0);
      return;
    } else {
      return;
    }
  } else if (grpc_client_setup_cb_begin(r->cs_request, "on_connect")) {
    grpc_create_chttp2_transport(
        r->setup->setup_callback, r->setup->setup_user_data,
        grpc_client_setup_get_channel_args(r->cs_request), tcp, NULL, 0,
        grpc_client_setup_get_mdctx(r->cs_request), 1);
    grpc_client_setup_cb_end(r->cs_request, "on_connect");
    done(r, 1);
    return;
  } else {
    done(r, 0);
  }
}
Пример #10
0
static void security_handshake_done(grpc_exec_ctx *exec_ctx,
                                    grpc_security_handshake *h,
                                    grpc_error *error) {
    grpc_timer_cancel(exec_ctx, &h->timer);
    if (!h->is_client_side) {
        security_connector_remove_handshake(h);
    }
    if (error == GRPC_ERROR_NONE) {
        h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->secure_endpoint,
              h->auth_context);
    } else {
        const char *msg = grpc_error_string(error);
        gpr_log(GPR_ERROR, "Security handshake failed: %s", msg);
        grpc_error_free_string(msg);

        if (h->secure_endpoint != NULL) {
            grpc_endpoint_shutdown(exec_ctx, h->secure_endpoint);
            grpc_endpoint_destroy(exec_ctx, h->secure_endpoint);
        } else {
            grpc_endpoint_destroy(exec_ctx, h->wrapped_endpoint);
        }
        h->cb(exec_ctx, h->user_data, GRPC_SECURITY_ERROR, NULL, NULL);
    }
    unref_handshake(h);
    GRPC_ERROR_UNREF(error);
}
Пример #11
0
static void allow_endpoint_shutdown_locked(grpc_chttp2_transport *t) {
  if (gpr_unref(&t->shutdown_ep_refs)) {
    if (t->ep) {
      grpc_endpoint_shutdown(t->ep);
    }
  }
}
Пример #12
0
static void on_timeout(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
    grpc_security_handshake *h = arg;
    if (error == GRPC_ERROR_NONE) {
        grpc_endpoint_shutdown(exec_ctx, h->wrapped_endpoint);
    }
    unref_handshake(h);
}
Пример #13
0
static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp,
                      grpc_pollset *accepting_pollset,
                      grpc_tcp_server_acceptor *acceptor) {
  server_state *state = arg;
  gpr_mu_lock(&state->mu);
  if (state->shutdown) {
    gpr_mu_unlock(&state->mu);
    grpc_endpoint_shutdown(exec_ctx, tcp, GRPC_ERROR_NONE);
    grpc_endpoint_destroy(exec_ctx, tcp);
    gpr_free(acceptor);
    return;
  }
  grpc_handshake_manager *handshake_mgr = grpc_handshake_manager_create();
  grpc_handshake_manager_pending_list_add(&state->pending_handshake_mgrs,
                                          handshake_mgr);
  gpr_mu_unlock(&state->mu);
  grpc_tcp_server_ref(state->tcp_server);
  server_connection_state *connection_state =
      gpr_malloc(sizeof(*connection_state));
  connection_state->server_state = state;
  connection_state->accepting_pollset = accepting_pollset;
  connection_state->acceptor = acceptor;
  connection_state->handshake_mgr = handshake_mgr;
  grpc_handshakers_add(exec_ctx, HANDSHAKER_SERVER, state->args,
                       connection_state->handshake_mgr);
  // TODO(roth): We should really get this timeout value from channel
  // args instead of hard-coding it.
  const gpr_timespec deadline = gpr_time_add(
      gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN));
  grpc_handshake_manager_do_handshake(exec_ctx, connection_state->handshake_mgr,
                                      tcp, state->args, deadline, acceptor,
                                      on_handshake_done, connection_state);
}
Пример #14
0
static void must_succeed(grpc_exec_ctx *exec_ctx, void *arg, int success) {
  GPR_ASSERT(g_connecting != NULL);
  GPR_ASSERT(success);
  grpc_endpoint_shutdown(exec_ctx, g_connecting);
  grpc_endpoint_destroy(exec_ctx, g_connecting);
  g_connecting = NULL;
  finish_connection();
}
Пример #15
0
static void on_connect(grpc_exec_ctx *exec_ctx, void *vargs, grpc_endpoint *tcp,
                       grpc_tcp_server_acceptor *acceptor) {
  struct server_thread_args *args = (struct server_thread_args *)vargs;
  (void)acceptor;
  grpc_endpoint_shutdown(exec_ctx, tcp);
  grpc_endpoint_destroy(exec_ctx, tcp);
  grpc_pollset_kick(args->pollset, NULL);
}
Пример #16
0
static void close_transport_locked(grpc_chttp2_transport *t) {
  if (!t->closed) {
    t->closed = 1;
    if (t->ep) {
      grpc_endpoint_shutdown(t->ep);
    }
  }
}
Пример #17
0
static void shutdown_during_write_test(grpc_endpoint_test_config config,
                                       size_t slice_size) {
    /* test that shutdown with a pending write creates no leaks */
    gpr_timespec deadline;
    size_t size;
    size_t nblocks;
    int current_data = 1;
    shutdown_during_write_test_state read_st;
    shutdown_during_write_test_state write_st;
    gpr_slice *slices;
    grpc_endpoint_test_fixture f =
        begin_test(config, "shutdown_during_write_test", slice_size);

    gpr_log(GPR_INFO, "testing shutdown during a write");

    read_st.ep = f.client_ep;
    write_st.ep = f.server_ep;
    read_st.done = 0;
    write_st.done = 0;

    grpc_endpoint_notify_on_read(
        read_st.ep, shutdown_during_write_test_read_handler, &read_st);
    for (size = 1;; size *= 2) {
        slices = allocate_blocks(size, 1, &nblocks, &current_data);
        switch (grpc_endpoint_write(write_st.ep, slices, nblocks,
                                    shutdown_during_write_test_write_handler,
                                    &write_st)) {
        case GRPC_ENDPOINT_WRITE_DONE:
            break;
        case GRPC_ENDPOINT_WRITE_ERROR:
            gpr_log(GPR_ERROR, "error writing");
            abort();
        case GRPC_ENDPOINT_WRITE_PENDING:
            grpc_endpoint_shutdown(write_st.ep);
            deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10);
            gpr_mu_lock(GRPC_POLLSET_MU(g_pollset));
            while (!write_st.done) {
                GPR_ASSERT(gpr_time_cmp(gpr_now(deadline.clock_type), deadline) < 0);
                grpc_pollset_work(g_pollset, deadline);
            }
            gpr_mu_unlock(GRPC_POLLSET_MU(g_pollset));
            grpc_endpoint_destroy(write_st.ep);
            gpr_mu_lock(GRPC_POLLSET_MU(g_pollset));
            while (!read_st.done) {
                GPR_ASSERT(gpr_time_cmp(gpr_now(deadline.clock_type), deadline) < 0);
                grpc_pollset_work(g_pollset, deadline);
            }
            gpr_mu_unlock(GRPC_POLLSET_MU(g_pollset));
            gpr_free(slices);
            end_test(config);
            return;
        }
        gpr_free(slices);
    }

    gpr_log(GPR_ERROR, "should never reach here");
    abort();
}
Пример #18
0
static void on_connect(void *arg, grpc_endpoint *tcp) {
  grpc_endpoint_shutdown(tcp);
  grpc_endpoint_destroy(tcp);

  gpr_mu_lock(&mu);
  nconnects++;
  gpr_cv_broadcast(&cv);
  gpr_mu_unlock(&mu);
}
Пример #19
0
static void on_connect(void *arg, grpc_endpoint *tcp) {
  grpc_endpoint_shutdown(tcp);
  grpc_endpoint_destroy(tcp);

  gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
  g_nconnects++;
  grpc_pollset_kick(&g_pollset);
  gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));
}
Пример #20
0
static void allow_endpoint_shutdown_unlocked(grpc_exec_ctx *exec_ctx,
                                             grpc_chttp2_transport *t) {
  if (gpr_unref(&t->shutdown_ep_refs)) {
    gpr_mu_lock(&t->mu);
    if (t->ep) {
      grpc_endpoint_shutdown(exec_ctx, t->ep);
    }
    gpr_mu_unlock(&t->mu);
  }
}
Пример #21
0
static void close_transport_locked(grpc_chttp2_transport *t) {
  if (!t->closed) {
    t->closed = 1;
    connectivity_state_set(&t->global, GRPC_CHANNEL_FATAL_FAILURE,
                           "close_transport");
    if (t->ep) {
      grpc_endpoint_shutdown(t->ep);
    }
  }
}
Пример #22
0
static void on_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp,
                       grpc_tcp_server_acceptor *acceptor) {
    grpc_endpoint_shutdown(exec_ctx, tcp);
    grpc_endpoint_destroy(exec_ctx, tcp);

    gpr_mu_lock(g_mu);
    on_connect_result_set(&g_result, acceptor);
    g_nconnects++;
    grpc_pollset_kick(g_pollset, NULL);
    gpr_mu_unlock(g_mu);
}
Пример #23
0
static void connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
  connector *c = (connector *)con;
  grpc_endpoint *ep;
  gpr_mu_lock(&c->mu);
  ep = c->connecting_endpoint;
  c->connecting_endpoint = NULL;
  gpr_mu_unlock(&c->mu);
  if (ep) {
    grpc_endpoint_shutdown(exec_ctx, ep);
  }
}
Пример #24
0
static void http_connect_handshaker_shutdown(grpc_exec_ctx* exec_ctx,
                                             grpc_handshaker* handshaker_in) {
  http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in;
  gpr_mu_lock(&handshaker->mu);
  if (!handshaker->shutdown) {
    handshaker->shutdown = true;
    grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint);
    cleanup_args_for_failure_locked(handshaker);
  }
  gpr_mu_unlock(&handshaker->mu);
}
Пример #25
0
static void must_succeed(grpc_exec_ctx *exec_ctx, void *arg,
                         grpc_error *error) {
  GPR_ASSERT(g_connecting != NULL);
  GPR_ASSERT(error == GRPC_ERROR_NONE);
  grpc_endpoint_shutdown(
      exec_ctx, g_connecting,
      GRPC_ERROR_CREATE_FROM_STATIC_STRING("must_succeed called"));
  grpc_endpoint_destroy(exec_ctx, g_connecting);
  g_connecting = NULL;
  finish_connection(exec_ctx);
}
Пример #26
0
static void security_handshaker_shutdown(grpc_exec_ctx *exec_ctx,
                                         grpc_handshaker *handshaker) {
  security_handshaker *h = (security_handshaker *)handshaker;
  gpr_mu_lock(&h->mu);
  if (!h->shutdown) {
    h->shutdown = true;
    grpc_endpoint_shutdown(exec_ctx, h->args->endpoint);
    cleanup_args_for_failure_locked(h);
  }
  gpr_mu_unlock(&h->mu);
}
Пример #27
0
static void on_connect(grpc_exec_ctx *exec_ctx, void *vargs, grpc_endpoint *tcp,
                       grpc_pollset *accepting_pollset,
                       grpc_tcp_server_acceptor *acceptor) {
  gpr_free(acceptor);
  struct server_thread_args *args = (struct server_thread_args *)vargs;
  grpc_endpoint_shutdown(exec_ctx, tcp,
                         GRPC_ERROR_CREATE_FROM_STATIC_STRING("Connected"));
  grpc_endpoint_destroy(exec_ctx, tcp);
  gpr_mu_lock(args->mu);
  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, NULL));
  gpr_mu_unlock(args->mu);
}
Пример #28
0
static void security_handshaker_shutdown(grpc_exec_ctx *exec_ctx,
                                         grpc_handshaker *handshaker,
                                         grpc_error *why) {
  security_handshaker *h = (security_handshaker *)handshaker;
  gpr_mu_lock(&h->mu);
  if (!h->shutdown) {
    h->shutdown = true;
    grpc_endpoint_shutdown(exec_ctx, h->args->endpoint, GRPC_ERROR_REF(why));
    cleanup_args_for_failure_locked(exec_ctx, h);
  }
  gpr_mu_unlock(&h->mu);
  GRPC_ERROR_UNREF(why);
}
Пример #29
0
static void http_connect_handshaker_shutdown(grpc_exec_ctx* exec_ctx,
                                             grpc_handshaker* handshaker_in,
                                             grpc_error* why) {
  http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in;
  gpr_mu_lock(&handshaker->mu);
  if (!handshaker->shutdown) {
    handshaker->shutdown = true;
    grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint,
                           GRPC_ERROR_REF(why));
    cleanup_args_for_failure_locked(exec_ctx, handshaker);
  }
  gpr_mu_unlock(&handshaker->mu);
  GRPC_ERROR_UNREF(why);
}
static void handle_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
  GPR_ASSERT(error == GRPC_ERROR_NONE);
  gpr_slice_buffer_move_into(&state.temp_incoming_buffer,
                             &state.incoming_buffer);
  gpr_log(GPR_DEBUG, "got %" PRIuPTR " bytes, magic is %" PRIuPTR " bytes",
          state.incoming_buffer.length, strlen(magic_connect_string));
  if (state.incoming_buffer.length > strlen(magic_connect_string)) {
    gpr_atm_rel_store(&state.done_atm, 1);
    grpc_endpoint_shutdown(exec_ctx, state.tcp);
    grpc_endpoint_destroy(exec_ctx, state.tcp);
  } else {
    grpc_endpoint_read(exec_ctx, state.tcp, &state.temp_incoming_buffer,
                       &on_read);
  }
}