static void read_and_write_test_write_handler(grpc_exec_ctx *exec_ctx, void *data, grpc_error *error) { struct read_and_write_test_state *state = data; grpc_slice *slices = NULL; size_t nslices; if (error == GRPC_ERROR_NONE) { state->bytes_written += state->current_write_size; if (state->target_bytes - state->bytes_written < state->current_write_size) { state->current_write_size = state->target_bytes - state->bytes_written; } if (state->current_write_size != 0) { slices = allocate_blocks(state->current_write_size, 8192, &nslices, &state->current_write_data); grpc_slice_buffer_reset_and_unref(&state->outgoing); grpc_slice_buffer_addn(&state->outgoing, slices, nslices); grpc_endpoint_write(exec_ctx, state->write_ep, &state->outgoing, &state->done_write); gpr_free(slices); return; } } gpr_log(GPR_INFO, "Write handler done"); gpr_mu_lock(g_mu); state->write_done = 1 + (error == GRPC_ERROR_NONE); GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)); gpr_mu_unlock(g_mu); }
/* Write to a socket using the grpc_tcp API, then drain it directly. Note that if the write does not complete immediately we need to drain the socket in parallel with the read. */ static void write_test(size_t num_bytes, size_t slice_size) { int sv[2]; grpc_endpoint *ep; struct write_socket_state state; size_t num_blocks; grpc_slice *slices; uint8_t current_data = 0; grpc_slice_buffer outgoing; grpc_closure write_done_closure; gpr_timespec deadline = grpc_timeout_seconds_to_deadline(20); grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; gpr_log(GPR_INFO, "Start write test with %" PRIuPTR " bytes, slice size %" PRIuPTR, num_bytes, slice_size); create_sockets(sv); grpc_resource_quota *resource_quota = grpc_resource_quota_create("write_test"); ep = grpc_tcp_create(grpc_fd_create(sv[1], "write_test"), resource_quota, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, "test"); grpc_resource_quota_unref_internal(&exec_ctx, resource_quota); grpc_endpoint_add_to_pollset(&exec_ctx, ep, g_pollset); state.ep = ep; state.write_done = 0; slices = allocate_blocks(num_bytes, slice_size, &num_blocks, ¤t_data); grpc_slice_buffer_init(&outgoing); grpc_slice_buffer_addn(&outgoing, slices, num_blocks); grpc_closure_init(&write_done_closure, write_done, &state, grpc_schedule_on_exec_ctx); grpc_endpoint_write(&exec_ctx, ep, &outgoing, &write_done_closure); drain_socket_blocking(sv[0], num_bytes, num_bytes); gpr_mu_lock(g_mu); for (;;) { grpc_pollset_worker *worker = NULL; if (state.write_done) { break; } GPR_ASSERT(GRPC_LOG_IF_ERROR( "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker, gpr_now(GPR_CLOCK_MONOTONIC), deadline))); gpr_mu_unlock(g_mu); grpc_exec_ctx_finish(&exec_ctx); gpr_mu_lock(g_mu); } gpr_mu_unlock(g_mu); grpc_slice_buffer_destroy_internal(&exec_ctx, &outgoing); grpc_endpoint_destroy(&exec_ctx, ep); gpr_free(slices); grpc_exec_ctx_finish(&exec_ctx); }
static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { security_handshaker *h = arg; gpr_mu_lock(&h->mu); if (error != GRPC_ERROR_NONE || h->shutdown) { security_handshake_failed_locked( exec_ctx, h, GRPC_ERROR_CREATE_REFERENCING("Handshake read failed", &error, 1)); gpr_mu_unlock(&h->mu); security_handshaker_unref(exec_ctx, h); return; } // Process received data. tsi_result result = TSI_OK; size_t consumed_slice_size = 0; size_t i; for (i = 0; i < h->args->read_buffer->count; i++) { consumed_slice_size = GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]); result = tsi_handshaker_process_bytes_from_peer( h->handshaker, GRPC_SLICE_START_PTR(h->args->read_buffer->slices[i]), &consumed_slice_size); if (!tsi_handshaker_is_in_progress(h->handshaker)) break; } if (tsi_handshaker_is_in_progress(h->handshaker)) { /* We may need more data. */ if (result == TSI_INCOMPLETE_DATA) { grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer, &h->on_handshake_data_received_from_peer); goto done; } else { error = send_handshake_bytes_to_peer_locked(exec_ctx, h); if (error != GRPC_ERROR_NONE) { security_handshake_failed_locked(exec_ctx, h, error); gpr_mu_unlock(&h->mu); security_handshaker_unref(exec_ctx, h); return; } goto done; } } if (result != TSI_OK) { security_handshake_failed_locked( exec_ctx, h, grpc_set_tsi_error_result( GRPC_ERROR_CREATE("Handshake failed"), result)); gpr_mu_unlock(&h->mu); security_handshaker_unref(exec_ctx, h); return; } /* Handshake is done and successful this point. */ bool has_left_overs_in_current_slice = (consumed_slice_size < GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i])); size_t num_left_overs = (has_left_overs_in_current_slice ? 1 : 0) + h->args->read_buffer->count - i - 1; if (num_left_overs > 0) { /* Put the leftovers in our buffer (ownership transfered). */ if (has_left_overs_in_current_slice) { grpc_slice_buffer_add( &h->left_overs, grpc_slice_split_tail(&h->args->read_buffer->slices[i], consumed_slice_size)); /* split_tail above increments refcount. */ grpc_slice_unref(h->args->read_buffer->slices[i]); } grpc_slice_buffer_addn( &h->left_overs, &h->args->read_buffer->slices[i + 1], num_left_overs - (size_t)has_left_overs_in_current_slice); } // Check peer. error = check_peer_locked(exec_ctx, h); if (error != GRPC_ERROR_NONE) { security_handshake_failed_locked(exec_ctx, h, error); gpr_mu_unlock(&h->mu); security_handshaker_unref(exec_ctx, h); return; } done: gpr_mu_unlock(&h->mu); }
// 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_internal(exec_ctx, &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_internal(exec_ctx, 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_FROM_COPIED_STRING(msg); gpr_free(msg); handshake_failed_locked(exec_ctx, handshaker, error); goto done; } // Success. Invoke handshake-done callback. grpc_closure_sched(exec_ctx, handshaker->on_handshake_done, error); 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); }