| /* |
| * |
| * Copyright 2016, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| /* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when |
| using that endpoint. Because of various transitive includes in uv.h, |
| including windows.h on Windows, uv.h must be included before other system |
| headers. Therefore, sockaddr.h must always be included first */ |
| #include "src/core/lib/iomgr/sockaddr.h" |
| |
| #include <string.h> |
| |
| #include <grpc/grpc.h> |
| #include <grpc/slice.h> |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/host_port.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/thd.h> |
| |
| #include "src/core/lib/iomgr/sockaddr.h" |
| #include "src/core/lib/slice/slice_internal.h" |
| #include "src/core/lib/slice/slice_string_helpers.h" |
| #include "src/core/lib/support/string.h" |
| #include "test/core/end2end/cq_verifier.h" |
| #include "test/core/util/port.h" |
| #include "test/core/util/test_config.h" |
| #include "test/core/util/test_tcp_server.h" |
| |
| #define HTTP1_RESP \ |
| "HTTP/1.0 400 Bad Request\n" \ |
| "Content-Type: text/html; charset=UTF-8\n" \ |
| "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" \ |
| "\x10\x07:status\x03" #STATUS_CODE |
| |
| #define UNPARSEABLE_RESP "Bad Request\n" |
| |
| #define HTTP2_DETAIL_MSG(STATUS_CODE) \ |
| "Received http2 header with status: " #STATUS_CODE |
| |
| #define UNPARSEABLE_DETAIL_MSG "Failed parsing HTTP/2" |
| |
| #define HTTP1_DETAIL_MSG "Trying to connect an http1.x server" |
| |
| /* TODO(zyc) Check the content of incomming data instead of using this length */ |
| /* The 'bad' server will start sending responses after reading this amount of |
| * data from the client. */ |
| #define SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD (size_t)200 |
| |
| struct rpc_state { |
| char *target; |
| grpc_completion_queue *cq; |
| grpc_channel *channel; |
| grpc_call *call; |
| size_t incoming_data_length; |
| grpc_slice_buffer temp_incoming_buffer; |
| grpc_slice_buffer outgoing_buffer; |
| grpc_endpoint *tcp; |
| gpr_atm done_atm; |
| bool write_done; |
| const char *response_payload; |
| size_t response_payload_length; |
| }; |
| |
| static int server_port; |
| static struct rpc_state state; |
| static grpc_closure on_read; |
| static grpc_closure on_write; |
| |
| static void *tag(intptr_t t) { return (void *)t; } |
| |
| static void done_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { |
| GPR_ASSERT(error == GRPC_ERROR_NONE); |
| |
| gpr_atm_rel_store(&state.done_atm, 1); |
| } |
| |
| static void handle_write(grpc_exec_ctx *exec_ctx) { |
| grpc_slice slice = grpc_slice_from_copied_buffer( |
| state.response_payload, state.response_payload_length); |
| |
| grpc_slice_buffer_reset_and_unref(&state.outgoing_buffer); |
| grpc_slice_buffer_add(&state.outgoing_buffer, slice); |
| grpc_endpoint_write(exec_ctx, state.tcp, &state.outgoing_buffer, &on_write); |
| } |
| |
| static void handle_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { |
| GPR_ASSERT(error == GRPC_ERROR_NONE); |
| state.incoming_data_length += state.temp_incoming_buffer.length; |
| |
| size_t i; |
| for (i = 0; i < state.temp_incoming_buffer.count; i++) { |
| char *dump = grpc_dump_slice(state.temp_incoming_buffer.slices[i], |
| GPR_DUMP_HEX | GPR_DUMP_ASCII); |
| gpr_log(GPR_DEBUG, "Server received: %s", dump); |
| gpr_free(dump); |
| } |
| |
| gpr_log(GPR_DEBUG, "got %" PRIuPTR " bytes, expected %" PRIuPTR " bytes", |
| state.incoming_data_length, |
| SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD); |
| if (state.incoming_data_length >= |
| SERVER_INCOMING_DATA_LENGTH_LOWER_THRESHOLD) { |
| handle_write(exec_ctx); |
| } else { |
| grpc_endpoint_read(exec_ctx, state.tcp, &state.temp_incoming_buffer, |
| &on_read); |
| } |
| } |
| |
| static void on_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp, |
| grpc_pollset *accepting_pollset, |
| grpc_tcp_server_acceptor *acceptor) { |
| gpr_free(acceptor); |
| test_tcp_server *server = arg; |
| grpc_closure_init(&on_read, handle_read, NULL, grpc_schedule_on_exec_ctx); |
| grpc_closure_init(&on_write, done_write, NULL, grpc_schedule_on_exec_ctx); |
| grpc_slice_buffer_init(&state.temp_incoming_buffer); |
| grpc_slice_buffer_init(&state.outgoing_buffer); |
| state.tcp = tcp; |
| state.incoming_data_length = 0; |
| grpc_endpoint_add_to_pollset(exec_ctx, tcp, server->pollset); |
| grpc_endpoint_read(exec_ctx, tcp, &state.temp_incoming_buffer, &on_read); |
| } |
| |
| static gpr_timespec n_sec_deadline(int seconds) { |
| return gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), |
| gpr_time_from_seconds(seconds, GPR_TIMESPAN)); |
| } |
| |
| static void start_rpc(int target_port, grpc_status_code expected_status, |
| const char *expected_detail) { |
| grpc_op ops[6]; |
| grpc_op *op; |
| grpc_metadata_array initial_metadata_recv; |
| grpc_metadata_array trailing_metadata_recv; |
| grpc_status_code status; |
| grpc_call_error error; |
| cq_verifier *cqv; |
| grpc_slice details; |
| |
| state.cq = |
| grpc_completion_queue_create(GRPC_CQ_NEXT, GRPC_CQ_DEFAULT_POLLING, NULL); |
| cqv = cq_verifier_create(state.cq); |
| gpr_join_host_port(&state.target, "127.0.0.1", target_port); |
| state.channel = grpc_insecure_channel_create(state.target, NULL, NULL); |
| grpc_slice host = grpc_slice_from_static_string("localhost"); |
| state.call = grpc_channel_create_call( |
| state.channel, NULL, GRPC_PROPAGATE_DEFAULTS, state.cq, |
| grpc_slice_from_static_string("/Service/Method"), &host, |
| gpr_inf_future(GPR_CLOCK_REALTIME), NULL); |
| |
| grpc_metadata_array_init(&initial_metadata_recv); |
| grpc_metadata_array_init(&trailing_metadata_recv); |
| |
| memset(ops, 0, sizeof(ops)); |
| op = ops; |
| op->op = GRPC_OP_SEND_INITIAL_METADATA; |
| op->data.send_initial_metadata.count = 0; |
| op->flags = 0; |
| op->reserved = NULL; |
| op++; |
| op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; |
| op->flags = 0; |
| op->reserved = NULL; |
| op++; |
| op->op = GRPC_OP_RECV_INITIAL_METADATA; |
| op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; |
| op->flags = 0; |
| op->reserved = NULL; |
| op++; |
| op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; |
| op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; |
| op->data.recv_status_on_client.status = &status; |
| op->data.recv_status_on_client.status_details = &details; |
| op->flags = 0; |
| op->reserved = NULL; |
| op++; |
| error = |
| grpc_call_start_batch(state.call, ops, (size_t)(op - ops), tag(1), NULL); |
| |
| GPR_ASSERT(GRPC_CALL_OK == error); |
| |
| CQ_EXPECT_COMPLETION(cqv, tag(1), 1); |
| cq_verify(cqv); |
| |
| GPR_ASSERT(status == expected_status); |
| GPR_ASSERT(-1 != grpc_slice_slice(details, grpc_slice_from_static_string( |
| expected_detail))); |
| |
| grpc_metadata_array_destroy(&initial_metadata_recv); |
| grpc_metadata_array_destroy(&trailing_metadata_recv); |
| grpc_slice_unref(details); |
| cq_verifier_destroy(cqv); |
| } |
| |
| static void cleanup_rpc(grpc_exec_ctx *exec_ctx) { |
| grpc_event ev; |
| grpc_slice_buffer_destroy_internal(exec_ctx, &state.temp_incoming_buffer); |
| grpc_slice_buffer_destroy_internal(exec_ctx, &state.outgoing_buffer); |
| 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); |
| } |
| |
| typedef struct { |
| test_tcp_server *server; |
| gpr_event *signal_when_done; |
| } poll_args; |
| |
| static void actually_poll_server(void *arg) { |
| poll_args *pa = arg; |
| gpr_timespec deadline = n_sec_deadline(10); |
| while (true) { |
| bool done = gpr_atm_acq_load(&state.done_atm) != 0; |
| gpr_timespec time_left = |
| gpr_time_sub(deadline, gpr_now(GPR_CLOCK_REALTIME)); |
| gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64 ".%09d", done, |
| time_left.tv_sec, time_left.tv_nsec); |
| if (done || gpr_time_cmp(time_left, gpr_time_0(GPR_TIMESPAN)) < 0) { |
| break; |
| } |
| test_tcp_server_poll(pa->server, 1); |
| } |
| gpr_event_set(pa->signal_when_done, (void *)1); |
| gpr_free(pa); |
| } |
| |
| static void 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; |
| gpr_thd_id id; |
| poll_args *pa = gpr_malloc(sizeof(*pa)); |
| pa->server = server; |
| pa->signal_when_done = signal_when_done; |
| gpr_thd_new(&id, actually_poll_server, pa, NULL); |
| } |
| |
| 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_ERROR_CREATE("Test Shutdown")); |
| grpc_endpoint_destroy(&exec_ctx, state.tcp); |
| cleanup_rpc(&exec_ctx); |
| grpc_exec_ctx_finish(&exec_ctx); |
| test_tcp_server_destroy(&test_server); |
| |
| grpc_shutdown(); |
| } |
| |
| int main(int argc, char **argv) { |
| grpc_test_init(argc, argv); |
| |
| /* status defined in hpack static table */ |
| run_test(HTTP2_RESP(204), sizeof(HTTP2_RESP(204)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(204)); |
| |
| run_test(HTTP2_RESP(206), sizeof(HTTP2_RESP(206)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(206)); |
| |
| run_test(HTTP2_RESP(304), sizeof(HTTP2_RESP(304)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(304)); |
| |
| run_test(HTTP2_RESP(400), sizeof(HTTP2_RESP(400)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(400)); |
| |
| run_test(HTTP2_RESP(404), sizeof(HTTP2_RESP(404)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(404)); |
| |
| run_test(HTTP2_RESP(500), sizeof(HTTP2_RESP(500)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(500)); |
| |
| /* status not defined in hpack static table */ |
| run_test(HTTP2_RESP(401), sizeof(HTTP2_RESP(401)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(401)); |
| |
| run_test(HTTP2_RESP(403), sizeof(HTTP2_RESP(403)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(403)); |
| |
| run_test(HTTP2_RESP(502), sizeof(HTTP2_RESP(502)) - 1, GRPC_STATUS_CANCELLED, |
| HTTP2_DETAIL_MSG(502)); |
| |
| /* unparseable response */ |
| run_test(UNPARSEABLE_RESP, sizeof(UNPARSEABLE_RESP) - 1, |
| GRPC_STATUS_UNAVAILABLE, UNPARSEABLE_DETAIL_MSG); |
| |
| /* http1 response */ |
| run_test(HTTP1_RESP, sizeof(HTTP1_RESP) - 1, GRPC_STATUS_UNAVAILABLE, |
| HTTP1_DETAIL_MSG); |
| |
| return 0; |
| } |