コード例 #1
0
ファイル: secure_endpoint.c プロジェクト: izouxv/grpc
static void destroy(grpc_exec_ctx *exec_ctx, secure_endpoint *secure_ep) {
  secure_endpoint *ep = secure_ep;
  grpc_endpoint_destroy(exec_ctx, ep->wrapped_ep);
  tsi_frame_protector_destroy(ep->protector);
  grpc_slice_buffer_destroy(&ep->leftover_bytes);
  grpc_slice_unref(ep->read_staging_buffer);
  grpc_slice_unref(ep->write_staging_buffer);
  grpc_slice_buffer_destroy(&ep->output_buffer);
  grpc_slice_buffer_destroy(&ep->source_buffer);
  gpr_mu_destroy(&ep->protector_mu);
  gpr_free(ep);
}
コード例 #2
0
static void cleanup_rpc(void) {
  grpc_event ev;
  grpc_slice_buffer_destroy(&state.incoming_buffer);
  grpc_slice_buffer_destroy(&state.temp_incoming_buffer);
  grpc_channel_credentials_unref(state.creds);
  grpc_call_destroy(state.call);
  grpc_completion_queue_shutdown(state.cq);
  do {
    ev = grpc_completion_queue_next(state.cq, n_sec_deadline(1), NULL);
  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
  grpc_completion_queue_destroy(state.cq);
  grpc_channel_destroy(state.channel);
  gpr_free(state.target);
}
コード例 #3
0
static void me_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
    passthru_endpoint *p = ((half *)ep)->parent;
    gpr_mu_lock(&p->mu);
    if (0 == --p->halves) {
        gpr_mu_unlock(&p->mu);
        gpr_mu_destroy(&p->mu);
        grpc_slice_buffer_destroy(&p->client.read_buffer);
        grpc_slice_buffer_destroy(&p->server.read_buffer);
        grpc_resource_user_unref(exec_ctx, p->client.resource_user);
        grpc_resource_user_unref(exec_ctx, p->server.resource_user);
        gpr_free(p);
    } else {
        gpr_mu_unlock(&p->mu);
    }
}
コード例 #4
0
ファイル: hpack_encoder_test.c プロジェクト: izouxv/grpc
static void verify_table_size_change_match_elem_size(const char *key,
                                                     const char *value) {
  grpc_slice_buffer output;
  grpc_mdelem *elem = grpc_mdelem_from_strings(key, value);
  size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem);
  size_t initial_table_size = g_compressor.table_size;
  grpc_linked_mdelem *e = gpr_malloc(sizeof(*e));
  grpc_metadata_batch b;
  grpc_metadata_batch_init(&b);
  e[0].md = elem;
  e[0].prev = NULL;
  e[0].next = NULL;
  b.list.head = &e[0];
  b.list.tail = &e[0];
  grpc_slice_buffer_init(&output);

  grpc_transport_one_way_stats stats;
  memset(&stats, 0, sizeof(stats));
  grpc_chttp2_encode_header(&g_compressor, 0xdeadbeef, &b, 0, 16384, &stats,
                            &output);
  grpc_slice_buffer_destroy(&output);
  grpc_metadata_batch_destroy(&b);

  GPR_ASSERT(g_compressor.table_size == elem_size + initial_table_size);
  gpr_free(e);
}
コード例 #5
0
ファイル: secure_endpoint_test.c プロジェクト: izouxv/grpc
static void test_leftover(grpc_endpoint_test_config config, size_t slice_size) {
  grpc_endpoint_test_fixture f = config.create_fixture(slice_size);
  grpc_slice_buffer incoming;
  grpc_slice s =
      grpc_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");

  grpc_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 == grpc_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);
  grpc_slice_unref(s);
  grpc_slice_buffer_destroy(&incoming);

  clean_up();
}
コード例 #6
0
ファイル: byte_buffer_reader.c プロジェクト: izouxv/grpc
int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
                                 grpc_byte_buffer *buffer) {
  grpc_slice_buffer decompressed_slices_buffer;
  reader->buffer_in = buffer;
  switch (reader->buffer_in->type) {
    case GRPC_BB_RAW:
      grpc_slice_buffer_init(&decompressed_slices_buffer);
      if (is_compressed(reader->buffer_in)) {
        if (grpc_msg_decompress(reader->buffer_in->data.raw.compression,
                                &reader->buffer_in->data.raw.slice_buffer,
                                &decompressed_slices_buffer) == 0) {
          gpr_log(GPR_ERROR,
                  "Unexpected error decompressing data for algorithm with enum "
                  "value '%d'.",
                  reader->buffer_in->data.raw.compression);
          memset(reader, 0, sizeof(*reader));
          return 0;
        } else { /* all fine */
          reader->buffer_out =
              grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices,
                                          decompressed_slices_buffer.count);
        }
        grpc_slice_buffer_destroy(&decompressed_slices_buffer);
      } else { /* not compressed, use the input buffer as output */
        reader->buffer_out = reader->buffer_in;
      }
      reader->current.index = 0;
      break;
  }
  return 1;
}
コード例 #7
0
ファイル: compress_filter.c プロジェクト: pquerna/grpc
/* Destructor for call_data */
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                              const grpc_call_final_info *final_info,
                              void *ignored) {
  /* grab pointers to our data from the call element */
  call_data *calld = elem->call_data;
  grpc_slice_buffer_destroy(&calld->slices);
}
コード例 #8
0
ファイル: hpack_encoder_test.c プロジェクト: izouxv/grpc
/* verify that the output generated by encoding the stream matches the
   hexstring passed in */
