|
|
|
@ -29,6 +29,7 @@ |
|
|
|
|
#include <grpc/support/alloc.h> |
|
|
|
|
#include <grpc/support/log.h> |
|
|
|
|
|
|
|
|
|
#include "src/core/lib/channel/channel_args.h" |
|
|
|
|
#include "src/core/lib/gpr/string.h" |
|
|
|
|
#include "src/core/lib/gprpp/host_port.h" |
|
|
|
|
#include "src/core/lib/gprpp/memory.h" |
|
|
|
@ -47,15 +48,16 @@ |
|
|
|
|
"Content-Length: 0\n" \
|
|
|
|
|
"Date: Tue, 07 Jun 2016 17:43:20 GMT\n\n" |
|
|
|
|
|
|
|
|
|
#define HTTP2_RESP(STATUS_CODE) \ |
|
|
|
|
"\x00\x00\x00\x04\x00\x00\x00\x00\x00" \
|
|
|
|
|
"\x00\x00>\x01\x04\x00\x00\x00\x01" \
|
|
|
|
|
"\x10\x0e" \
|
|
|
|
|
"content-length\x01" \
|
|
|
|
|
"0" \
|
|
|
|
|
"\x10\x0c" \
|
|
|
|
|
"content-type\x10" \
|
|
|
|
|
"application/grpc" \
|
|
|
|
|
#define HTTP2_SETTINGS_FRAME "\x00\x00\x00\x04\x00\x00\x00\x00\x00" |
|
|
|
|
|
|
|
|
|
#define HTTP2_RESP(STATUS_CODE) \ |
|
|
|
|
"\x00\x00>\x01\x04\x00\x00\x00\x01" \
|
|
|
|
|
"\x10\x0e" \
|
|
|
|
|
"content-length\x01" \
|
|
|
|
|
"0" \
|
|
|
|
|
"\x10\x0c" \
|
|
|
|
|
"content-type\x10" \
|
|
|
|
|
"application/grpc" \
|
|
|
|
|
"\x10\x07:status\x03" #STATUS_CODE |
|
|
|
|
|
|
|
|
|
#define UNPARSEABLE_RESP "Bad Request\n" |
|
|
|
@ -63,8 +65,6 @@ |
|
|
|
|
#define HTTP2_DETAIL_MSG(STATUS_CODE) \ |
|
|
|
|
"Received http2 header with status: " #STATUS_CODE |
|
|
|
|
|
|
|
|
|
#define HTTP1_DETAIL_MSG "Trying to connect an http1.x server" |
|
|
|
|
|
|
|
|
|
/* TODO(zyc) Check the content of incoming data instead of using this length */ |
|
|
|
|
/* The 'bad' server will start sending responses after reading this amount of
|
|
|
|
|
* data from the client. */ |
|
|
|
@ -80,24 +80,32 @@ struct rpc_state { |
|
|
|
|
grpc_slice_buffer outgoing_buffer; |
|
|
|
|
grpc_endpoint* tcp; |
|
|
|
|
gpr_atm done_atm; |
|
|
|
|
bool write_done; |
|
|
|
|
bool http2_response; |
|
|
|
|
bool send_settings; |
|
|
|
|
const char* response_payload; |
|
|
|
|
size_t response_payload_length; |
|
|
|
|
bool connection_attempt_made; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static int server_port; |
|
|
|
|
static struct rpc_state state; |
|
|
|
|
static grpc_closure on_read; |
|
|
|
|
static grpc_closure on_writing_settings_frame; |
|
|
|
|
static grpc_closure on_write; |
|
|
|
|
|
|
|
|
|
static void* tag(intptr_t t) { return (void*)t; } |
|
|
|
|
|
|
|
|
|
static void done_write(void* /*arg*/, grpc_error* error) { |
|
|
|
|
GPR_ASSERT(error == GRPC_ERROR_NONE); |
|
|
|
|
|
|
|
|
|
gpr_atm_rel_store(&state.done_atm, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void done_writing_settings_frame(void* /* arg */, grpc_error* error) { |
|
|
|
|
GPR_ASSERT(error == GRPC_ERROR_NONE); |
|
|
|
|
grpc_endpoint_read(state.tcp, &state.temp_incoming_buffer, &on_read, |
|
|
|
|
/*urgent=*/false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void handle_write() { |
|
|
|
|
grpc_slice slice = grpc_slice_from_copied_buffer( |
|
|
|
|
state.response_payload, state.response_payload_length); |
|
|
|
@ -108,7 +116,10 @@ static void handle_write() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void handle_read(void* /*arg*/, grpc_error* error) { |
|
|
|
|
GPR_ASSERT(error == GRPC_ERROR_NONE); |
|
|
|
|
if (error != GRPC_ERROR_NONE) { |
|
|
|
|
gpr_log(GPR_ERROR, "handle_read error: %s", grpc_error_string(error)); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
state.incoming_data_length += state.temp_incoming_buffer.length; |
|
|
|
|
|
|
|
|
|
size_t i; |
|
|
|
@ -119,11 +130,14 @@ static void handle_read(void* /*arg*/, grpc_error* error) { |
|
|
|
|
gpr_free(dump); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gpr_log(GPR_DEBUG, "got %" PRIuPTR " bytes, expected %" PRIuPTR " bytes", |
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"got %" PRIuPTR " bytes, expected %" PRIuPTR |
|
|
|
|
" bytes or a non-HTTP2 response to be sent", |
|
|
|
|
state.incoming_data_length, |
|
|
|
|
SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD); |
|
|
|
|
if (state.incoming_data_length >= |
|
|
|
|
SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD) { |
|
|
|
|
SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD || |
|
|
|
|
!state.http2_response) { |
|
|
|
|
handle_write(); |
|
|
|
|
} else { |
|
|
|
|
grpc_endpoint_read(state.tcp, &state.temp_incoming_buffer, &on_read, |
|
|
|
@ -137,14 +151,26 @@ static void on_connect(void* arg, grpc_endpoint* tcp, |
|
|
|
|
gpr_free(acceptor); |
|
|
|
|
test_tcp_server* server = static_cast<test_tcp_server*>(arg); |
|
|
|
|
GRPC_CLOSURE_INIT(&on_read, handle_read, nullptr, grpc_schedule_on_exec_ctx); |
|
|
|
|
GRPC_CLOSURE_INIT(&on_writing_settings_frame, done_writing_settings_frame, |
|
|
|
|
nullptr, grpc_schedule_on_exec_ctx); |
|
|
|
|
GRPC_CLOSURE_INIT(&on_write, done_write, nullptr, grpc_schedule_on_exec_ctx); |
|
|
|
|
grpc_slice_buffer_init(&state.temp_incoming_buffer); |
|
|
|
|
grpc_slice_buffer_init(&state.outgoing_buffer); |
|
|
|
|
state.connection_attempt_made = true; |
|
|
|
|
state.tcp = tcp; |
|
|
|
|
state.incoming_data_length = 0; |
|
|
|
|
grpc_endpoint_add_to_pollset(tcp, server->pollset[0]); |
|
|
|
|
grpc_endpoint_read(tcp, &state.temp_incoming_buffer, &on_read, |
|
|
|
|
/*urgent=*/false); |
|
|
|
|
if (state.send_settings) { |
|
|
|
|
// Send settings frame from server
|
|
|
|
|
grpc_slice slice = grpc_slice_from_static_buffer( |
|
|
|
|
HTTP2_SETTINGS_FRAME, sizeof(HTTP2_SETTINGS_FRAME) - 1); |
|
|
|
|
grpc_slice_buffer_add(&state.outgoing_buffer, slice); |
|
|
|
|
grpc_endpoint_write(state.tcp, &state.outgoing_buffer, |
|
|
|
|
&on_writing_settings_frame, nullptr); |
|
|
|
|
} else { |
|
|
|
|
grpc_endpoint_read(state.tcp, &state.temp_incoming_buffer, &on_read, |
|
|
|
|
/*urgent=*/false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static gpr_timespec n_sec_deadline(int seconds) { |
|
|
|
@ -166,13 +192,20 @@ static void start_rpc(int target_port, grpc_status_code expected_status, |
|
|
|
|
state.cq = grpc_completion_queue_create_for_next(nullptr); |
|
|
|
|
cqv = cq_verifier_create(state.cq); |
|
|
|
|
state.target = grpc_core::JoinHostPort("127.0.0.1", target_port); |
|
|
|
|
|
|
|
|
|
state.channel = |
|
|
|
|
grpc_insecure_channel_create(state.target.c_str(), nullptr, nullptr); |
|
|
|
|
grpc_slice host = grpc_slice_from_static_string("localhost"); |
|
|
|
|
// The default connect deadline is 20 seconds, so reduce the RPC deadline to 1
|
|
|
|
|
// second. This helps us verify - a) If the server responded with a non-HTTP2
|
|
|
|
|
// response, the connect fails immediately resulting in
|
|
|
|
|
// GRPC_STATUS_UNAVAILABLE instead of GRPC_STATUS_DEADLINE_EXCEEDED. b) If the
|
|
|
|
|
// server does not send a HTTP2 SETTINGs frame, the RPC fails with a
|
|
|
|
|
// DEADLINE_EXCEEDED.
|
|
|
|
|
state.call = grpc_channel_create_call( |
|
|
|
|
state.channel, nullptr, GRPC_PROPAGATE_DEFAULTS, state.cq, |
|
|
|
|
grpc_slice_from_static_string("/Service/Method"), &host, |
|
|
|
|
gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); |
|
|
|
|
n_sec_deadline(1), nullptr); |
|
|
|
|
|
|
|
|
|
grpc_metadata_array_init(&initial_metadata_recv); |
|
|
|
|
grpc_metadata_array_init(&trailing_metadata_recv); |
|
|
|
@ -241,7 +274,7 @@ typedef struct { |
|
|
|
|
|
|
|
|
|
static void actually_poll_server(void* arg) { |
|
|
|
|
poll_args* pa = static_cast<poll_args*>(arg); |
|
|
|
|
gpr_timespec deadline = n_sec_deadline(10); |
|
|
|
|
gpr_timespec deadline = n_sec_deadline(1); |
|
|
|
|
while (true) { |
|
|
|
|
bool done = gpr_atm_acq_load(&state.done_atm) != 0; |
|
|
|
|
gpr_timespec time_left = |
|
|
|
@ -251,7 +284,7 @@ static void actually_poll_server(void* arg) { |
|
|
|
|
if (done || gpr_time_cmp(time_left, gpr_time_0(GPR_TIMESPAN)) < 0) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
test_tcp_server_poll(pa->server, 1000); |
|
|
|
|
test_tcp_server_poll(pa->server, 100); |
|
|
|
|
} |
|
|
|
|
gpr_event_set(pa->signal_when_done, (void*)1); |
|
|
|
|
gpr_free(pa); |
|
|
|
@ -260,7 +293,7 @@ static void actually_poll_server(void* arg) { |
|
|
|
|
static grpc_core::Thread* poll_server_until_read_done( |
|
|
|
|
test_tcp_server* server, gpr_event* signal_when_done) { |
|
|
|
|
gpr_atm_rel_store(&state.done_atm, 0); |
|
|
|
|
state.write_done = 0; |
|
|
|
|
state.connection_attempt_made = false; |
|
|
|
|
poll_args* pa = static_cast<poll_args*>(gpr_malloc(sizeof(*pa))); |
|
|
|
|
pa->server = server; |
|
|
|
|
pa->signal_when_done = signal_when_done; |
|
|
|
@ -270,7 +303,8 @@ static grpc_core::Thread* poll_server_until_read_done( |
|
|
|
|
return th; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void run_test(const char* response_payload, |
|
|
|
|
static void run_test(bool http2_response, bool send_settings, |
|
|
|
|
const char* response_payload, |
|
|
|
|
size_t response_payload_length, |
|
|
|
|
grpc_status_code expected_status, |
|
|
|
|
const char* expected_detail) { |
|
|
|
@ -283,6 +317,8 @@ static void run_test(const char* response_payload, |
|
|
|
|
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.http2_response = http2_response; |
|
|
|
|
state.send_settings = send_settings; |
|
|
|
|
state.response_payload = response_payload; |
|
|
|
|
state.response_payload_length = response_payload_length; |
|
|
|
|
|
|
|
|
@ -292,7 +328,8 @@ static void run_test(const char* response_payload, |
|
|
|
|
start_rpc(server_port, expected_status, expected_detail); |
|
|
|
|
gpr_event_wait(&ev, gpr_inf_future(GPR_CLOCK_REALTIME)); |
|
|
|
|
thdptr->Join(); |
|
|
|
|
|
|
|
|
|
/* Proof that the server accepted the TCP connection. */ |
|
|
|
|
GPR_ASSERT(state.connection_attempt_made == true); |
|
|
|
|
/* clean up */ |
|
|
|
|
grpc_endpoint_shutdown(state.tcp, |
|
|
|
|
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Test Shutdown")); |
|
|
|
@ -309,43 +346,48 @@ int main(int argc, char** argv) { |
|
|
|
|
grpc_init(); |
|
|
|
|
|
|
|
|
|
/* status defined in hpack static table */ |
|
|
|
|
run_test(HTTP2_RESP(204), sizeof(HTTP2_RESP(204)) - 1, GRPC_STATUS_UNKNOWN, |
|
|
|
|
HTTP2_DETAIL_MSG(204)); |
|
|
|
|
run_test(HTTP2_RESP(206), sizeof(HTTP2_RESP(206)) - 1, GRPC_STATUS_UNKNOWN, |
|
|
|
|
HTTP2_DETAIL_MSG(206)); |
|
|
|
|
run_test(HTTP2_RESP(304), sizeof(HTTP2_RESP(304)) - 1, GRPC_STATUS_UNKNOWN, |
|
|
|
|
HTTP2_DETAIL_MSG(304)); |
|
|
|
|
run_test(HTTP2_RESP(400), sizeof(HTTP2_RESP(400)) - 1, GRPC_STATUS_INTERNAL, |
|
|
|
|
HTTP2_DETAIL_MSG(400)); |
|
|
|
|
run_test(HTTP2_RESP(404), sizeof(HTTP2_RESP(404)) - 1, |
|
|
|
|
run_test(true, true, HTTP2_RESP(204), sizeof(HTTP2_RESP(204)) - 1, |
|
|
|
|
GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(204)); |
|
|
|
|
run_test(true, true, HTTP2_RESP(206), sizeof(HTTP2_RESP(206)) - 1, |
|
|
|
|
GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(206)); |
|
|
|
|
run_test(true, true, HTTP2_RESP(304), sizeof(HTTP2_RESP(304)) - 1, |
|
|
|
|
GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(304)); |
|
|
|
|
run_test(true, true, HTTP2_RESP(400), sizeof(HTTP2_RESP(400)) - 1, |
|
|
|
|
GRPC_STATUS_INTERNAL, HTTP2_DETAIL_MSG(400)); |
|
|
|
|
run_test(true, true, HTTP2_RESP(404), sizeof(HTTP2_RESP(404)) - 1, |
|
|
|
|
GRPC_STATUS_UNIMPLEMENTED, HTTP2_DETAIL_MSG(404)); |
|
|
|
|
run_test(HTTP2_RESP(500), sizeof(HTTP2_RESP(500)) - 1, GRPC_STATUS_UNKNOWN, |
|
|
|
|
HTTP2_DETAIL_MSG(500)); |
|
|
|
|
run_test(true, true, HTTP2_RESP(500), sizeof(HTTP2_RESP(500)) - 1, |
|
|
|
|
GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(500)); |
|
|
|
|
|
|
|
|
|
/* status not defined in hpack static table */ |
|
|
|
|
run_test(HTTP2_RESP(401), sizeof(HTTP2_RESP(401)) - 1, |
|
|
|
|
run_test(true, true, HTTP2_RESP(401), sizeof(HTTP2_RESP(401)) - 1, |
|
|
|
|
GRPC_STATUS_UNAUTHENTICATED, HTTP2_DETAIL_MSG(401)); |
|
|
|
|
run_test(HTTP2_RESP(403), sizeof(HTTP2_RESP(403)) - 1, |
|
|
|
|
run_test(true, true, HTTP2_RESP(403), sizeof(HTTP2_RESP(403)) - 1, |
|
|
|
|
GRPC_STATUS_PERMISSION_DENIED, HTTP2_DETAIL_MSG(403)); |
|
|
|
|
run_test(HTTP2_RESP(429), sizeof(HTTP2_RESP(429)) - 1, |
|
|
|
|
run_test(true, true, HTTP2_RESP(429), sizeof(HTTP2_RESP(429)) - 1, |
|
|
|
|
GRPC_STATUS_UNAVAILABLE, HTTP2_DETAIL_MSG(429)); |
|
|
|
|
run_test(HTTP2_RESP(499), sizeof(HTTP2_RESP(499)) - 1, GRPC_STATUS_UNKNOWN, |
|
|
|
|
HTTP2_DETAIL_MSG(499)); |
|
|
|
|
run_test(HTTP2_RESP(502), sizeof(HTTP2_RESP(502)) - 1, |
|
|
|
|
run_test(true, true, HTTP2_RESP(499), sizeof(HTTP2_RESP(499)) - 1, |
|
|
|
|
GRPC_STATUS_UNKNOWN, HTTP2_DETAIL_MSG(499)); |
|
|
|
|
run_test(true, true, HTTP2_RESP(502), sizeof(HTTP2_RESP(502)) - 1, |
|
|
|
|
GRPC_STATUS_UNAVAILABLE, HTTP2_DETAIL_MSG(502)); |
|
|
|
|
run_test(HTTP2_RESP(503), sizeof(HTTP2_RESP(503)) - 1, |
|
|
|
|
run_test(true, true, HTTP2_RESP(503), sizeof(HTTP2_RESP(503)) - 1, |
|
|
|
|
GRPC_STATUS_UNAVAILABLE, HTTP2_DETAIL_MSG(503)); |
|
|
|
|
run_test(HTTP2_RESP(504), sizeof(HTTP2_RESP(504)) - 1, |
|
|
|
|
run_test(true, true, HTTP2_RESP(504), sizeof(HTTP2_RESP(504)) - 1, |
|
|
|
|
GRPC_STATUS_UNAVAILABLE, HTTP2_DETAIL_MSG(504)); |
|
|
|
|
|
|
|
|
|
/* unparseable response */ |
|
|
|
|
run_test(UNPARSEABLE_RESP, sizeof(UNPARSEABLE_RESP) - 1, GRPC_STATUS_UNKNOWN, |
|
|
|
|
nullptr); |
|
|
|
|
|
|
|
|
|
/* http1 response */ |
|
|
|
|
run_test(HTTP1_RESP_400, sizeof(HTTP1_RESP_400) - 1, GRPC_STATUS_INTERNAL, |
|
|
|
|
HTTP1_DETAIL_MSG); |
|
|
|
|
|
|
|
|
|
/* unparseable response. RPC should fail immediately due to a connect failure.
|
|
|
|
|
*/ |
|
|
|
|
run_test(false, false, UNPARSEABLE_RESP, sizeof(UNPARSEABLE_RESP) - 1, |
|
|
|
|
GRPC_STATUS_UNAVAILABLE, nullptr); |
|
|
|
|
|
|
|
|
|
/* http1 response. RPC should fail immediately due to a connect failure. */ |
|
|
|
|
run_test(false, false, HTTP1_RESP_400, sizeof(HTTP1_RESP_400) - 1, |
|
|
|
|
GRPC_STATUS_UNAVAILABLE, nullptr); |
|
|
|
|
|
|
|
|
|
/* http2 response without sending a SETTINGs frame. RPC should fail with
|
|
|
|
|
* DEADLINE_EXCEEDED since the RPC deadline is lower than the connection |
|
|
|
|
* attempt deadline. */ |
|
|
|
|
run_test(true, false, HTTP2_RESP(404), sizeof(HTTP2_RESP(404)) - 1, |
|
|
|
|
GRPC_STATUS_DEADLINE_EXCEEDED, nullptr); |
|
|
|
|
grpc_shutdown(); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|