static void verify(size_t window_available, int eof, size_t expect_window_used,
                   const char *expected, size_t nheaders, ...) {
  grpc_slice_buffer output;
  grpc_slice merged;
  grpc_slice expect = parse_hexstring(expected);
  size_t i;
  va_list l;
  grpc_linked_mdelem *e = gpr_malloc(sizeof(*e) * nheaders);
  grpc_metadata_batch b;

  grpc_metadata_batch_init(&b);

  va_start(l, nheaders);
  for (i = 0; i < nheaders; i++) {
    char *key = va_arg(l, char *);
    char *value = va_arg(l, char *);
    if (i) {
      e[i - 1].next = &e[i];
      e[i].prev = &e[i - 1];
    }
    e[i].md = grpc_mdelem_from_strings(key, value);
  }
  e[0].prev = NULL;
  e[nheaders - 1].next = NULL;
  va_end(l);

  b.list.head = &e[0];
  b.list.tail = &e[nheaders - 1];

  if (cap_to_delete == num_to_delete) {
    cap_to_delete = GPR_MAX(2 * cap_to_delete, 1000);
    to_delete = gpr_realloc(to_delete, sizeof(*to_delete) * cap_to_delete);
  }
  to_delete[num_to_delete++] = e;

  grpc_slice_buffer_init(&output);

  grpc_transport_one_way_stats stats;
  memset(&stats, 0, sizeof(stats));
  grpc_chttp2_encode_header(&g_compressor, 0xdeadbeef, &b, eof, 16384, &stats,
                            &output);
  merged = grpc_slice_merge(output.slices, output.count);
  grpc_slice_buffer_destroy(&output);
  grpc_metadata_batch_destroy(&b);

  if (0 != grpc_slice_cmp(merged, expect)) {
    char *expect_str = grpc_dump_slice(expect, GPR_DUMP_HEX | GPR_DUMP_ASCII);
    char *got_str = grpc_dump_slice(merged, GPR_DUMP_HEX | GPR_DUMP_ASCII);
    gpr_log(GPR_ERROR, "mismatched output for %s", expected);
    gpr_log(GPR_ERROR, "EXPECT: %s", expect_str);
    gpr_log(GPR_ERROR, "GOT:    %s", got_str);
    gpr_free(expect_str);
    gpr_free(got_str);
    g_failure = 1;
  }

  grpc_slice_unref(merged);
  grpc_slice_unref(expect);
}
コード例 #9
0
ファイル: tcp_posix.c プロジェクト: izouxv/grpc
static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
  grpc_fd_orphan(exec_ctx, tcp->em_fd, tcp->release_fd_cb, tcp->release_fd,
                 "tcp_unref_orphan");
  grpc_slice_buffer_destroy(&tcp->last_read_buffer);
  grpc_resource_user_unref(exec_ctx, tcp->resource_user);
  gpr_free(tcp->peer_string);
  gpr_free(tcp);
}
コード例 #10
0
// Unref and clean up handshaker.
static void http_connect_handshaker_unref(grpc_exec_ctx* exec_ctx,
                                          http_connect_handshaker* handshaker) {
  if (gpr_unref(&handshaker->refcount)) {
    gpr_mu_destroy(&handshaker->mu);
    if (handshaker->endpoint_to_destroy != NULL) {
      grpc_endpoint_destroy(exec_ctx, handshaker->endpoint_to_destroy);
    }
    if (handshaker->read_buffer_to_destroy != NULL) {
      grpc_slice_buffer_destroy(handshaker->read_buffer_to_destroy);
      gpr_free(handshaker->read_buffer_to_destroy);
    }
    gpr_free(handshaker->proxy_server);
    gpr_free(handshaker->server_name);
    grpc_slice_buffer_destroy(&handshaker->write_buffer);
    grpc_http_parser_destroy(&handshaker->http_parser);
    grpc_http_response_destroy(&handshaker->http_response);
    gpr_free(handshaker);
  }
}
コード例 #11
0
ファイル: uri_parser.c プロジェクト: izouxv/grpc
static void parse_query_parts(grpc_uri *uri) {
  static const char *QUERY_PARTS_SEPARATOR = "&";
  static const char *QUERY_PARTS_VALUE_SEPARATOR = "=";
  GPR_ASSERT(uri->query != NULL);
  if (uri->query[0] == '\0') {
    uri->query_parts = NULL;
    uri->query_parts_values = NULL;
    uri->num_query_parts = 0;
    return;
  }
  grpc_slice query_slice =
      grpc_slice_new(uri->query, strlen(uri->query), do_nothing);
  grpc_slice_buffer query_parts; /* the &-separated elements of the query */
  grpc_slice_buffer query_param_parts; /* the =-separated subelements */

  grpc_slice_buffer_init(&query_parts);
  grpc_slice_buffer_init(&query_param_parts);

  grpc_slice_split(query_slice, QUERY_PARTS_SEPARATOR, &query_parts);
  uri->query_parts = gpr_malloc(query_parts.count * sizeof(char *));
  uri->query_parts_values = gpr_malloc(query_parts.count * sizeof(char *));
  uri->num_query_parts = query_parts.count;
  for (size_t i = 0; i < query_parts.count; i++) {
    grpc_slice_split(query_parts.slices[i], QUERY_PARTS_VALUE_SEPARATOR,
                     &query_param_parts);
    GPR_ASSERT(query_param_parts.count > 0);
    uri->query_parts[i] =
        grpc_dump_slice(query_param_parts.slices[0], GPR_DUMP_ASCII);
    if (query_param_parts.count > 1) {
      /* TODO(dgq): only the first value after the separator is considered.
       * Perhaps all chars after the first separator for the query part should
       * be included, even if they include the separator. */
      uri->query_parts_values[i] =
          grpc_dump_slice(query_param_parts.slices[1], GPR_DUMP_ASCII);
    } else {
      uri->query_parts_values[i] = NULL;
    }
    grpc_slice_buffer_reset_and_unref(&query_param_parts);
  }
  grpc_slice_buffer_destroy(&query_parts);
  grpc_slice_buffer_destroy(&query_param_parts);
  grpc_slice_unref(query_slice);
}
コード例 #12
0
ファイル: security_handshaker.c プロジェクト: izouxv/grpc
static void security_handshaker_unref(grpc_exec_ctx *exec_ctx,
                                      security_handshaker *h) {
  if (gpr_unref(&h->refs)) {
    gpr_mu_destroy(&h->mu);
    tsi_handshaker_destroy(h->handshaker);
    if (h->endpoint_to_destroy != NULL) {
      grpc_endpoint_destroy(exec_ctx, h->endpoint_to_destroy);
    }
    if (h->read_buffer_to_destroy != NULL) {
      grpc_slice_buffer_destroy(h->read_buffer_to_destroy);
      gpr_free(h->read_buffer_to_destroy);
    }
    gpr_free(h->handshake_buffer);
    grpc_slice_buffer_destroy(&h->left_overs);
    grpc_slice_buffer_destroy(&h->outgoing);
    GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake");
    GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake");
    gpr_free(h);
  }
}
コード例 #13
0
static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
                              grpc_error *error) {
  grpc_handshaker_args *args = arg;
  on_done_closure *c = args->user_data;
  if (error != GRPC_ERROR_NONE) {
    const char *msg = grpc_error_string(error);
    gpr_log(GPR_ERROR, "Secure transport setup failed: %s", msg);
    grpc_error_free_string(msg);
    c->func(exec_ctx, c->arg, NULL);
  } else {
    grpc_channel_args_destroy(args->args);
    grpc_slice_buffer_destroy(args->read_buffer);
    gpr_free(args->read_buffer);
    c->func(exec_ctx, c->arg, args->endpoint);
  }
  grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr);
  gpr_free(c);
}
コード例 #14
0
ファイル: compress_filter.c プロジェクト: pquerna/grpc
static void finish_send_message(grpc_exec_ctx *exec_ctx,
                                grpc_call_element *elem) {
  call_data *calld = elem->call_data;
  int did_compress;
  grpc_slice_buffer tmp;
  grpc_slice_buffer_init(&tmp);
  did_compress =
      grpc_msg_compress(calld->compression_algorithm, &calld->slices, &tmp);
  if (did_compress) {
    if (grpc_compression_trace) {
      char *algo_name;
      const size_t before_size = calld->slices.length;
      const size_t after_size = tmp.length;
      const float savings_ratio = 1.0f - (float)after_size / (float)before_size;
      GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm,
                                                 &algo_name));
      gpr_log(GPR_DEBUG, "Compressed[%s] %" PRIuPTR " bytes vs. %" PRIuPTR
                         " bytes (%.2f%% savings)",
              algo_name, before_size, after_size, 100 * savings_ratio);
    }
    grpc_slice_buffer_swap(&calld->slices, &tmp);
    calld->send_flags |= GRPC_WRITE_INTERNAL_COMPRESS;
  } else {
    if (grpc_compression_trace) {
      char *algo_name;
      GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm,
                                                 &algo_name));
      gpr_log(GPR_DEBUG,
              "Algorithm '%s' enabled but decided not to compress. Input size: "
              "%" PRIuPTR,
              algo_name, calld->slices.length);
    }
  }

  grpc_slice_buffer_destroy(&tmp);

  grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
                                calld->send_flags);
  calld->send_op->send_message = &calld->replacement_stream.base;
  calld->post_send = calld->send_op->on_complete;
  calld->send_op->on_complete = &calld->send_done;

  grpc_call_next_op(exec_ctx, elem, calld->send_op);
}
コード例 #15
0
ファイル: chttp2_connector.c プロジェクト: izouxv/grpc
static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
                              grpc_error *error) {
  grpc_handshaker_args *args = arg;
  chttp2_connector *c = args->user_data;
  gpr_mu_lock(&c->mu);
  if (error != GRPC_ERROR_NONE || c->shutdown) {
    if (error == GRPC_ERROR_NONE) {
      error = GRPC_ERROR_CREATE("connector shutdown");
      // We were shut down after handshaking completed successfully, so
      // destroy the endpoint here.
      // 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, args->endpoint);
      grpc_endpoint_destroy(exec_ctx, args->endpoint);
      grpc_channel_args_destroy(args->args);
      grpc_slice_buffer_destroy(args->read_buffer);
      gpr_free(args->read_buffer);
    } else {
      error = GRPC_ERROR_REF(error);
    }
    memset(c->result, 0, sizeof(*c->result));
  } else {
    c->result->transport =
        grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1);
    GPR_ASSERT(c->result->transport);
    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport,
                                        args->read_buffer);
    c->result->channel_args = args->args;
  }
  grpc_closure *notify = c->notify;
  c->notify = NULL;
  grpc_exec_ctx_sched(exec_ctx, notify, error, NULL);
  grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr);
  c->handshake_mgr = NULL;
  gpr_mu_unlock(&c->mu);
  chttp2_connector_unref(exec_ctx, (grpc_connector *)c);
}
コード例 #16
0
ファイル: http_proxy.c プロジェクト: pquerna/grpc
// Helper function to destroy the proxy connection.
static void proxy_connection_unref(grpc_exec_ctx* exec_ctx,
                                   proxy_connection* conn) {
  if (gpr_unref(&conn->refcount)) {
    grpc_endpoint_destroy(exec_ctx, conn->client_endpoint);
    if (conn->server_endpoint != NULL)
      grpc_endpoint_destroy(exec_ctx, conn->server_endpoint);
    grpc_pollset_set_destroy(conn->pollset_set);
    grpc_slice_buffer_destroy(&conn->client_read_buffer);
    grpc_slice_buffer_destroy(&conn->client_deferred_write_buffer);
    grpc_slice_buffer_destroy(&conn->client_write_buffer);
    grpc_slice_buffer_destroy(&conn->server_read_buffer);
    grpc_slice_buffer_destroy(&conn->server_deferred_write_buffer);
    grpc_slice_buffer_destroy(&conn->server_write_buffer);
    grpc_http_parser_destroy(&conn->http_parser);
    grpc_http_request_destroy(&conn->http_request);
    gpr_free(conn);
  }
}
コード例 #17
0
ファイル: mock_endpoint.c プロジェクト: izouxv/grpc
static void me_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
  grpc_mock_endpoint *m = (grpc_mock_endpoint *)ep;
  grpc_slice_buffer_destroy(&m->read_buffer);
  grpc_resource_user_unref(exec_ctx, m->resource_user);
  gpr_free(m);
}
コード例 #18
0
static void test_strsplit(void) {
  grpc_slice_buffer *parts;
  grpc_slice str;

  LOG_TEST_NAME("test_strsplit");

  parts = gpr_malloc(sizeof(grpc_slice_buffer));
  grpc_slice_buffer_init(parts);

  str = grpc_slice_from_copied_string("one, two, three, four");
  grpc_slice_split(str, ", ", parts);
  GPR_ASSERT(4 == parts->count);
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[0], "one"));
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[1], "two"));
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[2], "three"));
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[3], "four"));
  grpc_slice_buffer_reset_and_unref(parts);
  grpc_slice_unref(str);

  /* separator not present in string */
  str = grpc_slice_from_copied_string("one two three four");
  grpc_slice_split(str, ", ", parts);
  GPR_ASSERT(1 == parts->count);
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[0], "one two three four"));
  grpc_slice_buffer_reset_and_unref(parts);
  grpc_slice_unref(str);

  /* separator at the end */
  str = grpc_slice_from_copied_string("foo,");
  grpc_slice_split(str, ",", parts);
  GPR_ASSERT(2 == parts->count);
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[0], "foo"));
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[1], ""));
  grpc_slice_buffer_reset_and_unref(parts);
  grpc_slice_unref(str);

  /* separator at the beginning */
  str = grpc_slice_from_copied_string(",foo");
  grpc_slice_split(str, ",", parts);
  GPR_ASSERT(2 == parts->count);
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[0], ""));
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[1], "foo"));
  grpc_slice_buffer_reset_and_unref(parts);
  grpc_slice_unref(str);

  /* standalone separator */
  str = grpc_slice_from_copied_string(",");
  grpc_slice_split(str, ",", parts);
  GPR_ASSERT(2 == parts->count);
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[0], ""));
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[1], ""));
  grpc_slice_buffer_reset_and_unref(parts);
  grpc_slice_unref(str);

  /* empty input */
  str = grpc_slice_from_copied_string("");
  grpc_slice_split(str, ", ", parts);
  GPR_ASSERT(1 == parts->count);
  GPR_ASSERT(0 == grpc_slice_str_cmp(parts->slices[0], ""));
  grpc_slice_buffer_reset_and_unref(parts);
  grpc_slice_unref(str);

  grpc_slice_buffer_destroy(parts);
  gpr_free(parts);
}
コード例 #19
0
// Callback invoked for reading HTTP CONNECT response.
static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg,
                         grpc_error* error) {
  http_connect_handshaker* handshaker = arg;
  gpr_mu_lock(&handshaker->mu);
  if (error != GRPC_ERROR_NONE || handshaker->shutdown) {
    // If the read failed or we're shutting down, clean up and invoke the
    // callback with the error.
    handshake_failed_locked(exec_ctx, handshaker, GRPC_ERROR_REF(error));
    goto done;
  }
  // Add buffer to parser.
  for (size_t i = 0; i < handshaker->args->read_buffer->count; ++i) {
    if (GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i]) > 0) {
      size_t body_start_offset = 0;
      error = grpc_http_parser_parse(&handshaker->http_parser,
                                     handshaker->args->read_buffer->slices[i],
                                     &body_start_offset);
      if (error != GRPC_ERROR_NONE) {
        handshake_failed_locked(exec_ctx, handshaker, error);
        goto done;
      }
      if (handshaker->http_parser.state == GRPC_HTTP_BODY) {
        // Remove the data we've already read from the read buffer,
        // leaving only the leftover bytes (if any).
        grpc_slice_buffer tmp_buffer;
        grpc_slice_buffer_init(&tmp_buffer);
        if (body_start_offset <
            GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i])) {
          grpc_slice_buffer_add(
              &tmp_buffer,
              grpc_slice_split_tail(&handshaker->args->read_buffer->slices[i],
                                    body_start_offset));
        }
        grpc_slice_buffer_addn(&tmp_buffer,
                               &handshaker->args->read_buffer->slices[i + 1],
                               handshaker->args->read_buffer->count - i - 1);
        grpc_slice_buffer_swap(handshaker->args->read_buffer, &tmp_buffer);
        grpc_slice_buffer_destroy(&tmp_buffer);
        break;
      }
    }
  }
  // If we're not done reading the response, read more data.
  // TODO(roth): In practice, I suspect that the response to a CONNECT
  // request will never include a body, in which case this check is
  // sufficient.  However, the language of RFC-2817 doesn't explicitly
  // forbid the response from including a body.  If there is a body,
  // it's possible that we might have parsed part but not all of the
  // body, in which case this check will cause us to fail to parse the
  // remainder of the body.  If that ever becomes an issue, we may
  // need to fix the HTTP parser to understand when the body is
  // complete (e.g., handling chunked transfer encoding or looking
  // at the Content-Length: header).
  if (handshaker->http_parser.state != GRPC_HTTP_BODY) {
    grpc_slice_buffer_reset_and_unref(handshaker->args->read_buffer);
    grpc_endpoint_read(exec_ctx, handshaker->args->endpoint,
                       handshaker->args->read_buffer,
                       &handshaker->response_read_closure);
    gpr_mu_unlock(&handshaker->mu);
    return;
  }
  // Make sure we got a 2xx response.
  if (handshaker->http_response.status < 200 ||
      handshaker->http_response.status >= 300) {
    char* msg;
    gpr_asprintf(&msg, "HTTP proxy returned response code %d",
                 handshaker->http_response.status);
    error = GRPC_ERROR_CREATE(msg);
    gpr_free(msg);
    handshake_failed_locked(exec_ctx, handshaker, error);
    goto done;
  }
  // Success.  Invoke handshake-done callback.
  grpc_exec_ctx_sched(exec_ctx, handshaker->on_handshake_done, error, NULL);
done:
  // Set shutdown to true so that subsequent calls to
  // http_connect_handshaker_shutdown() do nothing.
  handshaker->shutdown = true;
  gpr_mu_unlock(&handshaker->mu);
  http_connect_handshaker_unref(exec_ctx, handshaker);
}