From 80fa15c15121a7d0ec020dec8bfa3697a96058b6 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 16:10:49 -0800 Subject: [PATCH 001/143] Moving prototype from Google to GitHub I'd started some prototyping work on this change before the move to GitHub; this change restores things. --- include/grpc/grpc.h | 5 + src/core/surface/byte_buffer.c | 10 + src/core/surface/call.c | 149 ++++++++---- test/core/end2end/cq_verifier.c | 11 +- test/core/end2end/cq_verifier.h | 1 - test/core/end2end/dualstack_socket_test.c | 9 +- .../core/end2end/dualstack_socket_test.c.orig | 213 ++++++++++++++++++ test/core/end2end/no_server_test.c | 3 +- test/core/end2end/tests/cancel_after_accept.c | 4 +- .../cancel_after_accept_and_writes_closed.c | 4 +- test/core/end2end/tests/cancel_after_invoke.c | 4 +- .../core/end2end/tests/cancel_before_invoke.c | 3 +- test/core/end2end/tests/disappearing_server.c | 5 +- ..._server_shutdown_finishes_inflight_calls.c | 4 +- .../core/end2end/tests/invoke_large_request.c | 4 +- .../end2end/tests/max_concurrent_streams.c | 23 +- test/core/end2end/tests/ping_pong_streaming.c | 3 +- ...esponse_with_binary_metadata_and_payload.c | 4 +- ...quest_response_with_metadata_and_payload.c | 4 +- .../tests/request_response_with_payload.c | 4 +- ...ponse_with_trailing_metadata_and_payload.c | 4 +- .../tests/request_with_large_metadata.c | 4 +- .../core/end2end/tests/request_with_payload.c | 4 +- .../end2end/tests/simple_delayed_request.c | 4 +- test/core/end2end/tests/simple_request.c | 8 +- test/core/end2end/tests/thread_stress.c | 37 ++- .../writes_done_hangs_with_pending_read.c | 4 +- test/core/surface/lame_client_test.c | 3 +- 28 files changed, 387 insertions(+), 148 deletions(-) create mode 100644 test/core/end2end/dualstack_socket_test.c.orig diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 323a944f93f..f479fb8a409 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -158,6 +158,7 @@ typedef struct grpc_byte_buffer grpc_byte_buffer; /* Sample helpers to obtain byte buffers (these will certainly move place */ grpc_byte_buffer *grpc_byte_buffer_create(gpr_slice *slices, size_t nslices); +grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb); size_t grpc_byte_buffer_length(grpc_byte_buffer *bb); void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer); @@ -319,6 +320,10 @@ grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata, Produces a GRPC_FINISHED event with finished_tag when the call has been completed (there may be other events for the call pending at this time) */ +grpc_call_error grpc_call_invoke(grpc_call *call, + grpc_completion_queue *cq, + void *metadata_read_tag, + void *finished_tag, gpr_uint32 flags); grpc_call_error grpc_call_start_invoke(grpc_call *call, grpc_completion_queue *cq, void *invoke_accepted_tag, diff --git a/src/core/surface/byte_buffer.c b/src/core/surface/byte_buffer.c index 27a6c6e33d5..40876228945 100644 --- a/src/core/surface/byte_buffer.c +++ b/src/core/surface/byte_buffer.c @@ -49,6 +49,16 @@ grpc_byte_buffer *grpc_byte_buffer_create(gpr_slice *slices, size_t nslices) { return bb; } +grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) { + switch (bb->type) { + case GRPC_BB_SLICE_BUFFER: + return grpc_byte_buffer_create(bb->data.slice_buffer.slices, bb->data.slice_buffer.count); + } + gpr_log(GPR_INFO, "should never get here"); + abort(); + return NULL; +} + void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) { switch (bb->type) { case GRPC_BB_SLICE_BUFFER: diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 9c5f5064eba..f6d93bd9573 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -173,11 +173,14 @@ struct grpc_call { /* protects variables in this section */ gpr_mu read_mu; + gpr_uint8 received_start; + gpr_uint8 start_ok; gpr_uint8 reads_done; gpr_uint8 received_finish; gpr_uint8 received_metadata; gpr_uint8 have_read; gpr_uint8 have_alarm; + gpr_uint8 pending_writes_done; /* The current outstanding read message tag (only valid if have_read == 1) */ void *read_tag; void *metadata_tag; @@ -189,6 +192,8 @@ struct grpc_call { /* The current outstanding send message/context/invoke/end tag (only valid if have_write == 1) */ void *write_tag; + grpc_byte_buffer *pending_write; + gpr_uint32 pending_write_flags; /* The final status of the call */ grpc_status_code status_code; @@ -230,6 +235,9 @@ grpc_call *grpc_call_create(grpc_channel *channel, call->status_details = NULL; call->received_finish = 0; call->reads_done = 0; + call->received_start = 0; + call->pending_write = NULL; + call->pending_writes_done = 0; grpc_metadata_buffer_init(&call->incoming_metadata); gpr_ref_init(&call->internal_refcount, 1); grpc_call_stack_init(channel_stack, server_transport_data, @@ -330,16 +338,6 @@ grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata, return GRPC_CALL_OK; } -static void done_invoke(void *user_data, grpc_op_error error) { - grpc_call *call = user_data; - void *tag = call->write_tag; - - GPR_ASSERT(call->have_write); - call->have_write = 0; - call->write_tag = INVALID_TAG; - grpc_cq_end_invoke_accepted(call->cq, tag, call, NULL, NULL, error); -} - static void finish_call(grpc_call *call) { size_t count; grpc_metadata *elements; @@ -359,6 +357,88 @@ grpc_call_error grpc_call_start_invoke(grpc_call *call, void *invoke_accepted_tag, void *metadata_read_tag, void *finished_tag, gpr_uint32 flags) { + grpc_call_error err = grpc_call_invoke(call, cq, metadata_read_tag, finished_tag, flags); + if (err == GRPC_CALL_OK) { + grpc_cq_begin_op(call->cq, call, GRPC_INVOKE_ACCEPTED); + grpc_cq_end_invoke_accepted(call->cq, invoke_accepted_tag, call, do_nothing, NULL, GRPC_OP_OK); + } + return err; +} + +static void done_write(void *user_data, grpc_op_error error) { + grpc_call *call = user_data; + void *tag = call->write_tag; + + GPR_ASSERT(call->have_write); + call->have_write = 0; + call->write_tag = INVALID_TAG; + grpc_cq_end_write_accepted(call->cq, tag, call, NULL, NULL, error); +} + +static void done_writes_done(void *user_data, grpc_op_error error) { + grpc_call *call = user_data; + void *tag = call->write_tag; + + GPR_ASSERT(call->have_write); + call->have_write = 0; + call->write_tag = INVALID_TAG; + grpc_cq_end_finish_accepted(call->cq, tag, call, NULL, NULL, error); +} + +static void call_started(void *user_data, grpc_op_error error) { + grpc_call *call = user_data; + grpc_call_element *elem; + grpc_byte_buffer *pending_write = NULL; + gpr_uint32 pending_write_flags = 0; + gpr_uint8 pending_writes_done = 0; + int ok; + grpc_call_op op; + + gpr_mu_lock(&call->read_mu); + GPR_ASSERT(!call->received_start); + call->received_start = 1; + ok = call->start_ok = (error == GRPC_OP_OK); + pending_write = call->pending_write; + pending_write_flags = call->pending_write_flags; + pending_writes_done = call->pending_writes_done; + gpr_mu_unlock(&call->read_mu); + + if (pending_write) { + if (ok) { + op.type = GRPC_SEND_MESSAGE; + op.dir = GRPC_CALL_DOWN; + op.flags = pending_write_flags; + op.done_cb = done_write; + op.user_data = call; + op.data.message = pending_write; + + elem = CALL_ELEM_FROM_CALL(call, 0); + elem->filter->call_op(elem, NULL, &op); + } else { + done_write(call, error); + } + grpc_byte_buffer_destroy(pending_write); + } + if (pending_writes_done) { + if (ok) { + op.type = GRPC_SEND_FINISH; + op.dir = GRPC_CALL_DOWN; + op.flags = 0; + op.done_cb = done_writes_done; + op.user_data = call; + + elem = CALL_ELEM_FROM_CALL(call, 0); + elem->filter->call_op(elem, NULL, &op); + } else { + done_writes_done(call, error); + } + } +} + +grpc_call_error grpc_call_invoke(grpc_call *call, + grpc_completion_queue *cq, + void *metadata_read_tag, + void *finished_tag, gpr_uint32 flags) { grpc_call_element *elem; grpc_call_op op; @@ -390,7 +470,6 @@ grpc_call_error grpc_call_start_invoke(grpc_call *call, /* inform the completion queue of an incoming operation */ grpc_cq_begin_op(cq, call, GRPC_FINISHED); grpc_cq_begin_op(cq, call, GRPC_CLIENT_METADATA_READ); - grpc_cq_begin_op(cq, call, GRPC_INVOKE_ACCEPTED); gpr_mu_lock(&call->read_mu); @@ -401,8 +480,6 @@ grpc_call_error grpc_call_start_invoke(grpc_call *call, if (call->received_finish) { /* handle early cancellation */ - grpc_cq_end_invoke_accepted(call->cq, invoke_accepted_tag, call, NULL, NULL, - GRPC_OP_ERROR); grpc_cq_end_client_metadata_read(call->cq, metadata_read_tag, call, NULL, NULL, 0, NULL); finish_call(call); @@ -412,18 +489,15 @@ grpc_call_error grpc_call_start_invoke(grpc_call *call, return GRPC_CALL_OK; } - call->write_tag = invoke_accepted_tag; call->metadata_tag = metadata_read_tag; - call->have_write = 1; - gpr_mu_unlock(&call->read_mu); /* call down the filter stack */ op.type = GRPC_SEND_START; op.dir = GRPC_CALL_DOWN; op.flags = flags; - op.done_cb = done_invoke; + op.done_cb = call_started; op.data.start.pollset = grpc_cq_pollset(cq); op.user_data = call; @@ -516,26 +590,6 @@ grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq, return GRPC_CALL_OK; } -static void done_writes_done(void *user_data, grpc_op_error error) { - grpc_call *call = user_data; - void *tag = call->write_tag; - - GPR_ASSERT(call->have_write); - call->have_write = 0; - call->write_tag = INVALID_TAG; - grpc_cq_end_finish_accepted(call->cq, tag, call, NULL, NULL, error); -} - -static void done_write(void *user_data, grpc_op_error error) { - grpc_call *call = user_data; - void *tag = call->write_tag; - - GPR_ASSERT(call->have_write); - call->have_write = 0; - call->write_tag = INVALID_TAG; - grpc_cq_end_write_accepted(call->cq, tag, call, NULL, NULL, error); -} - void grpc_call_client_initial_metadata_complete( grpc_call_element *surface_element) { grpc_call *call = grpc_call_from_top_element(surface_element); @@ -635,8 +689,6 @@ grpc_call_error grpc_call_start_write(grpc_call *call, grpc_cq_begin_op(call->cq, call, GRPC_WRITE_ACCEPTED); - /* for now we do no buffering, so a NULL byte_buffer can have no impact - on our behavior -- succeed immediately */ /* TODO(ctiller): if flags & GRPC_WRITE_BUFFER_HINT == 0, this indicates a flush, and that flush should be propogated down from here */ if (byte_buffer == NULL) { @@ -647,6 +699,15 @@ grpc_call_error grpc_call_start_write(grpc_call *call, call->write_tag = tag; call->have_write = 1; + gpr_mu_lock(&call->read_mu); + if (!call->received_start) { + call->pending_write = grpc_byte_buffer_copy(byte_buffer); + call->pending_write_flags = flags; + + gpr_mu_unlock(&call->read_mu); + } else { + gpr_mu_unlock(&call->read_mu); + op.type = GRPC_SEND_MESSAGE; op.dir = GRPC_CALL_DOWN; op.flags = flags; @@ -656,6 +717,7 @@ grpc_call_error grpc_call_start_write(grpc_call *call, elem = CALL_ELEM_FROM_CALL(call, 0); elem->filter->call_op(elem, NULL, &op); + } return GRPC_CALL_OK; } @@ -687,6 +749,14 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) { call->write_tag = tag; call->have_write = 1; + gpr_mu_lock(&call->read_mu); + if (!call->received_start) { + call->pending_writes_done = 1; + + gpr_mu_unlock(&call->read_mu); + } else { + gpr_mu_unlock(&call->read_mu); + op.type = GRPC_SEND_FINISH; op.dir = GRPC_CALL_DOWN; op.flags = 0; @@ -695,6 +765,7 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) { elem = CALL_ELEM_FROM_CALL(call, 0); elem->filter->call_op(elem, NULL, &op); + } return GRPC_CALL_OK; } diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c index e5b7304743f..ba446c116af 100644 --- a/test/core/end2end/cq_verifier.c +++ b/test/core/end2end/cq_verifier.c @@ -70,7 +70,6 @@ typedef struct expectation { union { grpc_op_error finish_accepted; grpc_op_error write_accepted; - grpc_op_error invoke_accepted; struct { const char *method; const char *host; @@ -182,7 +181,7 @@ static void verify_matches(expectation *e, grpc_event *ev) { GPR_ASSERT(e->data.write_accepted == ev->data.write_accepted); break; case GRPC_INVOKE_ACCEPTED: - GPR_ASSERT(e->data.invoke_accepted == ev->data.invoke_accepted); + abort(); break; case GRPC_SERVER_RPC_NEW: GPR_ASSERT(string_equivalent(e->data.server_rpc_new.method, @@ -268,8 +267,7 @@ static size_t expectation_to_string(char *out, expectation *e) { return sprintf(out, "GRPC_WRITE_ACCEPTED result=%d", e->data.write_accepted); case GRPC_INVOKE_ACCEPTED: - return sprintf(out, "GRPC_INVOKE_ACCEPTED result=%d", - e->data.invoke_accepted); + return sprintf(out, "GRPC_INVOKE_ACCEPTED"); case GRPC_SERVER_RPC_NEW: timeout = gpr_time_sub(e->data.server_rpc_new.deadline, gpr_now()); return sprintf(out, "GRPC_SERVER_RPC_NEW method=%s host=%s timeout=%fsec", @@ -414,11 +412,6 @@ static metadata *metadata_from_args(va_list args) { } } -void cq_expect_invoke_accepted(cq_verifier *v, void *tag, - grpc_op_error result) { - add(v, GRPC_INVOKE_ACCEPTED, tag)->data.invoke_accepted = result; -} - void cq_expect_write_accepted(cq_verifier *v, void *tag, grpc_op_error result) { add(v, GRPC_WRITE_ACCEPTED, tag)->data.write_accepted = result; } diff --git a/test/core/end2end/cq_verifier.h b/test/core/end2end/cq_verifier.h index 9711e0067c1..73d5782a335 100644 --- a/test/core/end2end/cq_verifier.h +++ b/test/core/end2end/cq_verifier.h @@ -56,7 +56,6 @@ void cq_verify_empty(cq_verifier *v); Any functions taking ... expect a NULL terminated list of key/value pairs (each pair using two parameter slots) of metadata that MUST be present in the event. */ -void cq_expect_invoke_accepted(cq_verifier *v, void *tag, grpc_op_error result); void cq_expect_write_accepted(cq_verifier *v, void *tag, grpc_op_error result); void cq_expect_finish_accepted(cq_verifier *v, void *tag, grpc_op_error result); void cq_expect_read(cq_verifier *v, void *tag, gpr_slice bytes); diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c index b443caa2a67..23ffddc1467 100644 --- a/test/core/end2end/dualstack_socket_test.c +++ b/test/core/end2end/dualstack_socket_test.c @@ -107,13 +107,10 @@ void test_connect(const char *server_host, const char *client_host, int port, GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, client_cq, tag(1), tag(2), tag(3), 0)); + grpc_call_invoke(c, client_cq, tag(2), tag(3), 0)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); if (expect_ok) { /* Check for a successful request. */ - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); - - GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); cq_verify(v_client); @@ -142,10 +139,10 @@ void test_connect(const char *server_host, const char *client_host, int port, grpc_call_destroy(s); } else { /* Check for a failed connection. */ - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_ERROR); cq_expect_client_metadata_read(v_client, tag(2), NULL); cq_expect_finished_with_status(v_client, tag(3), GRPC_STATUS_CANCELLED, NULL, NULL); + cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_ERROR); cq_verify(v_client); grpc_call_destroy(c); diff --git a/test/core/end2end/dualstack_socket_test.c.orig b/test/core/end2end/dualstack_socket_test.c.orig new file mode 100644 index 00000000000..b443caa2a67 --- /dev/null +++ b/test/core/end2end/dualstack_socket_test.c.orig @@ -0,0 +1,213 @@ +/* + * + * Copyright 2014, 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. + * + */ + +#include "src/core/iomgr/socket_utils_posix.h" +#include +#include +#include +#include +#include "test/core/end2end/cq_verifier.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +/* This test exercises IPv4, IPv6, and dualstack sockets in various ways. */ + +static void *tag(gpr_intptr i) { return (void *)i; } + +static gpr_timespec ms_from_now(int ms) { + return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_MS * ms)); +} + +static void drain_cq(grpc_completion_queue *cq) { + grpc_event *ev; + grpc_completion_type type; + do { + ev = grpc_completion_queue_next(cq, ms_from_now(5000)); + GPR_ASSERT(ev); + type = ev->type; + grpc_event_finish(ev); + gpr_log(GPR_INFO, "Drained event type %d", type); + } while (type != GRPC_QUEUE_SHUTDOWN); +} + +void test_connect(const char *server_host, const char *client_host, int port, + int expect_ok) { + char *client_hostport; + char *server_hostport; + grpc_channel *client; + grpc_server *server; + grpc_completion_queue *client_cq; + grpc_completion_queue *server_cq; + grpc_call *c; + grpc_call *s; + cq_verifier *v_client; + cq_verifier *v_server; + gpr_timespec deadline; + + gpr_join_host_port(&client_hostport, client_host, port); + gpr_join_host_port(&server_hostport, server_host, port); + gpr_log(GPR_INFO, "Testing with server=%s client=%s (expecting %s)", + server_hostport, client_hostport, expect_ok ? "success" : "failure"); + + /* Create server. */ + server_cq = grpc_completion_queue_create(); + server = grpc_server_create(server_cq, NULL); + GPR_ASSERT(grpc_server_add_http2_port(server, server_hostport)); + grpc_server_start(server); + gpr_free(server_hostport); + v_server = cq_verifier_create(server_cq); + + /* Create client. */ + client_cq = grpc_completion_queue_create(); + client = grpc_channel_create(client_hostport, NULL); + gpr_free(client_hostport); + v_client = cq_verifier_create(client_cq); + + if (expect_ok) { + /* Normal deadline, shouldn't be reached. */ + deadline = ms_from_now(60000); + } else { + /* Give up faster when failure is expected. + BUG: Setting this to 1000 reveals a memory leak (b/18608927). */ + deadline = ms_from_now(1500); + } + + /* Send a trivial request. */ + c = grpc_channel_create_call(client, "/foo", "test.google.com", deadline); + GPR_ASSERT(c); + + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_start_invoke(c, client_cq, tag(1), tag(2), tag(3), 0)); + if (expect_ok) { + /* Check for a successful request. */ + cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); + cq_verify(v_client); + + GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); + cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); + cq_verify(v_client); + + GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(server, tag(100))); + cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com", + deadline, NULL); + cq_verify(v_server); + + GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, server_cq, tag(102), 0)); + cq_expect_client_metadata_read(v_client, tag(2), NULL); + cq_verify(v_client); + + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_start_write_status(s, GRPC_STATUS_UNIMPLEMENTED, "xyz", + tag(5))); + cq_expect_finished_with_status(v_client, tag(3), GRPC_STATUS_UNIMPLEMENTED, + "xyz", NULL); + cq_verify(v_client); + + cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK); + cq_verify(v_server); + cq_expect_finished(v_server, tag(102), NULL); + cq_verify(v_server); + + grpc_call_destroy(c); + grpc_call_destroy(s); + } else { + /* Check for a failed connection. */ + cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_ERROR); + cq_expect_client_metadata_read(v_client, tag(2), NULL); + cq_expect_finished_with_status(v_client, tag(3), GRPC_STATUS_CANCELLED, + NULL, NULL); + cq_verify(v_client); + + grpc_call_destroy(c); + } + + cq_verifier_destroy(v_client); + cq_verifier_destroy(v_server); + + /* Destroy client. */ + grpc_channel_destroy(client); + grpc_completion_queue_shutdown(client_cq); + drain_cq(client_cq); + grpc_completion_queue_destroy(client_cq); + + /* Destroy server. */ + grpc_server_shutdown(server); + grpc_server_destroy(server); + grpc_completion_queue_shutdown(server_cq); + drain_cq(server_cq); + grpc_completion_queue_destroy(server_cq); +} + +int main(int argc, char **argv) { + int do_ipv6 = 1; + int i; + int port = grpc_pick_unused_port_or_die(); + + grpc_test_init(argc, argv); + grpc_init(); + + if (!grpc_ipv6_loopback_available()) { + gpr_log(GPR_INFO, "Can't bind to ::1. Skipping IPv6 tests."); + do_ipv6 = 0; + } + + for (i = 0; i <= 1; i++) { + /* For coverage, test with and without dualstack sockets. */ + grpc_forbid_dualstack_sockets_for_testing = i; + + /* :: and 0.0.0.0 are handled identically. */ + test_connect("::", "127.0.0.1", port, 1); + test_connect("::", "::ffff:127.0.0.1", port, 1); + test_connect("::", "localhost", port, 1); + test_connect("0.0.0.0", "127.0.0.1", port, 1); + test_connect("0.0.0.0", "::ffff:127.0.0.1", port, 1); + test_connect("0.0.0.0", "localhost", port, 1); + if (do_ipv6) { + test_connect("::", "::1", port, 1); + test_connect("0.0.0.0", "::1", port, 1); + } + + /* These only work when the families agree. */ + test_connect("127.0.0.1", "127.0.0.1", port, 1); + if (do_ipv6) { + test_connect("::1", "::1", port, 1); + test_connect("::1", "127.0.0.1", port, 0); + test_connect("127.0.0.1", "::1", port, 0); + } + + } + + grpc_shutdown(); + + return 0; +} diff --git a/test/core/end2end/no_server_test.c b/test/core/end2end/no_server_test.c index ba6349c109b..0c251ab467a 100644 --- a/test/core/end2end/no_server_test.c +++ b/test/core/end2end/no_server_test.c @@ -57,10 +57,9 @@ int main(int argc, char **argv) { /* create a call, channel to a non existant server */ chan = grpc_channel_create("nonexistant:54321", NULL); call = grpc_channel_create_call(chan, "/foo", "nonexistant", deadline); - GPR_ASSERT(grpc_call_start_invoke(call, cq, tag(1), tag(2), tag(3), 0) == + GPR_ASSERT(grpc_call_invoke(call, cq, tag(2), tag(3), 0) == GRPC_CALL_OK); /* verify that all tags get completed */ - cq_expect_invoke_accepted(cqv, tag(1), GRPC_OP_ERROR); cq_expect_client_metadata_read(cqv, tag(2), NULL); cq_expect_finished_with_status(cqv, tag(3), GRPC_STATUS_CANCELLED, NULL, NULL); diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c index 3b570401ef9..5ec13b937c6 100644 --- a/test/core/end2end/tests/cancel_after_accept.c +++ b/test/core/end2end/tests/cancel_after_accept.c @@ -124,9 +124,7 @@ static void test_cancel_after_accept(grpc_end2end_test_config config, GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100))); cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com", diff --git a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c index e20b0ec0b24..19d240471e3 100644 --- a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c +++ b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c @@ -124,9 +124,7 @@ static void test_cancel_after_accept_and_writes_closed( GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100))); cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com", diff --git a/test/core/end2end/tests/cancel_after_invoke.c b/test/core/end2end/tests/cancel_after_invoke.c index 4fab9f7effa..ad0dd0c9ec9 100644 --- a/test/core/end2end/tests/cancel_after_invoke.c +++ b/test/core/end2end/tests/cancel_after_invoke.c @@ -122,9 +122,7 @@ static void test_cancel_after_invoke(grpc_end2end_test_config config, GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == call_cancel(c)); diff --git a/test/core/end2end/tests/cancel_before_invoke.c b/test/core/end2end/tests/cancel_before_invoke.c index 2d8d465b249..e523fa2f762 100644 --- a/test/core/end2end/tests/cancel_before_invoke.c +++ b/test/core/end2end/tests/cancel_before_invoke.c @@ -119,8 +119,7 @@ static void test_cancel_before_invoke(grpc_end2end_test_config config) { GPR_ASSERT(GRPC_CALL_OK == grpc_call_cancel(c)); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_ERROR); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); cq_expect_client_metadata_read(v_client, tag(2), NULL); cq_expect_finished_with_status(v_client, tag(3), GRPC_STATUS_CANCELLED, NULL, NULL); diff --git a/test/core/end2end/tests/disappearing_server.c b/test/core/end2end/tests/disappearing_server.c index c421798fc76..1cbb15f32e4 100644 --- a/test/core/end2end/tests/disappearing_server.c +++ b/test/core/end2end/tests/disappearing_server.c @@ -100,11 +100,8 @@ static void do_request_and_shutdown_server(grpc_end2end_test_fixture *f, c = grpc_channel_create_call(f->client, "/foo", "test.google.com", deadline); GPR_ASSERT(c); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c, f->client_cq, tag(1), + GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c, f->client_cq, tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - - cq_verify(v_client); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); diff --git a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c index 3855a7a3de4..824d9eba88a 100644 --- a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c +++ b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c @@ -115,9 +115,7 @@ static void test_early_server_shutdown_finishes_inflight_calls( GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c index bad86fb9dcd..bacecde01bb 100644 --- a/test/core/end2end/tests/invoke_large_request.c +++ b/test/core/end2end/tests/invoke_large_request.c @@ -126,9 +126,7 @@ static void test_invoke_large_request(grpc_end2end_test_config config) { GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, request_payload, tag(4), 0)); diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index a418d1b15f3..e2f30d07789 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -113,9 +113,7 @@ static void simple_request_body(grpc_end2end_test_fixture f) { GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); @@ -191,14 +189,17 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100))); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c1, f.client_cq, tag(300), + GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c1, f.client_cq, tag(301), tag(302), 0)); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c2, f.client_cq, tag(400), + GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c2, f.client_cq, tag(401), tag(402), 0)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c1, tag(303))); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c2, tag(303))); + ev = grpc_completion_queue_next( f.client_cq, gpr_time_add(gpr_now(), gpr_time_from_seconds(10))); GPR_ASSERT(ev); - GPR_ASSERT(ev->type == GRPC_INVOKE_ACCEPTED); + GPR_ASSERT(ev->type == GRPC_FINISH_ACCEPTED); GPR_ASSERT(ev->data.invoke_accepted == GRPC_OP_OK); /* The /alpha or /beta calls started above could be invoked (but NOT both); * check this here */ @@ -206,11 +207,6 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { live_call_obj = live_call == 300 ? c1 : c2; grpc_event_finish(ev); - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_writes_done(live_call_obj, tag(live_call + 3))); - cq_expect_finish_accepted(v_client, tag(live_call + 3), GRPC_OP_OK); - cq_verify(v_client); - cq_expect_server_rpc_new(v_server, &s1, tag(100), live_call == 300 ? "/alpha" : "/beta", "test.google.com", deadline, NULL); @@ -232,11 +228,6 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { GRPC_STATUS_UNIMPLEMENTED, "xyz", NULL); live_call = (live_call == 300) ? 400 : 300; live_call_obj = live_call == 300 ? c1 : c2; - cq_expect_invoke_accepted(v_client, tag(live_call), GRPC_OP_OK); - cq_verify(v_client); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_writes_done(live_call_obj, tag(live_call + 3))); cq_expect_finish_accepted(v_client, tag(live_call + 3), GRPC_OP_OK); cq_verify(v_client); diff --git a/test/core/end2end/tests/ping_pong_streaming.c b/test/core/end2end/tests/ping_pong_streaming.c index 02a05e8e0c7..1801d34a2e2 100644 --- a/test/core/end2end/tests/ping_pong_streaming.c +++ b/test/core/end2end/tests/ping_pong_streaming.c @@ -122,8 +122,7 @@ static void test_pingpong_streaming(grpc_end2end_test_config config, GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100))); diff --git a/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c index 8a0eea8072f..c6048bf96a3 100644 --- a/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c @@ -145,9 +145,7 @@ static void test_request_response_with_metadata_and_payload( GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta2, 0)); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, request_payload, tag(4), 0)); diff --git a/test/core/end2end/tests/request_response_with_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_metadata_and_payload.c index 77e490710c7..0eebebeb002 100644 --- a/test/core/end2end/tests/request_response_with_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_metadata_and_payload.c @@ -136,9 +136,7 @@ static void test_request_response_with_metadata_and_payload( GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta2, 0)); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, request_payload, tag(4), 0)); diff --git a/test/core/end2end/tests/request_response_with_payload.c b/test/core/end2end/tests/request_response_with_payload.c index deb61a7a8b9..b2c39e8d6d3 100644 --- a/test/core/end2end/tests/request_response_with_payload.c +++ b/test/core/end2end/tests/request_response_with_payload.c @@ -125,9 +125,7 @@ static void request_response_with_payload(grpc_end2end_test_fixture f) { GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, request_payload, tag(4), 0)); diff --git a/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c index 95e268441ba..8fbe514bf37 100644 --- a/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c @@ -138,9 +138,7 @@ static void test_request_response_with_metadata_and_payload( GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta2, 0)); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, request_payload, tag(4), 0)); diff --git a/test/core/end2end/tests/request_with_large_metadata.c b/test/core/end2end/tests/request_with_large_metadata.c index 26b165625e1..3c634a396de 100644 --- a/test/core/end2end/tests/request_with_large_metadata.c +++ b/test/core/end2end/tests/request_with_large_metadata.c @@ -128,9 +128,7 @@ static void test_request_with_large_metadata(grpc_end2end_test_config config) { GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta, 0)); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com", deadline, "key", meta.value, NULL); diff --git a/test/core/end2end/tests/request_with_payload.c b/test/core/end2end/tests/request_with_payload.c index 602ade6f901..db10fac6f5a 100644 --- a/test/core/end2end/tests/request_with_payload.c +++ b/test/core/end2end/tests/request_with_payload.c @@ -122,9 +122,7 @@ static void test_invoke_request_with_payload(grpc_end2end_test_config config) { GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100))); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, payload, tag(4), 0)); /* destroy byte buffer early to ensure async code keeps track of its contents diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c index b90316c32eb..c1b528cc3f2 100644 --- a/test/core/end2end/tests/simple_delayed_request.c +++ b/test/core/end2end/tests/simple_delayed_request.c @@ -106,10 +106,8 @@ static void simple_delayed_request_body(grpc_end2end_test_config config, c = grpc_channel_create_call(f->client, "/foo", "test.google.com", deadline); GPR_ASSERT(c); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c, f->client_cq, tag(1), + GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c, f->client_cq, tag(2), tag(3), 0)); - gpr_sleep_until(gpr_time_add(gpr_now(), gpr_time_from_micros(delay_us))); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); config.init_server(f, server_args); diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c index 3511f276e66..58d80230903 100644 --- a/test/core/end2end/tests/simple_request.c +++ b/test/core/end2end/tests/simple_request.c @@ -113,9 +113,7 @@ static void simple_request_body(grpc_end2end_test_fixture f) { GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); @@ -160,9 +158,7 @@ static void simple_request_body2(grpc_end2end_test_fixture f) { GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); diff --git a/test/core/end2end/tests/thread_stress.c b/test/core/end2end/tests/thread_stress.c index 4100b0e35d6..65a7930c1c7 100644 --- a/test/core/end2end/tests/thread_stress.c +++ b/test/core/end2end/tests/thread_stress.c @@ -106,25 +106,31 @@ static void drain_cq(int client, grpc_completion_queue *cq) { /* Kick off a new request - assumes g_mu taken */ static void start_request() { + gpr_slice slice = gpr_slice_malloc(100); + grpc_byte_buffer *buf; grpc_call *call = grpc_channel_create_call( g_fixture.client, "/Foo", "test.google.com", g_test_end_time); + + memset(GPR_SLICE_START_PTR(slice), 1, GPR_SLICE_LENGTH(slice)); + buf = grpc_byte_buffer_create(&slice, 1); + gpr_slice_unref(slice); + g_active_requests++; GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(call, g_fixture.client_cq, NULL, NULL, NULL, 0)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(call, NULL)); + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_start_write(call, buf, NULL, 0)); + + grpc_byte_buffer_destroy(buf); } /* Async client: handle sending requests, reading responses, and starting new requests when old ones finish */ static void client_thread(void *p) { - int id = (gpr_intptr)p; + gpr_intptr id = (gpr_intptr)p; grpc_event *ev; - gpr_slice slice = gpr_slice_malloc(100); - grpc_byte_buffer *buf; char *estr; - memset(GPR_SLICE_START_PTR(slice), id, GPR_SLICE_LENGTH(slice)); - - buf = grpc_byte_buffer_create(&slice, 1); - gpr_slice_unref(slice); for (;;) { ev = grpc_completion_queue_next(g_fixture.client_cq, n_seconds_time(1)); @@ -135,14 +141,6 @@ static void client_thread(void *p) { gpr_log(GPR_ERROR, "unexpected event: %s", estr); gpr_free(estr); break; - case GRPC_INVOKE_ACCEPTED: - /* better not keep going if the invoke failed */ - if (ev->data.invoke_accepted == GRPC_OP_OK) { - GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(ev->call, NULL)); - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_write(ev->call, buf, NULL, 0)); - } - break; case GRPC_READ: break; case GRPC_WRITE_ACCEPTED: @@ -173,7 +171,6 @@ static void client_thread(void *p) { gpr_mu_unlock(&g_mu); } - grpc_byte_buffer_destroy(buf); gpr_event_set(&g_client_done[id], (void *)1); } @@ -196,17 +193,17 @@ static void maybe_end_server_call(grpc_call *call, gpr_refcount *rc) { static void server_thread(void *p) { int id = (gpr_intptr)p; - grpc_event *ev; gpr_slice slice = gpr_slice_malloc(100); grpc_byte_buffer *buf; + grpc_event *ev; char *estr; - memset(GPR_SLICE_START_PTR(slice), id, GPR_SLICE_LENGTH(slice)); - - request_server_call(); + memset(GPR_SLICE_START_PTR(slice), 1, GPR_SLICE_LENGTH(slice)); buf = grpc_byte_buffer_create(&slice, 1); gpr_slice_unref(slice); + request_server_call(); + for (;;) { ev = grpc_completion_queue_next(g_fixture.server_cq, n_seconds_time(1)); if (ev) { diff --git a/test/core/end2end/tests/writes_done_hangs_with_pending_read.c b/test/core/end2end/tests/writes_done_hangs_with_pending_read.c index 2241519e894..d959111e43d 100644 --- a/test/core/end2end/tests/writes_done_hangs_with_pending_read.c +++ b/test/core/end2end/tests/writes_done_hangs_with_pending_read.c @@ -128,9 +128,7 @@ static void test_writes_done_hangs_with_pending_read( GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, request_payload, tag(4), 0)); diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c index 0520a39ea2f..11d5e4a4951 100644 --- a/test/core/surface/lame_client_test.c +++ b/test/core/surface/lame_client_test.c @@ -63,10 +63,9 @@ int main(int argc, char **argv) { /* and invoke the call */ GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(call, cq, tag(1), tag(2), tag(3), 0)); + grpc_call_invoke(call, cq, tag(2), tag(3), 0)); /* the call should immediately fail */ - cq_expect_invoke_accepted(cqv, tag(1), GRPC_OP_ERROR); cq_expect_client_metadata_read(cqv, tag(2), NULL); cq_expect_finished(cqv, tag(3), NULL); cq_verify(cqv); From 40fc7a66323d74b4303d582345807c4577584a77 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 16:11:58 -0800 Subject: [PATCH 002/143] clang-format codebase --- include/grpc/grpc.h | 7 ++- src/core/surface/byte_buffer.c | 3 +- src/core/surface/call.c | 53 ++++++++++--------- test/core/end2end/dualstack_socket_test.c | 3 +- test/core/end2end/no_server_test.c | 3 +- test/core/end2end/tests/disappearing_server.c | 4 +- .../end2end/tests/max_concurrent_streams.c | 8 +-- .../end2end/tests/simple_delayed_request.c | 4 +- test/core/end2end/tests/thread_stress.c | 5 +- test/core/surface/lame_client_test.c | 3 +- 10 files changed, 45 insertions(+), 48 deletions(-) diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index cc7ed4a9fbc..e51a1668e8b 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -320,10 +320,9 @@ grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata, Produces a GRPC_FINISHED event with finished_tag when the call has been completed (there may be other events for the call pending at this time) */ -grpc_call_error grpc_call_invoke(grpc_call *call, - grpc_completion_queue *cq, - void *metadata_read_tag, - void *finished_tag, gpr_uint32 flags); +grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq, + void *metadata_read_tag, void *finished_tag, + gpr_uint32 flags); grpc_call_error grpc_call_start_invoke(grpc_call *call, grpc_completion_queue *cq, void *invoke_accepted_tag, diff --git a/src/core/surface/byte_buffer.c b/src/core/surface/byte_buffer.c index 40876228945..d1be41074d3 100644 --- a/src/core/surface/byte_buffer.c +++ b/src/core/surface/byte_buffer.c @@ -52,7 +52,8 @@ grpc_byte_buffer *grpc_byte_buffer_create(gpr_slice *slices, size_t nslices) { grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) { switch (bb->type) { case GRPC_BB_SLICE_BUFFER: - return grpc_byte_buffer_create(bb->data.slice_buffer.slices, bb->data.slice_buffer.count); + return grpc_byte_buffer_create(bb->data.slice_buffer.slices, + bb->data.slice_buffer.count); } gpr_log(GPR_INFO, "should never get here"); abort(); diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 297d9587eb5..262fbe381ac 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -357,10 +357,12 @@ grpc_call_error grpc_call_start_invoke(grpc_call *call, void *invoke_accepted_tag, void *metadata_read_tag, void *finished_tag, gpr_uint32 flags) { - grpc_call_error err = grpc_call_invoke(call, cq, metadata_read_tag, finished_tag, flags); + grpc_call_error err = + grpc_call_invoke(call, cq, metadata_read_tag, finished_tag, flags); if (err == GRPC_CALL_OK) { grpc_cq_begin_op(call->cq, call, GRPC_INVOKE_ACCEPTED); - grpc_cq_end_invoke_accepted(call->cq, invoke_accepted_tag, call, do_nothing, NULL, GRPC_OP_OK); + grpc_cq_end_invoke_accepted(call->cq, invoke_accepted_tag, call, do_nothing, + NULL, GRPC_OP_OK); } return err; } @@ -421,11 +423,11 @@ static void call_started(void *user_data, grpc_op_error error) { } if (pending_writes_done) { if (ok) { - op.type = GRPC_SEND_FINISH; - op.dir = GRPC_CALL_DOWN; - op.flags = 0; - op.done_cb = done_writes_done; - op.user_data = call; + op.type = GRPC_SEND_FINISH; + op.dir = GRPC_CALL_DOWN; + op.flags = 0; + op.done_cb = done_writes_done; + op.user_data = call; elem = CALL_ELEM_FROM_CALL(call, 0); elem->filter->call_op(elem, NULL, &op); @@ -435,10 +437,9 @@ static void call_started(void *user_data, grpc_op_error error) { } } -grpc_call_error grpc_call_invoke(grpc_call *call, - grpc_completion_queue *cq, - void *metadata_read_tag, - void *finished_tag, gpr_uint32 flags) { +grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq, + void *metadata_read_tag, void *finished_tag, + gpr_uint32 flags) { grpc_call_element *elem; grpc_call_op op; @@ -708,15 +709,15 @@ grpc_call_error grpc_call_start_write(grpc_call *call, } else { gpr_mu_unlock(&call->read_mu); - op.type = GRPC_SEND_MESSAGE; - op.dir = GRPC_CALL_DOWN; - op.flags = flags; - op.done_cb = done_write; - op.user_data = call; - op.data.message = byte_buffer; + op.type = GRPC_SEND_MESSAGE; + op.dir = GRPC_CALL_DOWN; + op.flags = flags; + op.done_cb = done_write; + op.user_data = call; + op.data.message = byte_buffer; - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); + elem = CALL_ELEM_FROM_CALL(call, 0); + elem->filter->call_op(elem, NULL, &op); } return GRPC_CALL_OK; @@ -757,14 +758,14 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) { } else { gpr_mu_unlock(&call->read_mu); - op.type = GRPC_SEND_FINISH; - op.dir = GRPC_CALL_DOWN; - op.flags = 0; - op.done_cb = done_writes_done; - op.user_data = call; + op.type = GRPC_SEND_FINISH; + op.dir = GRPC_CALL_DOWN; + op.flags = 0; + op.done_cb = done_writes_done; + op.user_data = call; - elem = CALL_ELEM_FROM_CALL(call, 0); - elem->filter->call_op(elem, NULL, &op); + elem = CALL_ELEM_FROM_CALL(call, 0); + elem->filter->call_op(elem, NULL, &op); } return GRPC_CALL_OK; diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c index ac656d3719b..b765d3d43ea 100644 --- a/test/core/end2end/dualstack_socket_test.c +++ b/test/core/end2end/dualstack_socket_test.c @@ -115,8 +115,7 @@ void test_connect(const char *server_host, const char *client_host, int port, c = grpc_channel_create_call(client, "/foo", "test.google.com", deadline); GPR_ASSERT(c); - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_invoke(c, client_cq, tag(2), tag(3), 0)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c, client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); if (expect_ok) { /* Check for a successful request. */ diff --git a/test/core/end2end/no_server_test.c b/test/core/end2end/no_server_test.c index 0c251ab467a..2c432456ce4 100644 --- a/test/core/end2end/no_server_test.c +++ b/test/core/end2end/no_server_test.c @@ -57,8 +57,7 @@ int main(int argc, char **argv) { /* create a call, channel to a non existant server */ chan = grpc_channel_create("nonexistant:54321", NULL); call = grpc_channel_create_call(chan, "/foo", "nonexistant", deadline); - GPR_ASSERT(grpc_call_invoke(call, cq, tag(2), tag(3), 0) == - GRPC_CALL_OK); + GPR_ASSERT(grpc_call_invoke(call, cq, tag(2), tag(3), 0) == GRPC_CALL_OK); /* verify that all tags get completed */ cq_expect_client_metadata_read(cqv, tag(2), NULL); cq_expect_finished_with_status(cqv, tag(3), GRPC_STATUS_CANCELLED, NULL, diff --git a/test/core/end2end/tests/disappearing_server.c b/test/core/end2end/tests/disappearing_server.c index 1cbb15f32e4..d30e9eae5ff 100644 --- a/test/core/end2end/tests/disappearing_server.c +++ b/test/core/end2end/tests/disappearing_server.c @@ -100,8 +100,8 @@ static void do_request_and_shutdown_server(grpc_end2end_test_fixture *f, c = grpc_channel_create_call(f->client, "/foo", "test.google.com", deadline); GPR_ASSERT(c); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c, f->client_cq, - tag(2), tag(3), 0)); + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_invoke(c, f->client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index e2f30d07789..6e5cb1316b0 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -189,10 +189,10 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100))); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c1, f.client_cq, - tag(301), tag(302), 0)); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c2, f.client_cq, - tag(401), tag(402), 0)); + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_invoke(c1, f.client_cq, tag(301), tag(302), 0)); + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_invoke(c2, f.client_cq, tag(401), tag(402), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c1, tag(303))); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c2, tag(303))); diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c index c1b528cc3f2..6e2e16714ab 100644 --- a/test/core/end2end/tests/simple_delayed_request.c +++ b/test/core/end2end/tests/simple_delayed_request.c @@ -106,8 +106,8 @@ static void simple_delayed_request_body(grpc_end2end_test_config config, c = grpc_channel_create_call(f->client, "/foo", "test.google.com", deadline); GPR_ASSERT(c); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(c, f->client_cq, - tag(2), tag(3), 0)); + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_invoke(c, f->client_cq, tag(2), tag(3), 0)); config.init_server(f, server_args); diff --git a/test/core/end2end/tests/thread_stress.c b/test/core/end2end/tests/thread_stress.c index 65a7930c1c7..4ce85df22e0 100644 --- a/test/core/end2end/tests/thread_stress.c +++ b/test/core/end2end/tests/thread_stress.c @@ -118,9 +118,8 @@ static void start_request() { g_active_requests++; GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(call, g_fixture.client_cq, NULL, NULL, NULL, 0)); - GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(call, NULL)); - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_write(call, buf, NULL, 0)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(call, NULL)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(call, buf, NULL, 0)); grpc_byte_buffer_destroy(buf); } diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c index 11d5e4a4951..9b9f0202d6d 100644 --- a/test/core/surface/lame_client_test.c +++ b/test/core/surface/lame_client_test.c @@ -62,8 +62,7 @@ int main(int argc, char **argv) { GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(call, &md, 0)); /* and invoke the call */ - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_invoke(call, cq, tag(2), tag(3), 0)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(call, cq, tag(2), tag(3), 0)); /* the call should immediately fail */ cq_expect_client_metadata_read(cqv, tag(2), NULL); From cbbac9b10db599c9c78d5c94846333125215ca85 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 16:13:43 -0800 Subject: [PATCH 003/143] Compile fix --- test/core/end2end/tests/max_concurrent_streams.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index 6e5cb1316b0..57e271834c0 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -155,7 +155,6 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { grpc_call *s1; grpc_call *s2; int live_call; - grpc_call *live_call_obj; gpr_timespec deadline; cq_verifier *v_client; cq_verifier *v_server; @@ -204,7 +203,6 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { /* The /alpha or /beta calls started above could be invoked (but NOT both); * check this here */ live_call = (int)(gpr_intptr)ev->tag; - live_call_obj = live_call == 300 ? c1 : c2; grpc_event_finish(ev); cq_expect_server_rpc_new(v_server, &s1, tag(100), @@ -227,7 +225,6 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { cq_expect_finished_with_status(v_client, tag(live_call + 2), GRPC_STATUS_UNIMPLEMENTED, "xyz", NULL); live_call = (live_call == 300) ? 400 : 300; - live_call_obj = live_call == 300 ? c1 : c2; cq_expect_finish_accepted(v_client, tag(live_call + 3), GRPC_OP_OK); cq_verify(v_client); From 2163bad186eeeedac73e3a7b1a9b735e1dc6dd18 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 16:35:41 -0800 Subject: [PATCH 004/143] Calls are always started on the server. By the time we call accept, we have proof that we've received the start of a call, so we should set this bit to one. --- src/core/surface/call.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 262fbe381ac..6313fbce028 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -531,6 +531,7 @@ grpc_call_error grpc_call_server_accept(grpc_call *call, call->state = CALL_BOUNDCQ; call->cq = cq; call->finished_tag = finished_tag; + call->received_start = 1; if (prq_is_empty(&call->prq) && call->received_finish) { finish_call(call); From 97f7fca5ffe17565d020118a1c8b7db032cc70fb Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 16:36:31 -0800 Subject: [PATCH 005/143] Remove calls to grpc_call_start_invoke --- test/core/end2end/tests/census_simple_request.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/core/end2end/tests/census_simple_request.c b/test/core/end2end/tests/census_simple_request.c index 64c0d12ca4a..95bbefe381c 100644 --- a/test/core/end2end/tests/census_simple_request.c +++ b/test/core/end2end/tests/census_simple_request.c @@ -109,9 +109,7 @@ static void test_body(grpc_end2end_test_fixture f) { GPR_ASSERT(c); tag(1); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); From 9c0307bd4fe5528f05b301d19ad2c72472b26b3f Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 16:49:24 -0800 Subject: [PATCH 006/143] Update test to grpc_call_invoke --- test/core/end2end/tests/thread_stress.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/end2end/tests/thread_stress.c b/test/core/end2end/tests/thread_stress.c index 4ce85df22e0..36da4950bbc 100644 --- a/test/core/end2end/tests/thread_stress.c +++ b/test/core/end2end/tests/thread_stress.c @@ -116,8 +116,8 @@ static void start_request() { gpr_slice_unref(slice); g_active_requests++; - GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(call, g_fixture.client_cq, - NULL, NULL, NULL, 0)); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(call, g_fixture.client_cq, + NULL, NULL, 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(call, NULL)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(call, buf, NULL, 0)); From fc84e7b28e4a506393381f874708fee5f5411c91 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 16:49:44 -0800 Subject: [PATCH 007/143] Fix tag matching bug --- test/core/end2end/tests/max_concurrent_streams.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index 57e271834c0..5a0d458d209 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -202,7 +202,8 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { GPR_ASSERT(ev->data.invoke_accepted == GRPC_OP_OK); /* The /alpha or /beta calls started above could be invoked (but NOT both); * check this here */ - live_call = (int)(gpr_intptr)ev->tag; + /* We'll get tag 303 or 403, we want 300, 400 */ + live_call = ((int)(gpr_intptr)ev->tag) - 3; grpc_event_finish(ev); cq_expect_server_rpc_new(v_server, &s1, tag(100), From 44974e6bd8f7498d19e258bc99a15c04bc79735d Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 16:50:09 -0800 Subject: [PATCH 008/143] Only request more data once stream is connected. Otherwise we can cause segfaults down in the client_channel. --- src/core/surface/call.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 6313fbce028..bb4a0e77c7f 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -654,7 +654,7 @@ grpc_call_error grpc_call_start_read(grpc_call *call, void *tag) { } else { call->read_tag = tag; call->have_read = 1; - request_more = 1; + request_more = call->received_start; } } else if (prq_is_empty(&call->prq) && call->received_finish) { finish_call(call); From f93d53e458743d561a93cb11541ca3b28580a3d8 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 17:01:34 -0800 Subject: [PATCH 009/143] Starting to fix C++ --- src/cpp/client/channel.cc | 12 ++---------- src/cpp/stream/stream_context.cc | 9 +-------- src/cpp/stream/stream_context.h | 1 - 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc index ddda8c22d6d..223151932ca 100644 --- a/src/cpp/client/channel.cc +++ b/src/cpp/client/channel.cc @@ -104,7 +104,6 @@ Status Channel::StartBlockingRpc(const RpcMethod& method, context->set_call(call); grpc_event* ev; void* finished_tag = reinterpret_cast(call); - void* invoke_tag = reinterpret_cast(call) + 1; void* metadata_read_tag = reinterpret_cast(call) + 2; void* write_tag = reinterpret_cast(call) + 3; void* halfclose_tag = reinterpret_cast(call) + 4; @@ -115,19 +114,12 @@ Status Channel::StartBlockingRpc(const RpcMethod& method, // add_metadata from context // // invoke - GPR_ASSERT(grpc_call_start_invoke(call, cq, invoke_tag, metadata_read_tag, + GPR_ASSERT(grpc_call_invoke(call, cq, metadata_read_tag, finished_tag, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); - ev = grpc_completion_queue_pluck(cq, invoke_tag, gpr_inf_future); - bool success = ev->data.invoke_accepted == GRPC_OP_OK; - grpc_event_finish(ev); - if (!success) { - GetFinalStatus(cq, finished_tag, &status); - return status; - } // write request grpc_byte_buffer* write_buffer = nullptr; - success = SerializeProto(request, &write_buffer); + bool success = SerializeProto(request, &write_buffer); if (!success) { grpc_call_cancel(call); status = diff --git a/src/cpp/stream/stream_context.cc b/src/cpp/stream/stream_context.cc index 7936a30dfd7..720f3e27fe3 100644 --- a/src/cpp/stream/stream_context.cc +++ b/src/cpp/stream/stream_context.cc @@ -80,17 +80,10 @@ void StreamContext::Start(bool buffered) { if (is_client_) { // TODO(yangg) handle metadata send path int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0; - grpc_call_error error = grpc_call_start_invoke(call(), cq(), invoke_tag(), + grpc_call_error error = grpc_call_invoke(call(), cq(), client_metadata_read_tag(), finished_tag(), flag); GPR_ASSERT(GRPC_CALL_OK == error); - grpc_event* invoke_ev = - grpc_completion_queue_pluck(cq(), invoke_tag(), gpr_inf_future); - if (invoke_ev->data.invoke_accepted != GRPC_OP_OK) { - peer_halfclosed_ = true; - self_halfclosed_ = true; - } - grpc_event_finish(invoke_ev); } else { // TODO(yangg) metadata needs to be added before accept // TODO(yangg) correctly set flag to accept diff --git a/src/cpp/stream/stream_context.h b/src/cpp/stream/stream_context.h index f70fe6daa34..c1a8d8ae522 100644 --- a/src/cpp/stream/stream_context.h +++ b/src/cpp/stream/stream_context.h @@ -76,7 +76,6 @@ class StreamContext : public StreamContextInterface { void* read_tag() { return reinterpret_cast(this) + 1; } void* write_tag() { return reinterpret_cast(this) + 2; } void* halfclose_tag() { return reinterpret_cast(this) + 3; } - void* invoke_tag() { return reinterpret_cast(this) + 4; } void* client_metadata_read_tag() { return reinterpret_cast(this) + 5; } grpc_call* call() { return call_; } grpc_completion_queue* cq() { return cq_; } From bf444937c613370fb88acbc4bceefba1772cc3c1 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 17:13:08 -0800 Subject: [PATCH 010/143] Fix ordering problem leading to flaky test --- test/core/end2end/tests/max_concurrent_streams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index 5a0d458d209..e58b31a7cc3 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -225,8 +225,8 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { /* first request is finished, we should be able to start the second */ cq_expect_finished_with_status(v_client, tag(live_call + 2), GRPC_STATUS_UNIMPLEMENTED, "xyz", NULL); - live_call = (live_call == 300) ? 400 : 300; cq_expect_finish_accepted(v_client, tag(live_call + 3), GRPC_OP_OK); + live_call = (live_call == 300) ? 400 : 300; cq_verify(v_client); GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(200))); From 5c35008ea1108f84e51071cc12b2a2ae0b7f3d98 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 17:26:27 -0800 Subject: [PATCH 011/143] Unify signal handling in C --- test/core/iomgr/tcp_posix_test.c | 3 --- test/core/transport/chttp2_transport_end2end_test.c | 3 --- test/core/util/test_config.c | 3 +++ 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/test/core/iomgr/tcp_posix_test.c b/test/core/iomgr/tcp_posix_test.c index 7fd2567cec5..6af3ded98b0 100644 --- a/test/core/iomgr/tcp_posix_test.c +++ b/test/core/iomgr/tcp_posix_test.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -491,8 +490,6 @@ static grpc_endpoint_test_config configs[] = { int main(int argc, char **argv) { grpc_test_init(argc, argv); grpc_iomgr_init(); - /* disable SIGPIPE */ - signal(SIGPIPE, SIG_IGN); run_tests(); grpc_endpoint_tests(configs[0]); grpc_iomgr_shutdown(); diff --git a/test/core/transport/chttp2_transport_end2end_test.c b/test/core/transport/chttp2_transport_end2end_test.c index 30d2a174401..8b0f9aa25b5 100644 --- a/test/core/transport/chttp2_transport_end2end_test.c +++ b/test/core/transport/chttp2_transport_end2end_test.c @@ -107,9 +107,6 @@ grpc_transport_test_config fixture_configs[] = { int main(int argc, char **argv) { size_t i; - /* disable SIGPIPE */ - signal(SIGPIPE, SIG_IGN); - grpc_test_init(argc, argv); grpc_iomgr_init(); diff --git a/test/core/util/test_config.c b/test/core/util/test_config.c index fc5de9bbefb..1a8f1a5f496 100644 --- a/test/core/util/test_config.c +++ b/test/core/util/test_config.c @@ -34,9 +34,12 @@ #include "test/core/util/test_config.h" #include +#include #include void grpc_test_init(int argc, char **argv) { + /* disable SIGPIPE */ + signal(SIGPIPE, SIG_IGN); /* seed rng with pid, so we don't end up with the same random numbers as a concurrently running test binary */ srand(getpid()); From 0a00a08c53c3017f1d6c3224c94b84b9c56ab197 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 17:26:46 -0800 Subject: [PATCH 012/143] Use C signal handling in C++ end2end --- test/cpp/end2end/end2end_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index e01a6efe82a..3a1da68e47f 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -34,6 +34,7 @@ #include #include +#include "test/core/util/test_config.h" #include "test/cpp/util/echo_duplicate.pb.h" #include "test/cpp/util/echo.pb.h" #include "src/cpp/util/time.h" @@ -435,6 +436,7 @@ TEST_F(End2endTest, BadCredentials) { } // namespace grpc int main(int argc, char** argv) { + grpc_test_init(argc, argv); grpc_init(); ::testing::InitGoogleTest(&argc, argv); int result = RUN_ALL_TESTS(); From cc19464dd5d51f0f38771e997838f9ad9a9c06ff Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 13 Jan 2015 17:45:03 -0800 Subject: [PATCH 013/143] Use the first received status as authoritative. So that later cancellations do not clobber status. --- src/core/surface/call.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/surface/call.c b/src/core/surface/call.c index bb4a0e77c7f..ae6fa6336b2 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -181,6 +181,7 @@ struct grpc_call { gpr_uint8 have_read; gpr_uint8 have_alarm; gpr_uint8 pending_writes_done; + gpr_uint8 got_status_code; /* The current outstanding read message tag (only valid if have_read == 1) */ void *read_tag; void *metadata_tag; @@ -230,6 +231,7 @@ grpc_call *grpc_call_create(grpc_channel *channel, call->have_write = 0; call->have_alarm = 0; call->received_metadata = 0; + call->got_status_code = 0; call->status_code = server_transport_data != NULL ? GRPC_STATUS_OK : GRPC_STATUS_UNKNOWN; call->status_details = NULL; @@ -872,15 +874,18 @@ void grpc_call_recv_metadata(grpc_call_element *elem, grpc_call_op *op) { grpc_call *call = CALL_FROM_TOP_ELEM(elem); grpc_mdelem *md = op->data.metadata; grpc_mdstr *key = md->key; + gpr_log(GPR_DEBUG, "call %p got metadata %s %s", call, grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)); if (key == grpc_channel_get_status_string(call->channel)) { - call->status_code = decode_status(md); + if (!call->got_status_code) { + call->status_code = decode_status(md); + call->got_status_code = 1; + } grpc_mdelem_unref(md); op->done_cb(op->user_data, GRPC_OP_OK); } else if (key == grpc_channel_get_message_string(call->channel)) { - if (call->status_details) { - grpc_mdstr_unref(call->status_details); + if (!call->status_details) { + call->status_details = grpc_mdstr_ref(md->value); } - call->status_details = grpc_mdstr_ref(md->value); grpc_mdelem_unref(md); op->done_cb(op->user_data, GRPC_OP_OK); } else { From e4fcd6aa0e3f1d1c759f55cb72966693d8fa774b Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 11:33:03 -0800 Subject: [PATCH 014/143] Build all targets for a config at once --- tools/run_tests/run_tests.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index bb25b38e570..84193a1337c 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -75,10 +75,8 @@ def _build_and_run(check_cancelled): if not jobset.run( (['make', '-j', '%d' % (multiprocessing.cpu_count() + 1), - target, - 'CONFIG=%s' % cfg] - for cfg in build_configs - for target in _MAKE_TEST_TARGETS), + 'CONFIG=%s' % cfg] + _MAKE_TEST_TARGETS + for cfg in build_configs), check_cancelled, maxjobs=1): sys.exit(1) From 2ba0dacbde45ed3a491d78134244b3175c556494 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 11:49:12 -0800 Subject: [PATCH 015/143] Update C++ code to set status via the C api. This prevents mismatches from breaking tests. --- include/grpc++/stream.h | 6 ++-- include/grpc++/stream_context_interface.h | 2 +- include/grpc/grpc.h | 8 +++++ src/core/surface/call.c | 33 +++++++++++++++----- src/cpp/stream/stream_context.cc | 38 ++++++++--------------- src/cpp/stream/stream_context.h | 4 +-- third_party/libevent | 1 + 7 files changed, 54 insertions(+), 38 deletions(-) create mode 160000 third_party/libevent diff --git a/include/grpc++/stream.h b/include/grpc++/stream.h index 49f88a6f135..b8982f4d93d 100644 --- a/include/grpc++/stream.h +++ b/include/grpc++/stream.h @@ -96,7 +96,7 @@ class ClientReader : public ClientStreamingInterface, virtual bool Read(R* msg) { return context_->Read(msg); } - virtual void Cancel() { context_->FinishStream(Status::Cancelled, true); } + virtual void Cancel() { context_->Cancel(); } virtual const Status& Wait() { return context_->Wait(); } @@ -122,7 +122,7 @@ class ClientWriter : public ClientStreamingInterface, virtual void WritesDone() { context_->Write(nullptr, true); } - virtual void Cancel() { context_->FinishStream(Status::Cancelled, true); } + virtual void Cancel() { context_->Cancel(); } // Read the final response and wait for the final status. virtual const Status& Wait() { @@ -165,7 +165,7 @@ class ClientReaderWriter : public ClientStreamingInterface, virtual void WritesDone() { context_->Write(nullptr, true); } - virtual void Cancel() { context_->FinishStream(Status::Cancelled, true); } + virtual void Cancel() { context_->Cancel(); } virtual const Status& Wait() { return context_->Wait(); } diff --git a/include/grpc++/stream_context_interface.h b/include/grpc++/stream_context_interface.h index 535c0048e65..a84119800b7 100644 --- a/include/grpc++/stream_context_interface.h +++ b/include/grpc++/stream_context_interface.h @@ -53,7 +53,7 @@ class StreamContextInterface { virtual bool Read(google::protobuf::Message* msg) = 0; virtual bool Write(const google::protobuf::Message* msg, bool is_last) = 0; virtual const Status& Wait() = 0; - virtual void FinishStream(const Status& status, bool send) = 0; + virtual void Cancel() = 0; virtual google::protobuf::Message* request() = 0; virtual google::protobuf::Message* response() = 0; diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index e51a1668e8b..53e0d056305 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -369,6 +369,14 @@ grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call, Can be called multiple times, from any thread. */ grpc_call_error grpc_call_cancel(grpc_call *call); +/* Called by clients to cancel an RPC on the server. + Can be called multiple times, from any thread. + If a status has not been received for the call, set it to the status code + and description passed in. + Importantly, this function does not send status nor description to the + remote endpoint. */ +grpc_call_error grpc_call_cancel_with_status(grpc_call *call, grpc_status_code status, const char *description); + /* Queue a byte buffer for writing. flags is a bit-field combination of the write flags defined above. A write with byte_buffer null is allowed, and will not send any bytes on the diff --git a/src/core/surface/call.c b/src/core/surface/call.c index ae6fa6336b2..87e3de88e2d 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -278,6 +278,19 @@ void grpc_call_destroy(grpc_call *c) { grpc_call_internal_unref(c); } +static void maybe_set_status_code(grpc_call *call, gpr_uint32 status) { + if (!call->got_status_code) { + call->status_code = status; + call->got_status_code = 1; + } +} + +static void maybe_set_status_details(grpc_call *call, grpc_mdstr *status) { + if (!call->status_details) { + call->status_details = grpc_mdstr_ref(status); + } +} + grpc_call_error grpc_call_cancel(grpc_call *c) { grpc_call_element *elem; grpc_call_op op; @@ -294,6 +307,17 @@ grpc_call_error grpc_call_cancel(grpc_call *c) { return GRPC_CALL_OK; } +grpc_call_error grpc_call_cancel_with_status(grpc_call *c, grpc_status_code status, const char *description) { + grpc_mdstr *details = description? grpc_mdstr_from_string(c->metadata_context, description) : NULL; + gpr_mu_lock(&c->read_mu); + maybe_set_status_code(c, status); + if (details) { + maybe_set_status_details(c, details); + } + gpr_mu_unlock(&c->read_mu); + return grpc_call_cancel(c); +} + void grpc_call_execute_op(grpc_call *call, grpc_call_op *op) { grpc_call_element *elem; GPR_ASSERT(op->dir == GRPC_CALL_DOWN); @@ -876,16 +900,11 @@ void grpc_call_recv_metadata(grpc_call_element *elem, grpc_call_op *op) { grpc_mdstr *key = md->key; gpr_log(GPR_DEBUG, "call %p got metadata %s %s", call, grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)); if (key == grpc_channel_get_status_string(call->channel)) { - if (!call->got_status_code) { - call->status_code = decode_status(md); - call->got_status_code = 1; - } + maybe_set_status_code(call, decode_status(md)); grpc_mdelem_unref(md); op->done_cb(op->user_data, GRPC_OP_OK); } else if (key == grpc_channel_get_message_string(call->channel)) { - if (!call->status_details) { - call->status_details = grpc_mdstr_ref(md->value); - } + maybe_set_status_details(call, md->value); grpc_mdelem_unref(md); op->done_cb(op->user_data, GRPC_OP_OK); } else { diff --git a/src/cpp/stream/stream_context.cc b/src/cpp/stream/stream_context.cc index 720f3e27fe3..aec226b23c0 100644 --- a/src/cpp/stream/stream_context.cc +++ b/src/cpp/stream/stream_context.cc @@ -105,9 +105,7 @@ bool StreamContext::Read(google::protobuf::Message* msg) { if (read_ev->data.read) { if (!DeserializeProto(read_ev->data.read, msg)) { ret = false; - FinishStream( - Status(StatusCode::DATA_LOSS, "Failed to parse incoming proto"), - true); + grpc_call_cancel_with_status(call(), GRPC_STATUS_DATA_LOSS, "Failed to parse incoming proto"); } } else { ret = false; @@ -125,9 +123,7 @@ bool StreamContext::Write(const google::protobuf::Message* msg, bool is_last) { if (msg) { grpc_byte_buffer* out_buf = nullptr; if (!SerializeProto(*msg, &out_buf)) { - FinishStream(Status(StatusCode::INVALID_ARGUMENT, - "Failed to serialize outgoing proto"), - true); + grpc_call_cancel_with_status(call(), GRPC_STATUS_INVALID_ARGUMENT, "Failed to serialize outgoing proto"); return false; } int flag = is_last ? GRPC_WRITE_BUFFER_HINT : 0; @@ -165,29 +161,21 @@ const Status& StreamContext::Wait() { grpc_event_finish(metadata_ev); // TODO(yangg) protect states by a mutex, including other places. if (!self_halfclosed_ || !peer_halfclosed_) { - FinishStream(Status::Cancelled, true); - } else { - grpc_event* finish_ev = - grpc_completion_queue_pluck(cq(), finished_tag(), gpr_inf_future); - GPR_ASSERT(finish_ev->type == GRPC_FINISHED); - final_status_ = Status( - static_cast(finish_ev->data.finished.status), - finish_ev->data.finished.details ? finish_ev->data.finished.details - : ""); - grpc_event_finish(finish_ev); - } - return final_status_; -} - -void StreamContext::FinishStream(const Status& status, bool send) { - if (send) { - grpc_call_cancel(call()); - } + Cancel(); + } grpc_event* finish_ev = grpc_completion_queue_pluck(cq(), finished_tag(), gpr_inf_future); GPR_ASSERT(finish_ev->type == GRPC_FINISHED); + final_status_ = Status( + static_cast(finish_ev->data.finished.status), + finish_ev->data.finished.details ? finish_ev->data.finished.details + : ""); grpc_event_finish(finish_ev); - final_status_ = status; + return final_status_; +} + +void StreamContext::Cancel() { + grpc_call_cancel(call()); } } // namespace grpc diff --git a/src/cpp/stream/stream_context.h b/src/cpp/stream/stream_context.h index c1a8d8ae522..d6bd7a23708 100644 --- a/src/cpp/stream/stream_context.h +++ b/src/cpp/stream/stream_context.h @@ -48,7 +48,7 @@ namespace grpc { class ClientContext; class RpcMethod; -class StreamContext : public StreamContextInterface { +class StreamContext final : public StreamContextInterface { public: StreamContext(const RpcMethod& method, ClientContext* context, const google::protobuf::Message* request, @@ -63,7 +63,7 @@ class StreamContext : public StreamContextInterface { bool Read(google::protobuf::Message* msg) override; bool Write(const google::protobuf::Message* msg, bool is_last) override; const Status& Wait() override; - void FinishStream(const Status& status, bool send) override; + void Cancel() override; google::protobuf::Message* request() override { return request_; } google::protobuf::Message* response() override { return result_; } diff --git a/third_party/libevent b/third_party/libevent new file mode 160000 index 00000000000..f7d92c63928 --- /dev/null +++ b/third_party/libevent @@ -0,0 +1 @@ +Subproject commit f7d92c63928a1460f3d99b9bc418bd3b686a0dca From 3f8bd048af276a40805e36a179a76af2e60f8399 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 12:48:54 -0800 Subject: [PATCH 016/143] Fix return behavior of run_tools.py If not running in forever mode, and a test fails, fail run_tests.py also. If running in forever mode and make fails, wait for the next run. --- tools/run_tests/run_tests.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 84193a1337c..428f6c41b4b 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -78,17 +78,20 @@ def _build_and_run(check_cancelled): 'CONFIG=%s' % cfg] + _MAKE_TEST_TARGETS for cfg in build_configs), check_cancelled, maxjobs=1): - sys.exit(1) + return 1 # run all the tests - jobset.run(( + if not jobset.run(( config.run_command(x) for config in run_configs for filt in filters for x in itertools.chain.from_iterable(itertools.repeat( glob.glob('bins/%s/%s_test' % ( config.build_config, filt)), - runs_per_test))), check_cancelled) + runs_per_test))), check_cancelled): + return 2 + + return 0 if forever: @@ -100,5 +103,5 @@ if forever: while not have_files_changed(): time.sleep(1) else: - _build_and_run(lambda: False) + sys.exit(_build_and_run(lambda: False)) From 09b637538f4e7e65d6d3994929f386a809317c21 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 13:02:09 -0800 Subject: [PATCH 017/143] clang-format --- include/grpc/grpc.h | 8 +++++--- src/core/surface/call.c | 17 +++++++++++------ src/cpp/client/channel.cc | 5 ++--- src/cpp/stream/stream_context.cc | 20 +++++++++----------- test/core/end2end/tests/thread_stress.c | 4 ++-- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 53e0d056305..8ab3d258732 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -370,12 +370,14 @@ grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call, grpc_call_error grpc_call_cancel(grpc_call *call); /* Called by clients to cancel an RPC on the server. - Can be called multiple times, from any thread. + Can be called multiple times, from any thread. If a status has not been received for the call, set it to the status code - and description passed in. + and description passed in. Importantly, this function does not send status nor description to the remote endpoint. */ -grpc_call_error grpc_call_cancel_with_status(grpc_call *call, grpc_status_code status, const char *description); +grpc_call_error grpc_call_cancel_with_status(grpc_call *call, + grpc_status_code status, + const char *description); /* Queue a byte buffer for writing. flags is a bit-field combination of the write flags defined above. diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 87e3de88e2d..24c1567db92 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -286,9 +286,9 @@ static void maybe_set_status_code(grpc_call *call, gpr_uint32 status) { } static void maybe_set_status_details(grpc_call *call, grpc_mdstr *status) { - if (!call->status_details) { - call->status_details = grpc_mdstr_ref(status); - } + if (!call->status_details) { + call->status_details = grpc_mdstr_ref(status); + } } grpc_call_error grpc_call_cancel(grpc_call *c) { @@ -307,8 +307,12 @@ grpc_call_error grpc_call_cancel(grpc_call *c) { return GRPC_CALL_OK; } -grpc_call_error grpc_call_cancel_with_status(grpc_call *c, grpc_status_code status, const char *description) { - grpc_mdstr *details = description? grpc_mdstr_from_string(c->metadata_context, description) : NULL; +grpc_call_error grpc_call_cancel_with_status(grpc_call *c, + grpc_status_code status, + const char *description) { + grpc_mdstr *details = + description ? grpc_mdstr_from_string(c->metadata_context, description) + : NULL; gpr_mu_lock(&c->read_mu); maybe_set_status_code(c, status); if (details) { @@ -898,7 +902,8 @@ void grpc_call_recv_metadata(grpc_call_element *elem, grpc_call_op *op) { grpc_call *call = CALL_FROM_TOP_ELEM(elem); grpc_mdelem *md = op->data.metadata; grpc_mdstr *key = md->key; - gpr_log(GPR_DEBUG, "call %p got metadata %s %s", call, grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)); + gpr_log(GPR_DEBUG, "call %p got metadata %s %s", call, + grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)); if (key == grpc_channel_get_status_string(call->channel)) { maybe_set_status_code(call, decode_status(md)); grpc_mdelem_unref(md); diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc index 223151932ca..f476f77a49e 100644 --- a/src/cpp/client/channel.cc +++ b/src/cpp/client/channel.cc @@ -114,9 +114,8 @@ Status Channel::StartBlockingRpc(const RpcMethod& method, // add_metadata from context // // invoke - GPR_ASSERT(grpc_call_invoke(call, cq, metadata_read_tag, - finished_tag, - GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); + GPR_ASSERT(grpc_call_invoke(call, cq, metadata_read_tag, finished_tag, + GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); // write request grpc_byte_buffer* write_buffer = nullptr; bool success = SerializeProto(request, &write_buffer); diff --git a/src/cpp/stream/stream_context.cc b/src/cpp/stream/stream_context.cc index aec226b23c0..ebe71594c02 100644 --- a/src/cpp/stream/stream_context.cc +++ b/src/cpp/stream/stream_context.cc @@ -80,9 +80,8 @@ void StreamContext::Start(bool buffered) { if (is_client_) { // TODO(yangg) handle metadata send path int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0; - grpc_call_error error = grpc_call_invoke(call(), cq(), - client_metadata_read_tag(), - finished_tag(), flag); + grpc_call_error error = grpc_call_invoke( + call(), cq(), client_metadata_read_tag(), finished_tag(), flag); GPR_ASSERT(GRPC_CALL_OK == error); } else { // TODO(yangg) metadata needs to be added before accept @@ -105,7 +104,8 @@ bool StreamContext::Read(google::protobuf::Message* msg) { if (read_ev->data.read) { if (!DeserializeProto(read_ev->data.read, msg)) { ret = false; - grpc_call_cancel_with_status(call(), GRPC_STATUS_DATA_LOSS, "Failed to parse incoming proto"); + grpc_call_cancel_with_status(call(), GRPC_STATUS_DATA_LOSS, + "Failed to parse incoming proto"); } } else { ret = false; @@ -123,7 +123,8 @@ bool StreamContext::Write(const google::protobuf::Message* msg, bool is_last) { if (msg) { grpc_byte_buffer* out_buf = nullptr; if (!SerializeProto(*msg, &out_buf)) { - grpc_call_cancel_with_status(call(), GRPC_STATUS_INVALID_ARGUMENT, "Failed to serialize outgoing proto"); + grpc_call_cancel_with_status(call(), GRPC_STATUS_INVALID_ARGUMENT, + "Failed to serialize outgoing proto"); return false; } int flag = is_last ? GRPC_WRITE_BUFFER_HINT : 0; @@ -162,20 +163,17 @@ const Status& StreamContext::Wait() { // TODO(yangg) protect states by a mutex, including other places. if (!self_halfclosed_ || !peer_halfclosed_) { Cancel(); - } + } grpc_event* finish_ev = grpc_completion_queue_pluck(cq(), finished_tag(), gpr_inf_future); GPR_ASSERT(finish_ev->type == GRPC_FINISHED); final_status_ = Status( static_cast(finish_ev->data.finished.status), - finish_ev->data.finished.details ? finish_ev->data.finished.details - : ""); + finish_ev->data.finished.details ? finish_ev->data.finished.details : ""); grpc_event_finish(finish_ev); return final_status_; } -void StreamContext::Cancel() { - grpc_call_cancel(call()); -} +void StreamContext::Cancel() { grpc_call_cancel(call()); } } // namespace grpc diff --git a/test/core/end2end/tests/thread_stress.c b/test/core/end2end/tests/thread_stress.c index 36da4950bbc..3b571ebd084 100644 --- a/test/core/end2end/tests/thread_stress.c +++ b/test/core/end2end/tests/thread_stress.c @@ -116,8 +116,8 @@ static void start_request() { gpr_slice_unref(slice); g_active_requests++; - GPR_ASSERT(GRPC_CALL_OK == grpc_call_invoke(call, g_fixture.client_cq, - NULL, NULL, 0)); + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_invoke(call, g_fixture.client_cq, NULL, NULL, 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(call, NULL)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(call, buf, NULL, 0)); From 92eb29a7b743d90abcdb77631b629214feee2352 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 14:12:17 -0800 Subject: [PATCH 018/143] Remove unused private field --- test/cpp/qps/server.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/cpp/qps/server.cc b/test/cpp/qps/server.cc index b2a4cde59fa..eb810b8d559 100644 --- a/test/cpp/qps/server.cc +++ b/test/cpp/qps/server.cc @@ -80,9 +80,6 @@ bool SetPayload(PayloadType type, int size, Payload* payload) { } class TestServiceImpl : public TestService::Service { - private: - int num_rpcs; - public: Status CollectServerStats(ServerContext* context, const StatsRequest*, ServerStats* response) { From 0d65c784db24aaa5b3db7996e971b1898cec3bf1 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 14:24:31 -0800 Subject: [PATCH 019/143] Filter out ssl tests in the sans: they dont work yet --- tools/run_tests/run_tests.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 428f6c41b4b..2c27cd777a2 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -29,13 +29,23 @@ class ValgrindConfig(object): return ['valgrind', binary] +# SanConfig: compile with CONFIG=config, filter out incompatible binaries +class SanConfig(object): + def __init__(self, config): + self.build_config = config + + def run_command(self, binary): + if '_ssl_' in binary: + return None + return [binary] + # different configurations we can run under _CONFIGS = { 'dbg': SimpleConfig('dbg'), 'opt': SimpleConfig('opt'), - 'tsan': SimpleConfig('tsan'), - 'msan': SimpleConfig('msan'), - 'asan': SimpleConfig('asan'), + 'tsan': SanConfig('tsan'), + 'msan': SanConfig('msan'), + 'asan': SanConfig('asan'), 'gcov': SimpleConfig('gcov'), 'valgrind': ValgrindConfig('dbg'), } @@ -81,14 +91,15 @@ def _build_and_run(check_cancelled): return 1 # run all the tests - if not jobset.run(( - config.run_command(x) - for config in run_configs - for filt in filters - for x in itertools.chain.from_iterable(itertools.repeat( - glob.glob('bins/%s/%s_test' % ( - config.build_config, filt)), - runs_per_test))), check_cancelled): + if not jobset.run(itertools.ifilter( + lambda x: x is not None, ( + config.run_command(x) + for config in run_configs + for filt in filters + for x in itertools.chain.from_iterable(itertools.repeat( + glob.glob('bins/%s/%s_test' % ( + config.build_config, filt)), + runs_per_test)))), check_cancelled): return 2 return 0 From 1371abd306e33f87b8016a587e3decc73b3fafdf Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 15:58:45 -0800 Subject: [PATCH 020/143] Fix refcounting bug --- src/core/surface/call.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 24c1567db92..6f8e0d5db7c 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -465,6 +465,8 @@ static void call_started(void *user_data, grpc_op_error error) { done_writes_done(call, error); } } + + grpc_call_internal_unref(call); } grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq, @@ -531,6 +533,7 @@ grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq, op.done_cb = call_started; op.data.start.pollset = grpc_cq_pollset(cq); op.user_data = call; + grpc_call_internal_ref(call); elem = CALL_ELEM_FROM_CALL(call, 0); elem->filter->call_op(elem, NULL, &op); From d63b7896cc6509924548da2236bcfe77140bdb3e Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 15:59:44 -0800 Subject: [PATCH 021/143] Helgrind support for run_tests.py Also allow maxjobs to be tweaked based upon which configs are being run, to prevent memory starvation. --- tools/run_tests/run_tests.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 2c27cd777a2..eee6f0136dd 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -15,6 +15,7 @@ import watch_dirs class SimpleConfig(object): def __init__(self, config): self.build_config = config + self.maxjobs = 32 * multiprocessing.cpu_count() def run_command(self, binary): return [binary] @@ -22,17 +23,20 @@ class SimpleConfig(object): # ValgrindConfig: compile with some CONFIG=config, but use valgrind to run class ValgrindConfig(object): - def __init__(self, config): + def __init__(self, config, tool): self.build_config = config + self.tool = tool + self.maxjobs = 4 * multiprocessing.cpu_count() def run_command(self, binary): - return ['valgrind', binary] + return ['valgrind', binary, '--tool=%s' % self.tool] # SanConfig: compile with CONFIG=config, filter out incompatible binaries class SanConfig(object): def __init__(self, config): self.build_config = config + self.maxjobs = 16 * multiprocessing.cpu_count() def run_command(self, binary): if '_ssl_' in binary: @@ -47,7 +51,8 @@ _CONFIGS = { 'msan': SanConfig('msan'), 'asan': SanConfig('asan'), 'gcov': SimpleConfig('gcov'), - 'valgrind': ValgrindConfig('dbg'), + 'memcheck': ValgrindConfig('dbg', 'memcheck'), + 'helgrind': ValgrindConfig('dbg', 'helgrind') } @@ -91,15 +96,18 @@ def _build_and_run(check_cancelled): return 1 # run all the tests - if not jobset.run(itertools.ifilter( - lambda x: x is not None, ( - config.run_command(x) - for config in run_configs - for filt in filters - for x in itertools.chain.from_iterable(itertools.repeat( - glob.glob('bins/%s/%s_test' % ( - config.build_config, filt)), - runs_per_test)))), check_cancelled): + if not jobset.run( + itertools.ifilter( + lambda x: x is not None, ( + config.run_command(x) + for config in run_configs + for filt in filters + for x in itertools.chain.from_iterable(itertools.repeat( + glob.glob('bins/%s/%s_test' % ( + config.build_config, filt)), + runs_per_test)))), + check_cancelled, + maxjobs=min(c.maxjobs for c in run_configs)): return 2 return 0 From 240d02c0760cdc67345c972b18d43aa1bfea9ccf Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 16:16:52 -0800 Subject: [PATCH 022/143] Echo test uses grpc_call_invoke --- test/core/echo/client.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/core/echo/client.c b/test/core/echo/client.c index 1905863e117..5581cd7582b 100644 --- a/test/core/echo/client.c +++ b/test/core/echo/client.c @@ -79,11 +79,8 @@ int main(int argc, char **argv) { GPR_ASSERT(argc == 2); channel = grpc_channel_create(argv[1], NULL); call = grpc_channel_create_call(channel, "/foo", "localhost", gpr_inf_future); - GPR_ASSERT(grpc_call_start_invoke(call, cq, (void *)1, (void *)1, (void *)1, + GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, 0) == GRPC_CALL_OK); - ev = grpc_completion_queue_next(cq, gpr_inf_future); - GPR_ASSERT(ev->data.invoke_accepted == GRPC_OP_OK); - grpc_event_finish(ev); start_write_next_slice(call, bytes_written, WRITE_SLICE_LENGTH); bytes_written += WRITE_SLICE_LENGTH; From ca757968110a4ed0004a85e2357a2f277a0a54a6 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 16:17:05 -0800 Subject: [PATCH 023/143] Fling test uses grpc_call_invoke --- test/core/fling/client.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/core/fling/client.c b/test/core/fling/client.c index cc661c34c5f..068565edc20 100644 --- a/test/core/fling/client.c +++ b/test/core/fling/client.c @@ -55,9 +55,8 @@ static void init_ping_pong_request() {} static void step_ping_pong_request() { call = grpc_channel_create_call(channel, "/Reflector/reflectUnary", "localhost", gpr_inf_future); - GPR_ASSERT(grpc_call_start_invoke(call, cq, (void *)1, (void *)1, (void *)1, + GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); - grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); GPR_ASSERT(grpc_call_start_write(call, the_buffer, (void *)1, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); @@ -66,7 +65,6 @@ static void step_ping_pong_request() { grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); - grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); grpc_call_destroy(call); call = NULL; } @@ -74,10 +72,9 @@ static void step_ping_pong_request() { static void init_ping_pong_stream() { call = grpc_channel_create_call(channel, "/Reflector/reflectStream", "localhost", gpr_inf_future); - GPR_ASSERT(grpc_call_start_invoke(call, cq, (void *)1, (void *)1, (void *)1, + GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, 0) == GRPC_CALL_OK); grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); - grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); } static void step_ping_pong_stream() { From 808632ed4b9757c79bfbb8917ec79ca4a4a614d0 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 16:17:21 -0800 Subject: [PATCH 024/143] Remove grpc_call_start_invoke --- include/grpc/grpc.h | 7 ------- src/core/surface/call.c | 15 --------------- 2 files changed, 22 deletions(-) diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 8ab3d258732..f9a28e6f69b 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -313,8 +313,6 @@ grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata, flags is a bit-field combination of the write flags defined above. REQUIRES: Can be called at most once per call. Can only be called on the client. - Produces a GRPC_INVOKE_ACCEPTED event with invoke_accepted_tag when the - call has been invoked (meaning bytes can start flowing to the wire). Produces a GRPC_CLIENT_METADATA_READ event with metadata_read_tag when the servers initial metadata has been read. Produces a GRPC_FINISHED event with finished_tag when the call has been @@ -323,11 +321,6 @@ grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata, grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq, void *metadata_read_tag, void *finished_tag, gpr_uint32 flags); -grpc_call_error grpc_call_start_invoke(grpc_call *call, - grpc_completion_queue *cq, - void *invoke_accepted_tag, - void *metadata_read_tag, - void *finished_tag, gpr_uint32 flags); /* DEPRECATED: users should use grpc_call_server_accept, and grpc_call_server_end_initial_metadata instead now. diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 6f8e0d5db7c..eb39044e939 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -382,21 +382,6 @@ static void finish_call(grpc_call *call) { elements, count); } -grpc_call_error grpc_call_start_invoke(grpc_call *call, - grpc_completion_queue *cq, - void *invoke_accepted_tag, - void *metadata_read_tag, - void *finished_tag, gpr_uint32 flags) { - grpc_call_error err = - grpc_call_invoke(call, cq, metadata_read_tag, finished_tag, flags); - if (err == GRPC_CALL_OK) { - grpc_cq_begin_op(call->cq, call, GRPC_INVOKE_ACCEPTED); - grpc_cq_end_invoke_accepted(call->cq, invoke_accepted_tag, call, do_nothing, - NULL, GRPC_OP_OK); - } - return err; -} - static void done_write(void *user_data, grpc_op_error error) { grpc_call *call = user_data; void *tag = call->write_tag; From 408c7383cc56cf9531657ea17b1f9fdc5c41891b Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 16:25:47 -0800 Subject: [PATCH 025/143] clang-format --- test/core/echo/client.c | 4 ++-- test/core/fling/client.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/core/echo/client.c b/test/core/echo/client.c index 5581cd7582b..2ad29df53ca 100644 --- a/test/core/echo/client.c +++ b/test/core/echo/client.c @@ -79,8 +79,8 @@ int main(int argc, char **argv) { GPR_ASSERT(argc == 2); channel = grpc_channel_create(argv[1], NULL); call = grpc_channel_create_call(channel, "/foo", "localhost", gpr_inf_future); - GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, - 0) == GRPC_CALL_OK); + GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, 0) == + GRPC_CALL_OK); start_write_next_slice(call, bytes_written, WRITE_SLICE_LENGTH); bytes_written += WRITE_SLICE_LENGTH; diff --git a/test/core/fling/client.c b/test/core/fling/client.c index 068565edc20..0e21a064017 100644 --- a/test/core/fling/client.c +++ b/test/core/fling/client.c @@ -56,7 +56,7 @@ static void step_ping_pong_request() { call = grpc_channel_create_call(channel, "/Reflector/reflectUnary", "localhost", gpr_inf_future); GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, - GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); + GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); GPR_ASSERT(grpc_call_start_write(call, the_buffer, (void *)1, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); @@ -72,8 +72,8 @@ static void step_ping_pong_request() { static void init_ping_pong_stream() { call = grpc_channel_create_call(channel, "/Reflector/reflectStream", "localhost", gpr_inf_future); - GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, - 0) == GRPC_CALL_OK); + GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1, 0) == + GRPC_CALL_OK); grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future)); } From 0fc50596c7803c2b33ed08d7c72c109f1ae76497 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 14 Jan 2015 17:49:59 -0800 Subject: [PATCH 026/143] Initialize start_ok --- src/core/surface/call.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/surface/call.c b/src/core/surface/call.c index eb39044e939..d8a34cf68dc 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -232,6 +232,7 @@ grpc_call *grpc_call_create(grpc_channel *channel, call->have_alarm = 0; call->received_metadata = 0; call->got_status_code = 0; + call->start_ok = 0; call->status_code = server_transport_data != NULL ? GRPC_STATUS_OK : GRPC_STATUS_UNKNOWN; call->status_details = NULL; From cc0994c8cbd302aa99483e42d6d2855b0cd59fd4 Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Thu, 15 Jan 2015 06:47:41 +0100 Subject: [PATCH 027/143] Factoring visual studio code into a buildgen plugin. --- templates/vsprojects/vs2013/grpc.sln.template | 28 +++----------- .../vsprojects/vs2013/vcxproj_defs.include | 24 +----------- tools/buildgen/plugins/generate_vsprojects.py | 38 +++++++++++++++++++ 3 files changed, 45 insertions(+), 45 deletions(-) create mode 100755 tools/buildgen/plugins/generate_vsprojects.py diff --git a/templates/vsprojects/vs2013/grpc.sln.template b/templates/vsprojects/vs2013/grpc.sln.template index fa32f1c52e2..18dfb1af423 100644 --- a/templates/vsprojects/vs2013/grpc.sln.template +++ b/templates/vsprojects/vs2013/grpc.sln.template @@ -11,31 +11,13 @@ MinimumVisualStudioVersion = 10.0.40219.1 ## Visual Studio uses GUIDs for project types ## http://msdn.microsoft.com/en-us/library/hb23x61k%28v=vs.80%29.aspx cpp_proj_type = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}" - -for lib in libs: - lib.is_library = True -for target in targets: - target.is_library = False - -projects = [] -projects.extend(libs) -projects.extend(targets) -projects = [project for project in projects if project.get('vs_project_guid', None)] - -## Exclude C++ projects for now -projects = [project for project in projects if not project.get('c++', False)] - -for p in projects: - p.deps = p.get('deps',[]) - -project_dict = dict([(p.name, p) for p in projects]) %>\ -% for project in projects: +% for project in vsprojects: Project("${cpp_proj_type}") = "${project.name}", "${project.name}.vcxproj", "${project.vs_project_guid}" - % if project.deps: + % if project.get('deps', None): ProjectSection(ProjectDependencies) = postProject - % for dep in project.deps: - ${project_dict[dep].vs_project_guid} = ${project_dict[dep].vs_project_guid} + % for dep in project.get('deps', []): + ${vsproject_dict[dep].vs_project_guid} = ${vsproject_dict[dep].vs_project_guid} % endfor EndProjectSection % endif @@ -51,7 +33,7 @@ Global Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution -% for project in projects: +% for project in vsprojects: ${project.vs_project_guid}.Debug|Win32.ActiveCfg = Debug|Win32 ${project.vs_project_guid}.Debug|Win32.Build.0 = Debug|Win32 ${project.vs_project_guid}.Release|Win32.ActiveCfg = Release|Win32 diff --git a/templates/vsprojects/vs2013/vcxproj_defs.include b/templates/vsprojects/vs2013/vcxproj_defs.include index ef12c62818e..e21230abb76 100644 --- a/templates/vsprojects/vs2013/vcxproj_defs.include +++ b/templates/vsprojects/vs2013/vcxproj_defs.include @@ -2,27 +2,7 @@ <%def name="get_configuration_type(is_library)">${'StaticLibrary' if is_library else 'Application'}\ <%def name="get_subsystem(is_library)">${'Windows' if is_library else 'Console'}\ <%def name="gen_project(name, libs, targets)">\ -<% -## TODO(jtattermusch): this code is c&p from the solution template -for lib in libs: - lib.is_library = True -for target in targets: - target.is_library = False - -projects = [] -projects.extend(libs) -projects.extend(targets) -projects = [project for project in projects if project.get('vs_project_guid', None)] - -## Exclude C++ projects for now -projects = [project for project in projects if not project.get('c++', False)] - -for p in projects: - p.deps = p.get('deps',[]) - -project_dict = dict([(p.name, p) for p in projects]) -%>\ -% for project in projects: +% for project in vsprojects: % if project.name == name: @@ -124,7 +104,7 @@ project_dict = dict([(p.name, p) for p in projects]) % for dep in project.deps: - ${project_dict[dep].vs_project_guid} + ${vsproject_dict[dep].vs_project_guid} % endfor diff --git a/tools/buildgen/plugins/generate_vsprojects.py b/tools/buildgen/plugins/generate_vsprojects.py new file mode 100755 index 00000000000..36eb8f7008e --- /dev/null +++ b/tools/buildgen/plugins/generate_vsprojects.py @@ -0,0 +1,38 @@ +"""Buildgen vsprojects plugin. + +This parses the list of libraries, and generates globals "vsprojects" +and "vsproject_dict", to be used by the visual studio generators. + +""" + + +import re + + +def mako_plugin(dictionary): + """The exported plugin code for generate_vsprojeccts + + We want to help the work of the visual studio generators. + + """ + + libs = dictionary.get('libs', []) + targets = dictionary.get('targets', []) + + for lib in libs: + lib['is_library'] = True + for target in targets: + target['is_library'] = False + + projects = [] + projects.extend(libs) + projects.extend(targets) + projects = [project for project in projects if project.get('vs_project_guid', None)] + + ## Exclude C++ projects for now + projects = [project for project in projects if not project.get('c++', False)] + + project_dict = dict([(p['name'], p) for p in projects]) + + dictionary['vsprojects'] = projects + dictionary['vsproject_dict'] = project_dict From 3548ed879acdf0b6cfcb3eb7d4b7cb716444923c Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 15 Jan 2015 15:38:45 -0800 Subject: [PATCH 028/143] Updated to new call.invoke API --- src/node/call.cc | 31 ++++------ src/node/call.h | 2 +- src/node/client.js | 99 +++++++++----------------------- src/node/node_grpc.cc | 2 - src/node/test/call_test.js | 42 ++++++-------- src/node/test/constant_test.js | 1 - src/node/test/end_to_end_test.js | 57 ++++++++---------- src/node/test/server_test.js | 41 ++++++------- 8 files changed, 101 insertions(+), 174 deletions(-) diff --git a/src/node/call.cc b/src/node/call.cc index b8ee1786a68..6434c2f0d5f 100644 --- a/src/node/call.cc +++ b/src/node/call.cc @@ -78,8 +78,8 @@ void Call::Init(Handle exports) { tpl->InstanceTemplate()->SetInternalFieldCount(1); NanSetPrototypeTemplate(tpl, "addMetadata", FunctionTemplate::New(AddMetadata)->GetFunction()); - NanSetPrototypeTemplate(tpl, "startInvoke", - FunctionTemplate::New(StartInvoke)->GetFunction()); + NanSetPrototypeTemplate(tpl, "invoke", + FunctionTemplate::New(Invoke)->GetFunction()); NanSetPrototypeTemplate(tpl, "serverAccept", FunctionTemplate::New(ServerAccept)->GetFunction()); NanSetPrototypeTemplate( @@ -203,37 +203,30 @@ NAN_METHOD(Call::AddMetadata) { NanReturnUndefined(); } -NAN_METHOD(Call::StartInvoke) { +NAN_METHOD(Call::Invoke) { NanScope(); if (!HasInstance(args.This())) { - return NanThrowTypeError("startInvoke can only be called on Call objects"); + return NanThrowTypeError("invoke can only be called on Call objects"); } if (!args[0]->IsFunction()) { - return NanThrowTypeError("StartInvoke's first argument must be a function"); + return NanThrowTypeError("invoke's first argument must be a function"); } if (!args[1]->IsFunction()) { - return NanThrowTypeError( - "StartInvoke's second argument must be a function"); - } - if (!args[2]->IsFunction()) { - return NanThrowTypeError("StartInvoke's third argument must be a function"); + return NanThrowTypeError("invoke's second argument must be a function"); } - if (!args[3]->IsUint32()) { - return NanThrowTypeError( - "StartInvoke's fourth argument must be integer flags"); + if (!args[2]->IsUint32()) { + return NanThrowTypeError("invoke's third argument must be integer flags"); } Call *call = ObjectWrap::Unwrap(args.This()); unsigned int flags = args[3]->Uint32Value(); - grpc_call_error error = grpc_call_start_invoke( + grpc_call_error error = grpc_call_invoke( call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), - CreateTag(args[0], args.This()), CreateTag(args[1], args.This()), - CreateTag(args[2], args.This()), flags); + CreateTag(args[0], args.This()), CreateTag(args[1], args.This()), flags); if (error == GRPC_CALL_OK) { CompletionQueueAsyncWorker::Next(); CompletionQueueAsyncWorker::Next(); - CompletionQueueAsyncWorker::Next(); } else { - return NanThrowError("startInvoke failed", error); + return NanThrowError("invoke failed", error); } NanReturnUndefined(); } @@ -281,7 +274,7 @@ NAN_METHOD(Call::ServerEndInitialMetadata) { NAN_METHOD(Call::Cancel) { NanScope(); if (!HasInstance(args.This())) { - return NanThrowTypeError("startInvoke can only be called on Call objects"); + return NanThrowTypeError("cancel can only be called on Call objects"); } Call *call = ObjectWrap::Unwrap(args.This()); grpc_call_error error = grpc_call_cancel(call->wrapped_call); diff --git a/src/node/call.h b/src/node/call.h index 55a6fc65b84..1924a1bf428 100644 --- a/src/node/call.h +++ b/src/node/call.h @@ -61,7 +61,7 @@ class Call : public ::node::ObjectWrap { static NAN_METHOD(New); static NAN_METHOD(AddMetadata); - static NAN_METHOD(StartInvoke); + static NAN_METHOD(Invoke); static NAN_METHOD(ServerAccept); static NAN_METHOD(ServerEndInitialMetadata); static NAN_METHOD(Cancel); diff --git a/src/node/client.js b/src/node/client.js index edaa115d0fc..24f69d8bfde 100644 --- a/src/node/client.js +++ b/src/node/client.js @@ -50,101 +50,53 @@ util.inherits(GrpcClientStream, Duplex); function GrpcClientStream(call, options) { Duplex.call(this, options); var self = this; - // Indicates that we can start reading and have not received a null read - var can_read = false; + var finished = false; // Indicates that a read is currently pending var reading = false; - // Indicates that we can call startWrite - var can_write = false; // Indicates that a write is currently pending var writing = false; this._call = call; /** - * Callback to handle receiving a READ event. Pushes the data from that event - * onto the read queue and starts reading again if applicable. - * @param {grpc.Event} event The READ event object + * Callback to be called when a READ event is received. Pushes the data onto + * the read queue and starts reading again if applicable + * @param {grpc.Event} event READ event object */ function readCallback(event) { + if (finished) { + self.push(null); + return; + } var data = event.data; - if (self.push(data)) { - if (data == null) { - // Disable starting to read after null read was received - can_read = false; - reading = false; - } else { - call.startRead(readCallback); - } + if (self.push(data) && data != null) { + self._call.startRead(readCallback); } else { - // Indicate that reading can be resumed by calling startReading reading = false; } - }; - /** - * Initiate a read, which continues until self.push returns false (indicating - * that reading should be paused) or data is null (indicating that there is no - * more data to read). - */ - function startReading() { - call.startRead(readCallback); - } - // TODO(mlumish): possibly change queue implementation due to shift slowness - var write_queue = []; - /** - * Write the next chunk of data in the write queue if there is one. Otherwise - * indicate that there is no pending write. When the write succeeds, this - * function is called again. - */ - function writeNext() { - if (write_queue.length > 0) { - writing = true; - var next = write_queue.shift(); - var writeCallback = function(event) { - next.callback(); - writeNext(); - }; - call.startWrite(next.chunk, writeCallback, 0); - } else { - writing = false; - } } - call.startInvoke(function(event) { - can_read = true; - can_write = true; - startReading(); - writeNext(); - }, function(event) { + call.invoke(function(event) { self.emit('metadata', event.data); }, function(event) { + finished = true; self.emit('status', event.data); }, 0); this.on('finish', function() { call.writesDone(function() {}); }); /** - * Indicate that reads should start, and start them if the INVOKE_ACCEPTED - * event has been received. + * Start reading if there is not already a pending read. Reading will + * continue until self.push returns false (indicating reads should slow + * down) or the read data is null (indicating that there is no more data). */ - this._enableRead = function() { - if (!reading) { - reading = true; - if (can_read) { - startReading(); + this.startReading = function() { + if (finished) { + self.push(null); + } else { + if (!reading) { + reading = true; + self._call.startRead(readCallback); } } }; - /** - * Push the chunk onto the write queue, and write from the write queue if - * there is not a pending write - * @param {Buffer} chunk The chunk of data to write - * @param {function(Error=)} callback The callback to call when the write - * completes - */ - this._tryWrite = function(chunk, callback) { - write_queue.push({chunk: chunk, callback: callback}); - if (can_write && !writing) { - writeNext(); - } - }; } /** @@ -153,7 +105,7 @@ function GrpcClientStream(call, options) { * @param {number} size Ignored */ GrpcClientStream.prototype._read = function(size) { - this._enableRead(); + this.startReading(); }; /** @@ -164,7 +116,10 @@ GrpcClientStream.prototype._read = function(size) { * @param {function(Error=)} callback Ignored */ GrpcClientStream.prototype._write = function(chunk, encoding, callback) { - this._tryWrite(chunk, callback); + var self = this; + self._call.startWrite(chunk, function(event) { + callback(); + }, 0); }; /** diff --git a/src/node/node_grpc.cc b/src/node/node_grpc.cc index acee0386d20..bc1dfaf8996 100644 --- a/src/node/node_grpc.cc +++ b/src/node/node_grpc.cc @@ -148,8 +148,6 @@ void InitCompletionTypeConstants(Handle exports) { completion_type->Set(NanNew("QUEUE_SHUTDOWN"), QUEUE_SHUTDOWN); Handle READ(NanNew(GRPC_READ)); completion_type->Set(NanNew("READ"), READ); - Handle INVOKE_ACCEPTED(NanNew(GRPC_INVOKE_ACCEPTED)); - completion_type->Set(NanNew("INVOKE_ACCEPTED"), INVOKE_ACCEPTED); Handle WRITE_ACCEPTED(NanNew(GRPC_WRITE_ACCEPTED)); completion_type->Set(NanNew("WRITE_ACCEPTED"), WRITE_ACCEPTED); Handle FINISH_ACCEPTED(NanNew(GRPC_FINISH_ACCEPTED)); diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js index e6dc9664f10..6e52ec89bd3 100644 --- a/src/node/test/call_test.js +++ b/src/node/test/call_test.js @@ -118,12 +118,11 @@ describe('call', function() { call.addMetadata(5); }, TypeError); }); - it('should fail if startInvoke was already called', function(done) { + it('should fail if invoke was already called', function(done) { var call = new grpc.Call(channel, 'method', getDeadline(1)); - call.startInvoke(function() {}, - function() {}, - function() {done();}, - 0); + call.invoke(function() {}, + function() {done();}, + 0); assert.throws(function() { call.addMetadata({'key' : 'key', 'value' : new Buffer('value') }); }, function(err) { @@ -133,32 +132,26 @@ describe('call', function() { call.cancel(); }); }); - describe('startInvoke', function() { - it('should fail with fewer than 4 arguments', function() { + describe('invoke', function() { + it('should fail with fewer than 3 arguments', function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.throws(function() { - call.startInvoke(); + call.invoke(); }, TypeError); assert.throws(function() { - call.startInvoke(function() {}); + call.invoke(function() {}); }, TypeError); assert.throws(function() { - call.startInvoke(function() {}, - function() {}); - }, TypeError); - assert.throws(function() { - call.startInvoke(function() {}, - function() {}, - function() {}); + call.invoke(function() {}, + function() {}); }, TypeError); }); - it('should work with 3 args and an int', function(done) { + it('should work with 2 args and an int', function(done) { assert.doesNotThrow(function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); - call.startInvoke(function() {}, - function() {}, - function() {done();}, - 0); + call.invoke(function() {}, + function() {done();}, + 0); // Cancel to speed up the test call.cancel(); }); @@ -166,12 +159,11 @@ describe('call', function() { it('should reject incorrectly typed arguments', function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.throws(function() { - call.startInvoke(0, 0, 0, 0); + call.invoke(0, 0, 0); }, TypeError); assert.throws(function() { - call.startInvoke(function() {}, - function() {}, - function() {}, 'test'); + call.invoke(function() {}, + function() {}, 'test'); }); }); }); diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js index f65eea3cffe..0138a552265 100644 --- a/src/node/test/constant_test.js +++ b/src/node/test/constant_test.js @@ -94,7 +94,6 @@ var opErrorNames = [ var completionTypeNames = [ 'QUEUE_SHUTDOWN', 'READ', - 'INVOKE_ACCEPTED', 'WRITE_ACCEPTED', 'FINISH_ACCEPTED', 'CLIENT_METADATA_READ', diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js index 40bb5f3bbde..b9e7bb56912 100644 --- a/src/node/test/end_to_end_test.js +++ b/src/node/test/end_to_end_test.js @@ -72,16 +72,7 @@ describe('end-to-end', function() { var call = new grpc.Call(channel, 'dummy_method', deadline); - call.startInvoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.INVOKE_ACCEPTED); - - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - }); - },function(event) { + call.invoke(function(event) { assert.strictEqual(event.type, grpc.completionType.CLIENT_METADATA_READ); },function(event) { @@ -91,6 +82,11 @@ describe('end-to-end', function() { assert.strictEqual(status.details, status_text); done(); }, 0); + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + }); server.start(); server.requestCall(function(event) { @@ -131,28 +127,7 @@ describe('end-to-end', function() { var call = new grpc.Call(channel, 'dummy_method', deadline); - call.startInvoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.INVOKE_ACCEPTED); - call.startWrite( - new Buffer(req_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); - call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), reply_text); - done(); - }); - },function(event) { + call.invoke(function(event) { assert.strictEqual(event.type, grpc.completionType.CLIENT_METADATA_READ); done(); @@ -163,6 +138,24 @@ describe('end-to-end', function() { assert.strictEqual(status.details, status_text); done(); }, 0); + call.startWrite( + new Buffer(req_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }, 0); + call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), reply_text); + done(); + }); server.start(); server.requestCall(function(event) { diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js index 79f7b329485..9978853bc23 100644 --- a/src/node/test/server_test.js +++ b/src/node/test/server_test.js @@ -83,28 +83,7 @@ describe('echo server', function() { var call = new grpc.Call(channel, 'echo', deadline); - call.startInvoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.INVOKE_ACCEPTED); - call.startWrite( - new Buffer(req_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); - call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), req_text); - done(); - }); - },function(event) { + call.invoke(function(event) { assert.strictEqual(event.type, grpc.completionType.CLIENT_METADATA_READ); done(); @@ -116,6 +95,24 @@ describe('echo server', function() { server.shutdown(); done(); }, 0); + call.startWrite( + new Buffer(req_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }, 0); + call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), req_text); + done(); + }); }); }); }); From 1d89452e1128a0bfe7324ddaa5fcd26256d23b73 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 16 Jan 2015 09:57:05 -0800 Subject: [PATCH 029/143] Switched to new grpc_call_invoke API --- src/php/ext/grpc/call.c | 38 +++++++++---------- src/php/ext/grpc/php_grpc.c | 7 ++-- src/php/lib/Grpc/ActiveCall.php | 3 -- src/php/tests/unit_tests/CallTest.php | 6 +-- src/php/tests/unit_tests/EndToEndTest.php | 24 +++--------- .../tests/unit_tests/SecureEndToEndTest.php | 24 +++--------- 6 files changed, 35 insertions(+), 67 deletions(-) diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index c01af34e958..b171c9c176b 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -224,27 +224,25 @@ PHP_METHOD(Call, add_metadata) { /** * Invoke the RPC. Starts sending metadata and request headers over the wire * @param CompletionQueue $queue The completion queue to use with this call - * @param long $invoke_accepted_tag The tag to associate with this invocation * @param long $metadata_tag The tag to associate with returned metadata * @param long $finished_tag The tag to associate with the finished event * @param long $flags A bitwise combination of the Grpc\WRITE_* constants * (optional) * @return Void */ -PHP_METHOD(Call, start_invoke) { +PHP_METHOD(Call, invoke) { grpc_call_error error_code; long tag1; long tag2; - long tag3; zval *queue_obj; long flags = 0; - /* "Olll|l" == 1 Object, 3 mandatory longs, 1 optional long */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Olll|l", &queue_obj, - grpc_ce_completion_queue, &tag1, &tag2, &tag3, + /* "Oll|l" == 1 Object, 3 mandatory longs, 1 optional long */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oll|l", &queue_obj, + grpc_ce_completion_queue, &tag1, &tag2, &flags) == FAILURE) { zend_throw_exception( spl_ce_InvalidArgumentException, - "start_invoke needs a CompletionQueue, 3 longs, and an optional long", + "invoke needs a CompletionQueue, 2 longs, and an optional long", 1 TSRMLS_CC); return; } @@ -254,10 +252,9 @@ PHP_METHOD(Call, start_invoke) { wrapped_grpc_completion_queue *queue = (wrapped_grpc_completion_queue *)zend_object_store_get_object( queue_obj TSRMLS_CC); - error_code = - grpc_call_start_invoke(call->wrapped, queue->wrapped, (void *)tag1, - (void *)tag2, (void *)tag3, (gpr_uint32)flags); - MAYBE_THROW_CALL_ERROR(start_invoke, error_code); + error_code = grpc_call_invoke(call->wrapped, queue->wrapped, (void *)tag1, + (void *)tag2, (gpr_uint32)flags); + MAYBE_THROW_CALL_ERROR(invoke, error_code); } /** @@ -423,16 +420,15 @@ PHP_METHOD(Call, start_read) { static zend_function_entry call_methods[] = { PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(Call, server_accept, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, server_end_initial_metadata, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, add_metadata, NULL, ZEND_ACC_PUBLIC) PHP_ME( - Call, cancel, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, start_invoke, NULL, ZEND_ACC_PUBLIC) PHP_ME( - Call, start_read, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, start_write, NULL, ZEND_ACC_PUBLIC) PHP_ME( - Call, start_write_status, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, writes_done, NULL, ZEND_ACC_PUBLIC) - PHP_FE_END}; + PHP_ME(Call, server_accept, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, server_end_initial_metadata, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, add_metadata, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, cancel, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, invoke, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_read, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_write, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_write_status, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, writes_done, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; void grpc_init_call(TSRMLS_D) { zend_class_entry ce; diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c index c1042293aa4..492ac067398 100644 --- a/src/php/ext/grpc/php_grpc.c +++ b/src/php/ext/grpc/php_grpc.c @@ -33,7 +33,8 @@ zend_module_entry grpc_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif - "grpc", grpc_functions, PHP_MINIT(grpc), PHP_MSHUTDOWN(grpc), NULL, NULL, + "grpc", grpc_functions, PHP_MINIT(grpc), + PHP_MSHUTDOWN(grpc), NULL, NULL, PHP_MINFO(grpc), #if ZEND_MODULE_API_NO >= 20010901 PHP_GRPC_VERSION, @@ -106,11 +107,9 @@ PHP_MINIT_FUNCTION(grpc) { /* Register completion type constants */ REGISTER_LONG_CONSTANT("Grpc\\QUEUE_SHUTDOWN", GRPC_QUEUE_SHUTDOWN, CONST_CS); REGISTER_LONG_CONSTANT("Grpc\\READ", GRPC_READ, CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\INVOKE_ACCEPTED", GRPC_INVOKE_ACCEPTED, - CONST_CS); - REGISTER_LONG_CONSTANT("Grpc\\WRITE_ACCEPTED", GRPC_WRITE_ACCEPTED, CONST_CS); REGISTER_LONG_CONSTANT("Grpc\\FINISH_ACCEPTED", GRPC_FINISH_ACCEPTED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\WRITE_ACCEPTED", GRPC_WRITE_ACCEPTED, CONST_CS); REGISTER_LONG_CONSTANT("Grpc\\CLIENT_METADATA_READ", GRPC_CLIENT_METADATA_READ, CONST_CS); REGISTER_LONG_CONSTANT("Grpc\\FINISHED", GRPC_FINISHED, CONST_CS); diff --git a/src/php/lib/Grpc/ActiveCall.php b/src/php/lib/Grpc/ActiveCall.php index aa66dbb8488..836a4b09e3b 100755 --- a/src/php/lib/Grpc/ActiveCall.php +++ b/src/php/lib/Grpc/ActiveCall.php @@ -29,11 +29,8 @@ class ActiveCall { // Invoke the call. $this->call->start_invoke($this->completion_queue, - INVOKE_ACCEPTED, CLIENT_METADATA_READ, FINISHED, 0); - $this->completion_queue->pluck(INVOKE_ACCEPTED, - Timeval::inf_future()); $metadata_event = $this->completion_queue->pluck(CLIENT_METADATA_READ, Timeval::inf_future()); $this->metadata = $metadata_event->data; diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php index 253052a0382..795831cb65c 100755 --- a/src/php/tests/unit_tests/CallTest.php +++ b/src/php/tests/unit_tests/CallTest.php @@ -19,10 +19,10 @@ class CallTest extends PHPUnit_Framework_TestCase{ /** * @expectedException LogicException * @expectedExceptionCode Grpc\CALL_ERROR_INVALID_FLAGS - * @expectedExceptionMessage start_invoke + * @expectedExceptionMessage invoke */ - public function testStartInvokeRejectsBadFlags() { - $this->call->start_invoke($this->cq, 0, 0, 0, 0xDEADBEEF); + public function testInvokeRejectsBadFlags() { + $this->call->invoke($this->cq, 0, 0, 0xDEADBEEF); } /** diff --git a/src/php/tests/unit_tests/EndToEndTest.php b/src/php/tests/unit_tests/EndToEndTest.php index 3818f9531c9..78c5e9f93bf 100755 --- a/src/php/tests/unit_tests/EndToEndTest.php +++ b/src/php/tests/unit_tests/EndToEndTest.php @@ -25,18 +25,12 @@ class EndToEndTest extends PHPUnit_Framework_TestCase{ $deadline); $tag = 1; $this->assertEquals(Grpc\CALL_OK, - $call->start_invoke($this->client_queue, - $tag, - $tag, - $tag)); + $call->invoke($this->client_queue, + $tag, + $tag)); $server_tag = 2; - // the client invocation was accepted - $event = $this->client_queue->next($deadline); - $this->assertNotNull($event); - $this->assertEquals(Grpc\INVOKE_ACCEPTED, $event->type); - $call->writes_done($tag); $event = $this->client_queue->next($deadline); $this->assertNotNull($event); @@ -103,18 +97,12 @@ class EndToEndTest extends PHPUnit_Framework_TestCase{ $deadline); $tag = 1; $this->assertEquals(Grpc\CALL_OK, - $call->start_invoke($this->client_queue, - $tag, - $tag, - $tag)); + $call->invoke($this->client_queue, + $tag, + $tag)); $server_tag = 2; - // the client invocation was accepted - $event = $this->client_queue->next($deadline); - $this->assertNotNull($event); - $this->assertEquals(Grpc\INVOKE_ACCEPTED, $event->type); - // the client writes $call->start_write($req_text, $tag); $event = $this->client_queue->next($deadline); diff --git a/src/php/tests/unit_tests/SecureEndToEndTest.php b/src/php/tests/unit_tests/SecureEndToEndTest.php index c562a821a48..7c3ad8a07c6 100755 --- a/src/php/tests/unit_tests/SecureEndToEndTest.php +++ b/src/php/tests/unit_tests/SecureEndToEndTest.php @@ -37,17 +37,11 @@ class SecureEndToEndTest extends PHPUnit_Framework_TestCase{ $deadline); $tag = 1; $this->assertEquals(Grpc\CALL_OK, - $call->start_invoke($this->client_queue, - $tag, - $tag, - $tag)); + $call->invoke($this->client_queue, + $tag, + $tag)); $server_tag = 2; - // the client invocation was accepted - $event = $this->client_queue->next($deadline); - $this->assertNotNull($event); - $this->assertEquals(Grpc\INVOKE_ACCEPTED, $event->type); - $call->writes_done($tag); $event = $this->client_queue->next($deadline); $this->assertNotNull($event); @@ -113,18 +107,12 @@ class SecureEndToEndTest extends PHPUnit_Framework_TestCase{ $deadline); $tag = 1; $this->assertEquals(Grpc\CALL_OK, - $call->start_invoke($this->client_queue, - $tag, - $tag, - $tag)); + $call->invoke($this->client_queue, + $tag, + $tag)); $server_tag = 2; - // the client invocation was accepted - $event = $this->client_queue->next($deadline); - $this->assertNotNull($event); - $this->assertEquals(Grpc\INVOKE_ACCEPTED, $event->type); - // the client writes $call->start_write($req_text, $tag); $event = $this->client_queue->next($deadline); From 5430312c718de269a294b57da52a78ab6e0ca02d Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 16 Jan 2015 09:57:19 -0800 Subject: [PATCH 030/143] Ran different clang-format --- src/php/ext/grpc/channel.c | 2 +- src/php/ext/grpc/completion_queue.c | 16 ++++++++-------- src/php/ext/grpc/credentials.c | 13 ++++++------- src/php/ext/grpc/server.c | 8 ++++---- src/php/ext/grpc/server_credentials.c | 4 ++-- src/php/ext/grpc/timeval.c | 24 ++++++++++-------------- 6 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c index f0e4153b22a..2ab229f5e67 100644 --- a/src/php/ext/grpc/channel.c +++ b/src/php/ext/grpc/channel.c @@ -155,7 +155,7 @@ PHP_METHOD(Channel, close) { static zend_function_entry channel_methods[] = { PHP_ME(Channel, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; + PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; void grpc_init_channel(TSRMLS_D) { zend_class_entry ce; diff --git a/src/php/ext/grpc/completion_queue.c b/src/php/ext/grpc/completion_queue.c index 9785eab8cc1..3a93bfcff76 100644 --- a/src/php/ext/grpc/completion_queue.c +++ b/src/php/ext/grpc/completion_queue.c @@ -63,8 +63,8 @@ zend_object_value create_wrapped_grpc_completion_queue( */ PHP_METHOD(CompletionQueue, __construct) { wrapped_grpc_completion_queue *queue = - (wrapped_grpc_completion_queue *)zend_object_store_get_object( - getThis() TSRMLS_CC); + (wrapped_grpc_completion_queue *)zend_object_store_get_object(getThis() + TSRMLS_CC); queue->wrapped = grpc_completion_queue_create(); } @@ -86,8 +86,8 @@ PHP_METHOD(CompletionQueue, next) { return; } wrapped_grpc_completion_queue *completion_queue = - (wrapped_grpc_completion_queue *)zend_object_store_get_object( - getThis() TSRMLS_CC); + (wrapped_grpc_completion_queue *)zend_object_store_get_object(getThis() + TSRMLS_CC); wrapped_grpc_timeval *wrapped_timeout = (wrapped_grpc_timeval *)zend_object_store_get_object(timeout TSRMLS_CC); grpc_event *event = grpc_completion_queue_next(completion_queue->wrapped, @@ -109,8 +109,8 @@ PHP_METHOD(CompletionQueue, pluck) { "pluck needs a long and a Timeval", 1 TSRMLS_CC); } wrapped_grpc_completion_queue *completion_queue = - (wrapped_grpc_completion_queue *)zend_object_store_get_object( - getThis() TSRMLS_CC); + (wrapped_grpc_completion_queue *)zend_object_store_get_object(getThis() + TSRMLS_CC); wrapped_grpc_timeval *wrapped_timeout = (wrapped_grpc_timeval *)zend_object_store_get_object(timeout TSRMLS_CC); grpc_event *event = grpc_completion_queue_pluck( @@ -124,8 +124,8 @@ PHP_METHOD(CompletionQueue, pluck) { static zend_function_entry completion_queue_methods[] = { PHP_ME(CompletionQueue, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(CompletionQueue, next, NULL, ZEND_ACC_PUBLIC) - PHP_ME(CompletionQueue, pluck, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; + PHP_ME(CompletionQueue, next, NULL, ZEND_ACC_PUBLIC) + PHP_ME(CompletionQueue, pluck, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; void grpc_init_completion_queue(TSRMLS_D) { zend_class_entry ce; diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c index f486272531d..2a83d1cbc1e 100644 --- a/src/php/ext/grpc/credentials.c +++ b/src/php/ext/grpc/credentials.c @@ -151,13 +151,12 @@ PHP_METHOD(Credentials, createFake) { static zend_function_entry credentials_methods[] = { PHP_ME(Credentials, createDefault, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Credentials, createSsl, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Credentials, createComposite, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Credentials, createGce, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Credentials, createFake, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; + PHP_ME(Credentials, createSsl, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createComposite, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createGce, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createFake, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_FE_END}; void grpc_init_credentials(TSRMLS_D) { zend_class_entry ce; diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c index f4843757120..38777f3d541 100644 --- a/src/php/ext/grpc/server.c +++ b/src/php/ext/grpc/server.c @@ -176,10 +176,10 @@ PHP_METHOD(Server, start) { static zend_function_entry server_methods[] = { PHP_ME(Server, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(Server, request_call, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, add_http2_port, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, add_secure_http2_port, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; + PHP_ME(Server, request_call, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, add_http2_port, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, add_secure_http2_port, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; void grpc_init_server(TSRMLS_D) { zend_class_entry ce; diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c index 5b9ab3390d7..1f8e58aa4d1 100644 --- a/src/php/ext/grpc/server_credentials.c +++ b/src/php/ext/grpc/server_credentials.c @@ -102,8 +102,8 @@ PHP_METHOD(ServerCredentials, createFake) { static zend_function_entry server_credentials_methods[] = { PHP_ME(ServerCredentials, createSsl, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(ServerCredentials, createFake, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; + PHP_ME(ServerCredentials, createFake, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; void grpc_init_server_credentials(TSRMLS_D) { zend_class_entry ce; diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c index a5508115e47..cbbbf379156 100644 --- a/src/php/ext/grpc/timeval.c +++ b/src/php/ext/grpc/timeval.c @@ -217,20 +217,16 @@ PHP_METHOD(Timeval, sleep_until) { } static zend_function_entry timeval_methods[] = { - PHP_ME(Timeval, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) PHP_ME( - Timeval, add, NULL, - ZEND_ACC_PUBLIC) PHP_ME(Timeval, compare, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, inf_future, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, inf_past, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, now, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, similar, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, sleep_until, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Timeval, subtract, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Timeval, zero, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_FE_END}; + PHP_ME(Timeval, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Timeval, add, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, compare, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, inf_future, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, inf_past, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, now, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, similar, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, sleep_until, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, subtract, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, zero, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; void grpc_init_timeval(TSRMLS_D) { zend_class_entry ce; From 29fa1c2fa4052ce1f8d1fbf0f2916a1f0c824cb4 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 16 Jan 2015 15:26:11 -0800 Subject: [PATCH 031/143] Compile fix --- test/core/end2end/tests/graceful_server_shutdown.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/core/end2end/tests/graceful_server_shutdown.c b/test/core/end2end/tests/graceful_server_shutdown.c index 84ad4af1191..d9c9dbb8b20 100644 --- a/test/core/end2end/tests/graceful_server_shutdown.c +++ b/test/core/end2end/tests/graceful_server_shutdown.c @@ -114,9 +114,7 @@ static void test_early_server_shutdown_finishes_inflight_calls( GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0)); - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); + grpc_call_invoke(c, f.client_cq, tag(2), tag(3), 0)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); From 79daae3604a09148abcfad8d1812656ed490edcf Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 16 Jan 2015 16:17:21 -0800 Subject: [PATCH 032/143] Create README.md --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000000..b4a37c0f9d5 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +gRPC - An RPC library and framework +=================================== + +Copyright 2015 Google Inc. + +Installation +------------ + +See grpc/INSTALL for installation instructions for various platforms. + +Overview +-------- + +Remote Procedure Calls (RPCs) provide a useful abstraction for building +distributed applications and services. The libraries in this repository +provide a concrete implementation of the gRPC protocol, layered over HTTP/2. +These libraries enable communication between clients and servers using any +combination of the supported languages. From 771928628f9d65150c4da2875e1bf2fc531c2775 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 16 Jan 2015 16:28:50 -0800 Subject: [PATCH 033/143] Update README.md Added a section on protocol. --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index b4a37c0f9d5..05414d8e21d 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,16 @@ distributed applications and services. The libraries in this repository provide a concrete implementation of the gRPC protocol, layered over HTTP/2. These libraries enable communication between clients and servers using any combination of the supported languages. + +Developers using gRPC typically start with the description of an RPC service +(a collection of methods), and generate client and server side interfaces +which they use on the client-side and implement on the server side. + +Protocol +-------- + +The gRPC protocol specifies the abstract requirements for communication between +clients and servers. A concrete embedding over HTTP/2 completes the picture by +fleshing out the details of each of the required operations. + + From 724b7c693ad96a31cb49a340f05e5c915a9e62d4 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 20 Jan 2015 22:44:10 -0800 Subject: [PATCH 034/143] clang-format --- src/cpp/client/channel.cc | 14 +++++++------- src/cpp/stream/stream_context.h | 16 +++++++++------- test/core/end2end/tests/max_concurrent_streams.c | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc index 767247cd98f..c8b2bb2cf6e 100644 --- a/src/cpp/client/channel.cc +++ b/src/cpp/client/channel.cc @@ -102,12 +102,12 @@ Status Channel::StartBlockingRpc(const RpcMethod &method, grpc_call *call = grpc_channel_create_call( c_channel_, method.name(), target_.c_str(), context->RawDeadline()); context->set_call(call); - grpc_event* ev; - void* finished_tag = reinterpret_cast(call); - void* metadata_read_tag = reinterpret_cast(call) + 2; - void* write_tag = reinterpret_cast(call) + 3; - void* halfclose_tag = reinterpret_cast(call) + 4; - void* read_tag = reinterpret_cast(call) + 5; + grpc_event *ev; + void *finished_tag = reinterpret_cast(call); + void *metadata_read_tag = reinterpret_cast(call) + 2; + void *write_tag = reinterpret_cast(call) + 3; + void *halfclose_tag = reinterpret_cast(call) + 4; + void *read_tag = reinterpret_cast(call) + 5; grpc_completion_queue *cq = grpc_completion_queue_create(); context->set_cq(cq); @@ -117,7 +117,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method, GPR_ASSERT(grpc_call_invoke(call, cq, metadata_read_tag, finished_tag, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); // write request - grpc_byte_buffer* write_buffer = nullptr; + grpc_byte_buffer *write_buffer = nullptr; bool success = SerializeProto(request, &write_buffer); if (!success) { grpc_call_cancel(call); diff --git a/src/cpp/stream/stream_context.h b/src/cpp/stream/stream_context.h index fcc4239c90a..8def589841b 100644 --- a/src/cpp/stream/stream_context.h +++ b/src/cpp/stream/stream_context.h @@ -72,13 +72,15 @@ class StreamContext final : public StreamContextInterface { // Unique tags for plucking events from the c layer. this pointer is casted // to char* to create single byte step between tags. It implicitly relies on // that StreamContext is large enough to contain all the pointers. - void* finished_tag() { return reinterpret_cast(this); } - void* read_tag() { return reinterpret_cast(this) + 1; } - void* write_tag() { return reinterpret_cast(this) + 2; } - void* halfclose_tag() { return reinterpret_cast(this) + 3; } - void* client_metadata_read_tag() { return reinterpret_cast(this) + 5; } - grpc_call* call() { return call_; } - grpc_completion_queue* cq() { return cq_; } + void *finished_tag() { return reinterpret_cast(this); } + void *read_tag() { return reinterpret_cast(this) + 1; } + void *write_tag() { return reinterpret_cast(this) + 2; } + void *halfclose_tag() { return reinterpret_cast(this) + 3; } + void *client_metadata_read_tag() { + return reinterpret_cast(this) + 5; + } + grpc_call *call() { return call_; } + grpc_completion_queue *cq() { return cq_; } bool is_client_; const RpcMethod *method_; // not owned diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index a177a7b2f29..20f124ee9f2 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -204,7 +204,7 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { /* The /alpha or /beta calls started above could be invoked (but NOT both); * check this here */ /* We'll get tag 303 or 403, we want 300, 400 */ - live_call = ((int)(gpr_intptr)ev->tag) - 3; + live_call = ((int)(gpr_intptr) ev->tag) - 3; grpc_event_finish(ev); cq_expect_server_rpc_new(v_server, &s1, tag(100), From 0adce9db94793fc25603334268ce771e174fa673 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 21 Jan 2015 11:32:18 -0800 Subject: [PATCH 035/143] Update README.md Added more details in the Interface and Protocol sections. --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 05414d8e21d..1e5b61af95a 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,12 @@ gRPC - An RPC library and framework Copyright 2015 Google Inc. -Installation ------------- +#Installation See grpc/INSTALL for installation instructions for various platforms. -Overview --------- +#Overview + Remote Procedure Calls (RPCs) provide a useful abstraction for building distributed applications and services. The libraries in this repository @@ -17,15 +16,57 @@ provide a concrete implementation of the gRPC protocol, layered over HTTP/2. These libraries enable communication between clients and servers using any combination of the supported languages. + +##Interface + + Developers using gRPC typically start with the description of an RPC service (a collection of methods), and generate client and server side interfaces which they use on the client-side and implement on the server side. -Protocol --------- +By default, gRPC uses [Protocol Buffers](github.com/google/protobuf) as the +Interface Definition Language (IDL) for describing both the service interface +and the structure of the payload messages. It is possible to use other +alternatives if desired. + +###Surface API +Starting from an interface definition in a .proto file, gRPC provides +Protocol Compiler plugins that generate Client- and Server-side APIs. +gRPC users typically call into these APIs on the Client side and implement +the corresponding API on the server side. + +#### Synchronous vs. Async +Synchronous RPC calls, that block till a response arrives from the server, are +the closest approximation to the abstraction of a procedure call that RPC +aspires to. + +On the other hand, networks are inherently asynchronous and in many scenarios, +it is desirable to have the ability to start RPCs without blocking the current +thread. + +The gRPC programming surface in most languages comes in both Synchronous and +async flavors. + + +## Streaming + +gRPC supports streaming semantics, where either the client or the server (or both) +send a stream of messages on a single RPC call. The most general case is +Bidirectional Streaming where a single gRPC call establishes a stream where both +the client and the server can send a stream of messages to each other. The streamed +messages are delivered in the order they were sent. + + + + + +#Protocol The gRPC protocol specifies the abstract requirements for communication between clients and servers. A concrete embedding over HTTP/2 completes the picture by fleshing out the details of each of the required operations. +## Abstract gRPC protocol +A gRPC RPC comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory 'Call Header', followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata'). + From c896e192f7d7b529891f5769769fb3ce84895494 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 21 Jan 2015 11:36:23 -0800 Subject: [PATCH 036/143] Brought grpc_server_add_secure_http2_port in line with grpc_server_http2_port --- include/grpc/grpc.h | 3 ++- src/core/security/server_secure_chttp2.c | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 3c5b0de1956..f03f61d84ed 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -428,7 +428,8 @@ grpc_server *grpc_server_create(grpc_completion_queue *cq, REQUIRES: server not started */ int grpc_server_add_http2_port(grpc_server *server, const char *addr); -/* Add a secure port to server; returns 1 on success, 0 on failure +/* Add a secure port to server. + Returns bound port number on success, 0 on failure. REQUIRES: server not started */ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr); diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c index 931fa95651b..9dd43278222 100644 --- a/src/core/security/server_secure_chttp2.c +++ b/src/core/security/server_secure_chttp2.c @@ -93,6 +93,8 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) { grpc_tcp_server *tcp = NULL; size_t i; int count = 0; + int port_num = -1; + int port_temp; resolved = grpc_blocking_resolve_address(addr, "https"); if (!resolved) { @@ -105,9 +107,15 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) { } for (i = 0; i < resolved->naddrs; i++) { - if (grpc_tcp_server_add_port(tcp, - (struct sockaddr *)&resolved->addrs[i].addr, - resolved->addrs[i].len)) { + port_temp = grpc_tcp_server_add_port( + tcp, (struct sockaddr *)&resolved->addrs[i].addr, + resolved->addrs[i].len); + if (port_temp >= 0) { + if (port_num == -1) { + port_num = port_temp; + } else { + GPR_ASSERT(port_num == port_temp); + } count++; } } @@ -125,7 +133,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) { /* Register with the server only upon success */ grpc_server_add_listener(server, tcp, start, destroy); - return 1; + return port_num; /* Error path: cleanup and return */ error: From d2f20b29a6b3d9658efe5024bd8c2242b924e0f1 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 21 Jan 2015 11:55:39 -0800 Subject: [PATCH 037/143] Removed port_picker and bound to port 0 in tests instead --- src/node/port_picker.js | 52 ------ src/node/server.cc | 4 +- src/node/server.js | 4 +- src/node/surface_server.js | 3 +- src/node/test/client_server_test.js | 159 +++++++++--------- src/node/test/end_to_end_test.js | 239 ++++++++++++++-------------- src/node/test/math_client_test.js | 10 +- src/node/test/server_test.js | 87 +++++----- 8 files changed, 243 insertions(+), 315 deletions(-) delete mode 100644 src/node/port_picker.js diff --git a/src/node/port_picker.js b/src/node/port_picker.js deleted file mode 100644 index ad82f2a7f83..00000000000 --- a/src/node/port_picker.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * Copyright 2014, 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. - * - */ - -var net = require('net'); - -/** - * Finds a free port that a server can bind to, in the format - * "address:port" - * @param {function(string)} cb The callback that should execute when the port - * is available - */ -function nextAvailablePort(cb) { - var server = net.createServer(); - server.listen(function() { - var address = server.address(); - server.close(function() { - cb(address.address + ':' + address.port.toString()); - }); - }); -} - -exports.nextAvailablePort = nextAvailablePort; diff --git a/src/node/server.cc b/src/node/server.cc index 64826897cda..b102775d337 100644 --- a/src/node/server.cc +++ b/src/node/server.cc @@ -194,7 +194,7 @@ NAN_METHOD(Server::AddHttp2Port) { return NanThrowTypeError("addHttp2Port's argument must be a String"); } Server *server = ObjectWrap::Unwrap(args.This()); - NanReturnValue(NanNew(grpc_server_add_http2_port( + NanReturnValue(NanNew(grpc_server_add_http2_port( server->wrapped_server, *NanUtf8String(args[0])))); } @@ -208,7 +208,7 @@ NAN_METHOD(Server::AddSecureHttp2Port) { return NanThrowTypeError("addSecureHttp2Port's argument must be a String"); } Server *server = ObjectWrap::Unwrap(args.This()); - NanReturnValue(NanNew(grpc_server_add_secure_http2_port( + NanReturnValue(NanNew(grpc_server_add_secure_http2_port( server->wrapped_server, *NanUtf8String(args[0])))); } diff --git a/src/node/server.js b/src/node/server.js index e947032b297..9cee1dc57e5 100644 --- a/src/node/server.js +++ b/src/node/server.js @@ -256,9 +256,9 @@ Server.prototype.register = function(name, handler) { */ Server.prototype.bind = function(port, secure) { if (secure) { - this._server.addSecureHttp2Port(port); + return this._server.addSecureHttp2Port(port); } else { - this._server.addHttp2Port(port); + return this._server.addHttp2Port(port); } }; diff --git a/src/node/surface_server.js b/src/node/surface_server.js index b6e0c37b4cd..55f3583b091 100644 --- a/src/node/surface_server.js +++ b/src/node/surface_server.js @@ -353,8 +353,7 @@ function makeServerConstructor(services) { * @return {SurfaceServer} this */ SurfaceServer.prototype.bind = function(port, secure) { - this.inner_server.bind(port, secure); - return this; + return this.inner_server.bind(port, secure); }; /** diff --git a/src/node/test/client_server_test.js b/src/node/test/client_server_test.js index 534a5c464fb..2a25908684b 100644 --- a/src/node/test/client_server_test.js +++ b/src/node/test/client_server_test.js @@ -37,7 +37,6 @@ var path = require('path'); var grpc = require('bindings')('grpc.node'); var Server = require('../server'); var client = require('../client'); -var port_picker = require('../port_picker'); var common = require('../common'); var _ = require('highland'); @@ -80,55 +79,50 @@ function errorHandler(stream) { describe('echo client', function() { it('should receive echo responses', function(done) { - port_picker.nextAvailablePort(function(port) { - var server = new Server(); - server.bind(port); - server.register('echo', echoHandler); - server.start(); - - var messages = ['echo1', 'echo2', 'echo3', 'echo4']; - var channel = new grpc.Channel(port); - var stream = client.makeRequest( - channel, - 'echo'); - _(messages).map(function(val) { - return new Buffer(val); - }).pipe(stream); - var index = 0; - stream.on('data', function(chunk) { - assert.equal(messages[index], chunk.toString()); - index += 1; - }); - stream.on('end', function() { - server.shutdown(); - done(); - }); + var server = new Server(); + var port_num = server.bind('0.0.0.0:0'); + server.register('echo', echoHandler); + server.start(); + + var messages = ['echo1', 'echo2', 'echo3', 'echo4']; + var channel = new grpc.Channel('localhost:' + port_num); + var stream = client.makeRequest( + channel, + 'echo'); + _(messages).map(function(val) { + return new Buffer(val); + }).pipe(stream); + var index = 0; + stream.on('data', function(chunk) { + assert.equal(messages[index], chunk.toString()); + index += 1; + }); + stream.on('end', function() { + server.shutdown(); + done(); }); }); it('should get an error status that the server throws', function(done) { - port_picker.nextAvailablePort(function(port) { - var server = new Server(); - server.bind(port); - server.register('error', errorHandler); - server.start(); - - var channel = new grpc.Channel(port); - var stream = client.makeRequest( - channel, - 'error', - null, - getDeadline(1)); - - stream.on('data', function() {}); - stream.write(new Buffer('test')); - stream.end(); - stream.on('status', function(status) { - assert.equal(status.code, grpc.status.UNIMPLEMENTED); - assert.equal(status.details, 'error details'); - server.shutdown(); - done(); - }); - + var server = new Server(); + var port_num = server.bind('0.0.0.0:0'); + server.register('error', errorHandler); + server.start(); + + var channel = new grpc.Channel('localhost:' + port_num); + var stream = client.makeRequest( + channel, + 'error', + null, + getDeadline(1)); + + stream.on('data', function() {}); + stream.write(new Buffer('test')); + stream.end(); + stream.on('status', function(status) { + assert.equal(status.code, grpc.status.UNIMPLEMENTED); + assert.equal(status.details, 'error details'); + server.shutdown(); + done(); }); }); }); @@ -136,46 +130,43 @@ describe('echo client', function() { * and the insecure echo client test */ describe('secure echo client', function() { it('should recieve echo responses', function(done) { - port_picker.nextAvailablePort(function(port) { - fs.readFile(ca_path, function(err, ca_data) { + fs.readFile(ca_path, function(err, ca_data) { + assert.ifError(err); + fs.readFile(key_path, function(err, key_data) { assert.ifError(err); - fs.readFile(key_path, function(err, key_data) { + fs.readFile(pem_path, function(err, pem_data) { assert.ifError(err); - fs.readFile(pem_path, function(err, pem_data) { - assert.ifError(err); - var creds = grpc.Credentials.createSsl(ca_data); - var server_creds = grpc.ServerCredentials.createSsl(null, - key_data, - pem_data); - - var server = new Server({'credentials' : server_creds}); - server.bind(port, true); - server.register('echo', echoHandler); - server.start(); - - var messages = ['echo1', 'echo2', 'echo3', 'echo4']; - var channel = new grpc.Channel(port, { - 'grpc.ssl_target_name_override' : 'foo.test.google.com', - 'credentials' : creds - }); - var stream = client.makeRequest( - channel, - 'echo'); - - _(messages).map(function(val) { - return new Buffer(val); - }).pipe(stream); - var index = 0; - stream.on('data', function(chunk) { - assert.equal(messages[index], chunk.toString()); - index += 1; - }); - stream.on('end', function() { - server.shutdown(); - done(); - }); + var creds = grpc.Credentials.createSsl(ca_data); + var server_creds = grpc.ServerCredentials.createSsl(null, + key_data, + pem_data); + + var server = new Server({'credentials' : server_creds}); + var port_num = server.bind('0.0.0.0:0', true); + server.register('echo', echoHandler); + server.start(); + + var messages = ['echo1', 'echo2', 'echo3', 'echo4']; + var channel = new grpc.Channel('localhost:' + port_num, { + 'grpc.ssl_target_name_override' : 'foo.test.google.com', + 'credentials' : creds + }); + var stream = client.makeRequest( + channel, + 'echo'); + + _(messages).map(function(val) { + return new Buffer(val); + }).pipe(stream); + var index = 0; + stream.on('data', function(chunk) { + assert.equal(messages[index], chunk.toString()); + index += 1; + }); + stream.on('end', function() { + server.shutdown(); + done(); }); - }); }); }); diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js index 40bb5f3bbde..db3834dbbad 100644 --- a/src/node/test/end_to_end_test.js +++ b/src/node/test/end_to_end_test.js @@ -33,7 +33,6 @@ var assert = require('assert'); var grpc = require('bindings')('grpc.node'); -var port_picker = require('../port_picker'); /** * This is used for testing functions with multiple asynchronous calls that @@ -58,143 +57,139 @@ function multiDone(done, count) { describe('end-to-end', function() { it('should start and end a request without error', function(complete) { - port_picker.nextAvailablePort(function(port) { - var server = new grpc.Server(); - var done = multiDone(function() { - complete(); - server.shutdown(); - }, 2); - server.addHttp2Port(port); - var channel = new grpc.Channel(port); - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + 3); - var status_text = 'xyz'; - var call = new grpc.Call(channel, - 'dummy_method', - deadline); - call.startInvoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.INVOKE_ACCEPTED); + var server = new grpc.Server(); + var done = multiDone(function() { + complete(); + server.shutdown(); + }, 2); + var port_num = server.addHttp2Port('0.0.0.0:0'); + var channel = new grpc.Channel('localhost:' + port_num); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var status_text = 'xyz'; + var call = new grpc.Call(channel, + 'dummy_method', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - }); - },function(event) { + call.writesDone(function(event) { assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - },function(event) { + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + done(); + }, 0); + + server.start(); + server.requestCall(function(event) { + assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); + var server_call = event.call; + assert.notEqual(server_call, null); + server_call.serverAccept(function(event) { assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); - done(); }, 0); + server_call.serverEndInitialMetadata(0); + server_call.startWriteStatus( + grpc.status.OK, + status_text, + function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }); + }); - server.start(); - server.requestCall(function(event) { - assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); - var server_call = event.call; - assert.notEqual(server_call, null); - server_call.serverAccept(function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - }, 0); - server_call.serverEndInitialMetadata(0); - server_call.startWriteStatus( - grpc.status.OK, - status_text, - function(event) { + it('should send and receive data without error', function(complete) { + var req_text = 'client_request'; + var reply_text = 'server_response'; + var server = new grpc.Server(); + var done = multiDone(function() { + complete(); + server.shutdown(); + }, 6); + var port_num = server.addHttp2Port('0.0.0.0:0'); + var channel = new grpc.Channel('localhost:' + port_num); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var status_text = 'success'; + var call = new grpc.Call(channel, + 'dummy_method', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); + call.startWrite( + new Buffer(req_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + call.writesDone(function(event) { assert.strictEqual(event.type, grpc.completionType.FINISH_ACCEPTED); assert.strictEqual(event.data, grpc.opError.OK); done(); }); + }, 0); + call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), reply_text); + done(); }); - }); - }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + done(); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + done(); + }, 0); - it('should send and receive data without error', function(complete) { - port_picker.nextAvailablePort(function(port) { - var req_text = 'client_request'; - var reply_text = 'server_response'; - var server = new grpc.Server(); - var done = multiDone(function() { - complete(); - server.shutdown(); - }, 6); - server.addHttp2Port(port); - var channel = new grpc.Channel(port); - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + 3); - var status_text = 'success'; - var call = new grpc.Call(channel, - 'dummy_method', - deadline); - call.startInvoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.INVOKE_ACCEPTED); - call.startWrite( - new Buffer(req_text), + server.start(); + server.requestCall(function(event) { + assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); + var server_call = event.call; + assert.notEqual(server_call, null); + server_call.serverAccept(function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + done(); + }); + server_call.serverEndInitialMetadata(0); + server_call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), req_text); + server_call.startWrite( + new Buffer(reply_text), function(event) { assert.strictEqual(event.type, grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); + assert.strictEqual(event.data, + grpc.opError.OK); + server_call.startWriteStatus( + grpc.status.OK, + status_text, + function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); }, 0); - call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), reply_text); - done(); - }); - },function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - done(); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); - done(); - }, 0); - - server.start(); - server.requestCall(function(event) { - assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); - var server_call = event.call; - assert.notEqual(server_call, null); - server_call.serverAccept(function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - done(); - }); - server_call.serverEndInitialMetadata(0); - server_call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), req_text); - server_call.startWrite( - new Buffer(reply_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, - grpc.opError.OK); - server_call.startWriteStatus( - grpc.status.OK, - status_text, - function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); - }); }); }); }); diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js index 45c956d1793..29d5648d467 100644 --- a/src/node/test/math_client_test.js +++ b/src/node/test/math_client_test.js @@ -32,7 +32,6 @@ */ var assert = require('assert'); -var port_picker = require('../port_picker'); var grpc = require('..'); var math = grpc.load(__dirname + '/../examples/math.proto').math; @@ -50,11 +49,10 @@ var server = require('../examples/math_server.js'); describe('Math client', function() { before(function(done) { - port_picker.nextAvailablePort(function(port) { - server.bind(port).listen(); - math_client = new math.Math(port); - done(); - }); + var port_num = server.bind('0.0.0.0:0'); + server.listen(); + math_client = new math.Math('localhost:' + port_num); + done(); }); after(function() { server.shutdown(); diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js index 79f7b329485..61aef4677e3 100644 --- a/src/node/test/server_test.js +++ b/src/node/test/server_test.js @@ -34,7 +34,6 @@ var assert = require('assert'); var grpc = require('bindings')('grpc.node'); var Server = require('../server'); -var port_picker = require('../port_picker'); /** * This is used for testing functions with multiple asynchronous calls that @@ -68,54 +67,52 @@ function echoHandler(stream) { describe('echo server', function() { it('should echo inputs as responses', function(done) { done = multiDone(done, 4); - port_picker.nextAvailablePort(function(port) { - var server = new Server(); - server.bind(port); - server.register('echo', echoHandler); - server.start(); + var server = new Server(); + var port_num = server.bind('[::]:0'); + server.register('echo', echoHandler); + server.start(); - var req_text = 'echo test string'; - var status_text = 'OK'; + var req_text = 'echo test string'; + var status_text = 'OK'; - var channel = new grpc.Channel(port); - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + 3); - var call = new grpc.Call(channel, - 'echo', - deadline); - call.startInvoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.INVOKE_ACCEPTED); - call.startWrite( - new Buffer(req_text), - function(event) { + var channel = new grpc.Channel('localhost:' + port_num); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var call = new grpc.Call(channel, + 'echo', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); + call.startWrite( + new Buffer(req_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + call.writesDone(function(event) { assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); + grpc.completionType.FINISH_ACCEPTED); assert.strictEqual(event.data, grpc.opError.OK); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); - call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), req_text); - done(); - }); - },function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); + done(); + }); + }, 0); + call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), req_text); done(); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); - server.shutdown(); - done(); - }, 0); - }); + }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + done(); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + server.shutdown(); + done(); + }, 0); }); }); From f034e50ba9847250388c0608cd217dbd1dbc76db Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 21 Jan 2015 12:12:39 -0800 Subject: [PATCH 038/143] Modified interop tests to handle binding to port 0 --- src/node/interop/interop_server.js | 11 ++++++----- src/node/test/interop_sanity_test.js | 13 +++++-------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js index 735b7a6d18b..6d2bd7ae0de 100644 --- a/src/node/interop/interop_server.js +++ b/src/node/interop/interop_server.js @@ -157,7 +157,8 @@ function handleHalfDuplex(call) { * Get a server object bound to the given port * @param {string} port Port to which to bind * @param {boolean} tls Indicates that the bound port should use TLS - * @return {Server} Server object bound to the support + * @return {{server: Server, port: number}} Server object bound to the support, + * and port number that the server is bound to */ function getServer(port, tls) { // TODO(mlumish): enable TLS functionality @@ -183,8 +184,8 @@ function getServer(port, tls) { halfDuplexCall: handleHalfDuplex } }, options); - server.bind('0.0.0.0:' + port, tls); - return server; + var port_num = server.bind('0.0.0.0:' + port, tls); + return {server: server, port: port_num}; } if (require.main === module) { @@ -192,8 +193,8 @@ if (require.main === module) { var argv = parseArgs(process.argv, { string: ['port', 'use_tls'] }); - var server = getServer(argv.port, argv.use_tls === 'true'); - server.start(); + var server_obj = getServer(argv.port, argv.use_tls === 'true'); + server_obj.server.start(); } /** diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index 9959a165ad1..b0fc8f82002 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -34,8 +34,6 @@ var interop_server = require('../interop/interop_server.js'); var interop_client = require('../interop/interop_client.js'); -var port_picker = require('../port_picker'); - var server; var port; @@ -44,12 +42,11 @@ var name_override = 'foo.test.google.com'; describe('Interop tests', function() { before(function(done) { - port_picker.nextAvailablePort(function(addr) { - server = interop_server.getServer(addr.substring(addr.indexOf(':') + 1), true); - server.listen(); - port = addr; - done(); - }); + var server_obj = interop_server.getServer(0, true); + server = server_obj.server; + server.listen(); + port = 'localhost:' + server_obj.port; + done(); }); // This depends on not using a binary stream it.skip('should pass empty_unary', function(done) { From 2b5637615a20802ddf452321ded71f351e2803f8 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Wed, 21 Jan 2015 12:59:41 -0800 Subject: [PATCH 039/143] Updates ruby to stop using grpc_start_invoke --- src/ruby/ext/grpc/rb_call.c | 20 +++---- src/ruby/ext/grpc/rb_event.c | 6 +- src/ruby/lib/grpc/auth.rb | 68 +++++++++++++++++++++++ src/ruby/lib/grpc/generic/active_call.rb | 28 ++++------ src/ruby/lib/grpc/generic/bidi_call.rb | 4 +- src/ruby/spec/call_spec.rb | 16 +----- src/ruby/spec/client_server_spec.rb | 8 +-- src/ruby/spec/event_spec.rb | 3 +- src/ruby/spec/generic/active_call_spec.rb | 56 +++++++++---------- 9 files changed, 123 insertions(+), 86 deletions(-) create mode 100644 src/ruby/lib/grpc/auth.rb diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c index 76b80bcaa16..1b6565f729a 100644 --- a/src/ruby/ext/grpc/rb_call.c +++ b/src/ruby/ext/grpc/rb_call.c @@ -153,7 +153,7 @@ int grpc_rb_call_add_metadata_hash_cb(VALUE key, VALUE val, VALUE call_obj) { Add metadata elements to the call from a ruby hash, to be sent upon invocation. flags is a bit-field combination of the write flags defined - above. REQUIRES: grpc_call_start_invoke/grpc_call_accept have not been + above. REQUIRES: grpc_call_invoke/grpc_call_accept have not been called on this call. Produces no events. */ static VALUE grpc_rb_call_add_metadata(int argc, VALUE *argv, VALUE self) { @@ -196,16 +196,15 @@ static VALUE grpc_rb_call_cancel(VALUE self) { /* call-seq: - call.start_invoke(completion_queue, tag, flags=nil) + call.invoke(completion_queue, tag, flags=nil) Invoke the RPC. Starts sending metadata and request headers on the wire. flags is a bit-field combination of the write flags defined above. REQUIRES: Can be called at most once per call. Can only be called on the client. Produces a GRPC_INVOKE_ACCEPTED event on completion. */ -static VALUE grpc_rb_call_start_invoke(int argc, VALUE *argv, VALUE self) { +static VALUE grpc_rb_call_invoke(int argc, VALUE *argv, VALUE self) { VALUE cqueue = Qnil; - VALUE invoke_accepted_tag = Qnil; VALUE metadata_read_tag = Qnil; VALUE finished_tag = Qnil; VALUE flags = Qnil; @@ -213,17 +212,16 @@ static VALUE grpc_rb_call_start_invoke(int argc, VALUE *argv, VALUE self) { grpc_completion_queue *cq = NULL; grpc_call_error err; - /* "41" == 4 mandatory args, 1 (flags) is optional */ - rb_scan_args(argc, argv, "41", &cqueue, &invoke_accepted_tag, - &metadata_read_tag, &finished_tag, &flags); + /* "31" == 3 mandatory args, 1 (flags) is optional */ + rb_scan_args(argc, argv, "31", &cqueue, &metadata_read_tag, &finished_tag, + &flags); if (NIL_P(flags)) { flags = UINT2NUM(0); /* Default to no flags */ } cq = grpc_rb_get_wrapped_completion_queue(cqueue); Data_Get_Struct(self, grpc_call, call); - err = grpc_call_start_invoke(call, cq, ROBJECT(invoke_accepted_tag), - ROBJECT(metadata_read_tag), - ROBJECT(finished_tag), NUM2UINT(flags)); + err = grpc_call_invoke(call, cq, ROBJECT(metadata_read_tag), + ROBJECT(finished_tag), NUM2UINT(flags)); if (err != GRPC_CALL_OK) { rb_raise(rb_eCallError, "invoke failed: %s (code=%d)", grpc_call_error_detail_of(err), err); @@ -519,7 +517,7 @@ void Init_google_rpc_call() { grpc_rb_call_server_end_initial_metadata, -1); rb_define_method(rb_cCall, "add_metadata", grpc_rb_call_add_metadata, -1); rb_define_method(rb_cCall, "cancel", grpc_rb_call_cancel, 0); - rb_define_method(rb_cCall, "start_invoke", grpc_rb_call_start_invoke, -1); + rb_define_method(rb_cCall, "invoke", grpc_rb_call_invoke, -1); rb_define_method(rb_cCall, "start_read", grpc_rb_call_start_read, 1); rb_define_method(rb_cCall, "start_write", grpc_rb_call_start_write, -1); rb_define_method(rb_cCall, "start_write_status", diff --git a/src/ruby/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c index 0fae9502c30..a1ab6251c86 100644 --- a/src/ruby/ext/grpc/rb_event.c +++ b/src/ruby/ext/grpc/rb_event.c @@ -105,10 +105,6 @@ static VALUE grpc_rb_event_type(VALUE self) { case GRPC_READ: return rb_const_get(rb_mCompletionType, rb_intern("READ")); - case GRPC_INVOKE_ACCEPTED: - grpc_rb_event_result(self); /* validates the result */ - return rb_const_get(rb_mCompletionType, rb_intern("INVOKE_ACCEPTED")); - case GRPC_WRITE_ACCEPTED: grpc_rb_event_result(self); /* validates the result */ return rb_const_get(rb_mCompletionType, rb_intern("WRITE_ACCEPTED")); @@ -359,6 +355,8 @@ void Init_google_rpc_event() { rb_define_const(rb_mCompletionType, "FINISHED", INT2NUM(GRPC_FINISHED)); rb_define_const(rb_mCompletionType, "SERVER_RPC_NEW", INT2NUM(GRPC_SERVER_RPC_NEW)); + rb_define_const(rb_mCompletionType, "SERVER_SHUTDOWN", + INT2NUM(GRPC_SERVER_SHUTDOWN)); rb_define_const(rb_mCompletionType, "RESERVED", INT2NUM(GRPC_COMPLETION_DO_NOT_USE)); } diff --git a/src/ruby/lib/grpc/auth.rb b/src/ruby/lib/grpc/auth.rb new file mode 100644 index 00000000000..8817205b24a --- /dev/null +++ b/src/ruby/lib/grpc/auth.rb @@ -0,0 +1,68 @@ +# Copyright 2014, 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. + +require 'grpc' +require 'signet' + + +module Google + import Signet::OAuth2 + + # Google::RPC contains the General RPC module. + module RPC + # ServiceAccounCredentials can obtain credentials for a configured service + # account, scopes and issuer. + module Auth + class ServiceAccountCredentials + CREDENTIAL_URI = 'https://accounts.google.com/o/oauth2/token' + AUDIENCE_URI = 'https://accounts.google.com/o/oauth2/token' + + # Initializes an instance with the given scope, issuer and signing_key + def initialize(scope, issuer, key) + @auth_client = Client.new(token_credential_uri: CREDENTIAL_URI, + audience: AUDIENCE_URI, + scope: scope, + issuer: issuer, + signing_key: key) + @auth_token = nil + end + + def metadata_update_proc + proc do |input_md| + input + end + end + + def auth_creds + key = Google::APIClient::KeyUtils.load_from_pkcs12('client.p12', 'notasecret') + end + end + end + end +end diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb index bd684a8d07a..1cdc168bfe1 100644 --- a/src/ruby/lib/grpc/generic/active_call.rb +++ b/src/ruby/lib/grpc/generic/active_call.rb @@ -47,7 +47,7 @@ module Google include Core::TimeConsts attr_reader(:deadline) - # client_start_invoke begins a client invocation. + # client_invoke begins a client invocation. # # Flow Control note: this blocks until flow control accepts that client # request can go ahead. @@ -59,9 +59,9 @@ module Google # if a keyword value is a list, multiple metadata for it's key are sent # # @param call [Call] a call on which to start and invocation - # @param q [CompletionQueue] used to wait for INVOKE_ACCEPTED - # @param deadline [Fixnum,TimeSpec] the deadline for INVOKE_ACCEPTED - def self.client_start_invoke(call, q, _deadline, **kw) + # @param q [CompletionQueue] the completion queue + # @param deadline [Fixnum,TimeSpec] the deadline + def self.client_invoke(call, q, _deadline, **kw) fail(ArgumentError, 'not a call') unless call.is_a? Core::Call unless q.is_a? Core::CompletionQueue fail(ArgumentError, 'not a CompletionQueue') @@ -69,24 +69,16 @@ module Google call.add_metadata(kw) if kw.length > 0 invoke_accepted, client_metadata_read = Object.new, Object.new finished_tag = Object.new - call.start_invoke(q, invoke_accepted, client_metadata_read, - finished_tag) - - # wait for the invocation to be accepted - ev = q.pluck(invoke_accepted, INFINITE_FUTURE) - fail OutOfTime if ev.nil? - ev.close - + call.invoke(q, client_metadata_read, finished_tag) [finished_tag, client_metadata_read] end # Creates an ActiveCall. # - # ActiveCall should only be created after a call is accepted. That means - # different things on a client and a server. On the client, the call is - # accepted after call.start_invoke followed by receipt of the - # corresponding INVOKE_ACCEPTED. on the server, this is after - # call.accept. + # ActiveCall should only be created after a call is accepted. That + # means different things on a client and a server. On the client, the + # call is accepted after calling call.invoke. On the server, this is + # after call.accept. # # #initialize cannot determine if the call is accepted or not; so if a # call that's not accepted is used here, the error won't be visible until @@ -495,7 +487,7 @@ module Google private def start_call(**kw) - tags = ActiveCall.client_start_invoke(@call, @cq, @deadline, **kw) + tags = ActiveCall.client_invoke(@call, @cq, @deadline, **kw) @finished_tag, @read_metadata_tag = tags @started = true end diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb index 14ef6c531f9..7653192ad62 100644 --- a/src/ruby/lib/grpc/generic/bidi_call.rb +++ b/src/ruby/lib/grpc/generic/bidi_call.rb @@ -50,9 +50,7 @@ module Google # # BidiCall should only be created after a call is accepted. That means # different things on a client and a server. On the client, the call is - # accepted after call.start_invoke followed by receipt of the - # corresponding INVOKE_ACCEPTED. On the server, this is after - # call.accept. + # accepted after call.invoke. On the server, this is after call.accept. # # #initialize cannot determine if the call is accepted or not; so if a # call that's not accepted is used here, the error won't be visible until diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb index b8ecd64f393..9a510df1f38 100644 --- a/src/ruby/spec/call_spec.rb +++ b/src/ruby/spec/call_spec.rb @@ -122,24 +122,10 @@ describe GRPC::Core::Call do end end - describe '#start_invoke' do - it 'should cause the INVOKE_ACCEPTED event' do - call = make_test_call - expect(call.start_invoke(@client_queue, @tag, @tag, @tag)).to be_nil - ev = @client_queue.next(deadline) - expect(ev.call).to be_a(GRPC::Core::Call) - expect(ev.tag).to be(@tag) - expect(ev.type).to be(GRPC::Core::CompletionType::INVOKE_ACCEPTED) - expect(ev.call).to_not be(call) - end - end - describe '#start_write' do it 'should cause the WRITE_ACCEPTED event' do call = make_test_call - call.start_invoke(@client_queue, @tag, @tag, @tag) - ev = @client_queue.next(deadline) - expect(ev.type).to be(GRPC::Core::CompletionType::INVOKE_ACCEPTED) + call.invoke(@client_queue, @tag, @tag) expect(call.start_write(GRPC::Core::ByteBuffer.new('test_start_write'), @tag)).to be_nil ev = @client_queue.next(deadline) diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb index 1bcbc66446c..59b4bbd9d8e 100644 --- a/src/ruby/spec/client_server_spec.rb +++ b/src/ruby/spec/client_server_spec.rb @@ -83,10 +83,7 @@ shared_context 'setup: tags' do def client_sends(call, sent = 'a message') req = ByteBuffer.new(sent) - call.start_invoke(@client_queue, @tag, @tag, @client_finished_tag) - ev = @client_queue.pluck(@tag, TimeConsts::INFINITE_FUTURE) - expect(ev).not_to be_nil - expect(ev.type).to be(INVOKE_ACCEPTED) + call.invoke(@client_queue, @tag, @client_finished_tag) call.start_write(req, @tag) ev = @client_queue.pluck(@tag, TimeConsts::INFINITE_FUTURE) expect(ev).not_to be_nil @@ -233,8 +230,7 @@ shared_examples 'GRPC metadata delivery works OK' do call.add_metadata(md) # Client begins a call OK - call.start_invoke(@client_queue, @tag, @tag, @client_finished_tag) - expect_next_event_on(@client_queue, INVOKE_ACCEPTED, @tag) + call.invoke(@client_queue, @tag, @client_finished_tag) # ... server has all metadata available even though the client did not # send a write diff --git a/src/ruby/spec/event_spec.rb b/src/ruby/spec/event_spec.rb index 5dec07e1edf..7ef08d021bb 100644 --- a/src/ruby/spec/event_spec.rb +++ b/src/ruby/spec/event_spec.rb @@ -40,7 +40,8 @@ describe GRPC::Core::CompletionType do CLIENT_METADATA_READ: 5, FINISHED: 6, SERVER_RPC_NEW: 7, - RESERVED: 8 + SERVER_SHUTDOWN: 8, + RESERVED: 9 } end diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb index 898022f1859..443ba3d1922 100644 --- a/src/ruby/spec/generic/active_call_spec.rb +++ b/src/ruby/spec/generic/active_call_spec.rb @@ -60,8 +60,8 @@ describe GRPC::ActiveCall do describe 'restricted view methods' do before(:each) do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) @client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, finished_tag: done_tag, @@ -92,8 +92,8 @@ describe GRPC::ActiveCall do describe '#remote_send' do it 'allows a client to send a payload to the server' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) @client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, finished_tag: done_tag, @@ -118,8 +118,8 @@ describe GRPC::ActiveCall do it 'marshals the payload using the marshal func' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) marshal = proc { |x| 'marshalled:' + x } client_call = ActiveCall.new(call, @client_queue, marshal, @pass_through, deadline, @@ -139,11 +139,11 @@ describe GRPC::ActiveCall do end end - describe '#client_start_invoke' do + describe '#client_invoke' do it 'sends keywords as metadata to the server when the are present' do call = make_test_call - ActiveCall.client_start_invoke(call, @client_queue, deadline, - k1: 'v1', k2: 'v2') + ActiveCall.client_invoke(call, @client_queue, deadline, + k1: 'v1', k2: 'v2') @server.request_call(@server_tag) ev = @server_queue.next(deadline) expect(ev).to_not be_nil @@ -155,8 +155,8 @@ describe GRPC::ActiveCall do describe '#remote_read' do it 'reads the response sent by a server' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, finished_tag: done_tag, @@ -170,8 +170,8 @@ describe GRPC::ActiveCall do it 'saves metadata { status=200 } when the server adds no metadata' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, finished_tag: done_tag, @@ -187,8 +187,8 @@ describe GRPC::ActiveCall do it 'saves metadata add by the server' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, finished_tag: done_tag, @@ -205,7 +205,7 @@ describe GRPC::ActiveCall do it 'get a nil msg before a status when an OK status is sent' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, @@ -224,8 +224,8 @@ describe GRPC::ActiveCall do it 'unmarshals the response using the unmarshal func' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) unmarshal = proc { |x| 'unmarshalled:' + x } client_call = ActiveCall.new(call, @client_queue, @pass_through, unmarshal, deadline, @@ -251,8 +251,8 @@ describe GRPC::ActiveCall do it 'the returns an enumerator that can read n responses' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, finished_tag: done_tag, @@ -271,8 +271,8 @@ describe GRPC::ActiveCall do it 'the returns an enumerator that stops after an OK Status' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, read_metadata_tag: meta_tag, @@ -296,8 +296,8 @@ describe GRPC::ActiveCall do describe '#writes_done' do it 'finishes ok if the server sends a status response' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, finished_tag: done_tag, @@ -315,8 +315,8 @@ describe GRPC::ActiveCall do it 'finishes ok if the server sends an early status response' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, read_metadata_tag: meta_tag, @@ -334,8 +334,8 @@ describe GRPC::ActiveCall do it 'finishes ok if writes_done is true' do call = make_test_call - done_tag, meta_tag = ActiveCall.client_start_invoke(call, @client_queue, - deadline) + done_tag, meta_tag = ActiveCall.client_invoke(call, @client_queue, + deadline) client_call = ActiveCall.new(call, @client_queue, @pass_through, @pass_through, deadline, read_metadata_tag: meta_tag, From c6a33a21a0c43799af9b2e19de80ca576dfabde6 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Wed, 21 Jan 2015 13:17:09 -0800 Subject: [PATCH 040/143] Removed unexpected reference to auth.rb --- src/ruby/lib/grpc/auth.rb | 68 --------------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 src/ruby/lib/grpc/auth.rb diff --git a/src/ruby/lib/grpc/auth.rb b/src/ruby/lib/grpc/auth.rb deleted file mode 100644 index 8817205b24a..00000000000 --- a/src/ruby/lib/grpc/auth.rb +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2014, 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. - -require 'grpc' -require 'signet' - - -module Google - import Signet::OAuth2 - - # Google::RPC contains the General RPC module. - module RPC - # ServiceAccounCredentials can obtain credentials for a configured service - # account, scopes and issuer. - module Auth - class ServiceAccountCredentials - CREDENTIAL_URI = 'https://accounts.google.com/o/oauth2/token' - AUDIENCE_URI = 'https://accounts.google.com/o/oauth2/token' - - # Initializes an instance with the given scope, issuer and signing_key - def initialize(scope, issuer, key) - @auth_client = Client.new(token_credential_uri: CREDENTIAL_URI, - audience: AUDIENCE_URI, - scope: scope, - issuer: issuer, - signing_key: key) - @auth_token = nil - end - - def metadata_update_proc - proc do |input_md| - input - end - end - - def auth_creds - key = Google::APIClient::KeyUtils.load_from_pkcs12('client.p12', 'notasecret') - end - end - end - end -end From ec8956435dab45acc7e6308ab3854b2711752cb6 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 21 Jan 2015 13:43:39 -0800 Subject: [PATCH 041/143] Switched binary stream to object stream --- src/node/client.js | 53 ++++++++++++++++++++--- src/node/server.js | 64 +++++++++++++++++++++++----- src/node/test/interop_sanity_test.js | 2 +- 3 files changed, 103 insertions(+), 16 deletions(-) diff --git a/src/node/client.js b/src/node/client.js index edaa115d0fc..fe7e07e29cb 100644 --- a/src/node/client.js +++ b/src/node/client.js @@ -45,10 +45,22 @@ util.inherits(GrpcClientStream, Duplex); * from stream.Duplex. * @constructor * @param {grpc.Call} call Call object to proxy - * @param {object} options Stream options + * @param {function(*):Buffer} serialize Serialization function for requests + * @param {function(Buffer):*} deserialize Deserialization function for + * responses */ -function GrpcClientStream(call, options) { - Duplex.call(this, options); +function GrpcClientStream(call, serialize, deserialize) { + Duplex.call(this, {objectMode: true}); + if (!serialize) { + serialize = function(value) { + return value; + }; + } + if (!deserialize) { + deserialize = function(value) { + return value; + }; + } var self = this; // Indicates that we can start reading and have not received a null read var can_read = false; @@ -59,6 +71,32 @@ function GrpcClientStream(call, options) { // Indicates that a write is currently pending var writing = false; this._call = call; + + /** + * Serialize a request value to a buffer. Always maps null to null. Otherwise + * uses the provided serialize function + * @param {*} value The value to serialize + * @return {Buffer} The serialized value + */ + this.serialize = function(value) { + if (value === null || value === undefined) { + return null; + } + return serialize(value); + }; + + /** + * Deserialize a response buffer to a value. Always maps null to null. + * Otherwise uses the provided deserialize function. + * @param {Buffer} buffer The buffer to deserialize + * @return {*} The deserialized value + */ + this.deserialize = function(buffer) { + if (buffer === null) { + return null; + } + return deserialize(buffer); + }; /** * Callback to handle receiving a READ event. Pushes the data from that event * onto the read queue and starts reading again if applicable. @@ -66,7 +104,7 @@ function GrpcClientStream(call, options) { */ function readCallback(event) { var data = event.data; - if (self.push(data)) { + if (self.push(self.deserialize(data))) { if (data == null) { // Disable starting to read after null read was received can_read = false; @@ -102,7 +140,7 @@ function GrpcClientStream(call, options) { next.callback(); writeNext(); }; - call.startWrite(next.chunk, writeCallback, 0); + call.startWrite(self.serialize(next.chunk), writeCallback, 0); } else { writing = false; } @@ -171,6 +209,9 @@ GrpcClientStream.prototype._write = function(chunk, encoding, callback) { * Make a request on the channel to the given method with the given arguments * @param {grpc.Channel} channel The channel on which to make the request * @param {string} method The method to request + * @param {function(*):Buffer} serialize Serialization function for requests + * @param {function(Buffer):*} deserialize Deserialization function for + * responses * @param {array=} metadata Array of metadata key/value pairs to add to the call * @param {(number|Date)=} deadline The deadline for processing this request. * Defaults to infinite future. @@ -178,6 +219,8 @@ GrpcClientStream.prototype._write = function(chunk, encoding, callback) { */ function makeRequest(channel, method, + serialize, + deserialize, metadata, deadline) { if (deadline === undefined) { diff --git a/src/node/server.js b/src/node/server.js index e947032b297..c5f56bc4fc2 100644 --- a/src/node/server.js +++ b/src/node/server.js @@ -47,10 +47,21 @@ util.inherits(GrpcServerStream, Duplex); * from stream.Duplex. * @constructor * @param {grpc.Call} call Call object to proxy - * @param {object} options Stream options + * @param {function(*):Buffer} serialize Serialization function for responses + * @param {function(Buffer):*} deserialize Deserialization function for requests */ -function GrpcServerStream(call, options) { - Duplex.call(this, options); +function GrpcServerStream(call, serialize, deserialize) { + Duplex.call(this, {objectMode: true}); + if (!serialize) { + serialize = function(value) { + return value; + }; + } + if (!deserialize) { + deserialize = function(value) { + return value; + }; + } this._call = call; // Indicate that a status has been sent var finished = false; @@ -59,6 +70,33 @@ function GrpcServerStream(call, options) { 'code' : grpc.status.OK, 'details' : 'OK' }; + + /** + * Serialize a response value to a buffer. Always maps null to null. Otherwise + * uses the provided serialize function + * @param {*} value The value to serialize + * @return {Buffer} The serialized value + */ + this.serialize = function(value) { + if (value === null || value === undefined) { + return null; + } + return serialize(value); + }; + + /** + * Deserialize a request buffer to a value. Always maps null to null. + * Otherwise uses the provided deserialize function. + * @param {Buffer} buffer The buffer to deserialize + * @return {*} The deserialized value + */ + this.deserialize = function(buffer) { + if (buffer === null) { + return null; + } + return deserialize(buffer); + }; + /** * Send the pending status */ @@ -75,7 +113,6 @@ function GrpcServerStream(call, options) { * @param {Error} err The error object */ function setStatus(err) { - console.log('Server setting status to', err); var code = grpc.status.INTERNAL; var details = 'Unknown Error'; @@ -113,7 +150,7 @@ function GrpcServerStream(call, options) { return; } var data = event.data; - if (self.push(data) && data != null) { + if (self.push(deserialize(data)) && data != null) { self._call.startRead(readCallback); } else { reading = false; @@ -155,7 +192,7 @@ GrpcServerStream.prototype._read = function(size) { */ GrpcServerStream.prototype._write = function(chunk, encoding, callback) { var self = this; - self._call.startWrite(chunk, function(event) { + self._call.startWrite(self.serialize(chunk), function(event) { callback(); }, 0); }; @@ -211,12 +248,13 @@ function Server(options) { } }, 0); call.serverEndInitialMetadata(0); - var stream = new GrpcServerStream(call); + var stream = new GrpcServerStream(call, handler.serialize, + handler.deserialize); Object.defineProperty(stream, 'cancelled', { get: function() { return cancelled;} }); try { - handler(stream, data.metadata); + handler.func(stream, data.metadata); } catch (e) { stream.emit('error', e); } @@ -237,14 +275,20 @@ function Server(options) { * handle/respond to. * @param {function} handler Function that takes a stream of request values and * returns a stream of response values + * @param {function(*):Buffer} serialize Serialization function for responses + * @param {function(Buffer):*} deserialize Deserialization function for requests * @return {boolean} True if the handler was set. False if a handler was already * set for that name. */ -Server.prototype.register = function(name, handler) { +Server.prototype.register = function(name, handler, serialize, deserialize) { if (this.handlers.hasOwnProperty(name)) { return false; } - this.handlers[name] = handler; + this.handlers[name] = { + func: handler, + serialize: serialize, + deserialize: deserialize + }; return true; }; diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index 9959a165ad1..7aa95c6aebf 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -52,7 +52,7 @@ describe('Interop tests', function() { }); }); // This depends on not using a binary stream - it.skip('should pass empty_unary', function(done) { + it('should pass empty_unary', function(done) { interop_client.runTest(port, name_override, 'empty_unary', true, done); }); it('should pass large_unary', function(done) { From e7d7559db3073e54ee15febad398d9b927345378 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 14:58:32 -0800 Subject: [PATCH 042/143] Add a gitignore for win32 stuff --- vsprojects/vs2013/.gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 vsprojects/vs2013/.gitignore diff --git a/vsprojects/vs2013/.gitignore b/vsprojects/vs2013/.gitignore new file mode 100644 index 00000000000..75fb4ed6b6f --- /dev/null +++ b/vsprojects/vs2013/.gitignore @@ -0,0 +1,5 @@ +Debug +Release +*.suo +grpc.opensdf +grpc.sdf From e005d221463a1415891de1da01ad90dd841dde91 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 15:02:20 -0800 Subject: [PATCH 043/143] Const correctness fixes --- test/core/end2end/cq_verifier.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c index 9fa513126f6..679537995c0 100644 --- a/test/core/end2end/cq_verifier.c +++ b/test/core/end2end/cq_verifier.c @@ -56,8 +56,8 @@ typedef struct metadata { size_t count; size_t cap; - const char **keys; - const char **values; + char **keys; + char **values; } metadata; /* details what we expect to find on a single event - and forms a linked @@ -409,11 +409,11 @@ static metadata *metadata_from_args(va_list args) { if (md->cap == md->count) { md->cap = GPR_MAX(md->cap + 1, md->cap * 3 / 2); - md->keys = gpr_realloc(md->keys, sizeof(const char *) * md->cap); - md->values = gpr_realloc(md->values, sizeof(const char *) * md->cap); + md->keys = gpr_realloc(md->keys, sizeof(char *) * md->cap); + md->values = gpr_realloc(md->values, sizeof(char *) * md->cap); } - md->keys[md->count] = key; - md->values[md->count] = value; + md->keys[md->count] = (char *)key; + md->values[md->count] = (char *)value; md->count++; } } From f2d39b7d838d6a0a196e02ce0b4813e06e581af3 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 15:06:08 -0800 Subject: [PATCH 044/143] Copy pollset_posix to pollset_windows --- build.json | 2 + src/core/iomgr/pollset_windows.c | 290 +++++++++++++++++++++++++++++++ src/core/iomgr/pollset_windows.h | 98 +++++++++++ 3 files changed, 390 insertions(+) create mode 100644 src/core/iomgr/pollset_windows.c create mode 100644 src/core/iomgr/pollset_windows.h diff --git a/build.json b/build.json index 9a5134ecd6a..40572f256e1 100644 --- a/build.json +++ b/build.json @@ -49,6 +49,7 @@ "src/core/iomgr/pollset_kick.h", "src/core/iomgr/pollset_kick_posix.h", "src/core/iomgr/pollset_posix.h", + "src/core/iomgr/pollset_windows.h", "src/core/iomgr/resolve_address.h", "src/core/iomgr/sockaddr.h", "src/core/iomgr/sockaddr_posix.h", @@ -126,6 +127,7 @@ "src/core/iomgr/pollset_kick_posix.c", "src/core/iomgr/pollset_multipoller_with_poll_posix.c", "src/core/iomgr/pollset_posix.c", + "src/core/iomgr/pollset_windows.c", "src/core/iomgr/resolve_address_posix.c", "src/core/iomgr/sockaddr_utils.c", "src/core/iomgr/socket_utils_common_posix.c", diff --git a/src/core/iomgr/pollset_windows.c b/src/core/iomgr/pollset_windows.c new file mode 100644 index 00000000000..2555322532c --- /dev/null +++ b/src/core/iomgr/pollset_windows.c @@ -0,0 +1,290 @@ +/* + * + * Copyright 2014, 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. + * + */ + +#include "src/core/iomgr/pollset_posix.h" + +#include +#include +#include +#include +#include + +#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/fd_posix.h" +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/socket_utils_posix.h" +#include +#include +#include +#include + +static grpc_pollset g_backup_pollset; +static int g_shutdown_backup_poller; +static gpr_event g_backup_poller_done; + +static void backup_poller(void *p) { + gpr_timespec delta = gpr_time_from_millis(100); + gpr_timespec last_poll = gpr_now(); + + gpr_mu_lock(&g_backup_pollset.mu); + while (g_shutdown_backup_poller == 0) { + gpr_timespec next_poll = gpr_time_add(last_poll, delta); + grpc_pollset_work(&g_backup_pollset, next_poll); + gpr_mu_unlock(&g_backup_pollset.mu); + gpr_sleep_until(next_poll); + gpr_mu_lock(&g_backup_pollset.mu); + last_poll = next_poll; + } + gpr_mu_unlock(&g_backup_pollset.mu); + + gpr_event_set(&g_backup_poller_done, (void *)1); +} + +void grpc_pollset_kick(grpc_pollset *p) { + if (!p->counter) return; + grpc_pollset_kick_kick(&p->kick_state); +} + +void grpc_pollset_force_kick(grpc_pollset *p) { grpc_pollset_kick(p); } + +/* global state management */ + +grpc_pollset *grpc_backup_pollset(void) { return &g_backup_pollset; } + +void grpc_pollset_global_init(void) { + gpr_thd_id id; + + /* Initialize kick fd state */ + grpc_pollset_kick_global_init(); + + /* initialize the backup pollset */ + grpc_pollset_init(&g_backup_pollset); + + /* start the backup poller thread */ + g_shutdown_backup_poller = 0; + gpr_event_init(&g_backup_poller_done); + gpr_thd_new(&id, backup_poller, NULL, NULL); +} + +void grpc_pollset_global_shutdown(void) { + /* terminate the backup poller thread */ + gpr_mu_lock(&g_backup_pollset.mu); + g_shutdown_backup_poller = 1; + gpr_mu_unlock(&g_backup_pollset.mu); + gpr_event_wait(&g_backup_poller_done, gpr_inf_future); + + /* destroy the backup pollset */ + grpc_pollset_destroy(&g_backup_pollset); + + /* destroy the kick pipes */ + grpc_pollset_kick_global_destroy(); +} + +/* main interface */ + +static void become_empty_pollset(grpc_pollset *pollset); +static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd); + +void grpc_pollset_init(grpc_pollset *pollset) { + gpr_mu_init(&pollset->mu); + gpr_cv_init(&pollset->cv); + grpc_pollset_kick_init(&pollset->kick_state); + become_empty_pollset(pollset); +} + +void grpc_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { + gpr_mu_lock(&pollset->mu); + pollset->vtable->add_fd(pollset, fd); + gpr_cv_broadcast(&pollset->cv); + gpr_mu_unlock(&pollset->mu); +} + +void grpc_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { + gpr_mu_lock(&pollset->mu); + pollset->vtable->del_fd(pollset, fd); + gpr_cv_broadcast(&pollset->cv); + gpr_mu_unlock(&pollset->mu); +} + +int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) { + /* pollset->mu already held */ + gpr_timespec now; + now = gpr_now(); + if (gpr_time_cmp(now, deadline) > 0) { + return 0; + } + if (grpc_maybe_call_delayed_callbacks(&pollset->mu, 1)) { + return 1; + } + if (grpc_alarm_check(&pollset->mu, now, &deadline)) { + return 1; + } + return pollset->vtable->maybe_work(pollset, deadline, now, 1); +} + +void grpc_pollset_destroy(grpc_pollset *pollset) { + pollset->vtable->destroy(pollset); + grpc_pollset_kick_destroy(&pollset->kick_state); + gpr_mu_destroy(&pollset->mu); + gpr_cv_destroy(&pollset->cv); +} + +/* + * empty_pollset - a vtable that provides polling for NO file descriptors + */ + +static void empty_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { + become_unary_pollset(pollset, fd); +} + +static void empty_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) {} + +static int empty_pollset_maybe_work(grpc_pollset *pollset, + gpr_timespec deadline, gpr_timespec now, + int allow_synchronous_callback) { + return 0; +} + +static void empty_pollset_destroy(grpc_pollset *pollset) {} + +static const grpc_pollset_vtable empty_pollset = { + empty_pollset_add_fd, empty_pollset_del_fd, empty_pollset_maybe_work, + empty_pollset_destroy}; + +static void become_empty_pollset(grpc_pollset *pollset) { + pollset->vtable = &empty_pollset; +} + +/* + * unary_poll_pollset - a vtable that provides polling for one file descriptor + * via poll() + */ + +static void unary_poll_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { + grpc_fd *fds[2]; + if (fd == pollset->data.ptr) return; + fds[0] = pollset->data.ptr; + fds[1] = fd; + grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds)); + grpc_fd_unref(fds[0]); +} + +static void unary_poll_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { + if (fd == pollset->data.ptr) { + grpc_fd_unref(pollset->data.ptr); + become_empty_pollset(pollset); + } +} + +static int unary_poll_pollset_maybe_work(grpc_pollset *pollset, + gpr_timespec deadline, + gpr_timespec now, + int allow_synchronous_callback) { + struct pollfd pfd[2]; + grpc_fd *fd; + int timeout; + int r; + + if (pollset->counter) { + return 0; + } + fd = pollset->data.ptr; + if (grpc_fd_is_orphaned(fd)) { + grpc_fd_unref(fd); + become_empty_pollset(pollset); + return 0; + } + if (gpr_time_cmp(deadline, gpr_inf_future) == 0) { + timeout = -1; + } else { + timeout = gpr_time_to_millis(gpr_time_sub(deadline, now)); + if (timeout <= 0) { + return 1; + } + } + pfd[0].fd = grpc_pollset_kick_pre_poll(&pollset->kick_state); + if (pfd[0].fd < 0) { + /* Already kicked */ + return 1; + } + pfd[0].events = POLLIN; + pfd[0].revents = 0; + pfd[1].fd = fd->fd; + pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT); + pfd[1].revents = 0; + pollset->counter = 1; + gpr_mu_unlock(&pollset->mu); + + r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout); + if (r < 0) { + if (errno != EINTR) { + gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); + } + } else if (r == 0) { + /* do nothing */ + } else { + if (pfd[0].revents & POLLIN) { + grpc_pollset_kick_consume(&pollset->kick_state); + } + if (pfd[1].revents & POLLIN) { + grpc_fd_become_readable(fd, allow_synchronous_callback); + } + if (pfd[1].revents & POLLOUT) { + grpc_fd_become_writable(fd, allow_synchronous_callback); + } + } + + grpc_pollset_kick_post_poll(&pollset->kick_state); + + gpr_mu_lock(&pollset->mu); + grpc_fd_end_poll(fd, pollset); + pollset->counter = 0; + gpr_cv_broadcast(&pollset->cv); + return 1; +} + +static void unary_poll_pollset_destroy(grpc_pollset *pollset) { + GPR_ASSERT(pollset->counter == 0); + grpc_fd_unref(pollset->data.ptr); +} + +static const grpc_pollset_vtable unary_poll_pollset = { + unary_poll_pollset_add_fd, unary_poll_pollset_del_fd, + unary_poll_pollset_maybe_work, unary_poll_pollset_destroy}; + +static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd) { + pollset->vtable = &unary_poll_pollset; + pollset->counter = 0; + pollset->data.ptr = fd; + grpc_fd_ref(fd); +} diff --git a/src/core/iomgr/pollset_windows.h b/src/core/iomgr/pollset_windows.h new file mode 100644 index 00000000000..f62433707e7 --- /dev/null +++ b/src/core/iomgr/pollset_windows.h @@ -0,0 +1,98 @@ +/* + * + * Copyright 2014, 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. + * + */ + +#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_POSIX_H_ +#define __GRPC_INTERNAL_IOMGR_POLLSET_POSIX_H_ + +#include + +#include "src/core/iomgr/pollset_kick.h" + +typedef struct grpc_pollset_vtable grpc_pollset_vtable; + +/* forward declare only in this file to avoid leaking impl details via + pollset.h; real users of grpc_fd should always include 'fd_posix.h' and not + use the struct tag */ +struct grpc_fd; + +typedef struct grpc_pollset { + /* pollsets under posix can mutate representation as fds are added and + removed. + For example, we may choose a poll() based implementation on linux for + few fds, and an epoll() based implementation for many fds */ + const grpc_pollset_vtable *vtable; + gpr_mu mu; + gpr_cv cv; + grpc_pollset_kick_state kick_state; + int counter; + union { + int fd; + void *ptr; + } data; +} grpc_pollset; + +struct grpc_pollset_vtable { + void (*add_fd)(grpc_pollset *pollset, struct grpc_fd *fd); + void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd); + int (*maybe_work)(grpc_pollset *pollset, gpr_timespec deadline, + gpr_timespec now, int allow_synchronous_callback); + void (*destroy)(grpc_pollset *pollset); +}; + +#define GRPC_POLLSET_MU(pollset) (&(pollset)->mu) +#define GRPC_POLLSET_CV(pollset) (&(pollset)->cv) + +/* Add an fd to a pollset */ +void grpc_pollset_add_fd(grpc_pollset *pollset, struct grpc_fd *fd); +/* Force remove an fd from a pollset (normally they are removed on the next + poll after an fd is orphaned) */ +void grpc_pollset_del_fd(grpc_pollset *pollset, struct grpc_fd *fd); + +/* Force any current pollers to break polling */ +void grpc_pollset_force_kick(grpc_pollset *pollset); +/* Returns the fd to listen on for kicks */ +int grpc_kick_read_fd(grpc_pollset *p); +/* Call after polling has been kicked to leave the kicked state */ +void grpc_kick_drain(grpc_pollset *p); + +/* All fds get added to a backup pollset to ensure that progress is made + regardless of applications listening to events. Relying on this is slow + however (the backup pollset only listens every 100ms or so) - so it's not + to be relied on. */ +grpc_pollset *grpc_backup_pollset(void); + +/* turn a pollset into a multipoller: platform specific */ +void grpc_platform_become_multipoller(grpc_pollset *pollset, + struct grpc_fd **fds, size_t fd_count); + +#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_POSIX_H_ */ From e1addfee06035c7eb1bcd86ff2be84640f1ec318 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 15:08:12 -0800 Subject: [PATCH 045/143] Build projects --- Makefile | 5 +++++ vsprojects/vs2013/grpc.vcxproj | 3 +++ vsprojects/vs2013/grpc_unsecure.vcxproj | 3 +++ 3 files changed, 11 insertions(+) diff --git a/Makefile b/Makefile index 1fefcfd09fe..8338f0f0491 100644 --- a/Makefile +++ b/Makefile @@ -1356,6 +1356,7 @@ LIBGRPC_SRC = \ src/core/iomgr/pollset_kick_posix.c \ src/core/iomgr/pollset_multipoller_with_poll_posix.c \ src/core/iomgr/pollset_posix.c \ + src/core/iomgr/pollset_windows.c \ src/core/iomgr/resolve_address_posix.c \ src/core/iomgr/sockaddr_utils.c \ src/core/iomgr/socket_utils_common_posix.c \ @@ -1474,6 +1475,7 @@ src/core/iomgr/iomgr_posix.c: $(OPENSSL_DEP) src/core/iomgr/pollset_kick_posix.c: $(OPENSSL_DEP) src/core/iomgr/pollset_multipoller_with_poll_posix.c: $(OPENSSL_DEP) src/core/iomgr/pollset_posix.c: $(OPENSSL_DEP) +src/core/iomgr/pollset_windows.c: $(OPENSSL_DEP) src/core/iomgr/resolve_address_posix.c: $(OPENSSL_DEP) src/core/iomgr/sockaddr_utils.c: $(OPENSSL_DEP) src/core/iomgr/socket_utils_common_posix.c: $(OPENSSL_DEP) @@ -1609,6 +1611,7 @@ objs/$(CONFIG)/src/core/iomgr/iomgr_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_kick_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_multipoller_with_poll_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_posix.o: +objs/$(CONFIG)/src/core/iomgr/pollset_windows.o: objs/$(CONFIG)/src/core/iomgr/resolve_address_posix.o: objs/$(CONFIG)/src/core/iomgr/sockaddr_utils.o: objs/$(CONFIG)/src/core/iomgr/socket_utils_common_posix.o: @@ -1764,6 +1767,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/iomgr/pollset_kick_posix.c \ src/core/iomgr/pollset_multipoller_with_poll_posix.c \ src/core/iomgr/pollset_posix.c \ + src/core/iomgr/pollset_windows.c \ src/core/iomgr/resolve_address_posix.c \ src/core/iomgr/sockaddr_utils.c \ src/core/iomgr/socket_utils_common_posix.c \ @@ -1882,6 +1886,7 @@ objs/$(CONFIG)/src/core/iomgr/iomgr_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_kick_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_multipoller_with_poll_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_posix.o: +objs/$(CONFIG)/src/core/iomgr/pollset_windows.o: objs/$(CONFIG)/src/core/iomgr/resolve_address_posix.o: objs/$(CONFIG)/src/core/iomgr/sockaddr_utils.o: objs/$(CONFIG)/src/core/iomgr/socket_utils_common_posix.o: diff --git a/vsprojects/vs2013/grpc.vcxproj b/vsprojects/vs2013/grpc.vcxproj index 05a9966c0e0..15ec7256243 100644 --- a/vsprojects/vs2013/grpc.vcxproj +++ b/vsprojects/vs2013/grpc.vcxproj @@ -122,6 +122,7 @@ + @@ -254,6 +255,8 @@ + + diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj b/vsprojects/vs2013/grpc_unsecure.vcxproj index 05a9966c0e0..15ec7256243 100644 --- a/vsprojects/vs2013/grpc_unsecure.vcxproj +++ b/vsprojects/vs2013/grpc_unsecure.vcxproj @@ -122,6 +122,7 @@ + @@ -254,6 +255,8 @@ + + From d14a1a5f04481a3262d7cbd2d247d01349c5d85b Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 15:26:29 -0800 Subject: [PATCH 046/143] Disable compilation of some files They're not needed on Windows --- include/grpc/support/port_platform.h | 8 + src/core/iomgr/endpoint_pair_posix.c | 6 + src/core/iomgr/fd_posix.c | 6 + src/core/iomgr/pollset.h | 4 + src/core/iomgr/pollset_kick.h | 6 +- src/core/iomgr/pollset_kick_posix.c | 6 + src/core/iomgr/pollset_kick_windows.h | 45 +++++ src/core/iomgr/pollset_posix.c | 6 + src/core/iomgr/pollset_windows.c | 258 +----------------------- src/core/iomgr/pollset_windows.h | 54 +---- vsprojects/vs2013/grpc_unsecure.vcxproj | 6 +- 11 files changed, 96 insertions(+), 309 deletions(-) create mode 100644 src/core/iomgr/pollset_kick_windows.h diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h index 05a5bbe1bc9..8eb23e5eff0 100644 --- a/include/grpc/support/port_platform.h +++ b/include/grpc/support/port_platform.h @@ -132,6 +132,14 @@ #error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32 #endif +#if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_POLLSET) +#error Must define GPR_POSIX_POLLSET to use GPR_POSIX_MULTIPOLL_WITH_POLL +#endif + +#if defined(GPR_POSIX_SOCKET) + defined(GPR_WIN32) != 1 +#error Must define exactly one of GPR_POSIX_POLLSET, GPR_WIN32 +#endif + typedef int16_t gpr_int16; typedef int32_t gpr_int32; typedef int64_t gpr_int64; diff --git a/src/core/iomgr/endpoint_pair_posix.c b/src/core/iomgr/endpoint_pair_posix.c index f08d1344ebb..3f53402cf3c 100644 --- a/src/core/iomgr/endpoint_pair_posix.c +++ b/src/core/iomgr/endpoint_pair_posix.c @@ -31,6 +31,10 @@ * */ +#include + +#ifdef GPR_POSIX_SOCKET + #include "src/core/iomgr/endpoint_pair.h" #include @@ -59,3 +63,5 @@ grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(size_t read_slice_size) { p.server = grpc_tcp_create(grpc_fd_create(sv[0]), read_slice_size); return p; } + +#endif diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c index 3cd2f9a8e0c..9f70a26c643 100644 --- a/src/core/iomgr/fd_posix.c +++ b/src/core/iomgr/fd_posix.c @@ -31,6 +31,10 @@ * */ +#include + +#ifdef GPR_POSIX_SOCKET + #include "src/core/iomgr/fd_posix.h" #include @@ -272,3 +276,5 @@ void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback) { void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback) { set_ready(fd, &fd->writest, allow_synchronous_callback); } + +#endif diff --git a/src/core/iomgr/pollset.h b/src/core/iomgr/pollset.h index 36d80d5c297..b9fcf45ea68 100644 --- a/src/core/iomgr/pollset.h +++ b/src/core/iomgr/pollset.h @@ -48,6 +48,10 @@ #include "src/core/iomgr/pollset_posix.h" #endif +#ifdef GPR_WIN32 +#include "src/core/iomgr/pollset_windows.h" +#endif + void grpc_pollset_init(grpc_pollset *pollset); void grpc_pollset_destroy(grpc_pollset *pollset); diff --git a/src/core/iomgr/pollset_kick.h b/src/core/iomgr/pollset_kick.h index f088818b9a1..02f3e414337 100644 --- a/src/core/iomgr/pollset_kick.h +++ b/src/core/iomgr/pollset_kick.h @@ -41,8 +41,10 @@ #ifdef GPR_POSIX_SOCKET #include "src/core/iomgr/pollset_kick_posix.h" -#else -#error "No pollset kick support on platform" +#endif + +#ifdef GPR_WIN32 +#include "src/core/iomgr/pollset_kick_windows.h" #endif void grpc_pollset_kick_global_init(void); diff --git a/src/core/iomgr/pollset_kick_posix.c b/src/core/iomgr/pollset_kick_posix.c index d16e49e4595..a04a6b430af 100644 --- a/src/core/iomgr/pollset_kick_posix.c +++ b/src/core/iomgr/pollset_kick_posix.c @@ -31,6 +31,10 @@ * */ +#include + +#ifdef GPR_POSIX_SOCKET + #include "src/core/iomgr/pollset_kick_posix.h" #include @@ -159,3 +163,5 @@ void grpc_pollset_kick_kick(grpc_pollset_kick_state *kick_state) { } gpr_mu_unlock(&kick_state->mu); } + +#endif diff --git a/src/core/iomgr/pollset_kick_windows.h b/src/core/iomgr/pollset_kick_windows.h new file mode 100644 index 00000000000..243e519dad6 --- /dev/null +++ b/src/core/iomgr/pollset_kick_windows.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_KICK_WINDOWS_H_ +#define __GRPC_INTERNAL_IOMGR_POLLSET_KICK_WINDOWS_H_ + +#include + +struct grpc_kick_pipe_info; + +typedef struct grpc_pollset_kick_state { + int unused; +} grpc_pollset_kick_state; + +#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_KICK_WINDOWS_H_ */ diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 2555322532c..39e2dc46672 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -31,6 +31,10 @@ * */ +#include + +#ifdef GPR_POSIX_SOCKET + #include "src/core/iomgr/pollset_posix.h" #include @@ -288,3 +292,5 @@ static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd) { pollset->data.ptr = fd; grpc_fd_ref(fd); } + +#endif /* GPR_POSIX_POLLSET */ diff --git a/src/core/iomgr/pollset_windows.c b/src/core/iomgr/pollset_windows.c index 2555322532c..c913c21461c 100644 --- a/src/core/iomgr/pollset_windows.c +++ b/src/core/iomgr/pollset_windows.c @@ -31,260 +31,8 @@ * */ -#include "src/core/iomgr/pollset_posix.h" +#include -#include -#include -#include -#include -#include +#ifdef GPR_WIN32 -#include "src/core/iomgr/alarm_internal.h" -#include "src/core/iomgr/fd_posix.h" -#include "src/core/iomgr/iomgr_internal.h" -#include "src/core/iomgr/socket_utils_posix.h" -#include -#include -#include -#include - -static grpc_pollset g_backup_pollset; -static int g_shutdown_backup_poller; -static gpr_event g_backup_poller_done; - -static void backup_poller(void *p) { - gpr_timespec delta = gpr_time_from_millis(100); - gpr_timespec last_poll = gpr_now(); - - gpr_mu_lock(&g_backup_pollset.mu); - while (g_shutdown_backup_poller == 0) { - gpr_timespec next_poll = gpr_time_add(last_poll, delta); - grpc_pollset_work(&g_backup_pollset, next_poll); - gpr_mu_unlock(&g_backup_pollset.mu); - gpr_sleep_until(next_poll); - gpr_mu_lock(&g_backup_pollset.mu); - last_poll = next_poll; - } - gpr_mu_unlock(&g_backup_pollset.mu); - - gpr_event_set(&g_backup_poller_done, (void *)1); -} - -void grpc_pollset_kick(grpc_pollset *p) { - if (!p->counter) return; - grpc_pollset_kick_kick(&p->kick_state); -} - -void grpc_pollset_force_kick(grpc_pollset *p) { grpc_pollset_kick(p); } - -/* global state management */ - -grpc_pollset *grpc_backup_pollset(void) { return &g_backup_pollset; } - -void grpc_pollset_global_init(void) { - gpr_thd_id id; - - /* Initialize kick fd state */ - grpc_pollset_kick_global_init(); - - /* initialize the backup pollset */ - grpc_pollset_init(&g_backup_pollset); - - /* start the backup poller thread */ - g_shutdown_backup_poller = 0; - gpr_event_init(&g_backup_poller_done); - gpr_thd_new(&id, backup_poller, NULL, NULL); -} - -void grpc_pollset_global_shutdown(void) { - /* terminate the backup poller thread */ - gpr_mu_lock(&g_backup_pollset.mu); - g_shutdown_backup_poller = 1; - gpr_mu_unlock(&g_backup_pollset.mu); - gpr_event_wait(&g_backup_poller_done, gpr_inf_future); - - /* destroy the backup pollset */ - grpc_pollset_destroy(&g_backup_pollset); - - /* destroy the kick pipes */ - grpc_pollset_kick_global_destroy(); -} - -/* main interface */ - -static void become_empty_pollset(grpc_pollset *pollset); -static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd); - -void grpc_pollset_init(grpc_pollset *pollset) { - gpr_mu_init(&pollset->mu); - gpr_cv_init(&pollset->cv); - grpc_pollset_kick_init(&pollset->kick_state); - become_empty_pollset(pollset); -} - -void grpc_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { - gpr_mu_lock(&pollset->mu); - pollset->vtable->add_fd(pollset, fd); - gpr_cv_broadcast(&pollset->cv); - gpr_mu_unlock(&pollset->mu); -} - -void grpc_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { - gpr_mu_lock(&pollset->mu); - pollset->vtable->del_fd(pollset, fd); - gpr_cv_broadcast(&pollset->cv); - gpr_mu_unlock(&pollset->mu); -} - -int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) { - /* pollset->mu already held */ - gpr_timespec now; - now = gpr_now(); - if (gpr_time_cmp(now, deadline) > 0) { - return 0; - } - if (grpc_maybe_call_delayed_callbacks(&pollset->mu, 1)) { - return 1; - } - if (grpc_alarm_check(&pollset->mu, now, &deadline)) { - return 1; - } - return pollset->vtable->maybe_work(pollset, deadline, now, 1); -} - -void grpc_pollset_destroy(grpc_pollset *pollset) { - pollset->vtable->destroy(pollset); - grpc_pollset_kick_destroy(&pollset->kick_state); - gpr_mu_destroy(&pollset->mu); - gpr_cv_destroy(&pollset->cv); -} - -/* - * empty_pollset - a vtable that provides polling for NO file descriptors - */ - -static void empty_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { - become_unary_pollset(pollset, fd); -} - -static void empty_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) {} - -static int empty_pollset_maybe_work(grpc_pollset *pollset, - gpr_timespec deadline, gpr_timespec now, - int allow_synchronous_callback) { - return 0; -} - -static void empty_pollset_destroy(grpc_pollset *pollset) {} - -static const grpc_pollset_vtable empty_pollset = { - empty_pollset_add_fd, empty_pollset_del_fd, empty_pollset_maybe_work, - empty_pollset_destroy}; - -static void become_empty_pollset(grpc_pollset *pollset) { - pollset->vtable = &empty_pollset; -} - -/* - * unary_poll_pollset - a vtable that provides polling for one file descriptor - * via poll() - */ - -static void unary_poll_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { - grpc_fd *fds[2]; - if (fd == pollset->data.ptr) return; - fds[0] = pollset->data.ptr; - fds[1] = fd; - grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds)); - grpc_fd_unref(fds[0]); -} - -static void unary_poll_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { - if (fd == pollset->data.ptr) { - grpc_fd_unref(pollset->data.ptr); - become_empty_pollset(pollset); - } -} - -static int unary_poll_pollset_maybe_work(grpc_pollset *pollset, - gpr_timespec deadline, - gpr_timespec now, - int allow_synchronous_callback) { - struct pollfd pfd[2]; - grpc_fd *fd; - int timeout; - int r; - - if (pollset->counter) { - return 0; - } - fd = pollset->data.ptr; - if (grpc_fd_is_orphaned(fd)) { - grpc_fd_unref(fd); - become_empty_pollset(pollset); - return 0; - } - if (gpr_time_cmp(deadline, gpr_inf_future) == 0) { - timeout = -1; - } else { - timeout = gpr_time_to_millis(gpr_time_sub(deadline, now)); - if (timeout <= 0) { - return 1; - } - } - pfd[0].fd = grpc_pollset_kick_pre_poll(&pollset->kick_state); - if (pfd[0].fd < 0) { - /* Already kicked */ - return 1; - } - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = fd->fd; - pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT); - pfd[1].revents = 0; - pollset->counter = 1; - gpr_mu_unlock(&pollset->mu); - - r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout); - if (r < 0) { - if (errno != EINTR) { - gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); - } - } else if (r == 0) { - /* do nothing */ - } else { - if (pfd[0].revents & POLLIN) { - grpc_pollset_kick_consume(&pollset->kick_state); - } - if (pfd[1].revents & POLLIN) { - grpc_fd_become_readable(fd, allow_synchronous_callback); - } - if (pfd[1].revents & POLLOUT) { - grpc_fd_become_writable(fd, allow_synchronous_callback); - } - } - - grpc_pollset_kick_post_poll(&pollset->kick_state); - - gpr_mu_lock(&pollset->mu); - grpc_fd_end_poll(fd, pollset); - pollset->counter = 0; - gpr_cv_broadcast(&pollset->cv); - return 1; -} - -static void unary_poll_pollset_destroy(grpc_pollset *pollset) { - GPR_ASSERT(pollset->counter == 0); - grpc_fd_unref(pollset->data.ptr); -} - -static const grpc_pollset_vtable unary_poll_pollset = { - unary_poll_pollset_add_fd, unary_poll_pollset_del_fd, - unary_poll_pollset_maybe_work, unary_poll_pollset_destroy}; - -static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd) { - pollset->vtable = &unary_poll_pollset; - pollset->counter = 0; - pollset->data.ptr = fd; - grpc_fd_ref(fd); -} +#endif /* GPR_WIN32 */ \ No newline at end of file diff --git a/src/core/iomgr/pollset_windows.h b/src/core/iomgr/pollset_windows.h index f62433707e7..53b9ffa5abb 100644 --- a/src/core/iomgr/pollset_windows.h +++ b/src/core/iomgr/pollset_windows.h @@ -31,68 +31,24 @@ * */ -#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_POSIX_H_ -#define __GRPC_INTERNAL_IOMGR_POLLSET_POSIX_H_ +#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_ +#define __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_ #include #include "src/core/iomgr/pollset_kick.h" -typedef struct grpc_pollset_vtable grpc_pollset_vtable; - /* forward declare only in this file to avoid leaking impl details via pollset.h; real users of grpc_fd should always include 'fd_posix.h' and not use the struct tag */ struct grpc_fd; typedef struct grpc_pollset { - /* pollsets under posix can mutate representation as fds are added and - removed. - For example, we may choose a poll() based implementation on linux for - few fds, and an epoll() based implementation for many fds */ - const grpc_pollset_vtable *vtable; - gpr_mu mu; - gpr_cv cv; - grpc_pollset_kick_state kick_state; - int counter; - union { - int fd; - void *ptr; - } data; + gpr_mu mu; + gpr_cv cv; } grpc_pollset; -struct grpc_pollset_vtable { - void (*add_fd)(grpc_pollset *pollset, struct grpc_fd *fd); - void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd); - int (*maybe_work)(grpc_pollset *pollset, gpr_timespec deadline, - gpr_timespec now, int allow_synchronous_callback); - void (*destroy)(grpc_pollset *pollset); -}; - #define GRPC_POLLSET_MU(pollset) (&(pollset)->mu) #define GRPC_POLLSET_CV(pollset) (&(pollset)->cv) -/* Add an fd to a pollset */ -void grpc_pollset_add_fd(grpc_pollset *pollset, struct grpc_fd *fd); -/* Force remove an fd from a pollset (normally they are removed on the next - poll after an fd is orphaned) */ -void grpc_pollset_del_fd(grpc_pollset *pollset, struct grpc_fd *fd); - -/* Force any current pollers to break polling */ -void grpc_pollset_force_kick(grpc_pollset *pollset); -/* Returns the fd to listen on for kicks */ -int grpc_kick_read_fd(grpc_pollset *p); -/* Call after polling has been kicked to leave the kicked state */ -void grpc_kick_drain(grpc_pollset *p); - -/* All fds get added to a backup pollset to ensure that progress is made - regardless of applications listening to events. Relying on this is slow - however (the backup pollset only listens every 100ms or so) - so it's not - to be relied on. */ -grpc_pollset *grpc_backup_pollset(void); - -/* turn a pollset into a multipoller: platform specific */ -void grpc_platform_become_multipoller(grpc_pollset *pollset, - struct grpc_fd **fds, size_t fd_count); - -#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_POSIX_H_ */ +#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_ */ diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj b/vsprojects/vs2013/grpc_unsecure.vcxproj index 15ec7256243..17b3ab76500 100644 --- a/vsprojects/vs2013/grpc_unsecure.vcxproj +++ b/vsprojects/vs2013/grpc_unsecure.vcxproj @@ -1,4 +1,4 @@ - + @@ -11,7 +11,7 @@ - {29D16885-7228-4C31-81ED-5F9187C7F2A9} + {46CEDFFF-9692-456A-AA24-38B5D6BCF4C5} @@ -368,4 +368,4 @@ - + \ No newline at end of file From 6a6e4a137021a74dda7bd7c2430c890a289febe8 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 15:28:24 -0800 Subject: [PATCH 047/143] Build projects --- build.json | 1 + vsprojects/vs2013/grpc.vcxproj | 1 + vsprojects/vs2013/grpc_unsecure.vcxproj | 7 ++++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build.json b/build.json index 40572f256e1..5057bceff27 100644 --- a/build.json +++ b/build.json @@ -48,6 +48,7 @@ "src/core/iomgr/pollset.h", "src/core/iomgr/pollset_kick.h", "src/core/iomgr/pollset_kick_posix.h", + "src/core/iomgr/pollset_kick_windows.h", "src/core/iomgr/pollset_posix.h", "src/core/iomgr/pollset_windows.h", "src/core/iomgr/resolve_address.h", diff --git a/vsprojects/vs2013/grpc.vcxproj b/vsprojects/vs2013/grpc.vcxproj index 15ec7256243..8249272f78c 100644 --- a/vsprojects/vs2013/grpc.vcxproj +++ b/vsprojects/vs2013/grpc.vcxproj @@ -121,6 +121,7 @@ + diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj b/vsprojects/vs2013/grpc_unsecure.vcxproj index 17b3ab76500..8249272f78c 100644 --- a/vsprojects/vs2013/grpc_unsecure.vcxproj +++ b/vsprojects/vs2013/grpc_unsecure.vcxproj @@ -1,4 +1,4 @@ - + @@ -11,7 +11,7 @@ - {46CEDFFF-9692-456A-AA24-38B5D6BCF4C5} + {29D16885-7228-4C31-81ED-5F9187C7F2A9} @@ -121,6 +121,7 @@ + @@ -368,4 +369,4 @@ - \ No newline at end of file + From dedbae744a6c8d816daeb42b1b080398d1b9d2c5 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 15:37:55 -0800 Subject: [PATCH 048/143] Typo fix --- include/grpc/support/port_platform.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h index 8eb23e5eff0..118a919aee4 100644 --- a/include/grpc/support/port_platform.h +++ b/include/grpc/support/port_platform.h @@ -132,8 +132,8 @@ #error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32 #endif -#if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_POLLSET) -#error Must define GPR_POSIX_POLLSET to use GPR_POSIX_MULTIPOLL_WITH_POLL +#if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_SOCKET) +#error Must define GPR_POSIX_SOCKET to use GPR_POSIX_MULTIPOLL_WITH_POLL #endif #if defined(GPR_POSIX_SOCKET) + defined(GPR_WIN32) != 1 From 0c0b60c322a92d8c72c14a1b1cf057bcc5c16cc4 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 15:49:28 -0800 Subject: [PATCH 049/143] Add platform ifdefs, fix up some MSVC warnings --- src/core/iomgr/resolve_address.h | 6 ++++-- src/core/iomgr/socket_utils_common_posix.c | 6 ++++++ src/core/iomgr/tcp_client_posix.c | 6 ++++++ src/core/iomgr/tcp_posix.c | 6 ++++++ src/core/iomgr/tcp_server.h | 3 --- src/core/iomgr/tcp_server_posix.c | 6 ++++++ src/core/transport/chttp2/frame_data.c | 2 +- src/core/transport/chttp2/hpack_parser.c | 2 +- src/core/transport/chttp2_transport.c | 2 +- src/core/transport/stream_op.c | 2 +- test/core/transport/transport_end2end_tests.c | 5 +++-- test/core/util/test_config.c | 2 ++ 12 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/core/iomgr/resolve_address.h b/src/core/iomgr/resolve_address.h index 37ec0f03350..7b537b17674 100644 --- a/src/core/iomgr/resolve_address.h +++ b/src/core/iomgr/resolve_address.h @@ -34,10 +34,12 @@ #ifndef __GRPC_INTERNAL_IOMGR_RESOLVE_ADDRESS_H__ #define __GRPC_INTERNAL_IOMGR_RESOLVE_ADDRESS_H__ -#include +#include + +#define GRPC_MAX_SOCKADDR_SIZE 128 typedef struct { - struct sockaddr_storage addr; + char addr[GRPC_MAX_SOCKADDR_SIZE]; int len; } grpc_resolved_address; diff --git a/src/core/iomgr/socket_utils_common_posix.c b/src/core/iomgr/socket_utils_common_posix.c index d65b025d700..4b1ae3d4c76 100644 --- a/src/core/iomgr/socket_utils_common_posix.c +++ b/src/core/iomgr/socket_utils_common_posix.c @@ -31,6 +31,10 @@ * */ +#include + +#ifdef GPR_POSIX_SOCKET + #include "src/core/iomgr/socket_utils_posix.h" #include @@ -187,3 +191,5 @@ int grpc_create_dualstack_socket(const struct sockaddr *addr, int type, *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE; return socket(family, type, protocol); } + +#endif diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c index d675c2dcece..851530ce685 100644 --- a/src/core/iomgr/tcp_client_posix.c +++ b/src/core/iomgr/tcp_client_posix.c @@ -31,6 +31,10 @@ * */ +#include + +#ifdef GPR_POSIX_SOCKET + #include "src/core/iomgr/tcp_client.h" #include @@ -229,3 +233,5 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now()); grpc_fd_notify_on_write(ac->fd, on_writable, ac); } + +#endif diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c index 657f34aaf99..64996bd07d1 100644 --- a/src/core/iomgr/tcp_posix.c +++ b/src/core/iomgr/tcp_posix.c @@ -31,6 +31,10 @@ * */ +#include + +#ifdef GPR_POSIX_SOCKET + #include "src/core/iomgr/tcp_posix.h" #include @@ -539,3 +543,5 @@ grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size) { tcp->em_fd = em_fd; return &tcp->base; } + +#endif diff --git a/src/core/iomgr/tcp_server.h b/src/core/iomgr/tcp_server.h index 8ffd7d3569e..7923963ad9e 100644 --- a/src/core/iomgr/tcp_server.h +++ b/src/core/iomgr/tcp_server.h @@ -34,9 +34,6 @@ #ifndef __GRPC_INTERNAL_IOMGR_TCP_SERVER_H__ #define __GRPC_INTERNAL_IOMGR_TCP_SERVER_H__ -#include -#include - #include "src/core/iomgr/endpoint.h" /* Forward decl of grpc_tcp_server */ diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c index 5762eb8a970..9f8e1db7b50 100644 --- a/src/core/iomgr/tcp_server_posix.c +++ b/src/core/iomgr/tcp_server_posix.c @@ -31,6 +31,10 @@ * */ +#include + +#ifdef GRPC_POSIX_SOCKET + #define _GNU_SOURCE #include "src/core/iomgr/tcp_server.h" @@ -364,3 +368,5 @@ void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset, } gpr_mu_unlock(&s->mu); } + +#endif diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c index c22a2237370..00b020b31b1 100644 --- a/src/core/transport/chttp2/frame_data.c +++ b/src/core/transport/chttp2/frame_data.c @@ -141,7 +141,7 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( gpr_slice_sub(slice, cur - beg, end - beg)); p->state = GRPC_CHTTP2_DATA_FH_0; return GRPC_CHTTP2_PARSE_OK; - } else if (end - cur > p->frame_size) { + } else if ((gpr_uint32)(end - cur) > p->frame_size) { state->need_flush_reads = 1; grpc_sopb_add_slice( &p->incoming_sopb, diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c index 07b0f81098c..64e08ffac72 100644 --- a/src/core/transport/chttp2/hpack_parser.c +++ b/src/core/transport/chttp2/hpack_parser.c @@ -1212,7 +1212,7 @@ static int huff_nibble(grpc_chttp2_hpack_parser *p, gpr_uint8 nibble) { gpr_int16 next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble]; if (emit != -1) { if (emit >= 0 && emit < 256) { - gpr_uint8 c = emit; + gpr_uint8 c = (gpr_uint8) emit; if (!append_string(p, &c, (&c) + 1)) return 0; } else { assert(emit == 256); diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index e61afb71ae8..17b37d6d4ae 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -1611,7 +1611,7 @@ static int process_read(transport *t, gpr_slice slice) { } t->deframe_state = DTS_FH_0; return 1; - } else if (end - cur > t->incoming_frame_size) { + } else if ((gpr_uint32)(end - cur) > t->incoming_frame_size) { if (!parse_frame_slice( t, gpr_slice_sub_no_ref(slice, cur - beg, cur + t->incoming_frame_size - beg), diff --git a/src/core/transport/stream_op.c b/src/core/transport/stream_op.c index c77c8cde1f1..555543fc4b8 100644 --- a/src/core/transport/stream_op.c +++ b/src/core/transport/stream_op.c @@ -63,7 +63,7 @@ void grpc_sopb_reset(grpc_stream_op_buffer *sopb) { } void grpc_stream_ops_unref_owned_objects(grpc_stream_op *ops, size_t nops) { - int i; + size_t i; for (i = 0; i < nops; i++) { switch (ops[i].type) { case GRPC_OP_SLICE: diff --git a/test/core/transport/transport_end2end_tests.c b/test/core/transport/transport_end2end_tests.c index 712081bc8ab..5d26ef53b90 100644 --- a/test/core/transport/transport_end2end_tests.c +++ b/test/core/transport/transport_end2end_tests.c @@ -129,7 +129,8 @@ static void expect_metadata(test_stream *s, int from_client, const char *key, /* Convert some number of seconds into a gpr_timespec that many seconds in the future */ static gpr_timespec deadline_from_seconds(double deadline_seconds) { - return gpr_time_add(gpr_now(), gpr_time_from_micros(deadline_seconds * 1e6)); + return gpr_time_add(gpr_now(), + gpr_time_from_micros((long)(deadline_seconds * 1e6))); } /* Init a test_user_data instance */ @@ -573,7 +574,7 @@ static grpc_transport_setup_result setup_client_transport( name - the name of this test */ static void begin_test(test_fixture *f, grpc_transport_test_config *config, const char *name) { - gpr_timespec timeout = gpr_time_add(gpr_now(), gpr_time_from_micros(100e6)); + gpr_timespec timeout = gpr_time_add(gpr_now(), gpr_time_from_seconds(100)); gpr_log(GPR_INFO, "BEGIN: %s/%s", name, config->name); diff --git a/test/core/util/test_config.c b/test/core/util/test_config.c index 6df86b593f4..5f3b55da75b 100644 --- a/test/core/util/test_config.c +++ b/test/core/util/test_config.c @@ -48,8 +48,10 @@ static int seed(void) { return _getpid(); } #endif void grpc_test_init(int argc, char **argv) { +#ifndef GPR_WIN32 /* disable SIGPIPE */ signal(SIGPIPE, SIG_IGN); +#endif /* seed rng with pid, so we don't end up with the same random numbers as a concurrently running test binary */ srand(seed()); From a172ad7d41d0cc7ede7e3d8754e367e60ab750c1 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 15:51:47 -0800 Subject: [PATCH 050/143] Typo fix --- src/core/iomgr/tcp_server.h | 2 +- src/core/iomgr/tcp_server_posix.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/iomgr/tcp_server.h b/src/core/iomgr/tcp_server.h index 7923963ad9e..c4d836e9b53 100644 --- a/src/core/iomgr/tcp_server.h +++ b/src/core/iomgr/tcp_server.h @@ -60,7 +60,7 @@ void grpc_tcp_server_start(grpc_tcp_server *server, grpc_pollset *pollset, For raw access to the underlying sockets, see grpc_tcp_server_get_fd(). */ /* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle all of the multiple socket port matching logic in one place */ -int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr, +int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, int addr_len); /* Returns the file descriptor of the Nth listening socket on this server, diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c index 9f8e1db7b50..f1e5a6a8cd2 100644 --- a/src/core/iomgr/tcp_server_posix.c +++ b/src/core/iomgr/tcp_server_posix.c @@ -33,7 +33,7 @@ #include -#ifdef GRPC_POSIX_SOCKET +#ifdef GPR_POSIX_SOCKET #define _GNU_SOURCE #include "src/core/iomgr/tcp_server.h" @@ -269,7 +269,7 @@ static int add_socket_to_server(grpc_tcp_server *s, int fd, return port; } -int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr, +int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, int addr_len) { int allocated_port1 = -1; int allocated_port2 = -1; From 8a3ca244b789a36edfcfaa7cf1c5f4a5ece1427a Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Wed, 21 Jan 2015 16:09:11 -0800 Subject: [PATCH 051/143] bugfix: corrects a bug in grpc_sync_images --- tools/gce_setup/grpc_docker.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index bf776126b5a..314ccb0ca8b 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -412,7 +412,6 @@ grpc_sync_images() { # declare vars local so that they don't pollute the shell environment # where they this func is used. local grpc_zone grpc_project dry_run # set by _grpc_set_project_and_zone - # set by grpc_sync_images local grpc_hosts # set the project zone and check that all necessary args are provided @@ -425,7 +424,7 @@ grpc_sync_images() { local host for host in $grpc_hosts do - gce_has_instance $grpc_project $h || return 1; + gce_has_instance $grpc_project $host || return 1; local ssh_cmd="bash -l -c \"$cmd\"" echo "will run:" echo " $ssh_cmd" From b4ee3b597789f4a8cb7e729a7e5f547a6b967198 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 16:22:50 -0800 Subject: [PATCH 052/143] Make libraries compile on Darwin --- Makefile | 150 +++++++++++++++++++++++++++++++++++- templates/Makefile.template | 10 ++- 2 files changed, 158 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1fefcfd09fe..28bb81b6dea 100644 --- a/Makefile +++ b/Makefile @@ -524,8 +524,12 @@ libs/$(CONFIG)/zlib/libz.a: $(Q)cp third_party/zlib/libz.a libs/$(CONFIG)/zlib libs/$(CONFIG)/openssl/libssl.a: - $(E) "[MAKE] Building openssl" + $(E) "[MAKE] Building openssl for $(SYSTEM)" +ifeq ($(SYSTEM),Darwin) + $(Q)(cd third_party/openssl ; CC="$(CC) -fPIC -fvisibility=hidden $(CPPFLAGS_$(CONFIG)) $(OPENSSL_CFLAGS_$(CONFIG))" ./Configure darwin64-x86_64-cc $(OPENSSL_CONFIG_$(CONFIG))) +else $(Q)(cd third_party/openssl ; CC="$(CC) -fPIC -fvisibility=hidden $(CPPFLAGS_$(CONFIG)) $(OPENSSL_CFLAGS_$(CONFIG))" ./config $(OPENSSL_CONFIG_$(CONFIG))) +endif $(Q)$(MAKE) -C third_party/openssl clean $(Q)$(MAKE) -C third_party/openssl build_crypto build_ssl $(Q)mkdir -p libs/$(CONFIG)/openssl @@ -1220,7 +1224,11 @@ LIBGPR_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGPR_S libs/$(CONFIG)/libgpr.a: $(ZLIB_DEP) $(LIBGPR_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libgpr.a $(Q) $(AR) rcs libs/$(CONFIG)/libgpr.a $(LIBGPR_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libgpr.a +endif @@ -1296,7 +1304,11 @@ endif libs/$(CONFIG)/libgpr_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGPR_TEST_UTIL_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libgpr_test_util.a $(Q) $(AR) rcs libs/$(CONFIG)/libgpr_test_util.a $(LIBGPR_TEST_UTIL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libgpr_test_util.a +endif @@ -1530,6 +1542,7 @@ endif libs/$(CONFIG)/libgrpc.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libgrpc.a $(Q) $(AR) rcs libs/$(CONFIG)/libgrpc.a $(LIBGRPC_OBJS) $(Q) rm -rf tmp-merge $(Q) mkdir tmp-merge @@ -1538,6 +1551,9 @@ libs/$(CONFIG)/libgrpc.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_OBJS) $(Q) rm -f libs/$(CONFIG)/libgrpc.a tmp-merge/__.SYMDEF* $(Q) ar rcs libs/$(CONFIG)/libgrpc.a tmp-merge/* $(Q) rm -rf tmp-merge +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libgrpc.a +endif @@ -1706,7 +1722,11 @@ endif libs/$(CONFIG)/libgrpc_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_TEST_UTIL_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libgrpc_test_util.a $(Q) $(AR) rcs libs/$(CONFIG)/libgrpc_test_util.a $(LIBGRPC_TEST_UTIL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libgrpc_test_util.a +endif @@ -1827,7 +1847,11 @@ LIBGRPC_UNSECURE_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename libs/$(CONFIG)/libgrpc_unsecure.a: $(ZLIB_DEP) $(LIBGRPC_UNSECURE_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libgrpc_unsecure.a $(Q) $(AR) rcs libs/$(CONFIG)/libgrpc_unsecure.a $(LIBGRPC_UNSECURE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libgrpc_unsecure.a +endif @@ -2020,7 +2044,11 @@ endif libs/$(CONFIG)/libgrpc++.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC++_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libgrpc++.a $(Q) $(AR) rcs libs/$(CONFIG)/libgrpc++.a $(LIBGRPC++_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libgrpc++.a +endif @@ -2102,7 +2130,11 @@ endif libs/$(CONFIG)/libgrpc++_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC++_TEST_UTIL_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libgrpc++_test_util.a $(Q) $(AR) rcs libs/$(CONFIG)/libgrpc++_test_util.a $(LIBGRPC++_TEST_UTIL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libgrpc++_test_util.a +endif @@ -2145,7 +2177,11 @@ endif libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_CHTTP2_FAKE_SECURITY_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a $(LIBEND2END_FIXTURE_CHTTP2_FAKE_SECURITY_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a +endif @@ -2184,7 +2220,11 @@ endif libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_CHTTP2_FULLSTACK_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a $(LIBEND2END_FIXTURE_CHTTP2_FULLSTACK_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a +endif @@ -2223,7 +2263,11 @@ endif libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_CHTTP2_SIMPLE_SSL_FULLSTACK_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a $(LIBEND2END_FIXTURE_CHTTP2_SIMPLE_SSL_FULLSTACK_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a +endif @@ -2262,7 +2306,11 @@ endif libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a $(LIBEND2END_FIXTURE_CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a +endif @@ -2301,7 +2349,11 @@ endif libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a +endif @@ -2340,7 +2392,11 @@ endif libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a $(LIBEND2END_FIXTURE_CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a +endif @@ -2366,7 +2422,11 @@ LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuf libs/$(CONFIG)/libend2end_test_cancel_after_accept.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_cancel_after_accept.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_cancel_after_accept.a +endif @@ -2388,7 +2448,11 @@ LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_OBJS = $(addprefix objs/$( libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a $(LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_AND_WRITES_CLOSED_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a +endif @@ -2410,7 +2474,11 @@ LIBEND2END_TEST_CANCEL_AFTER_INVOKE_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuf libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_AFTER_INVOKE_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBEND2END_TEST_CANCEL_AFTER_INVOKE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a +endif @@ -2432,7 +2500,11 @@ LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_OBJS = $(addprefix objs/$(CONFIG)/, $(addsu libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a +endif @@ -2454,7 +2526,11 @@ LIBEND2END_TEST_CANCEL_IN_A_VACUUM_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuff libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_IN_A_VACUUM_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBEND2END_TEST_CANCEL_IN_A_VACUUM_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a +endif @@ -2476,7 +2552,11 @@ LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_OBJS = $(addprefix objs/$(CONFIG)/, $(adds libs/$(CONFIG)/libend2end_test_census_simple_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_census_simple_request.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_census_simple_request.a +endif @@ -2498,7 +2578,11 @@ LIBEND2END_TEST_DISAPPEARING_SERVER_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuf libs/$(CONFIG)/libend2end_test_disappearing_server.a: $(ZLIB_DEP) $(LIBEND2END_TEST_DISAPPEARING_SERVER_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_disappearing_server.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBEND2END_TEST_DISAPPEARING_SERVER_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_disappearing_server.a +endif @@ -2520,7 +2604,11 @@ LIBEND2END_TEST_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_OBJS = $(addprefix libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a: $(ZLIB_DEP) $(LIBEND2END_TEST_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a $(LIBEND2END_TEST_EARLY_SERVER_SHUTDOWN_FINISHES_INFLIGHT_CALLS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a +endif @@ -2542,7 +2630,11 @@ LIBEND2END_TEST_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_OBJS = $(addprefix objs/$(CO libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a: $(ZLIB_DEP) $(LIBEND2END_TEST_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a $(LIBEND2END_TEST_EARLY_SERVER_SHUTDOWN_FINISHES_TAGS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a +endif @@ -2564,7 +2656,11 @@ LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_OBJS = $(addprefix objs/$(CONFIG)/, $(a libs/$(CONFIG)/libend2end_test_graceful_server_shutdown.a: $(ZLIB_DEP) $(LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_graceful_server_shutdown.a +endif @@ -2586,7 +2682,11 @@ LIBEND2END_TEST_INVOKE_LARGE_REQUEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsu libs/$(CONFIG)/libend2end_test_invoke_large_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_INVOKE_LARGE_REQUEST_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_invoke_large_request.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBEND2END_TEST_INVOKE_LARGE_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_invoke_large_request.a +endif @@ -2608,7 +2708,11 @@ LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_OBJS = $(addprefix objs/$(CONFIG)/, $(add libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a: $(ZLIB_DEP) $(LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a +endif @@ -2630,7 +2734,11 @@ LIBEND2END_TEST_NO_OP_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(base libs/$(CONFIG)/libend2end_test_no_op.a: $(ZLIB_DEP) $(LIBEND2END_TEST_NO_OP_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_no_op.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_no_op.a $(LIBEND2END_TEST_NO_OP_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_no_op.a +endif @@ -2652,7 +2760,11 @@ LIBEND2END_TEST_PING_PONG_STREAMING_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuf libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a: $(ZLIB_DEP) $(LIBEND2END_TEST_PING_PONG_STREAMING_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBEND2END_TEST_PING_PONG_STREAMING_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a +endif @@ -2674,7 +2786,11 @@ LIBEND2END_TEST_REQUEST_RESPONSE_WITH_BINARY_METADATA_AND_PAYLOAD_OBJS = $(addpr libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a: $(ZLIB_DEP) $(LIBEND2END_TEST_REQUEST_RESPONSE_WITH_BINARY_METADATA_AND_PAYLOAD_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a $(LIBEND2END_TEST_REQUEST_RESPONSE_WITH_BINARY_METADATA_AND_PAYLOAD_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a +endif @@ -2696,7 +2812,11 @@ LIBEND2END_TEST_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_OBJS = $(addprefix ob libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a: $(ZLIB_DEP) $(LIBEND2END_TEST_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a $(LIBEND2END_TEST_REQUEST_RESPONSE_WITH_METADATA_AND_PAYLOAD_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a +endif @@ -2718,7 +2838,11 @@ LIBEND2END_TEST_REQUEST_RESPONSE_WITH_PAYLOAD_OBJS = $(addprefix objs/$(CONFIG)/ libs/$(CONFIG)/libend2end_test_request_response_with_payload.a: $(ZLIB_DEP) $(LIBEND2END_TEST_REQUEST_RESPONSE_WITH_PAYLOAD_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_request_response_with_payload.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_request_response_with_payload.a $(LIBEND2END_TEST_REQUEST_RESPONSE_WITH_PAYLOAD_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_request_response_with_payload.a +endif @@ -2740,7 +2864,11 @@ LIBEND2END_TEST_REQUEST_RESPONSE_WITH_TRAILING_METADATA_AND_PAYLOAD_OBJS = $(add libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a: $(ZLIB_DEP) $(LIBEND2END_TEST_REQUEST_RESPONSE_WITH_TRAILING_METADATA_AND_PAYLOAD_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a $(LIBEND2END_TEST_REQUEST_RESPONSE_WITH_TRAILING_METADATA_AND_PAYLOAD_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a +endif @@ -2762,7 +2890,11 @@ LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_OBJS = $(addprefix objs/$(CONFIG)/, $(add libs/$(CONFIG)/libend2end_test_simple_delayed_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_simple_delayed_request.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_simple_delayed_request.a +endif @@ -2784,7 +2916,11 @@ LIBEND2END_TEST_SIMPLE_REQUEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix . libs/$(CONFIG)/libend2end_test_simple_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_SIMPLE_REQUEST_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_simple_request.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_simple_request.a $(LIBEND2END_TEST_SIMPLE_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_simple_request.a +endif @@ -2806,7 +2942,11 @@ LIBEND2END_TEST_THREAD_STRESS_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o libs/$(CONFIG)/libend2end_test_thread_stress.a: $(ZLIB_DEP) $(LIBEND2END_TEST_THREAD_STRESS_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_thread_stress.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_thread_stress.a $(LIBEND2END_TEST_THREAD_STRESS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_thread_stress.a +endif @@ -2828,7 +2968,11 @@ LIBEND2END_TEST_WRITES_DONE_HANGS_WITH_PENDING_READ_OBJS = $(addprefix objs/$(CO libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a: $(ZLIB_DEP) $(LIBEND2END_TEST_WRITES_DONE_HANGS_WITH_PENDING_READ_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a $(LIBEND2END_TEST_WRITES_DONE_HANGS_WITH_PENDING_READ_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a +endif @@ -2869,7 +3013,11 @@ endif libs/$(CONFIG)/libend2end_certs.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_CERTS_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libend2end_certs.a $(Q) $(AR) rcs libs/$(CONFIG)/libend2end_certs.a $(LIBEND2END_CERTS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libend2end_certs.a +endif diff --git a/templates/Makefile.template b/templates/Makefile.template index 11b9438a685..beb3a66fd78 100644 --- a/templates/Makefile.template +++ b/templates/Makefile.template @@ -340,8 +340,12 @@ libs/$(CONFIG)/zlib/libz.a: $(Q)cp third_party/zlib/libz.a libs/$(CONFIG)/zlib libs/$(CONFIG)/openssl/libssl.a: - $(E) "[MAKE] Building openssl" + $(E) "[MAKE] Building openssl for $(SYSTEM)" +ifeq ($(SYSTEM),Darwin) + $(Q)(cd third_party/openssl ; CC="$(CC) -fPIC -fvisibility=hidden $(CPPFLAGS_$(CONFIG)) $(OPENSSL_CFLAGS_$(CONFIG))" ./Configure darwin64-x86_64-cc $(OPENSSL_CONFIG_$(CONFIG))) +else $(Q)(cd third_party/openssl ; CC="$(CC) -fPIC -fvisibility=hidden $(CPPFLAGS_$(CONFIG)) $(OPENSSL_CFLAGS_$(CONFIG))" ./config $(OPENSSL_CONFIG_$(CONFIG))) +endif $(Q)$(MAKE) -C third_party/openssl clean $(Q)$(MAKE) -C third_party/openssl build_crypto build_ssl $(Q)mkdir -p libs/$(CONFIG)/openssl @@ -695,6 +699,7 @@ libs/$(CONFIG)/lib${lib.name}.a: $(ZLIB_DEP) $(LIB${lib.name.upper()}_OBJS) % endif $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/lib${lib.name}.a $(Q) $(AR) rcs libs/$(CONFIG)/lib${lib.name}.a $(LIB${lib.name.upper()}_OBJS) % if lib.get('baselib', False): % if lib.get('secure', True): @@ -707,6 +712,9 @@ libs/$(CONFIG)/lib${lib.name}.a: $(ZLIB_DEP) $(LIB${lib.name.upper()}_OBJS) $(Q) rm -rf tmp-merge % endif % endif +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/lib${lib.name}.a +endif <% if lib.language == 'c++': From d19d850357e6204484a1e57677705c6b973217db Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 16:28:39 -0800 Subject: [PATCH 053/143] This field is 8 bits on Mac --- test/core/iomgr/sockaddr_utils_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/core/iomgr/sockaddr_utils_test.c b/test/core/iomgr/sockaddr_utils_test.c index 14018ed66c5..3e653da4c97 100644 --- a/test/core/iomgr/sockaddr_utils_test.c +++ b/test/core/iomgr/sockaddr_utils_test.c @@ -213,9 +213,9 @@ static void test_sockaddr_to_string(void) { expect_sockaddr_str("[::fffe:c000:263]:12345", &input6, 1); memset(&dummy, 0, sizeof(dummy)); - dummy.sa_family = 999; - expect_sockaddr_str("(sockaddr family=999)", &dummy, 0); - expect_sockaddr_str("(sockaddr family=999)", &dummy, 1); + dummy.sa_family = 123; + expect_sockaddr_str("(sockaddr family=123)", &dummy, 0); + expect_sockaddr_str("(sockaddr family=123)", &dummy, 1); GPR_ASSERT(errno == 0xDEADBEEF); } From 96b49557ec34ed52ac067089a9b496b27d9a0218 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 16:29:01 -0800 Subject: [PATCH 054/143] Sort out some more compile flags for Mac --- Makefile | 20 ++++++++++++++------ templates/Makefile.template | 20 ++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 28bb81b6dea..f88e579fe75 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,14 @@ # This currently builds C and C++ code. + +# Basic platform detection +HOST_SYSTEM = $(shell uname | cut -f 1 -d_) +ifeq ($(SYSTEM),) +SYSTEM = $(HOST_SYSTEM) +endif + + # Configurations VALID_CONFIG_opt = 1 @@ -115,10 +123,15 @@ LDFLAGS += $(LDFLAGS_$(CONFIG)) CFLAGS += -std=c89 -pedantic CXXFLAGS += -std=c++11 CPPFLAGS += -g -fPIC -Wall -Werror -Wno-long-long -LDFLAGS += -g -pthread -fPIC +LDFLAGS += -g -fPIC INCLUDES = . include gens +ifeq ($(SYSTEM),Darwin) +LIBS = m z +else LIBS = rt m z pthread +LDFLAGS += -pthread +endif LIBSXX = protobuf LIBS_PROTOC = protoc protobuf @@ -156,11 +169,6 @@ HOST_LDLIBS = $(LDLIBS) # These are automatically computed variables. # There shouldn't be any need to change anything from now on. -HOST_SYSTEM = $(shell uname | cut -f 1 -d_) -ifeq ($(SYSTEM),) -SYSTEM = $(HOST_SYSTEM) -endif - ifeq ($(SYSTEM),MINGW32) SHARED_EXT = dll endif diff --git a/templates/Makefile.template b/templates/Makefile.template index beb3a66fd78..b80e80ca818 100644 --- a/templates/Makefile.template +++ b/templates/Makefile.template @@ -19,6 +19,14 @@ return 'gens/' + m.group(1) + '.pb.cc' %> + +# Basic platform detection +HOST_SYSTEM = $(shell uname | cut -f 1 -d_) +ifeq ($(SYSTEM),) +SYSTEM = $(HOST_SYSTEM) +endif + + # Configurations VALID_CONFIG_opt = 1 @@ -132,10 +140,15 @@ LDFLAGS += $(LDFLAGS_$(CONFIG)) CFLAGS += -std=c89 -pedantic CXXFLAGS += -std=c++11 CPPFLAGS += -g -fPIC -Wall -Werror -Wno-long-long -LDFLAGS += -g -pthread -fPIC +LDFLAGS += -g -fPIC INCLUDES = . include gens +ifeq ($(SYSTEM),Darwin) +LIBS = m z +else LIBS = rt m z pthread +LDFLAGS += -pthread +endif LIBSXX = protobuf LIBS_PROTOC = protoc protobuf @@ -173,11 +186,6 @@ HOST_LDLIBS = $(LDLIBS) # These are automatically computed variables. # There shouldn't be any need to change anything from now on. -HOST_SYSTEM = $(shell uname | cut -f 1 -d_) -ifeq ($(SYSTEM),) -SYSTEM = $(HOST_SYSTEM) -endif - ifeq ($(SYSTEM),MINGW32) SHARED_EXT = dll endif From 5d8fbe266322fca7ff192a222e16733c020e4960 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 16:33:54 -0800 Subject: [PATCH 055/143] Make this socket creation portable --- test/core/iomgr/tcp_client_posix_test.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/core/iomgr/tcp_client_posix_test.c b/test/core/iomgr/tcp_client_posix_test.c index 79ba777e85e..f9212e73737 100644 --- a/test/core/iomgr/tcp_client_posix_test.c +++ b/test/core/iomgr/tcp_client_posix_test.c @@ -40,6 +40,7 @@ #include #include "src/core/iomgr/iomgr.h" +#include "src/core/iomgr/socket_utils_posix.h" #include #include @@ -138,7 +139,8 @@ void test_times_out(void) { /* tie up the listen buffer, which is somewhat arbitrarily sized. */ for (i = 0; i < NUM_CLIENT_CONNECTS; ++i) { - client_fd[i] = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + client_fd[i] = socket(AF_INET, SOCK_STREAM, 0); + grpc_set_socket_nonblocking(client_fd[i], 1); do { r = connect(client_fd[i], (struct sockaddr *)&addr, addr_len); } while (r == -1 && errno == EINTR); From 9f44bfff6deaeef5f761ab76185f822bd59cfb8d Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 17:14:01 -0800 Subject: [PATCH 056/143] Make option setting work on Mac --- src/core/iomgr/socket_utils_common_posix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/iomgr/socket_utils_common_posix.c b/src/core/iomgr/socket_utils_common_posix.c index d65b025d700..bd29e2b5fd2 100644 --- a/src/core/iomgr/socket_utils_common_posix.c +++ b/src/core/iomgr/socket_utils_common_posix.c @@ -99,7 +99,7 @@ int grpc_set_socket_reuse_addr(int fd, int reuse) { socklen_t intlen = sizeof(newval); return 0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) && 0 == getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen) && - newval == val; + (newval != 0) == val; } /* disable nagle */ @@ -109,7 +109,7 @@ int grpc_set_socket_low_latency(int fd, int low_latency) { socklen_t intlen = sizeof(newval); return 0 == setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) && 0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen) && - newval == val; + (newval != 0) == val; } static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT; From cdc2a738c333cc63bd66a546929f41e358af6ab9 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 21 Jan 2015 17:26:04 -0800 Subject: [PATCH 057/143] Indicated that serialize and deserialize are optional --- src/node/client.js | 4 ++-- src/node/server.js | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/node/client.js b/src/node/client.js index fe7e07e29cb..f913b06f298 100644 --- a/src/node/client.js +++ b/src/node/client.js @@ -45,8 +45,8 @@ util.inherits(GrpcClientStream, Duplex); * from stream.Duplex. * @constructor * @param {grpc.Call} call Call object to proxy - * @param {function(*):Buffer} serialize Serialization function for requests - * @param {function(Buffer):*} deserialize Deserialization function for + * @param {function(*):Buffer=} serialize Serialization function for requests + * @param {function(Buffer):*=} deserialize Deserialization function for * responses */ function GrpcClientStream(call, serialize, deserialize) { diff --git a/src/node/server.js b/src/node/server.js index 27a18f5228c..eca20aa5fd0 100644 --- a/src/node/server.js +++ b/src/node/server.js @@ -47,8 +47,9 @@ util.inherits(GrpcServerStream, Duplex); * from stream.Duplex. * @constructor * @param {grpc.Call} call Call object to proxy - * @param {function(*):Buffer} serialize Serialization function for responses - * @param {function(Buffer):*} deserialize Deserialization function for requests + * @param {function(*):Buffer=} serialize Serialization function for responses + * @param {function(Buffer):*=} deserialize Deserialization function for + * requests */ function GrpcServerStream(call, serialize, deserialize) { Duplex.call(this, {objectMode: true}); From 93da6ac64742002e60f009057a74de6d848e8ab1 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 18:05:07 -0800 Subject: [PATCH 058/143] Fix memory corruption if >2 ports --- src/core/iomgr/tcp_server_posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c index 5762eb8a970..e6c02186eea 100644 --- a/src/core/iomgr/tcp_server_posix.c +++ b/src/core/iomgr/tcp_server_posix.c @@ -252,7 +252,7 @@ static int add_socket_to_server(grpc_tcp_server *s, int fd, if (s->nports == s->port_capacity) { s->port_capacity *= 2; s->ports = - gpr_realloc(s->ports, sizeof(server_port *) * s->port_capacity); + gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity); } sp = &s->ports[s->nports++]; sp->server = s; From 6f5e2c42f97ab90b36ca5a2c6026daba0e6f2b1c Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 18:05:31 -0800 Subject: [PATCH 059/143] Surface failure count --- tools/run_tests/jobset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py index 7a6d979ba30..8f16a4ff2c2 100755 --- a/tools/run_tests/jobset.py +++ b/tools/run_tests/jobset.py @@ -160,8 +160,8 @@ class Jobset(object): self._completed += 1 self._running.remove(job) if dead: return - message('WAITING', '%d jobs running, %d complete' % ( - len(self._running), self._completed)) + message('WAITING', '%d jobs running, %d complete, %d failed' % ( + len(self._running), self._completed, self._failures)) time.sleep(0.1) def cancelled(self): From 86af8cffc5122426c5fc82ca55c4d7fd7d8257d5 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 21 Jan 2015 18:05:40 -0800 Subject: [PATCH 060/143] TIPS client implementation. --- Makefile | 145 +++++++- build.json | 51 +++ examples/tips/client.cc | 62 ++++ examples/tips/client.h | 53 +++ examples/tips/client_main.cc | 85 +++++ examples/tips/client_test.cc | 110 ++++++ examples/tips/empty.proto | 19 + examples/tips/label.proto | 49 +++ examples/tips/messages.proto | 94 +++++ examples/tips/pubsub.proto | 702 +++++++++++++++++++++++++++++++++++ examples/tips/server.cc | 231 ++++++++++++ examples/tips/test.proto | 42 +++ 12 files changed, 1641 insertions(+), 2 deletions(-) create mode 100644 examples/tips/client.cc create mode 100644 examples/tips/client.h create mode 100644 examples/tips/client_main.cc create mode 100644 examples/tips/client_test.cc create mode 100644 examples/tips/empty.proto create mode 100644 examples/tips/label.proto create mode 100644 examples/tips/messages.proto create mode 100644 examples/tips/pubsub.proto create mode 100644 examples/tips/server.cc create mode 100644 examples/tips/test.proto diff --git a/Makefile b/Makefile index 7668ca8b741..db835c8f7a7 100644 --- a/Makefile +++ b/Makefile @@ -363,6 +363,8 @@ qps_client: bins/$(CONFIG)/qps_client qps_server: bins/$(CONFIG)/qps_server interop_server: bins/$(CONFIG)/interop_server interop_client: bins/$(CONFIG)/interop_client +tips_client: bins/$(CONFIG)/tips_client +tips_client_test: bins/$(CONFIG)/tips_client_test end2end_test: bins/$(CONFIG)/end2end_test channel_arguments_test: bins/$(CONFIG)/channel_arguments_test credentials_test: bins/$(CONFIG)/credentials_test @@ -539,13 +541,13 @@ privatelibs: privatelibs_c privatelibs_cxx privatelibs_c: libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/$(CONFIG)/libend2end_test_cancel_after_accept.a libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a libs/$(CONFIG)/libend2end_test_census_simple_request.a libs/$(CONFIG)/libend2end_test_disappearing_server.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a libs/$(CONFIG)/libend2end_test_graceful_server_shutdown.a libs/$(CONFIG)/libend2end_test_invoke_large_request.a libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a libs/$(CONFIG)/libend2end_test_no_op.a libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_simple_delayed_request.a libs/$(CONFIG)/libend2end_test_simple_request.a libs/$(CONFIG)/libend2end_test_thread_stress.a libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a libs/$(CONFIG)/libend2end_certs.a -privatelibs_cxx: libs/$(CONFIG)/libgrpc++_test_util.a +privatelibs_cxx: libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libtips_client_lib.a buildtests: buildtests_c buildtests_cxx buildtests_c: privatelibs_c bins/$(CONFIG)/grpc_byte_buffer_reader_test bins/$(CONFIG)/gpr_cancellable_test bins/$(CONFIG)/gpr_log_test bins/$(CONFIG)/gpr_useful_test bins/$(CONFIG)/gpr_cmdline_test bins/$(CONFIG)/gpr_histogram_test bins/$(CONFIG)/gpr_host_port_test bins/$(CONFIG)/gpr_slice_buffer_test bins/$(CONFIG)/gpr_slice_test bins/$(CONFIG)/gpr_string_test bins/$(CONFIG)/gpr_sync_test bins/$(CONFIG)/gpr_thd_test bins/$(CONFIG)/gpr_time_test bins/$(CONFIG)/murmur_hash_test bins/$(CONFIG)/grpc_stream_op_test bins/$(CONFIG)/alpn_test bins/$(CONFIG)/time_averaged_stats_test bins/$(CONFIG)/chttp2_stream_encoder_test bins/$(CONFIG)/hpack_table_test bins/$(CONFIG)/chttp2_stream_map_test bins/$(CONFIG)/hpack_parser_test bins/$(CONFIG)/transport_metadata_test bins/$(CONFIG)/chttp2_status_conversion_test bins/$(CONFIG)/chttp2_transport_end2end_test bins/$(CONFIG)/tcp_posix_test bins/$(CONFIG)/dualstack_socket_test bins/$(CONFIG)/no_server_test bins/$(CONFIG)/resolve_address_test bins/$(CONFIG)/sockaddr_utils_test bins/$(CONFIG)/tcp_server_posix_test bins/$(CONFIG)/tcp_client_posix_test bins/$(CONFIG)/grpc_channel_stack_test bins/$(CONFIG)/metadata_buffer_test bins/$(CONFIG)/grpc_completion_queue_test bins/$(CONFIG)/census_window_stats_test bins/$(CONFIG)/census_statistics_quick_test bins/$(CONFIG)/census_statistics_small_log_test bins/$(CONFIG)/census_statistics_performance_test bins/$(CONFIG)/census_statistics_multiple_writers_test bins/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test bins/$(CONFIG)/census_stub_test bins/$(CONFIG)/census_hash_table_test bins/$(CONFIG)/fling_server bins/$(CONFIG)/fling_client bins/$(CONFIG)/fling_test bins/$(CONFIG)/echo_server bins/$(CONFIG)/echo_client bins/$(CONFIG)/echo_test bins/$(CONFIG)/message_compress_test bins/$(CONFIG)/bin_encoder_test bins/$(CONFIG)/secure_endpoint_test bins/$(CONFIG)/httpcli_format_request_test bins/$(CONFIG)/httpcli_parser_test bins/$(CONFIG)/httpcli_test bins/$(CONFIG)/grpc_credentials_test bins/$(CONFIG)/grpc_base64_test bins/$(CONFIG)/grpc_json_token_test bins/$(CONFIG)/timeout_encoding_test bins/$(CONFIG)/fd_posix_test bins/$(CONFIG)/fling_stream_test bins/$(CONFIG)/lame_client_test bins/$(CONFIG)/alarm_test bins/$(CONFIG)/alarm_list_test bins/$(CONFIG)/alarm_heap_test bins/$(CONFIG)/time_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fake_security_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fake_security_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fake_security_census_simple_request_test bins/$(CONFIG)/chttp2_fake_security_disappearing_server_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fake_security_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fake_security_invoke_large_request_test bins/$(CONFIG)/chttp2_fake_security_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fake_security_no_op_test bins/$(CONFIG)/chttp2_fake_security_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_payload_test bins/$(CONFIG)/chttp2_fake_security_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fake_security_simple_delayed_request_test bins/$(CONFIG)/chttp2_fake_security_simple_request_test bins/$(CONFIG)/chttp2_fake_security_thread_stress_test bins/$(CONFIG)/chttp2_fake_security_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_fullstack_no_op_test bins/$(CONFIG)/chttp2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_after_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_before_invoke_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_census_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_disappearing_server_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_invoke_large_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_max_concurrent_streams_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_no_op_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_ping_pong_streaming_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_delayed_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_simple_request_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_thread_stress_test bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_no_op_test bins/$(CONFIG)/chttp2_socket_pair_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_writes_done_hangs_with_pending_read_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_accept_and_writes_closed_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_after_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_before_invoke_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_cancel_in_a_vacuum_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_census_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_disappearing_server_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_inflight_calls_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_early_server_shutdown_finishes_tags_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_invoke_large_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_max_concurrent_streams_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_no_op_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_ping_pong_streaming_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_binary_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_request_response_with_trailing_metadata_and_payload_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_delayed_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_simple_request_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_thread_stress_test bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_writes_done_hangs_with_pending_read_test -buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/thread_pool_test bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/interop_server bins/$(CONFIG)/interop_client bins/$(CONFIG)/end2end_test bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test +buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/thread_pool_test bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/interop_server bins/$(CONFIG)/interop_client bins/$(CONFIG)/tips_client bins/$(CONFIG)/tips_client_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test test: test_c test_cxx @@ -949,6 +951,8 @@ test_cxx: buildtests_cxx $(Q) ./bins/$(CONFIG)/qps_client || ( echo test qps_client failed ; exit 1 ) $(E) "[RUN] Testing qps_server" $(Q) ./bins/$(CONFIG)/qps_server || ( echo test qps_server failed ; exit 1 ) + $(E) "[RUN] Testing tips_client_test" + $(Q) ./bins/$(CONFIG)/tips_client_test || ( echo test tips_client_test failed ; exit 1 ) $(E) "[RUN] Testing end2end_test" $(Q) ./bins/$(CONFIG)/end2end_test || ( echo test end2end_test failed ; exit 1 ) $(E) "[RUN] Testing channel_arguments_test" @@ -998,6 +1002,21 @@ strip-shared_cxx: shared_cxx $(E) "[STRIP] Stripping libgrpc++.so" $(Q) $(STRIP) libs/$(CONFIG)/libgrpc++.$(SHARED_EXT) +gens/examples/tips/empty.pb.cc: examples/tips/empty.proto $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=gens --grpc_out=gens --plugin=protoc-gen-grpc=bins/$(CONFIG)/cpp_plugin $< + +gens/examples/tips/label.pb.cc: examples/tips/label.proto $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=gens --grpc_out=gens --plugin=protoc-gen-grpc=bins/$(CONFIG)/cpp_plugin $< + +gens/examples/tips/pubsub.pb.cc: examples/tips/pubsub.proto $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=gens --grpc_out=gens --plugin=protoc-gen-grpc=bins/$(CONFIG)/cpp_plugin $< + gens/test/cpp/interop/empty.pb.cc: test/cpp/interop/empty.proto $(PROTOC_PLUGINS) $(E) "[PROTOC] Generating protobuf CC file from $<" $(Q) mkdir -p `dirname $@` @@ -2108,6 +2127,54 @@ objs/$(CONFIG)/test/cpp/util/create_test_channel.o: gens/test/cpp/util/messa objs/$(CONFIG)/test/cpp/end2end/async_test_server.o: gens/test/cpp/util/messages.pb.cc gens/test/cpp/util/echo.pb.cc gens/test/cpp/util/echo_duplicate.pb.cc +LIBTIPS_CLIENT_LIB_SRC = \ + gens/examples/tips/label.pb.cc \ + gens/examples/tips/empty.pb.cc \ + gens/examples/tips/pubsub.pb.cc \ + examples/tips/client.cc \ + + +LIBTIPS_CLIENT_LIB_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBTIPS_CLIENT_LIB_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL with ALPN. + +libs/$(CONFIG)/libtips_client_lib.a: openssl_dep_error + + +else + +ifneq ($(OPENSSL_DEP),) +examples/tips/label.proto: $(OPENSSL_DEP) +examples/tips/empty.proto: $(OPENSSL_DEP) +examples/tips/pubsub.proto: $(OPENSSL_DEP) +examples/tips/client.cc: $(OPENSSL_DEP) +endif + +libs/$(CONFIG)/libtips_client_lib.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBTIPS_CLIENT_LIB_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(AR) rcs libs/$(CONFIG)/libtips_client_lib.a $(LIBTIPS_CLIENT_LIB_OBJS) + + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBTIPS_CLIENT_LIB_OBJS:.o=.dep) +endif +endif + + + + +objs/$(CONFIG)/examples/tips/client.o: gens/examples/tips/label.pb.cc gens/examples/tips/empty.pb.cc gens/examples/tips/pubsub.pb.cc + + LIBEND2END_FIXTURE_CHTTP2_FAKE_SECURITY_SRC = \ test/core/end2end/fixtures/chttp2_fake_security.c \ @@ -5230,6 +5297,68 @@ endif endif +TIPS_CLIENT_SRC = \ + examples/tips/client_main.cc \ + +TIPS_CLIENT_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL with ALPN. + +bins/$(CONFIG)/tips_client: openssl_dep_error + +else + +bins/$(CONFIG)/tips_client: $(TIPS_CLIENT_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(TIPS_CLIENT_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_client + +endif + +objs/$(CONFIG)/examples/tips/client_main.o: libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a + +deps_tips_client: $(TIPS_CLIENT_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TIPS_CLIENT_OBJS:.o=.dep) +endif +endif + + +TIPS_CLIENT_TEST_SRC = \ + examples/tips/client_test.cc \ + +TIPS_CLIENT_TEST_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(TIPS_CLIENT_TEST_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL with ALPN. + +bins/$(CONFIG)/tips_client_test: openssl_dep_error + +else + +bins/$(CONFIG)/tips_client_test: $(TIPS_CLIENT_TEST_OBJS) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(TIPS_CLIENT_TEST_OBJS) $(GTEST_LIB) libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS) $(LDLIBS_SECURE) -o bins/$(CONFIG)/tips_client_test + +endif + +objs/$(CONFIG)/examples/tips/client_test.o: libs/$(CONFIG)/libtips_client_lib.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++.a libs/$(CONFIG)/libgrpc.a libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgpr.a + +deps_tips_client_test: $(TIPS_CLIENT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TIPS_CLIENT_TEST_OBJS:.o=.dep) +endif +endif + + END2END_TEST_SRC = \ test/cpp/end2end/end2end_test.cc \ @@ -5714,6 +5843,8 @@ CHTTP2_FAKE_SECURITY_GRACEFUL_SERVER_SHUTDOWN_TEST_OBJS = $(addprefix objs/$(CON ifeq ($(NO_SECURE),true) +# You can't build secure targets if you don't have OpenSSL with ALPN. + bins/$(CONFIG)/chttp2_fake_security_graceful_server_shutdown_test: openssl_dep_error else @@ -6350,6 +6481,8 @@ CHTTP2_FULLSTACK_GRACEFUL_SERVER_SHUTDOWN_TEST_OBJS = $(addprefix objs/$(CONFIG) ifeq ($(NO_SECURE),true) +# You can't build secure targets if you don't have OpenSSL with ALPN. + bins/$(CONFIG)/chttp2_fullstack_graceful_server_shutdown_test: openssl_dep_error else @@ -6986,6 +7119,8 @@ CHTTP2_SIMPLE_SSL_FULLSTACK_GRACEFUL_SERVER_SHUTDOWN_TEST_OBJS = $(addprefix obj ifeq ($(NO_SECURE),true) +# You can't build secure targets if you don't have OpenSSL with ALPN. + bins/$(CONFIG)/chttp2_simple_ssl_fullstack_graceful_server_shutdown_test: openssl_dep_error else @@ -7622,6 +7757,8 @@ CHTTP2_SIMPLE_SSL_WITH_OAUTH2_FULLSTACK_GRACEFUL_SERVER_SHUTDOWN_TEST_OBJS = $(a ifeq ($(NO_SECURE),true) +# You can't build secure targets if you don't have OpenSSL with ALPN. + bins/$(CONFIG)/chttp2_simple_ssl_with_oauth2_fullstack_graceful_server_shutdown_test: openssl_dep_error else @@ -8258,6 +8395,8 @@ CHTTP2_SOCKET_PAIR_GRACEFUL_SERVER_SHUTDOWN_TEST_OBJS = $(addprefix objs/$(CONFI ifeq ($(NO_SECURE),true) +# You can't build secure targets if you don't have OpenSSL with ALPN. + bins/$(CONFIG)/chttp2_socket_pair_graceful_server_shutdown_test: openssl_dep_error else @@ -8894,6 +9033,8 @@ CHTTP2_SOCKET_PAIR_ONE_BYTE_AT_A_TIME_GRACEFUL_SERVER_SHUTDOWN_TEST_OBJS = $(add ifeq ($(NO_SECURE),true) +# You can't build secure targets if you don't have OpenSSL with ALPN. + bins/$(CONFIG)/chttp2_socket_pair_one_byte_at_a_time_graceful_server_shutdown_test: openssl_dep_error else diff --git a/build.json b/build.json index 1e465404a57..a3dea9c3797 100644 --- a/build.json +++ b/build.json @@ -400,6 +400,22 @@ "test/cpp/end2end/async_test_server.cc" ], "c++": true + }, + { + "name": "tips_client_lib", + "build": "private", + "src": [ + "examples/tips/label.proto", + "examples/tips/empty.proto", + "examples/tips/pubsub.proto", + "examples/tips/client.cc" + ], + "deps": [ + "grpc++", + "grpc", + "gpr" + ], + "c++": true } ], "targets": [ @@ -1404,6 +1420,41 @@ "gpr" ] }, + { + "name": "tips_client", + "build": "test", + "run": false, + "c++": true, + "src": [ + "examples/tips/client_main.cc" + ], + "deps": [ + "tips_client_lib", + "grpc++_test_util", + "grpc_test_util", + "grpc++", + "grpc", + "gpr_test_util", + "gpr" + ] + }, + { + "name": "tips_client_test", + "build": "test", + "c++": true, + "src": [ + "examples/tips/client_test.cc" + ], + "deps": [ + "tips_client_lib", + "grpc++_test_util", + "grpc_test_util", + "grpc++", + "grpc", + "gpr_test_util", + "gpr" + ] + }, { "name": "end2end_test", "build": "test", diff --git a/examples/tips/client.cc b/examples/tips/client.cc new file mode 100644 index 00000000000..bfbd59c9d56 --- /dev/null +++ b/examples/tips/client.cc @@ -0,0 +1,62 @@ +/* + * + * Copyright 2014, 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. + * + */ + +#include + +#include "examples/tips/client.h" + +using grpc::ChannelInterface; +using grpc::ClientContext; +using tech::pubsub::Topic; +using tech::pubsub::PublisherService; + +namespace grpc { +namespace examples { +namespace tips { + +Client::Client(std::shared_ptr channel) + : stub_(PublisherService::NewStub(channel)) { +} + +Status Client::CreateTopic(grpc::string topic) { + Topic request; + Topic response; + request.set_name(topic); + ClientContext context; + + return stub_->CreateTopic(&context, request, &response); +} + +} // namesapce tips +} // namespace examples +} // namespace grpc diff --git a/examples/tips/client.h b/examples/tips/client.h new file mode 100644 index 00000000000..83e79118235 --- /dev/null +++ b/examples/tips/client.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2014, 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. + * + */ + +#include +#include +#include "examples/tips/pubsub.pb.h" + +namespace grpc { +namespace examples { +namespace tips { + +class Client { + public: + Client(std::shared_ptr channel); + Status CreateTopic(grpc::string topic); + + private: + std::unique_ptr stub_; +}; + +} // namesapce tips +} // namespace examples +} // namespace grpc diff --git a/examples/tips/client_main.cc b/examples/tips/client_main.cc new file mode 100644 index 00000000000..0324b4b8b08 --- /dev/null +++ b/examples/tips/client_main.cc @@ -0,0 +1,85 @@ +/* + * + * Copyright 2014, 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. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "test/cpp/util/create_test_channel.h" + +DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls."); +DEFINE_bool(use_prod_roots, false, "True to use SSL roots for production GFE"); +DEFINE_int32(server_port, 0, "Server port."); +DEFINE_string(server_host, "127.0.0.1", "Server host to connect to"); +DEFINE_string(server_host_override, "foo.test.google.com", + "Override the server host which is sent in HTTP header"); + +using grpc::ChannelInterface; +using grpc::ClientContext; +using grpc::CreateTestChannel; + +void CreateTopic(std::shared_ptr channel, grpc::string topic); + +int main(int argc, char** argv) { + grpc_init(); + + google::ParseCommandLineFlags(&argc, &argv, true); + + gpr_log(GPR_INFO, "Start TIPS client"); + + GPR_ASSERT(FLAGS_server_port); + const int host_port_buf_size = 1024; + char host_port[host_port_buf_size]; + snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(), + FLAGS_server_port); + + std::shared_ptr channel( + CreateTestChannel(host_port, FLAGS_server_host_override, FLAGS_enable_ssl, + FLAGS_use_prod_roots)); + + CreateTopic(channel, "test"); + + channel.reset(); + grpc_shutdown(); + return 0; +} diff --git a/examples/tips/client_test.cc b/examples/tips/client_test.cc new file mode 100644 index 00000000000..d40f0c0d8b4 --- /dev/null +++ b/examples/tips/client_test.cc @@ -0,0 +1,110 @@ +/* + * + * Copyright 2014, 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "examples/tips/client.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +using grpc::ChannelInterface; + +namespace { + +} // + +namespace grpc { +namespace testing { +namespace { + +const char kTopic[] = "test topic"; + +class PublishServiceImpl : public tech::pubsub::PublisherService::Service { + public: + Status CreateTopic(::grpc::ServerContext* context, + const ::tech::pubsub::Topic* request, + ::tech::pubsub::Topic* response) override { + EXPECT_EQ(request->name(), kTopic); + return Status::OK; + } +}; + +class End2endTest : public ::testing::Test { + protected: + void SetUp() override { + int port = grpc_pick_unused_port_or_die(); + server_address_ << "localhost:" << port; + // Setup server + ServerBuilder builder; + builder.AddPort(server_address_.str()); + builder.RegisterService(service_.service()); + server_ = builder.BuildAndStart(); + + channel_ = CreateChannel(server_address_.str(), ChannelArguments()); + } + + void TearDown() override { server_->Shutdown(); } + + std::unique_ptr server_; + std::ostringstream server_address_; + PublishServiceImpl service_; + + std::shared_ptr channel_; +}; + +TEST_F(End2endTest, CreateTopic) { + grpc::examples::tips::Client client(channel_); + client.CreateTopic(kTopic); +} + +} // namespace +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc_test_init(argc, argv); + grpc_init(); + ::testing::InitGoogleTest(&argc, argv); + gpr_log(GPR_INFO, "Start test ..."); + int result = RUN_ALL_TESTS(); + grpc_shutdown(); + return result; +} diff --git a/examples/tips/empty.proto b/examples/tips/empty.proto new file mode 100644 index 00000000000..a698ab6f8a9 --- /dev/null +++ b/examples/tips/empty.proto @@ -0,0 +1,19 @@ +syntax = "proto2"; + +package proto2; + +// An empty message that you can re-use to avoid defining duplicated empty +// messages in your project. A typical example is to use it as argument or the +// return value of a service API. For instance: +// +// service Foo { +// rpc Bar (proto2.Empty) returns (proto2.Empty) { }; +// }; +// +// BEGIN GOOGLE-INTERNAL +// The difference between this one and net/rpc/empty-message.proto is that +// 1) The generated message here is in proto2 C++ API. +// 2) The proto2.Empty has minimum dependencies +// (no message_set or net/rpc dependencies) +// END GOOGLE-INTERNAL +message Empty {} diff --git a/examples/tips/label.proto b/examples/tips/label.proto new file mode 100644 index 00000000000..e79e1db77c2 --- /dev/null +++ b/examples/tips/label.proto @@ -0,0 +1,49 @@ +// Labels provide a way to associate user-defined metadata with various +// objects. Labels may be used to organize objects into non-hierarchical +// groups; think metadata tags attached to mp3s. For more details, see +// http://go/exosphere:labels + +syntax = "proto2"; + +package tech.label; + +// A key-value pair applied to a given object. +message Label { + // The key of a label is a syntactically valid URL (as per RFC 1738) with + // the "scheme" and initial slashes omitted and with the additional + // restrictions noted below. Each key should be globally unique. The + // "host" portion is called the "namespace" and is not necessarily + // resolvable to a network endpoint. Instead, the namespace indicates what + // system or entity defines the semantics of the label. Namespaces do not + // restrict the set of objects to which a label may be associated. + // + // Keys are defined by the following grammar: + // + // key = hostname "/" kpath + // kpath = ksegment *[ "/" ksegment ] + // ksegment = alphadigit | *[ alphadigit | "-" | "_" | "." ] + // + // where "hostname" and "alphadigit" are defined as in RFC 1738. + // + // Example key: + // spanner.google.com/universe + required string key = 1; + + // The value of the label. + oneof value { + // A string value. + string str_value = 2; + // An integer value. + int64 num_value = 3; + } +} + +// A collection of labels, such as the set of all labels attached to an +// object. Each label in the set must have a different key. +// +// Users should prefer to embed "repeated Label" directly when possible. +// This message should only be used in cases where that isn't possible (e.g. +// with oneof). +message Labels { + repeated Label label = 1; +} diff --git a/examples/tips/messages.proto b/examples/tips/messages.proto new file mode 100644 index 00000000000..29db0dd8b1a --- /dev/null +++ b/examples/tips/messages.proto @@ -0,0 +1,94 @@ +// Message definitions to be used by integration test service definitions. + +syntax = "proto2"; + +package grpc.testing; + +// The type of payload that should be returned. +enum PayloadType { + // Compressable text format. + COMPRESSABLE = 0; + + // Uncompressable binary format. + UNCOMPRESSABLE = 1; + + // Randomly chosen from all other formats defined in this enum. + RANDOM = 2; +} + +// A block of data, to simply increase gRPC message size. +message Payload { + // The type of data in body. + optional PayloadType type = 1; + // Primary contents of payload. + optional bytes body = 2; +} + +// Unary request. +message SimpleRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, server randomly chooses one from other formats. + optional PayloadType response_type = 1; + + // Desired payload size in the response from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + optional int32 response_size = 2; + + // Optional input payload sent along with the request. + optional Payload payload = 3; +} + +// Unary response, as configured by the request. +message SimpleResponse { + // Payload to increase message size. + optional Payload payload = 1; + // The user the request came from, for verifying authentication was + // successful when the client expected it. + optional int64 effective_gaia_user_id = 2; +} + +// Client-streaming request. +message StreamingInputCallRequest { + // Optional input payload sent along with the request. + optional Payload payload = 1; + + // Not expecting any payload from the response. +} + +// Client-streaming response. +message StreamingInputCallResponse { + // Aggregated size of payloads received from the client. + optional int32 aggregated_payload_size = 1; +} + +// Configuration for a particular response. +message ResponseParameters { + // Desired payload sizes in responses from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + optional int32 size = 1; + + // Desired interval between consecutive responses in the response stream in + // microseconds. + optional int32 interval_us = 2; +} + +// Server-streaming request. +message StreamingOutputCallRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, the payload from each response in the stream + // might be of different types. This is to simulate a mixed type of payload + // stream. + optional PayloadType response_type = 1; + + // Configuration for each expected response message. + repeated ResponseParameters response_parameters = 2; + + // Optional input payload sent along with the request. + optional Payload payload = 3; +} + +// Server-streaming response, as configured by the request and parameters. +message StreamingOutputCallResponse { + // Payload to increase response size. + optional Payload payload = 1; +} diff --git a/examples/tips/pubsub.proto b/examples/tips/pubsub.proto new file mode 100644 index 00000000000..0b3bd5d012a --- /dev/null +++ b/examples/tips/pubsub.proto @@ -0,0 +1,702 @@ +// Specification of the Pubsub API. + +syntax = "proto2"; + +import "examples/tips/empty.proto"; +import "examples/tips/label.proto"; + +package tech.pubsub; + +// ----------------------------------------------------------------------------- +// Overview of the Pubsub API +// ----------------------------------------------------------------------------- + +// This file describes an API for a Pubsub system. This system provides a +// reliable many-to-many communication mechanism between independently written +// publishers and subscribers where the publisher publishes messages to "topics" +// and each subscriber creates a "subscription" and consumes messages from it. +// +// (a) The pubsub system maintains bindings between topics and subscriptions. +// (b) A publisher publishes messages into a topic. +// (c) The pubsub system delivers messages from topics into relevant +// subscriptions. +// (d) A subscriber receives pending messages from its subscription and +// acknowledges or nacks each one to the pubsub system. +// (e) The pubsub system removes acknowledged messages from that subscription. + +// ----------------------------------------------------------------------------- +// Data Model +// ----------------------------------------------------------------------------- + +// The data model consists of the following: +// +// * Topic: A topic is a resource to which messages are published by publishers. +// Topics are named, and the name of the topic is unique within the pubsub +// system. +// +// * Subscription: A subscription records the subscriber's interest in a topic. +// It can optionally include a query to select a subset of interesting +// messages. The pubsub system maintains a logical cursor tracking the +// matching messages which still need to be delivered and acked so that +// they can retried as needed. The set of messages that have not been +// acknowledged is called the subscription backlog. +// +// * Message: A message is a unit of data that flows in the system. It contains +// opaque data from the publisher along with its labels. +// +// * Message Labels (optional): A set of opaque key, value pairs assigned +// by the publisher which the subscriber can use for filtering out messages +// in the topic. For example, a label with key "foo.com/device_type" and +// value "mobile" may be added for messages that are only relevant for a +// mobile subscriber; a subscriber on a phone may decide to create a +// subscription only for messages that have this label. + +// ----------------------------------------------------------------------------- +// Publisher Flow +// ----------------------------------------------------------------------------- + +// A publisher publishes messages to the topic using the Publish request: +// +// PubsubMessage message; +// message.set_data("...."); +// Label label; +// label.set_key("foo.com/key1"); +// label.set_str_value("value1"); +// message.add_label(label); +// PublishRequest request; +// request.set_topic("topicName"); +// request.set_message(message); +// PublisherService.Publish(request); + +// ----------------------------------------------------------------------------- +// Subscriber Flow +// ----------------------------------------------------------------------------- + +// The subscriber part of the API is richer than the publisher part and has a +// number of concepts w.r.t. subscription creation and monitoring: +// +// (1) A subscriber creates a subscription using the CreateSubscription call. +// It may specify an optional "query" to indicate that it wants to receive +// only messages with a certain set of labels using the label query syntax. +// It may also specify an optional truncation policy to indicate when old +// messages from the subcription can be removed. +// +// (2) A subscriber receives messages in one of two ways: via push or pull. +// +// (a) To receive messages via push, the PushConfig field must be specified in +// the Subscription parameter when creating a subscription. The PushConfig +// specifies an endpoint at which the subscriber must expose the +// PushEndpointService. Messages are received via the HandlePubsubEvent +// method. The push subscriber responds to the HandlePubsubEvent method +// with a result code that indicates one of three things: Ack (the message +// has been successfully processed and the Pubsub system may delete it), +// Nack (the message has been rejected, the Pubsub system should resend it +// at a later time), or Push-Back (this is a Nack with the additional +// semantics that the subscriber is overloaded and the pubsub system should +// back off on the rate at which it is invoking HandlePubsubEvent). The +// endpoint may be a load balancer for better scalability. +// +// (b) To receive messages via pull a subscriber calls the Pull method on the +// SubscriberService to get messages from the subscription. For each +// individual message, the subscriber may use the ack_id received in the +// PullResponse to Ack the message, Nack the message, or modify the ack +// deadline with ModifyAckDeadline. See the +// Subscription.ack_deadline_seconds field documentation for details on the +// ack deadline behavior. +// +// Note: Messages may be consumed in parallel by multiple subscribers making +// Pull calls to the same subscription; this will result in the set of +// messages from the subscription being shared and each subscriber +// receiving a subset of the messages. +// +// (4) The subscriber can explicitly truncate the current subscription. +// +// (5) "Truncated" events are delivered when a subscription is +// truncated, whether due to the subscription's truncation policy +// or an explicit request from the subscriber. +// +// Subscription creation: +// +// Subscription subscription; +// subscription.set_topic("topicName"); +// subscription.set_name("subscriptionName"); +// subscription.push_config().set_push_endpoint("machinename:8888"); +// SubscriberService.CreateSubscription(subscription); +// +// Consuming messages via push: +// +// TODO(eschapira): Add HTTP push example. +// +// The port 'machinename:8888' must be bound to a stubby server that implements +// the PushEndpointService with the following method: +// +// int HandlePubsubEvent(PubsubEvent event) { +// if (event.subscription().equals("subscriptionName")) { +// if (event.has_message()) { +// Process(event.message().data()); +// } else if (event.truncated()) { +// ProcessTruncatedEvent(); +// } +// } +// return OK; // This return code implies an acknowledgment +// } +// +// Consuming messages via pull: +// +// The subscription must be created without setting the push_config field. +// +// PullRequest pull_request; +// pull_request.set_subscription("subscriptionName"); +// pull_request.set_return_immediately(false); +// while (true) { +// PullResponse pull_response; +// if (SubscriberService.Pull(pull_request, pull_response) == OK) { +// PubsubEvent event = pull_response.pubsub_event(); +// if (event.has_message()) { +// Process(event.message().data()); +// } else if (event.truncated()) { +// ProcessTruncatedEvent(); +// } +// AcknowledgeRequest ack_request; +// ackRequest.set_subscription("subscriptionName"); +// ackRequest.set_ack_id(pull_response.ack_id()); +// SubscriberService.Acknowledge(ack_request); +// } +// } + +// ----------------------------------------------------------------------------- +// Reliability Semantics +// ----------------------------------------------------------------------------- + +// When a subscriber successfully creates a subscription using +// Subscriber.CreateSubscription, it establishes a "subscription point" with +// respect to that subscription - the subscriber is guaranteed to receive any +// message published after this subscription point that matches the +// subscription's query. Note that messages published before the Subscription +// point may or may not be delivered. +// +// If the system truncates the subscription according to the specified +// truncation policy, the system delivers a subscription status event with the +// "truncated" field set to true. We refer to such events as "truncation +// events". A truncation event: +// +// * Informs the subscriber that part of the subscription messages have been +// discarded. The subscriber may want to recover from the message loss, e.g., +// by resyncing its state with its backend. +// * Establishes a new subscription point, i.e., the subscriber is guaranteed to +// receive all changes published after the trunction event is received (or +// until another truncation event is received). +// +// Note that messages are not delivered in any particular order by the pubsub +// system. Furthermore, the system guarantees at-least-once delivery +// of each message or truncation events until acked. + +// ----------------------------------------------------------------------------- +// Deletion +// ----------------------------------------------------------------------------- + +// Both topics and subscriptions may be deleted. Deletion of a topic implies +// deletion of all attached subscriptions. +// +// When a subscription is deleted directly by calling DeleteSubscription, all +// messages are immediately dropped. If it is a pull subscriber, future pull +// requests will return NOT_FOUND. +// +// When a topic is deleted all corresponding subscriptions are immediately +// deleted, and subscribers experience the same behavior as directly deleting +// the subscription. + +// ----------------------------------------------------------------------------- +// The Publisher service and its protos. +// ----------------------------------------------------------------------------- + +// The service that an application uses to manipulate topics, and to send +// messages to a topic. +service PublisherService { + + // Creates the given topic with the given name. + rpc CreateTopic(Topic) returns (Topic) { + } + + // Adds a message to the topic. Returns NOT_FOUND if the topic does not + // exist. + // (-- For different error code values returned via Stubby, see + // util/task/codes.proto. --) + rpc Publish(PublishRequest) returns (proto2.Empty) { + } + + // Adds one or more messages to the topic. Returns NOT_FOUND if the topic does + // not exist. + rpc PublishBatch(PublishBatchRequest) returns (PublishBatchResponse) { + } + + // Gets the configuration of a topic. Since the topic only has the name + // attribute, this method is only useful to check the existence of a topic. + // If other attributes are added in the future, they will be returned here. + rpc GetTopic(GetTopicRequest) returns (Topic) { + } + + // Lists matching topics. + rpc ListTopics(ListTopicsRequest) returns (ListTopicsResponse) { + } + + // Deletes the topic with the given name. All subscriptions to this topic + // are also deleted. Returns NOT_FOUND if the topic does not exist. + // After a topic is deleted, a new topic may be created with the same name. + rpc DeleteTopic(DeleteTopicRequest) returns (proto2.Empty) { + } +} + +// A topic resource. +message Topic { + // Name of the topic. + optional string name = 1; +} + +// A message data and its labels. +message PubsubMessage { + // The message payload. + optional bytes data = 1; + + // Optional list of labels for this message. Keys in this collection must + // be unique. + //(-- TODO(eschapira): Define how key namespace may be scoped to the topic.--) + repeated tech.label.Label label = 2; + + // ID of this message assigned by the server at publication time. Guaranteed + // to be unique within the topic. This value may be read by a subscriber + // that receives a PubsubMessage via a Pull call or a push delivery. It must + // not be populated by a publisher in a Publish call. + optional string message_id = 3; +} + +// Request for the GetTopic method. +message GetTopicRequest { + // The name of the topic to get. + optional string topic = 1; +} + +// Request for the Publish method. +message PublishRequest { + // The message in the request will be published on this topic. + optional string topic = 1; + + // The message to publish. + optional PubsubMessage message = 2; +} + +// Request for the PublishBatch method. +message PublishBatchRequest { + // The messages in the request will be published on this topic. + optional string topic = 1; + + // The messages to publish. + repeated PubsubMessage messages = 2; +} + +// Response for the PublishBatch method. +message PublishBatchResponse { + // The server-assigned ID of each published message, in the same order as + // the messages in the request. IDs are guaranteed to be unique within + // the topic. + repeated string message_ids = 1; +} + +// Request for the ListTopics method. +message ListTopicsRequest { + // A valid label query expression. + // (-- Which labels are required or supported is implementation-specific. --) + optional string query = 1; + + // Maximum number of topics to return. + // (-- If not specified or <= 0, the implementation will select a reasonable + // value. --) + optional int32 max_results = 2; + + // The value obtained in the last ListTopicsResponse + // for continuation. + optional string page_token = 3; + +} + +// Response for the ListTopics method. +message ListTopicsResponse { + // The resulting topics. + repeated Topic topic = 1; + + // If not empty, indicates that there are more topics that match the request, + // and this value should be passed to the next ListTopicsRequest + // to continue. + optional string next_page_token = 2; +} + +// Request for the Delete method. +message DeleteTopicRequest { + // Name of the topic to delete. + optional string topic = 1; +} + +// ----------------------------------------------------------------------------- +// The Subscriber service and its protos. +// ----------------------------------------------------------------------------- + +// The service that an application uses to manipulate subscriptions and to +// consume messages from a subscription via the pull method. +service SubscriberService { + + // Creates a subscription on a given topic for a given subscriber. + // If the subscription already exists, returns ALREADY_EXISTS. + // If the corresponding topic doesn't exist, returns NOT_FOUND. + // + // If the name is not provided in the request, the server will assign a random + // name for this subscription on the same project as the topic. + rpc CreateSubscription(Subscription) returns (Subscription) { + } + + // Gets the configuration details of a subscription. + rpc GetSubscription(GetSubscriptionRequest) returns (Subscription) { + } + + // Lists matching subscriptions. + rpc ListSubscriptions(ListSubscriptionsRequest) + returns (ListSubscriptionsResponse) { + } + + // Deletes an existing subscription. All pending messages in the subscription + // are immediately dropped. Calls to Pull after deletion will return + // NOT_FOUND. + rpc DeleteSubscription(DeleteSubscriptionRequest) returns (proto2.Empty) { + } + + // Removes all the pending messages in the subscription and releases the + // storage associated with them. Results in a truncation event to be sent to + // the subscriber. Messages added after this call returns are stored in the + // subscription as before. + rpc TruncateSubscription(TruncateSubscriptionRequest) returns (proto2.Empty) { + } + + // + // Push subscriber calls. + // + + // Modifies the PushConfig for a specified subscription. + // This method can be used to suspend the flow of messages to an endpoint + // by clearing the PushConfig field in the request. Messages + // will be accumulated for delivery even if no push configuration is + // defined or while the configuration is modified. + rpc ModifyPushConfig(ModifyPushConfigRequest) returns (proto2.Empty) { + } + + // + // Pull Subscriber calls + // + + // Pulls a single message from the server. + // If return_immediately is true, and no messages are available in the + // subscription, this method returns FAILED_PRECONDITION. The system is free + // to return an UNAVAILABLE error if no messages are available in a + // reasonable amount of time (to reduce system load). + rpc Pull(PullRequest) returns (PullResponse) { + } + + // Pulls messages from the server. Returns an empty list if there are no + // messages available in the backlog. The system is free to return UNAVAILABLE + // if there are too many pull requests outstanding for the given subscription. + rpc PullBatch(PullBatchRequest) returns (PullBatchResponse) { + } + + // Modifies the Ack deadline for a message received from a pull request. + rpc ModifyAckDeadline(ModifyAckDeadlineRequest) returns (proto2.Empty) { + } + + // Acknowledges a particular received message: the Pub/Sub system can remove + // the given message from the subscription. Acknowledging a message whose + // Ack deadline has expired may succeed, but the message could have been + // already redelivered. Acknowledging a message more than once will not + // result in an error. This is only used for messages received via pull. + rpc Acknowledge(AcknowledgeRequest) returns (proto2.Empty) { + } + + // Refuses processing a particular received message. The system will + // redeliver this message to some consumer of the subscription at some + // future time. This is only used for messages received via pull. + rpc Nack(NackRequest) returns (proto2.Empty) { + } +} + +// A subscription resource. +message Subscription { + // Name of the subscription. + optional string name = 1; + + // The name of the topic from which this subscription is receiving messages. + optional string topic = 2; + + // If query is non-empty, only messages on the subscriber's + // topic whose labels match the query will be returned. Otherwise all + // messages on the topic will be returned. + // (-- The query syntax is described in tech/label/proto/label_query.proto --) + optional string query = 3; + + // The subscriber may specify requirements for truncating unacknowledged + // subscription entries. The system will honor the + // CreateSubscription request only if it can meet these + // requirements. If this field is not specified, messages are never truncated + // by the system. + optional TruncationPolicy truncation_policy = 4; + + // Specifies which messages can be truncated by the system. + message TruncationPolicy { + oneof policy { + // If max_bytes is specified, the system is allowed to drop + // old messages to keep the combined size of stored messages under + // max_bytes. This is a hint; the system may keep more than + // this many bytes, but will make a best effort to keep the size from + // growing much beyond this parameter. + int64 max_bytes = 1; + + // If max_age_seconds is specified, the system is allowed to + // drop messages that have been stored for at least this many seconds. + // This is a hint; the system may keep these messages, but will make a + // best effort to remove them when their maximum age is reached. + int64 max_age_seconds = 2; + } + } + + // If push delivery is used with this subscription, this field is + // used to configure it. + optional PushConfig push_config = 5; + + // For either push or pull delivery, the value is the maximum time after a + // subscriber receives a message before the subscriber should acknowledge or + // Nack the message. If the Ack deadline for a message passes without an + // Ack or a Nack, the Pub/Sub system will eventually redeliver the message. + // If a subscriber acknowledges after the deadline, the Pub/Sub system may + // accept the Ack, but it is possible that the message has been already + // delivered again. Multiple Acks to the message are allowed and will + // succeed. + // + // For push delivery, this value is used to set the request timeout for + // the call to the push endpoint. + // + // For pull delivery, this value is used as the initial value for the Ack + // deadline. It may be overridden for a specific pull request (message) with + // ModifyAckDeadline. + // While a message is outstanding (i.e. it has been delivered to a pull + // subscriber and the subscriber has not yet Acked or Nacked), the Pub/Sub + // system will not deliver that message to another pull subscriber + // (on a best-effort basis). + optional int32 ack_deadline_seconds = 6; + + // If this parameter is set to n, the system is allowed to (but not required + // to) delete the subscription when at least n seconds have elapsed since the + // client presence was detected. (Presence is detected through any + // interaction using the subscription ID, including Pull(), Get(), or + // acknowledging a message.) + // + // If this parameter is not set, the subscription will stay live until + // explicitly deleted. + // + // Clients can detect such garbage collection when a Get call or a Pull call + // (for pull subscribers only) returns NOT_FOUND. + optional int64 garbage_collect_seconds = 7; +} + +// Configuration for a push delivery endpoint. +message PushConfig { + // A URL locating the endpoint to which messages should be pushed. + // For example, a Webhook endpoint might use "https://example.com/push". + // (-- An Android application might use "gcm:", where is a + // GCM registration id allocated for pushing messages to the application. --) + optional string push_endpoint = 1; +} + +// An event indicating a received message or truncation event. +message PubsubEvent { + // The subscription that received the event. + optional string subscription = 1; + + oneof type { + // A received message. + PubsubMessage message = 2; + + // Indicates that this subscription has been truncated. + bool truncated = 3; + + // Indicates that this subscription has been deleted. (Note that pull + // subscribers will always receive NOT_FOUND in response in their pull + // request on the subscription, rather than seeing this boolean.) + bool deleted = 4; + } +} + +// Request for the GetSubscription method. +message GetSubscriptionRequest { + // The name of the subscription to get. + optional string subscription = 1; +} + +// Request for the ListSubscriptions method. +message ListSubscriptionsRequest { + // A valid label query expression. + // (-- Which labels are required or supported is implementation-specific. + // TODO(eschapira): This method must support to query by topic. We must + // define the key URI for the "topic" label. --) + optional string query = 1; + + // Maximum number of subscriptions to return. + // (-- If not specified or <= 0, the implementation will select a reasonable + // value. --) + optional int32 max_results = 3; + + // The value obtained in the last ListSubscriptionsResponse + // for continuation. + optional string page_token = 4; +} + +// Response for the ListSubscriptions method. +message ListSubscriptionsResponse { + // The subscriptions that match the request. + repeated Subscription subscription = 1; + + // If not empty, indicates that there are more subscriptions that match the + // request and this value should be passed to the next + // ListSubscriptionsRequest to continue. + optional string next_page_token = 2; +} + +// Request for the TruncateSubscription method. +message TruncateSubscriptionRequest { + // The subscription that is being truncated. + optional string subscription = 1; +} + +// Request for the DeleteSubscription method. +message DeleteSubscriptionRequest { + // The subscription to delete. + optional string subscription = 1; +} + +// Request for the ModifyPushConfig method. +message ModifyPushConfigRequest { + // The name of the subscription. + optional string subscription = 1; + + // An empty push_config indicates that the Pub/Sub system should + // pause pushing messages from the given subscription. + optional PushConfig push_config = 2; +} + +// ----------------------------------------------------------------------------- +// The protos used by a pull subscriber. +// ----------------------------------------------------------------------------- + +// Request for the Pull method. +message PullRequest { + // The subscription from which a message should be pulled. + optional string subscription = 1; + + // If this is specified as true the system will respond immediately even if + // it is not able to return a message in the Pull response. Otherwise the + // system is allowed to wait until at least one message is available rather + // than returning FAILED_PRECONDITION. The client may cancel the request if + // it does not wish to wait any longer for the response. + optional bool return_immediately = 2; +} + +// Either a PubsubMessage or a truncation event. One of these two +// must be populated. +message PullResponse { + // This ID must be used to acknowledge the received event or message. + optional string ack_id = 1; + + // A pubsub message or truncation event. + optional PubsubEvent pubsub_event = 2; +} + +// Request for the PullBatch method. +message PullBatchRequest { + // The subscription from which messages should be pulled. + optional string subscription = 1; + + // If this is specified as true the system will respond immediately even if + // it is not able to return a message in the Pull response. Otherwise the + // system is allowed to wait until at least one message is available rather + // than returning no messages. The client may cancel the request if it does + // not wish to wait any longer for the response. + optional bool return_immediately = 2; + + // The maximum number of PubsubEvents returned for this request. The Pub/Sub + // system may return fewer than the number of events specified. + optional int32 max_events = 3; +} + +// Response for the PullBatch method. +message PullBatchResponse { + + // Received Pub/Sub messages or status events. The Pub/Sub system will return + // zero messages if there are no more messages available in the backlog. The + // Pub/Sub system may return fewer than the max_events requested even if + // there are more messages available in the backlog. + repeated PullResponse pull_responses = 2; +} + +// Request for the ModifyAckDeadline method. +message ModifyAckDeadlineRequest { + // The name of the subscription from which messages are being pulled. + optional string subscription = 1; + + // The acknowledgment ID. + optional string ack_id = 2; + + // The new Ack deadline. Must be >= 0. + optional int32 ack_deadline_seconds = 3; +} + +// Request for the Acknowledge method. +message AcknowledgeRequest { + // The subscription whose message is being acknowledged. + optional string subscription = 1; + + // The acknowledgment ID for the message being acknowledged. This was + // returned by the Pub/Sub system in the Pull response. + repeated string ack_id = 2; +} + +// Request for the Nack method. +message NackRequest { + // The subscription whose message is being Nacked. + optional string subscription = 1; + + // The acknowledgment ID for the message being refused. This was returned by + // the Pub/Sub system in the Pull response. + repeated string ack_id = 2; +} + +// ----------------------------------------------------------------------------- +// The service and protos used by a push subscriber. +// ----------------------------------------------------------------------------- + +// The service that a subscriber uses to handle messages sent via push +// delivery. +// This service is not currently exported for HTTP clients. +// TODO(eschapira): Explain HTTP subscribers. +service PushEndpointService { + // Sends a PubsubMessage or a subscription status event to a + // push endpoint. + // The push endpoint responds with an empty message and a code from + // util/task/codes.proto. The following codes have a particular meaning to the + // Pub/Sub system: + // OK - This is interpreted by Pub/Sub as Ack. + // ABORTED - This is intepreted by Pub/Sub as a Nack, without implying + // pushback for congestion control. The Pub/Sub system will + // retry this message at a later time. + // UNAVAILABLE - This is intepreted by Pub/Sub as a Nack, with the additional + // semantics of push-back. The Pub/Sub system will use an AIMD + // congestion control algorithm to backoff the rate of sending + // messages from this subscription. + // Any other code, or a failure to respond, will be interpreted in the same + // way as ABORTED; i.e. the system will retry the message at a later time to + // ensure reliable delivery. + rpc HandlePubsubEvent(PubsubEvent) returns (proto2.Empty); +} diff --git a/examples/tips/server.cc b/examples/tips/server.cc new file mode 100644 index 00000000000..2171a6aac0d --- /dev/null +++ b/examples/tips/server.cc @@ -0,0 +1,231 @@ +/* + * + * Copyright 2014, 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. + * + */ + +#include +#include +#include + +#include +#include +#include +#include "test/core/end2end/data/ssl_test_data.h" +#include +#include +#include +#include +#include +#include +#include +#include "examples/tips/test.pb.h" +#include "examples/tips/empty.pb.h" +#include "examples/tips/messages.pb.h" + +DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls."); +DEFINE_int32(port, 0, "Server port."); + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::ServerCredentials; +using grpc::ServerCredentialsFactory; +using grpc::ServerReader; +using grpc::ServerReaderWriter; +using grpc::ServerWriter; +using grpc::SslServerCredentialsOptions; +using grpc::testing::Payload; +using grpc::testing::PayloadType; +using grpc::testing::SimpleRequest; +using grpc::testing::SimpleResponse; +using grpc::testing::StreamingInputCallRequest; +using grpc::testing::StreamingInputCallResponse; +using grpc::testing::StreamingOutputCallRequest; +using grpc::testing::StreamingOutputCallResponse; +using grpc::testing::TestService; +using grpc::Status; + +bool SetPayload(PayloadType type, int size, Payload* payload) { + PayloadType response_type = type; + // TODO(yangg): Support UNCOMPRESSABLE payload. + if (type != PayloadType::COMPRESSABLE) { + return false; + } + payload->set_type(response_type); + std::unique_ptr body(new char[size]()); + payload->set_body(body.get(), size); + return true; +} + +class TestServiceImpl : public TestService::Service { + public: + Status EmptyCall(ServerContext* context, const grpc::testing::Empty* request, + grpc::testing::Empty* response) { + return Status::OK; + } + + Status UnaryCall(ServerContext* context, const SimpleRequest* request, + SimpleResponse* response) { + if (request->has_response_size() && request->response_size() > 0) { + if (!SetPayload(request->response_type(), request->response_size(), + response->mutable_payload())) { + return Status(grpc::StatusCode::INTERNAL, "Error creating payload."); + } + } + return Status::OK; + } + + Status StreamingOutputCall( + ServerContext* context, const StreamingOutputCallRequest* request, + ServerWriter* writer) { + StreamingOutputCallResponse response; + bool write_success = true; + response.mutable_payload()->set_type(request->response_type()); + for (int i = 0; write_success && i < request->response_parameters_size(); + i++) { + response.mutable_payload()->set_body( + grpc::string(request->response_parameters(i).size(), '\0')); + write_success = writer->Write(response); + } + if (write_success) { + return Status::OK; + } else { + return Status(grpc::StatusCode::INTERNAL, "Error writing response."); + } + } + + Status StreamingInputCall(ServerContext* context, + ServerReader* reader, + StreamingInputCallResponse* response) { + StreamingInputCallRequest request; + int aggregated_payload_size = 0; + while (reader->Read(&request)) { + if (request.has_payload() && request.payload().has_body()) { + aggregated_payload_size += request.payload().body().size(); + } + } + response->set_aggregated_payload_size(aggregated_payload_size); + return Status::OK; + } + + Status FullDuplexCall( + ServerContext* context, + ServerReaderWriter* stream) { + StreamingOutputCallRequest request; + StreamingOutputCallResponse response; + bool write_success = true; + while (write_success && stream->Read(&request)) { + response.mutable_payload()->set_type(request.payload().type()); + if (request.response_parameters_size() == 0) { + return Status(grpc::StatusCode::INTERNAL, + "Request does not have response parameters."); + } + response.mutable_payload()->set_body( + grpc::string(request.response_parameters(0).size(), '\0')); + write_success = stream->Write(response); + } + if (write_success) { + return Status::OK; + } else { + return Status(grpc::StatusCode::INTERNAL, "Error writing response."); + } + } + + Status HalfDuplexCall( + ServerContext* context, + ServerReaderWriter* stream) { + std::vector requests; + StreamingOutputCallRequest request; + while (stream->Read(&request)) { + requests.push_back(request); + } + + StreamingOutputCallResponse response; + bool write_success = true; + for (unsigned int i = 0; write_success && i < requests.size(); i++) { + response.mutable_payload()->set_type(requests[i].payload().type()); + if (requests[i].response_parameters_size() == 0) { + return Status(grpc::StatusCode::INTERNAL, + "Request does not have response parameters."); + } + response.mutable_payload()->set_body( + grpc::string(requests[i].response_parameters(0).size(), '\0')); + write_success = stream->Write(response); + } + if (write_success) { + return Status::OK; + } else { + return Status(grpc::StatusCode::INTERNAL, "Error writing response."); + } + } +}; + +void RunServer() { + std::ostringstream server_address; + server_address << "localhost:" << FLAGS_port; + TestServiceImpl service; + + SimpleRequest request; + SimpleResponse response; + + ServerBuilder builder; + builder.AddPort(server_address.str()); + builder.RegisterService(service.service()); + if (FLAGS_enable_ssl) { + SslServerCredentialsOptions ssl_opts = { + "", + {reinterpret_cast(test_server1_key), + test_server1_key_size}, + {reinterpret_cast(test_server1_cert), + test_server1_cert_size}}; + std::shared_ptr creds = + ServerCredentialsFactory::SslCredentials(ssl_opts); + builder.SetCredentials(creds); + } + std::unique_ptr server(builder.BuildAndStart()); + gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str()); + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(5)); + } +} + +int main(int argc, char** argv) { + grpc_init(); + google::ParseCommandLineFlags(&argc, &argv, true); + + GPR_ASSERT(FLAGS_port != 0); + RunServer(); + + grpc_shutdown(); + return 0; +} diff --git a/examples/tips/test.proto b/examples/tips/test.proto new file mode 100644 index 00000000000..807a3fe677b --- /dev/null +++ b/examples/tips/test.proto @@ -0,0 +1,42 @@ +// An integration test service that covers all the method signature permutations +// of unary/streaming requests/responses. +syntax = "proto2"; + +import "examples/tips/empty.proto"; +import "examples/tips/messages.proto"; + +package grpc.testing; + +// A simple service to test the various types of RPCs and experiment with +// performance with various types of payload. +service TestService { + // One empty request followed by one empty response. + rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); + + // One request followed by one response. + // The server returns the client payload as-is. + rpc UnaryCall(SimpleRequest) returns (SimpleResponse); + + // One request followed by a sequence of responses (streamed download). + // The server returns the payload with client desired type and sizes. + rpc StreamingOutputCall(StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by one response (streamed upload). + // The server returns the aggregated size of client payload as the result. + rpc StreamingInputCall(stream StreamingInputCallRequest) + returns (StreamingInputCallResponse); + + // A sequence of requests with each request served by the server immediately. + // As one request could lead to multiple responses, this interface + // demonstrates the idea of full duplexing. + rpc FullDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by a sequence of responses. + // The server buffers all the client requests and then serves them in order. A + // stream of responses are returned to the client when the server starts with + // first request. + rpc HalfDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); +} From ad145bc9cb1103e1c3dc75610ab221a8cbbacc07 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 21:28:27 -0800 Subject: [PATCH 061/143] Use the right parameters to syscalls --- src/core/iomgr/socket_utils_posix.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/core/iomgr/socket_utils_posix.c b/src/core/iomgr/socket_utils_posix.c index e8c80710377..06c5033d457 100644 --- a/src/core/iomgr/socket_utils_posix.c +++ b/src/core/iomgr/socket_utils_posix.c @@ -50,12 +50,22 @@ int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, fd = accept(sockfd, addr, addrlen); if (fd >= 0) { - flags = fcntl(fd, F_GETFL, 0); - flags |= nonblock ? O_NONBLOCK : 0; - flags |= cloexec ? FD_CLOEXEC : 0; - GPR_ASSERT(fcntl(fd, F_SETFL, flags) == 0); + if (nonblock) { + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) goto close_and_error; + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) goto close_and_error; + } + if (cloexec) { + flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) goto close_and_error; + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != 0) goto close_and_error; + } } return fd; + +close_and_error: + close(fd); + return -1; } #endif /* GPR_POSIX_SOCKETUTILS */ From 018afed1b3083c2d032ad31906194f66d52097a3 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 21:35:36 -0800 Subject: [PATCH 062/143] Must init iomgr --- test/core/iomgr/resolve_address_test.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/core/iomgr/resolve_address_test.c b/test/core/iomgr/resolve_address_test.c index 26a4bc67e6f..319ee634d6c 100644 --- a/test/core/iomgr/resolve_address_test.c +++ b/test/core/iomgr/resolve_address_test.c @@ -32,6 +32,7 @@ */ #include "src/core/iomgr/resolve_address.h" +#include "src/core/iomgr/iomgr.h" #include #include #include @@ -122,7 +123,7 @@ static void test_unparseable_hostports(void) { int main(int argc, char** argv) { grpc_test_init(argc, argv); - + grpc_iomgr_init(); test_localhost(); test_default_port(); test_missing_default_port(); @@ -130,6 +131,6 @@ int main(int argc, char** argv) { test_ipv6_without_port(); test_invalid_ip_addresses(); test_unparseable_hostports(); - + grpc_iomgr_shutdown(); return 0; } From a83567d72907d3b8abe3ca006363db8afedd1698 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 21:51:51 -0800 Subject: [PATCH 063/143] Fix math in time_posix.c --- src/core/support/time_posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c index 78d4c3b4468..9e11f8a865d 100644 --- a/src/core/support/time_posix.c +++ b/src/core/support/time_posix.c @@ -61,7 +61,7 @@ gpr_timespec gpr_now(void) { struct timeval now_tv; gettimeofday(&now_tv, NULL); now.tv_sec = now_tv.tv_sec; - now.tv_nsec = now_tv.tv_usec / 1000; + now.tv_nsec = now_tv.tv_usec * 1000; return now; } #endif From 9497b3c1c467ae8d13258e835e71119e77884999 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 21 Jan 2015 22:10:26 -0800 Subject: [PATCH 064/143] Bugfix connection code in test --- test/core/iomgr/fd_posix_test.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/test/core/iomgr/fd_posix_test.c b/test/core/iomgr/fd_posix_test.c index 136f34a8b09..05c91ffdd4d 100644 --- a/test/core/iomgr/fd_posix_test.c +++ b/test/core/iomgr/fd_posix_test.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -79,7 +80,7 @@ static void create_test_socket(int port, int *socket_fd, /* Use local address for test */ sin->sin_family = AF_INET; - sin->sin_addr.s_addr = 0; + sin->sin_addr.s_addr = htonl(0x7f000001); sin->sin_port = htons(port); } @@ -164,7 +165,7 @@ static void session_read_cb(void *arg, /*session*/ grpc_fd_notify_on_read(se->em_fd, session_read_cb, se); } else { gpr_log(GPR_ERROR, "Unhandled read error %s", strerror(errno)); - GPR_ASSERT(0); + abort(); } } } @@ -316,7 +317,7 @@ static void client_session_write(void *arg, /*client*/ gpr_mu_unlock(&cl->mu); } else { gpr_log(GPR_ERROR, "unknown errno %s", strerror(errno)); - GPR_ASSERT(0); + abort(); } } @@ -325,10 +326,20 @@ static void client_start(client *cl, int port) { int fd; struct sockaddr_in sin; create_test_socket(port, &fd, &sin); - if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1 && - errno != EINPROGRESS) { - gpr_log(GPR_ERROR, "Failed to connect to the server"); - GPR_ASSERT(0); + if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + if (errno == EINPROGRESS) { + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLOUT; + pfd.revents = 0; + if (poll(&pfd, 1, -1) == -1) { + gpr_log(GPR_ERROR, "poll() failed during connect; errno=%d", errno); + abort(); + } + } else { + gpr_log(GPR_ERROR, "Failed to connect to the server (errno=%d)", errno); + abort(); + } } cl->em_fd = grpc_fd_create(fd); From bb20de4c3f0a03d4206d3fb1434c67b0ea514398 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 22 Jan 2015 10:13:34 -0800 Subject: [PATCH 065/143] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1e5b61af95a..4ee4c573d6c 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,6 @@ the client and the server can send a stream of messages to each other. The strea messages are delivered in the order they were sent. - - - #Protocol The gRPC protocol specifies the abstract requirements for communication between @@ -67,6 +64,10 @@ clients and servers. A concrete embedding over HTTP/2 completes the picture by fleshing out the details of each of the required operations. ## Abstract gRPC protocol -A gRPC RPC comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory 'Call Header', followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata'). +A gRPC RPC comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory `Call Header`, followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata`). + +## Implementation over HTTP/2 +The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPAC compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers). +**TODO(a11r): Add a section on flow control.** From 546e42fdd2ae111655baa9b146fe590812b0faad Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 22 Jan 2015 10:20:36 -0800 Subject: [PATCH 066/143] Update README.md Added a short blurb on flow control. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ee4c573d6c..9e8b8e77917 100644 --- a/README.md +++ b/README.md @@ -69,5 +69,5 @@ A gRPC RPC comprises of a bidirectional stream of messages, initiated by the cli ## Implementation over HTTP/2 The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPAC compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers). - -**TODO(a11r): Add a section on flow control.** +## Flow Control +gRPC inherits the flow control mchanims in HTTP/2 and uses them to enable fine-grained control of the amount of memory used for buffering in-flight messages. From 48054c0a19a3cd24a213f7b89ad1c31f3f8c293b Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 12:15:39 -0800 Subject: [PATCH 067/143] Minor bugfixes; adds independent command for support scripts on GCE machines --- tools/gce_setup/grpc_docker.sh | 64 ++++++++++++++++++++++++- tools/gce_setup/shared_startup_funcs.sh | 18 +++++-- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index 314ccb0ca8b..a19915594a7 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -86,6 +86,7 @@ grpc_add_docker_user() { } _grpc_update_image_args() { + echo "image_args $@" # default the host, root storage uri and docker file root grpc_gs_root='gs://tmp-grpc-dev/admin/' grpc_dockerfile_root='tools/dockerfile' @@ -95,7 +96,7 @@ _grpc_update_image_args() { # see if -p or -z is used to override the the project or zone local OPTIND local OPTARG - while getopts :r:d:h name + while getopts :r:d:h: name do case $name in d) grpc_dockerfile_root=$OPTARG ;; @@ -261,7 +262,7 @@ _grpc_set_project_and_zone() { local OPTIND local OPTARG local arg_func - while getopts :p:z:f:n name + while getopts :np:z:f: name do case $name in f) declare -F $OPTARG >> /dev/null && { @@ -392,6 +393,65 @@ grpc_interop_test_args() { } } +_grpc_sync_scripts_args() { + grpc_gce_script_root='tools/gce_setup' + + local OPTIND + local OPTARG + while getopts :s: name + do + case $name in + s) grpc_gce_script_root=$OPTARG ;; + :) continue ;; # ignore -s without args, just use the defaults + \?) echo "-$OPTARG: unknown flag; it's ignored" 1>&2; continue ;; + esac + done + shift $((OPTIND-1)) + + [[ -d $grpc_gce_script_root ]] || { + echo "Could not locate gce script dir: $grpc_gce_script_root" 1>&2 + return 1 + } + + [[ $# -lt 1 ]] && { + echo "$FUNCNAME: missing arg: host1 [host2 ... hostN]" 1>&2 + return 1 + } + grpc_hosts="$@" +} + +# Updates the latest version of the support scripts on some hosts. +# +# call-seq; +# grpc_sync_scripts , .. +# +# Updates the GCE docker instance +grpc_sync_scripts() { + _grpc_ensure_gcloud_ssh || return 1; + + # declare vars local so that they don't pollute the shell environment + # where they this func is used. + local grpc_zone grpc_project dry_run # set by _grpc_set_project_and_zone + local grpc_hosts grpc_gce_script_root + + # set the project zone and check that all necessary args are provided + _grpc_set_project_and_zone -f _grpc_sync_scripts_args "$@" || return 1 + + local func_lib="shared_startup_funcs.sh" + local gce_func_lib="/var/local/startup_scripts/$func_lib" + local project_opt="--project $grpc_project" + local zone_opt="--zone $grpc_zone" + local host + for host in $grpc_hosts + do + gce_has_instance $grpc_project $host || return 1; + # Update the remote copy of the GCE func library. + local src_func_lib="$grpc_gce_script_root/$func_lib" + local rmt_func_lib="$host:$gce_func_lib" + gcloud compute copy-files $src_func_lib $rmt_func_lib $project_opt $zone_opt || return 1 + done +} + grpc_sync_images_args() { [[ $# -lt 1 ]] && { echo "$FUNCNAME: missing arg: host1 [host2 ... hostN]" 1>&2 diff --git a/tools/gce_setup/shared_startup_funcs.sh b/tools/gce_setup/shared_startup_funcs.sh index 9c747466a91..f9a6aafb095 100755 --- a/tools/gce_setup/shared_startup_funcs.sh +++ b/tools/gce_setup/shared_startup_funcs.sh @@ -367,7 +367,7 @@ grpc_docker_launch_registry() { grpc_docker_pull_known() { local addr=$1 [[ -n $addr ]] || addr="0.0.0.0:5000" - local known="base cxx php_base php ruby_base ruby java_base java" + local known="base cxx php_base php ruby_base ruby java_base java go" echo "... pulling docker images for '$known'" for i in $known do @@ -402,10 +402,15 @@ grpc_dockerfile_install() { [[ -d $dockerfile_dir ]] || { echo "$FUNCNAME: not a valid dir: $dockerfile_dir"; return 1; } - # For grpc/base, sync the ssh key into the .ssh dir in the dockerfile context - + # For specific base images, sync the ssh key into the .ssh dir in the dockerfile context [[ $image_label == "grpc/base" ]] && { - grpc_docker_sync_github_key $dockerfile_dir/.ssh || return 1; + grpc_docker_sync_github_key $dockerfile_dir/.ssh 'base_ssh_key'|| return 1; + } + [[ $image_label == "grpc/go" ]] && { + grpc_docker_sync_github_key $dockerfile_dir/.ssh 'go_ssh_key'|| return 1; + } + [[ $image_label == "grpc/java_base" ]] && { + grpc_docker_sync_github_key $dockerfile_dir/.ssh 'java_base_ssh_key'|| return 1; } # TODO(temiola): maybe make cache/no-cache a func option? @@ -445,6 +450,9 @@ grpc_docker_sync_github_key() { local target_dir=$1 [[ -n $target_dir ]] || { echo "$FUNCNAME: missing arg: target_dir" >&2; return 1; } + local key_file=$2 + [[ -n $key_file ]] || { echo "$FUNCNAME: missing arg: key_file" >&2; return 1; } + # determine the admin root; the parent of the dockerfile root, local gs_dockerfile_root=$(load_metadata "attributes/gs_dockerfile_root") [[ -n $gs_dockerfile_root ]] || { @@ -454,7 +462,7 @@ grpc_docker_sync_github_key() { local gcs_admin_root=$(dirname $gs_dockerfile_root) # cp the file from gsutil to a known local area - local gcs_key_path=$gcs_admin_root/github/ssh_key + local gcs_key_path=$gcs_admin_root/github/$key_file local local_key_path=$target_dir/github.rsa mkdir -p $target_dir || { echo "$FUNCNAME: could not create dir: $target_dir" 1>&2 From 59d095a7df038201a89df6445220ca0a34c80a22 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Thu, 22 Jan 2015 20:23:45 +0000 Subject: [PATCH 068/143] The logging_pool module. --- src/python/__init__.py | 0 src/python/_framework/__init__.py | 0 src/python/_framework/foundation/__init__.py | 0 .../foundation/_logging_pool_test.py | 64 ++++++++++++++ .../_framework/foundation/logging_pool.py | 83 +++++++++++++++++++ 5 files changed, 147 insertions(+) create mode 100644 src/python/__init__.py create mode 100644 src/python/_framework/__init__.py create mode 100644 src/python/_framework/foundation/__init__.py create mode 100644 src/python/_framework/foundation/_logging_pool_test.py create mode 100644 src/python/_framework/foundation/logging_pool.py diff --git a/src/python/__init__.py b/src/python/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_framework/__init__.py b/src/python/_framework/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_framework/foundation/__init__.py b/src/python/_framework/foundation/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_framework/foundation/_logging_pool_test.py b/src/python/_framework/foundation/_logging_pool_test.py new file mode 100644 index 00000000000..ffe07c788da --- /dev/null +++ b/src/python/_framework/foundation/_logging_pool_test.py @@ -0,0 +1,64 @@ +# Copyright 2015, 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. + +"""Tests for google3.net.rpc.python.framework.foundation.logging_pool.""" + +import unittest + +from _framework.foundation import logging_pool + +_POOL_SIZE = 16 + + +class LoggingPoolTest(unittest.TestCase): + + def testUpAndDown(self): + pool = logging_pool.pool(_POOL_SIZE) + pool.shutdown(wait=True) + + with logging_pool.pool(_POOL_SIZE) as pool: + self.assertIsNotNone(pool) + + def testTaskExecuted(self): + test_list = [] + + with logging_pool.pool(_POOL_SIZE) as pool: + pool.submit(lambda: test_list.append(object())).result() + + self.assertTrue(test_list) + + def testException(self): + with logging_pool.pool(_POOL_SIZE) as pool: + raised_exception = pool.submit(lambda: 1/0).exception() + + self.assertIsNotNone(raised_exception) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/_framework/foundation/logging_pool.py b/src/python/_framework/foundation/logging_pool.py new file mode 100644 index 00000000000..7c7a6eebfca --- /dev/null +++ b/src/python/_framework/foundation/logging_pool.py @@ -0,0 +1,83 @@ +# Copyright 2015, 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. + +"""A thread pool that logs exceptions raised by tasks executed within it.""" + +import functools +import logging + +from concurrent import futures + + +def _wrap(behavior): + """Wraps an arbitrary callable behavior in exception-logging.""" + @functools.wraps(behavior) + def _wrapping(*args, **kwargs): + try: + return behavior(*args, **kwargs) + except Exception as e: + logging.exception('Unexpected exception from task run in logging pool!') + raise + return _wrapping + + +class _LoggingPool(object): + """An exception-logging futures.ThreadPoolExecutor-compatible thread pool.""" + + def __init__(self, backing_pool): + self._backing_pool = backing_pool + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._backing_pool.shutdown(wait=True) + + def submit(self, fn, *args, **kwargs): + return self._backing_pool.submit(_wrap(fn), *args, **kwargs) + + def map(self, func, *iterables, **kwargs): + return self._backing_pool.map( + _wrap(func), *iterables, timeout=kwargs.get('timeout', None)) + + def shutdown(self, wait=True): + self._backing_pool.shutdown(wait=wait) + + +def pool(max_workers): + """Creates a thread pool that logs exceptions raised by the tasks within it. + + Args: + max_workers: The maximum number of worker threads to allow the pool. + + Returns: + A futures.ThreadPoolExecutor-compatible thread pool that logs exceptions + raised by the tasks executed within it. + """ + return _LoggingPool(futures.ThreadPoolExecutor(max_workers)) From 840615ed276c7b94db70e817a4dde729278684e4 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Thu, 22 Jan 2015 20:31:47 +0000 Subject: [PATCH 069/143] Add Python to run-tests. --- .gitignore | 3 +++ INSTALL | 2 +- tools/run_tests/build_python.sh | 10 ++++++++++ tools/run_tests/run_python.sh | 10 ++++++++++ tools/run_tests/run_tests.py | 18 +++++++++++++++++- 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100755 tools/run_tests/build_python.sh create mode 100755 tools/run_tests/run_python.sh diff --git a/.gitignore b/.gitignore index 63332d1b16b..002e3e661cb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ gens libs objs +# Python virtual environment (pre-3.4 only) +python2.7_virtual_environment + # gcov coverage data coverage *.gcno diff --git a/INSTALL b/INSTALL index 98c20f58983..48511aff7df 100644 --- a/INSTALL +++ b/INSTALL @@ -17,7 +17,7 @@ A typical unix installation won't require any more steps than running: You don't need anything else than GNU Make and gcc. Under a Debian or Ubuntu system, this should boil down to the following package: - # apt-get install build-essential + # apt-get install build-essential python-all-dev python-virtualenv ******************************* diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh new file mode 100755 index 00000000000..6899ac7fe3e --- /dev/null +++ b/tools/run_tests/build_python.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex + +# change to grpc repo root +cd $(dirname $0)/../.. + +root=`pwd` +virtualenv python2.7_virtual_environment +python2.7_virtual_environment/bin/pip install enum34==1.0.4 futures==2.2.0 diff --git a/tools/run_tests/run_python.sh b/tools/run_tests/run_python.sh new file mode 100755 index 00000000000..0d5ed0238db --- /dev/null +++ b/tools/run_tests/run_python.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex + +# change to grpc repo root +cd $(dirname $0)/../.. + +root=`pwd` +python2.7_virtual_environment/bin/python2.7 -B -m unittest discover -s src/python -p '*.py' +python3.4 -B -m unittest discover -s src/python -p '*.py' diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 15c523731bd..da849f04cb0 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -75,6 +75,21 @@ class PhpLanguage(object): return [['tools/run_tests/build_php.sh']] +class PythonLanguage(object): + + def __init__(self): + self.allow_hashing = False + + def test_binaries(self, config): + return ['tools/run_tests/run_python.sh'] + + def make_targets(self): + return[] + + def build_steps(self): + return [['tools/run_tests/build_python.sh']] + + # different configurations we can run under _CONFIGS = { 'dbg': SimpleConfig('dbg'), @@ -92,7 +107,8 @@ _DEFAULT = ['dbg', 'opt'] _LANGUAGES = { 'c++': CLanguage('cxx', 'c++'), 'c': CLanguage('c', 'c'), - 'php': PhpLanguage() + 'php': PhpLanguage(), + 'python': PythonLanguage(), } # parse command line From cbaacad71956658ac4469b2a77effe1682919469 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 22 Jan 2015 13:24:54 -0800 Subject: [PATCH 070/143] Fixed junk data bug in PHP SSL Credentials --- src/php/ext/grpc/credentials.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c index c63196bf909..46c825a48fa 100644 --- a/src/php/ext/grpc/credentials.c +++ b/src/php/ext/grpc/credentials.c @@ -81,6 +81,8 @@ PHP_METHOD(Credentials, createSsl) { int root_certs_length, private_key_length = 0, cert_chain_length = 0; + pem_key_cert_pair.private_key = pem_key_cert_pair.cert_chain = NULL; + /* "s|s!s! == 1 string, 2 optional nullable strings */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!", &pem_root_certs, &root_certs_length, From 8c1109737f835f23056325e366aced23fa24db37 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Thu, 22 Jan 2015 14:11:42 -0800 Subject: [PATCH 071/143] Remove unecessary files which was added by mistake. --- examples/tips/messages.proto | 94 -------------- examples/tips/server.cc | 231 ----------------------------------- examples/tips/test.proto | 42 ------- 3 files changed, 367 deletions(-) delete mode 100644 examples/tips/messages.proto delete mode 100644 examples/tips/server.cc delete mode 100644 examples/tips/test.proto diff --git a/examples/tips/messages.proto b/examples/tips/messages.proto deleted file mode 100644 index 29db0dd8b1a..00000000000 --- a/examples/tips/messages.proto +++ /dev/null @@ -1,94 +0,0 @@ -// Message definitions to be used by integration test service definitions. - -syntax = "proto2"; - -package grpc.testing; - -// The type of payload that should be returned. -enum PayloadType { - // Compressable text format. - COMPRESSABLE = 0; - - // Uncompressable binary format. - UNCOMPRESSABLE = 1; - - // Randomly chosen from all other formats defined in this enum. - RANDOM = 2; -} - -// A block of data, to simply increase gRPC message size. -message Payload { - // The type of data in body. - optional PayloadType type = 1; - // Primary contents of payload. - optional bytes body = 2; -} - -// Unary request. -message SimpleRequest { - // Desired payload type in the response from the server. - // If response_type is RANDOM, server randomly chooses one from other formats. - optional PayloadType response_type = 1; - - // Desired payload size in the response from the server. - // If response_type is COMPRESSABLE, this denotes the size before compression. - optional int32 response_size = 2; - - // Optional input payload sent along with the request. - optional Payload payload = 3; -} - -// Unary response, as configured by the request. -message SimpleResponse { - // Payload to increase message size. - optional Payload payload = 1; - // The user the request came from, for verifying authentication was - // successful when the client expected it. - optional int64 effective_gaia_user_id = 2; -} - -// Client-streaming request. -message StreamingInputCallRequest { - // Optional input payload sent along with the request. - optional Payload payload = 1; - - // Not expecting any payload from the response. -} - -// Client-streaming response. -message StreamingInputCallResponse { - // Aggregated size of payloads received from the client. - optional int32 aggregated_payload_size = 1; -} - -// Configuration for a particular response. -message ResponseParameters { - // Desired payload sizes in responses from the server. - // If response_type is COMPRESSABLE, this denotes the size before compression. - optional int32 size = 1; - - // Desired interval between consecutive responses in the response stream in - // microseconds. - optional int32 interval_us = 2; -} - -// Server-streaming request. -message StreamingOutputCallRequest { - // Desired payload type in the response from the server. - // If response_type is RANDOM, the payload from each response in the stream - // might be of different types. This is to simulate a mixed type of payload - // stream. - optional PayloadType response_type = 1; - - // Configuration for each expected response message. - repeated ResponseParameters response_parameters = 2; - - // Optional input payload sent along with the request. - optional Payload payload = 3; -} - -// Server-streaming response, as configured by the request and parameters. -message StreamingOutputCallResponse { - // Payload to increase response size. - optional Payload payload = 1; -} diff --git a/examples/tips/server.cc b/examples/tips/server.cc deleted file mode 100644 index 2171a6aac0d..00000000000 --- a/examples/tips/server.cc +++ /dev/null @@ -1,231 +0,0 @@ -/* - * - * Copyright 2014, 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. - * - */ - -#include -#include -#include - -#include -#include -#include -#include "test/core/end2end/data/ssl_test_data.h" -#include -#include -#include -#include -#include -#include -#include -#include "examples/tips/test.pb.h" -#include "examples/tips/empty.pb.h" -#include "examples/tips/messages.pb.h" - -DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls."); -DEFINE_int32(port, 0, "Server port."); - -using grpc::Server; -using grpc::ServerBuilder; -using grpc::ServerContext; -using grpc::ServerCredentials; -using grpc::ServerCredentialsFactory; -using grpc::ServerReader; -using grpc::ServerReaderWriter; -using grpc::ServerWriter; -using grpc::SslServerCredentialsOptions; -using grpc::testing::Payload; -using grpc::testing::PayloadType; -using grpc::testing::SimpleRequest; -using grpc::testing::SimpleResponse; -using grpc::testing::StreamingInputCallRequest; -using grpc::testing::StreamingInputCallResponse; -using grpc::testing::StreamingOutputCallRequest; -using grpc::testing::StreamingOutputCallResponse; -using grpc::testing::TestService; -using grpc::Status; - -bool SetPayload(PayloadType type, int size, Payload* payload) { - PayloadType response_type = type; - // TODO(yangg): Support UNCOMPRESSABLE payload. - if (type != PayloadType::COMPRESSABLE) { - return false; - } - payload->set_type(response_type); - std::unique_ptr body(new char[size]()); - payload->set_body(body.get(), size); - return true; -} - -class TestServiceImpl : public TestService::Service { - public: - Status EmptyCall(ServerContext* context, const grpc::testing::Empty* request, - grpc::testing::Empty* response) { - return Status::OK; - } - - Status UnaryCall(ServerContext* context, const SimpleRequest* request, - SimpleResponse* response) { - if (request->has_response_size() && request->response_size() > 0) { - if (!SetPayload(request->response_type(), request->response_size(), - response->mutable_payload())) { - return Status(grpc::StatusCode::INTERNAL, "Error creating payload."); - } - } - return Status::OK; - } - - Status StreamingOutputCall( - ServerContext* context, const StreamingOutputCallRequest* request, - ServerWriter* writer) { - StreamingOutputCallResponse response; - bool write_success = true; - response.mutable_payload()->set_type(request->response_type()); - for (int i = 0; write_success && i < request->response_parameters_size(); - i++) { - response.mutable_payload()->set_body( - grpc::string(request->response_parameters(i).size(), '\0')); - write_success = writer->Write(response); - } - if (write_success) { - return Status::OK; - } else { - return Status(grpc::StatusCode::INTERNAL, "Error writing response."); - } - } - - Status StreamingInputCall(ServerContext* context, - ServerReader* reader, - StreamingInputCallResponse* response) { - StreamingInputCallRequest request; - int aggregated_payload_size = 0; - while (reader->Read(&request)) { - if (request.has_payload() && request.payload().has_body()) { - aggregated_payload_size += request.payload().body().size(); - } - } - response->set_aggregated_payload_size(aggregated_payload_size); - return Status::OK; - } - - Status FullDuplexCall( - ServerContext* context, - ServerReaderWriter* stream) { - StreamingOutputCallRequest request; - StreamingOutputCallResponse response; - bool write_success = true; - while (write_success && stream->Read(&request)) { - response.mutable_payload()->set_type(request.payload().type()); - if (request.response_parameters_size() == 0) { - return Status(grpc::StatusCode::INTERNAL, - "Request does not have response parameters."); - } - response.mutable_payload()->set_body( - grpc::string(request.response_parameters(0).size(), '\0')); - write_success = stream->Write(response); - } - if (write_success) { - return Status::OK; - } else { - return Status(grpc::StatusCode::INTERNAL, "Error writing response."); - } - } - - Status HalfDuplexCall( - ServerContext* context, - ServerReaderWriter* stream) { - std::vector requests; - StreamingOutputCallRequest request; - while (stream->Read(&request)) { - requests.push_back(request); - } - - StreamingOutputCallResponse response; - bool write_success = true; - for (unsigned int i = 0; write_success && i < requests.size(); i++) { - response.mutable_payload()->set_type(requests[i].payload().type()); - if (requests[i].response_parameters_size() == 0) { - return Status(grpc::StatusCode::INTERNAL, - "Request does not have response parameters."); - } - response.mutable_payload()->set_body( - grpc::string(requests[i].response_parameters(0).size(), '\0')); - write_success = stream->Write(response); - } - if (write_success) { - return Status::OK; - } else { - return Status(grpc::StatusCode::INTERNAL, "Error writing response."); - } - } -}; - -void RunServer() { - std::ostringstream server_address; - server_address << "localhost:" << FLAGS_port; - TestServiceImpl service; - - SimpleRequest request; - SimpleResponse response; - - ServerBuilder builder; - builder.AddPort(server_address.str()); - builder.RegisterService(service.service()); - if (FLAGS_enable_ssl) { - SslServerCredentialsOptions ssl_opts = { - "", - {reinterpret_cast(test_server1_key), - test_server1_key_size}, - {reinterpret_cast(test_server1_cert), - test_server1_cert_size}}; - std::shared_ptr creds = - ServerCredentialsFactory::SslCredentials(ssl_opts); - builder.SetCredentials(creds); - } - std::unique_ptr server(builder.BuildAndStart()); - gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str()); - while (true) { - std::this_thread::sleep_for(std::chrono::seconds(5)); - } -} - -int main(int argc, char** argv) { - grpc_init(); - google::ParseCommandLineFlags(&argc, &argv, true); - - GPR_ASSERT(FLAGS_port != 0); - RunServer(); - - grpc_shutdown(); - return 0; -} diff --git a/examples/tips/test.proto b/examples/tips/test.proto deleted file mode 100644 index 807a3fe677b..00000000000 --- a/examples/tips/test.proto +++ /dev/null @@ -1,42 +0,0 @@ -// An integration test service that covers all the method signature permutations -// of unary/streaming requests/responses. -syntax = "proto2"; - -import "examples/tips/empty.proto"; -import "examples/tips/messages.proto"; - -package grpc.testing; - -// A simple service to test the various types of RPCs and experiment with -// performance with various types of payload. -service TestService { - // One empty request followed by one empty response. - rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); - - // One request followed by one response. - // The server returns the client payload as-is. - rpc UnaryCall(SimpleRequest) returns (SimpleResponse); - - // One request followed by a sequence of responses (streamed download). - // The server returns the payload with client desired type and sizes. - rpc StreamingOutputCall(StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); - - // A sequence of requests followed by one response (streamed upload). - // The server returns the aggregated size of client payload as the result. - rpc StreamingInputCall(stream StreamingInputCallRequest) - returns (StreamingInputCallResponse); - - // A sequence of requests with each request served by the server immediately. - // As one request could lead to multiple responses, this interface - // demonstrates the idea of full duplexing. - rpc FullDuplexCall(stream StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); - - // A sequence of requests followed by a sequence of responses. - // The server buffers all the client requests and then serves them in order. A - // stream of responses are returned to the client when the server starts with - // first request. - rpc HalfDuplexCall(stream StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); -} From 3d4cba49014a1822b95d4af294e661ef2d4acb53 Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Thu, 22 Jan 2015 23:42:15 +0100 Subject: [PATCH 072/143] kNumResponseMessages is an int, so i is doing signedness mismatch. --- test/cpp/interop/client.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc index 04cfeb86cb3..2a18ddb72e6 100644 --- a/test/cpp/interop/client.cc +++ b/test/cpp/interop/client.cc @@ -188,7 +188,7 @@ void DoResponseStreamingWithSlowConsumer( grpc::ClientContext context; StreamingOutputCallRequest request; - for (unsigned int i = 0; i < kNumResponseMessages; ++i) { + for (int i = 0; i < kNumResponseMessages; ++i) { ResponseParameters* response_parameter = request.add_response_parameters(); response_parameter->set_size(kResponseMessageSize); } @@ -196,7 +196,7 @@ void DoResponseStreamingWithSlowConsumer( std::unique_ptr> stream( stub->StreamingOutputCall(&context, &request)); - unsigned int i = 0; + int i = 0; while (stream->Read(&response)) { GPR_ASSERT(response.payload().body() == grpc::string(kResponseMessageSize, '\0')); From 819f755b74b217acfee017ec9aa7e8d0ea3a706a Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Thu, 22 Jan 2015 14:52:49 -0800 Subject: [PATCH 073/143] fixed some typos --- examples/tips/client.cc | 7 +++---- examples/tips/client.h | 2 +- examples/tips/client_main.cc | 32 ++++++++++---------------------- examples/tips/client_test.cc | 4 ---- examples/tips/empty.proto | 6 ------ examples/tips/label.proto | 3 +-- 6 files changed, 15 insertions(+), 39 deletions(-) diff --git a/examples/tips/client.cc b/examples/tips/client.cc index bfbd59c9d56..a902a21c3c6 100644 --- a/examples/tips/client.cc +++ b/examples/tips/client.cc @@ -35,7 +35,6 @@ #include "examples/tips/client.h" -using grpc::ChannelInterface; using grpc::ClientContext; using tech::pubsub::Topic; using tech::pubsub::PublisherService; @@ -44,7 +43,7 @@ namespace grpc { namespace examples { namespace tips { -Client::Client(std::shared_ptr channel) +Client::Client(std::shared_ptr channel) : stub_(PublisherService::NewStub(channel)) { } @@ -52,11 +51,11 @@ Status Client::CreateTopic(grpc::string topic) { Topic request; Topic response; request.set_name(topic); - ClientContext context; + grpc::ClientContext context; return stub_->CreateTopic(&context, request, &response); } -} // namesapce tips +} // namespace tips } // namespace examples } // namespace grpc diff --git a/examples/tips/client.h b/examples/tips/client.h index 83e79118235..152a2a396dd 100644 --- a/examples/tips/client.h +++ b/examples/tips/client.h @@ -48,6 +48,6 @@ class Client { std::unique_ptr stub_; }; -} // namesapce tips +} // namespace tips } // namespace examples } // namespace grpc diff --git a/examples/tips/client_main.cc b/examples/tips/client_main.cc index 0324b4b8b08..17567b6f17a 100644 --- a/examples/tips/client_main.cc +++ b/examples/tips/client_main.cc @@ -31,40 +31,26 @@ * */ -#include -#include -#include -#include - #include #include #include -#include #include -#include #include #include -#include + +#include "examples/tips/client.h" #include "test/cpp/util/create_test_channel.h" -DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls."); -DEFINE_bool(use_prod_roots, false, "True to use SSL roots for production GFE"); +DEFINE_bool(enable_ssl, true, "Whether to use ssl/tls."); +DEFINE_bool(use_prod_roots, true, "True to use SSL roots for production GFE"); DEFINE_int32(server_port, 0, "Server port."); DEFINE_string(server_host, "127.0.0.1", "Server host to connect to"); DEFINE_string(server_host_override, "foo.test.google.com", "Override the server host which is sent in HTTP header"); -using grpc::ChannelInterface; -using grpc::ClientContext; -using grpc::CreateTestChannel; - -void CreateTopic(std::shared_ptr channel, grpc::string topic); - int main(int argc, char** argv) { grpc_init(); - google::ParseCommandLineFlags(&argc, &argv, true); - gpr_log(GPR_INFO, "Start TIPS client"); GPR_ASSERT(FLAGS_server_port); @@ -73,11 +59,13 @@ int main(int argc, char** argv) { snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(), FLAGS_server_port); - std::shared_ptr channel( - CreateTestChannel(host_port, FLAGS_server_host_override, FLAGS_enable_ssl, - FLAGS_use_prod_roots)); + std::shared_ptr channel( + grpc::CreateTestChannel(host_port, FLAGS_server_host_override, + FLAGS_enable_ssl, FLAGS_use_prod_roots)); - CreateTopic(channel, "test"); + grpc::examples::tips::Client client(channel); + grpc::Status s = client.CreateTopic("test"); + GPR_ASSERT(s.IsOk()); channel.reset(); grpc_shutdown(); diff --git a/examples/tips/client_test.cc b/examples/tips/client_test.cc index d40f0c0d8b4..69238f2c6fc 100644 --- a/examples/tips/client_test.cc +++ b/examples/tips/client_test.cc @@ -47,10 +47,6 @@ using grpc::ChannelInterface; -namespace { - -} // - namespace grpc { namespace testing { namespace { diff --git a/examples/tips/empty.proto b/examples/tips/empty.proto index a698ab6f8a9..adf66b5e614 100644 --- a/examples/tips/empty.proto +++ b/examples/tips/empty.proto @@ -10,10 +10,4 @@ package proto2; // rpc Bar (proto2.Empty) returns (proto2.Empty) { }; // }; // -// BEGIN GOOGLE-INTERNAL -// The difference between this one and net/rpc/empty-message.proto is that -// 1) The generated message here is in proto2 C++ API. -// 2) The proto2.Empty has minimum dependencies -// (no message_set or net/rpc dependencies) -// END GOOGLE-INTERNAL message Empty {} diff --git a/examples/tips/label.proto b/examples/tips/label.proto index e79e1db77c2..e93ac9dea30 100644 --- a/examples/tips/label.proto +++ b/examples/tips/label.proto @@ -1,7 +1,6 @@ // Labels provide a way to associate user-defined metadata with various // objects. Labels may be used to organize objects into non-hierarchical -// groups; think metadata tags attached to mp3s. For more details, see -// http://go/exosphere:labels +// groups; think metadata tags attached to mp3s. syntax = "proto2"; From c6fdcae0875e5bea62a5d5badef96261cd2a5c4a Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Thu, 22 Jan 2015 15:01:43 -0800 Subject: [PATCH 074/143] add space line between include --- examples/tips/client.h | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/tips/client.h b/examples/tips/client.h index 152a2a396dd..3f4f1fd8369 100644 --- a/examples/tips/client.h +++ b/examples/tips/client.h @@ -33,6 +33,7 @@ #include #include + #include "examples/tips/pubsub.pb.h" namespace grpc { From d34c690e8d0e080d7bfb4117b80d32e6d2354ba1 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Thu, 22 Jan 2015 15:12:20 -0800 Subject: [PATCH 075/143] delete using grpc:: --- examples/tips/client.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/tips/client.cc b/examples/tips/client.cc index a902a21c3c6..695ff80e8af 100644 --- a/examples/tips/client.cc +++ b/examples/tips/client.cc @@ -35,7 +35,6 @@ #include "examples/tips/client.h" -using grpc::ClientContext; using tech::pubsub::Topic; using tech::pubsub::PublisherService; @@ -43,7 +42,7 @@ namespace grpc { namespace examples { namespace tips { -Client::Client(std::shared_ptr channel) +Client::Client(std::shared_ptr channel) : stub_(PublisherService::NewStub(channel)) { } @@ -51,7 +50,7 @@ Status Client::CreateTopic(grpc::string topic) { Topic request; Topic response; request.set_name(topic); - grpc::ClientContext context; + ClientContext context; return stub_->CreateTopic(&context, request, &response); } From fb6d7fd5720773da1d68f05c2d58cd16bdaf7018 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 12:16:12 -0800 Subject: [PATCH 076/143] Adds Dockerfile for Go --- tools/dockerfile/grpc_go/Dockerfile | 27 +++++++++++++++++++++++++++ tools/dockerfile/grpc_go/README.md | 4 ++++ 2 files changed, 31 insertions(+) create mode 100644 tools/dockerfile/grpc_go/Dockerfile create mode 100644 tools/dockerfile/grpc_go/README.md diff --git a/tools/dockerfile/grpc_go/Dockerfile b/tools/dockerfile/grpc_go/Dockerfile new file mode 100644 index 00000000000..ab463b2a006 --- /dev/null +++ b/tools/dockerfile/grpc_go/Dockerfile @@ -0,0 +1,27 @@ +# Dockerfile for gRPC Go +FROM golang:1.4 + +# Install SSH to that Go source can be pulled securely. +RUN apt-get update && apt-get install -y ssh + +# Install a GitHub SSH service credential that gives access to the GitHub repo while it's private +# +# TODO: remove this once the repo is public +ADD .ssh .ssh +RUN chmod 600 /.ssh/github.rsa +RUN mkdir -p $HOME/.ssh && echo 'Host github.com' > $HOME/.ssh/config +RUN echo " IdentityFile /.ssh/github.rsa" >> $HOME/.ssh/config +RUN echo 'StrictHostKeyChecking no' >> $HOME/.ssh/config + +# Force go get to use the GitHub ssh url instead of https, and use the SSH creds +RUN git config --global url."git@github.com:".insteadOf "https://github.com/" + +# Get the source from GitHub +RUN go get github.com/google/grpc-go + +# Build the interop client and server +RUN cd src/github.com/google/grpc-go/interop/client && go install +RUN cd src/github.com/google/grpc-go/interop/server && go install + +# Specify the default command such that the interop server runs on its known testing port +CMD ["/bin/bash", "-c 'cd src/github.com/google/grpc-go/interop/server && go run server.go --use_tls=true --port=8020'"] diff --git a/tools/dockerfile/grpc_go/README.md b/tools/dockerfile/grpc_go/README.md new file mode 100644 index 00000000000..0d6ad3e391c --- /dev/null +++ b/tools/dockerfile/grpc_go/README.md @@ -0,0 +1,4 @@ +GRPC Go Dockerfile +================== + +Dockerfile for gRPC Go development, testing and deployment. From c8ee19b9052981321992ae0bab7980422211c162 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 17:11:57 -0800 Subject: [PATCH 077/143] Adds a bash func that runs the interop test client command --- tools/gce_setup/grpc_docker.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index a19915594a7..bfa4c808be4 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -634,6 +634,18 @@ grpc_interop_gen_ruby_cmd() { echo $the_cmd } +# constructs the full dockerized Go interop test cmd. +# +# call-seq: +# flags= .... # generic flags to include the command +# cmd=$($grpc_gen_test_cmd $flags) +grpc_interop_gen_go_cmd() { + local cmd_prefix="sudo docker run grpc/go bin/bash -c"; + local test_script="cd /go/src/github.com/google/grpc-go/interop/client"; + local test_script+=" && go run client.go --use_tls=true"; + local the_cmd="$cmd_prefix '$test_script $@ 1>&2'"; +} + # constructs the full dockerized java interop test cmd. # # call-seq: @@ -664,4 +676,4 @@ grpc_interop_gen_php_cmd() { } -# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs|go +# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs From 1a8582f18e1e52151b30845543a2a6b534651fcd Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 17:12:44 -0800 Subject: [PATCH 078/143] removes noisy output when syncing --- tools/gce_setup/shared_startup_funcs.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/gce_setup/shared_startup_funcs.sh b/tools/gce_setup/shared_startup_funcs.sh index f9a6aafb095..f1dbca9a2e6 100755 --- a/tools/gce_setup/shared_startup_funcs.sh +++ b/tools/gce_setup/shared_startup_funcs.sh @@ -371,7 +371,8 @@ grpc_docker_pull_known() { echo "... pulling docker images for '$known'" for i in $known do - sudo docker pull ${addr}/grpc/$i \ + echo "<--- grpc/$i" + sudo docker pull ${addr}/grpc/$i > /dev/null 2>&1 \ && sudo docker tag ${addr}/grpc/$i grpc/$i || { # log and continue echo "docker op error: could not pull ${addr}/grpc/$i" From 9d9b805cb1e7ab2b9d052c9b5c41813a0718f85a Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Fri, 23 Jan 2015 02:27:06 +0100 Subject: [PATCH 079/143] Adressing comments. --- tools/buildgen/plugins/generate_vsprojects.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/buildgen/plugins/generate_vsprojects.py b/tools/buildgen/plugins/generate_vsprojects.py index 021b8438682..982e6812e61 100755 --- a/tools/buildgen/plugins/generate_vsprojects.py +++ b/tools/buildgen/plugins/generate_vsprojects.py @@ -6,9 +6,6 @@ and "vsproject_dict", to be used by the visual studio generators. """ -import re - - def mako_plugin(dictionary): """The exported plugin code for generate_vsprojeccts @@ -27,10 +24,13 @@ def mako_plugin(dictionary): projects = [] projects.extend(libs) projects.extend(targets) - projects = [project for project in projects if project.get('vs_project_guid', None)] + # Exclude projects without a visual project guid, such as the tests. + projects = [project for project in projects + if project.get('vs_project_guid', None)] - ## Exclude C++ projects for now - projects = [project for project in projects if not project.language == 'c++'] + # Exclude C++ projects for now + projects = [project for project in projects + if not project['language'] == 'c++'] project_dict = dict([(p['name'], p) for p in projects]) From be5201826b492f5649007b4d216a19767dbb724f Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Fri, 23 Jan 2015 02:32:49 +0100 Subject: [PATCH 080/143] Regenerating project files. --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 14b708f89c9..f5a4faf2e1f 100644 --- a/Makefile +++ b/Makefile @@ -2210,7 +2210,11 @@ endif libs/$(CONFIG)/libtips_client_lib.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBTIPS_CLIENT_LIB_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` + $(Q) rm -f libs/$(CONFIG)/libtips_client_lib.a $(Q) $(AR) rcs libs/$(CONFIG)/libtips_client_lib.a $(LIBTIPS_CLIENT_LIB_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib libs/$(CONFIG)/libtips_client_lib.a +endif From 1bbe9ea2325413c6780530680bfa328450f73b59 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 17:40:07 -0800 Subject: [PATCH 081/143] Removed beefcake extension hook, it was no longer needed --- src/ruby/lib/grpc/beefcake.rb | 57 ----------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 src/ruby/lib/grpc/beefcake.rb diff --git a/src/ruby/lib/grpc/beefcake.rb b/src/ruby/lib/grpc/beefcake.rb deleted file mode 100644 index fd3ebbf4b84..00000000000 --- a/src/ruby/lib/grpc/beefcake.rb +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2014, 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. - -require 'beefcake' - -module Beefcake - # Re-open the beefcake message module to add a static encode - # - # This is a temporary measure while beefcake is used as the default proto - # library for developing grpc ruby. Once that changes to the official proto - # library this can be removed. It's necessary to allow the update the service - # module to assume a static encode method. - # TODO(temiola): remove this. - module Message - # additional mixin module that adds static encode method when include - module StaticEncode - # encodes o with its instance#encode method - def encode(o) - o.encode - end - end - - # extend self.included in Beefcake::Message to include StaticEncode - def self.included(o) - o.extend StaticEncode - o.extend Dsl - o.extend Decode - o.send(:include, Encode) - end - end -end From eaed637dba7f672179130066bbc230e6dfcff74f Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 18:00:50 -0800 Subject: [PATCH 082/143] Removes Google username from TODOs, removes defunct TODOs --- src/ruby/bin/interop/interop_server.rb | 4 ++-- src/ruby/ext/grpc/extconf.rb | 8 ++------ src/ruby/ext/grpc/rb_completion_queue.c | 2 +- src/ruby/lib/grpc/generic/bidi_call.rb | 2 +- src/ruby/lib/grpc/generic/rpc_server.rb | 6 +----- src/ruby/lib/grpc/logconfig.rb | 2 +- src/ruby/spec/client_server_spec.rb | 2 +- 7 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb index 1a08eb97dfe..83212823f62 100755 --- a/src/ruby/bin/interop/interop_server.rb +++ b/src/ruby/bin/interop/interop_server.rb @@ -145,8 +145,8 @@ class TestTarget < Grpc::Testing::TestService::Service end def half_duplex_call(reqs) - # TODO(temiola): clarify the behaviour of the half_duplex_call, it's not - # currently used in any tests + # TODO: update with unique behaviour of the half_duplex_call if that's + # ever required by any of the tests. full_duplex_call(reqs) end end diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb index a6dbbf3aca1..cbf41eda8b7 100644 --- a/src/ruby/ext/grpc/extconf.rb +++ b/src/ruby/ext/grpc/extconf.rb @@ -68,13 +68,9 @@ $CFLAGS << ' -Wno-return-type ' $CFLAGS << ' -Wall ' $CFLAGS << ' -pedantic ' -$LDFLAGS << ' -lgrpc -lgpr' - -# crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy') -# -# TODO(temiola): figure out why this stopped working, but the so is built OK -# and the tests pass +$LDFLAGS << ' -lgrpc -lgpr -ldl' +crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy') have_library('grpc', 'grpc_channel_destroy') crash('need gpr lib') unless have_library('gpr', 'gpr_now') create_makefile('grpc/grpc') diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c index c1b74e2606d..47776a991a1 100644 --- a/src/ruby/ext/grpc/rb_completion_queue.c +++ b/src/ruby/ext/grpc/rb_completion_queue.c @@ -75,7 +75,7 @@ static void grpc_rb_completion_queue_shutdown_drain(grpc_completion_queue *cq) { grpc_completion_queue_shutdown(cq); next_call.cq = cq; next_call.event = NULL; - /* TODO(temiola): the timeout should be a module level constant that defaults + /* TODO: the timeout should be a module level constant that defaults * to gpr_inf_future. * * - at the moment this does not work, it stalls. Using a small timeout like diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb index 14ef6c531f9..36877dc6481 100644 --- a/src/ruby/lib/grpc/generic/bidi_call.rb +++ b/src/ruby/lib/grpc/generic/bidi_call.rb @@ -142,7 +142,7 @@ module Google # during bidi-streaming, read the requests to send from a separate thread # read so that read_loop does not block waiting for requests to read. def start_write_loop(requests, is_client: true) - Thread.new do # TODO(temiola) run on a thread pool + Thread.new do # TODO: run on a thread pool write_tag = Object.new begin count = 0 diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb index 5ea3cc94d65..40c5ec118e3 100644 --- a/src/ruby/lib/grpc/generic/rpc_server.rb +++ b/src/ruby/lib/grpc/generic/rpc_server.rb @@ -233,10 +233,6 @@ module Google end def new_active_server_call(call, new_server_rpc) - # TODO(temiola): perhaps reuse the main server completion queue here, - # but for now, create a new completion queue per call, pending best - # practice usage advice from the c core. - # Accept the call. This is necessary even if a status is to be sent # back immediately finished_tag = Object.new @@ -340,7 +336,7 @@ module Google @workers.size.times { schedule { throw :exit } } @stopped = true - # TODO(temiola): allow configuration of the keepalive period + # TODO: allow configuration of the keepalive period keep_alive = 5 @stop_mutex.synchronize do @stop_cond.wait(@stop_mutex, keep_alive) if @workers.size > 0 diff --git a/src/ruby/lib/grpc/logconfig.rb b/src/ruby/lib/grpc/logconfig.rb index 6d8e1899a06..6442f23e895 100644 --- a/src/ruby/lib/grpc/logconfig.rb +++ b/src/ruby/lib/grpc/logconfig.rb @@ -34,7 +34,7 @@ include Logging.globally # logger is accessible everywhere Logging.logger.root.appenders = Logging.appenders.stdout Logging.logger.root.level = :info -# TODO(temiola): provide command-line configuration for logging +# TODO: provide command-line configuration for logging Logging.logger['Google::RPC'].level = :debug Logging.logger['Google::RPC::ActiveCall'].level = :info Logging.logger['Google::RPC::BidiCall'].level = :info diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb index 1bcbc66446c..df70e56bca7 100644 --- a/src/ruby/spec/client_server_spec.rb +++ b/src/ruby/spec/client_server_spec.rb @@ -294,7 +294,7 @@ shared_examples 'GRPC metadata delivery works OK' do expect_next_event_on(@server_queue, WRITE_ACCEPTED, @server_tag) # there is the HTTP status metadata, though there should not be any - # TODO(temiola): update this with the bug number to be resolved + # TODO: update this with the bug number to be resolved ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ, @tag) expect(ev.result).to eq(':status' => '200') end From 9fe51788e3c32c5414f2b5740c146239ac8407f5 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 18:23:32 -0800 Subject: [PATCH 083/143] Removes google3 specifics from the README.md, aligns the description with an open source release --- src/ruby/README.md | 83 +++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/src/ruby/README.md b/src/ruby/README.md index 23aec2b20af..7f7558dc677 100755 --- a/src/ruby/README.md +++ b/src/ruby/README.md @@ -1,64 +1,63 @@ -Ruby for GRPC -============= +gRPC Ruby +========= -LAYOUT ------- +A Ruby implementation of gRPC, Google's RPC library. -Directory structure is the recommended layout for [ruby extensions](http://guides.rubygems.org/gems-with-extensions/) - * ext: the extension code - * lib: the entrypoint grpc ruby library to be used in a 'require' statement - * test: tests +INSTALLATION PREREQUISITES +-------------------------- +This requires Ruby 2.x, as the rpc api surface uses keyword args. -DEPENDENCIES ------------- +INSTALLING +---------- -* Extension +- Install the gRPC core library +TODO: describe this, once the core distribution mechanism is defined. -The extension can be built and tested using -[rake](https://rubygems.org/gems/rake). However, the rake-extensiontask rule -is not supported on older versions of rubygems, and the necessary version of -rubygems. +$ gem install grpc -This is resolved by using [RVM](https://rvm.io/) instead; install a single-user -ruby environment, and develop on the latest stable version of ruby (2.1.5). +Installing from source +---------------------- -INSTALLATION PREREQUISITES --------------------------- - -Install RVM +- Build or Install the gRPC core +E.g, from the root of the grpc [git repo](https://github.com/google/grpc) +$ cd ../.. +$ make && sudo make install +- Install Ruby 2.x. Consider doing this with [RVM](http://rvm.io), it's a nice way of controlling + the exact ruby version that's used. $ command curl -sSL https://rvm.io/mpapis.asc | gpg --import - $ \curl -sSL https://get.rvm.io | bash -s stable --ruby $ $ # follow the instructions to ensure that your're using the latest stable version of Ruby $ # and that the rvm command is installed -$ -$ gem install bundler # install bundler, the standard ruby package manager -HACKING -------- +- Install [bundler](http://bundler.io/) +$ gem install bundler -The extension can be built and tested using the Rakefile. +- Finally, install grpc ruby locally. +$ cd +$ bundle install +$ rake # compiles the extension, runs the unit tests, see rake -T for other options -$ # create a workspace -$ git5 start net/grpc -$ -$ # build the C library and install it in $HOME/grpc_dev -$ /net/grpc/c/build_gyp/build_grpc_dev.sh -$ -$ # build the ruby extension and test it. -$ cd google3_dir/net/grpc/ruby -$ rake -Finally, install grpc ruby locally. +CONTENTS +-------- -$ cd -$ -$ # update the Gemfile, modify the line beginning # gem 'beefcake' to refer to -$ # the patched beefcake dir -$ -$ bundle install +Directory structure is the layout for [ruby extensions](http://guides.rubygems.org/gems-with-extensions/) + + * ext: the extension code + * lib: the entrypoint grpc ruby library to be used in a 'require' statement + * spec: tests + * bin: example gRPC clients and servers, e.g, +```ruby +# client +stub = Math::Math::Stub.new('my.test.math.server.com:8080') +req = Math::DivArgs.new(dividend: 7, divisor: 3) +logger.info("div(7/3): req=#{req.inspect}") +resp = stub.div(req, INFINITE_FUTURE) +logger.info("Answer: #{resp.inspect}") +``` From 5d11c1ad0dd634cb88c434fe7d162a6f6532ff58 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 18:39:34 -0800 Subject: [PATCH 084/143] Remove Go links --- src/ruby/grpc.gemspec | 8 ++++---- src/ruby/spec/testdata/README | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec index 8d7f44f30e2..bf7e5ad4a32 100755 --- a/src/ruby/grpc.gemspec +++ b/src/ruby/grpc.gemspec @@ -5,11 +5,11 @@ require 'grpc/version' Gem::Specification.new do |s| s.name = 'grpc' s.version = Google::RPC::VERSION - s.authors = ['One Platform Team'] - s.email = 'stubby-team@google.com' - s.homepage = 'http://go/grpc' + s.authors = ['gRPC Authors'] + s.email = 'tbetbetbe@gmail.com' + s.homepage = 'https://github.com/google/grpc/tree/master/src/ruby' s.summary = 'Google RPC system in Ruby' - s.description = 'Send RPCs from Ruby' + s.description = 'Send RPCs from Ruby using Google's RPC system' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- spec/*`.split("\n") diff --git a/src/ruby/spec/testdata/README b/src/ruby/spec/testdata/README index ed72661e974..cb20dcb49fb 100755 --- a/src/ruby/spec/testdata/README +++ b/src/ruby/spec/testdata/README @@ -1,4 +1 @@ These are test keys *NOT* to be used in production. -http://go/keyhunt requires this README - -CONFIRMEDTESTKEY From d428c54681bb83c8908262d10e552f609514309d Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 19:04:08 -0800 Subject: [PATCH 085/143] removed a reference to a google3 dir --- src/ruby/bin/interop/README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ruby/bin/interop/README.md b/src/ruby/bin/interop/README.md index 04020868a4c..84fc6636203 100755 --- a/src/ruby/bin/interop/README.md +++ b/src/ruby/bin/interop/README.md @@ -1,11 +1,8 @@ Interop test protos =================== -These were generated by a patched version of beefcake and a patched version of -protoc. +These ruby classes were generated with protoc v3, using grpc's ruby compiler +plugin. -- set up and access of the patched versions is described in ../../README.md - -The actual test proto is found in Google3 at - -- third_party/stubby/testing/proto/test.proto +- As of 2015/01 protoc v3 is available in the +[google-protobuf](https://github.com/google/protobuf) repo From 1e0d80d7a0408ee5495eb316bdcf620a4833f08a Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Thu, 22 Jan 2015 20:47:22 -0800 Subject: [PATCH 086/143] Add Newline --- src/core/iomgr/pollset_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/iomgr/pollset_windows.c b/src/core/iomgr/pollset_windows.c index c913c21461c..3fb39918b35 100644 --- a/src/core/iomgr/pollset_windows.c +++ b/src/core/iomgr/pollset_windows.c @@ -35,4 +35,4 @@ #ifdef GPR_WIN32 -#endif /* GPR_WIN32 */ \ No newline at end of file +#endif /* GPR_WIN32 */ From e86076159ce9239479e454b2d2cc4b7858c48479 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Thu, 22 Jan 2015 20:49:02 -0800 Subject: [PATCH 087/143] Fix indentation --- test/core/end2end/cq_verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c index 679537995c0..7a7f197907d 100644 --- a/test/core/end2end/cq_verifier.c +++ b/test/core/end2end/cq_verifier.c @@ -413,7 +413,7 @@ static metadata *metadata_from_args(va_list args) { md->values = gpr_realloc(md->values, sizeof(char *) * md->cap); } md->keys[md->count] = (char *)key; - md->values[md->count] = (char *)value; + md->values[md->count] = (char *)value; md->count++; } } From 5d11f8e7a1044dce3de37b2d990df39196ec1de7 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Fri, 23 Jan 2015 04:11:23 -0800 Subject: [PATCH 088/143] fix a typo --- src/ruby/grpc.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec index bf7e5ad4a32..450362f5a82 100755 --- a/src/ruby/grpc.gemspec +++ b/src/ruby/grpc.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.email = 'tbetbetbe@gmail.com' s.homepage = 'https://github.com/google/grpc/tree/master/src/ruby' s.summary = 'Google RPC system in Ruby' - s.description = 'Send RPCs from Ruby using Google's RPC system' + s.description = 'Send RPCs from Ruby using Google\'s RPC system' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- spec/*`.split("\n") From 09bc379417c7bbc7429d97adb3ab1d5f17d192ce Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Fri, 23 Jan 2015 04:35:16 -0800 Subject: [PATCH 089/143] Fixes a regression in the ruby dockerfile - the line that built and installed grpc ruby was mistakenly removed. --- tools/dockerfile/grpc_ruby/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/dockerfile/grpc_ruby/Dockerfile b/tools/dockerfile/grpc_ruby/Dockerfile index 43ec0183ca0..c677ceffff3 100644 --- a/tools/dockerfile/grpc_ruby/Dockerfile +++ b/tools/dockerfile/grpc_ruby/Dockerfile @@ -9,6 +9,9 @@ RUN cd /var/local/git/grpc \ # Build the C core. RUN make install_c -C /var/local/git/grpc +# Install the grpc gem locally with its dependencies and build the extension +RUN /bin/bash -l -c 'cd /var/local/git/grpc/src/ruby && bundle && rake compile:grpc && gem build grpc.gemspec && gem install grpc' + # TODO add a command to run the unittest tests when the bug below is fixed # - the tests fail due to an error in the C threading library: # they fail with 'ruby: __pthread_mutex_cond_lock_adjust for unknown reasons' at the end of a testcase From b776a1996d56cb5233f98e83b7603c391fbe25db Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Fri, 23 Jan 2015 05:19:57 -0800 Subject: [PATCH 090/143] Updates the Go dockerfile to reflect the package restructuring. - also corrects an error in the Go client test command --- tools/dockerfile/grpc_go/Dockerfile | 8 ++++---- tools/gce_setup/grpc_docker.sh | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tools/dockerfile/grpc_go/Dockerfile b/tools/dockerfile/grpc_go/Dockerfile index ab463b2a006..1b998152a78 100644 --- a/tools/dockerfile/grpc_go/Dockerfile +++ b/tools/dockerfile/grpc_go/Dockerfile @@ -17,11 +17,11 @@ RUN echo 'StrictHostKeyChecking no' >> $HOME/.ssh/config RUN git config --global url."git@github.com:".insteadOf "https://github.com/" # Get the source from GitHub -RUN go get github.com/google/grpc-go +RUN go get github.com/google/grpc-go/rpc # Build the interop client and server -RUN cd src/github.com/google/grpc-go/interop/client && go install -RUN cd src/github.com/google/grpc-go/interop/server && go install +RUN cd src/github.com/google/grpc-go/rpc/interop/client && go install +RUN cd src/github.com/google/grpc-go/rpc/interop/server && go install # Specify the default command such that the interop server runs on its known testing port -CMD ["/bin/bash", "-c 'cd src/github.com/google/grpc-go/interop/server && go run server.go --use_tls=true --port=8020'"] +CMD ["/bin/bash", "-c", "cd src/github.com/google/grpc-go/rpc/interop/server && go run server.go --use_tls=true --port=8020"] diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index bfa4c808be4..e61c83b36f0 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -640,10 +640,11 @@ grpc_interop_gen_ruby_cmd() { # flags= .... # generic flags to include the command # cmd=$($grpc_gen_test_cmd $flags) grpc_interop_gen_go_cmd() { - local cmd_prefix="sudo docker run grpc/go bin/bash -c"; - local test_script="cd /go/src/github.com/google/grpc-go/interop/client"; - local test_script+=" && go run client.go --use_tls=true"; - local the_cmd="$cmd_prefix '$test_script $@ 1>&2'"; + local cmd_prefix="sudo docker run grpc/go /bin/bash -c" + local test_script="cd /go/src/github.com/google/grpc-go/rpc/interop/client" + local test_script+=" && go run client.go --use_tls=true" + local the_cmd="$cmd_prefix '$test_script $@'" + echo $the_cmd } # constructs the full dockerized java interop test cmd. From 7453c3da4cd10ea57080601515eaa271a073bb64 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 23 Jan 2015 10:04:53 -0800 Subject: [PATCH 091/143] Update README.md Addressed review comments. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9e8b8e77917..fa39d3b3089 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -gRPC - An RPC library and framework +[gRPC - An RPC library and framework](http://github.com/google/grpc) =================================== Copyright 2015 Google Inc. @@ -35,8 +35,8 @@ Protocol Compiler plugins that generate Client- and Server-side APIs. gRPC users typically call into these APIs on the Client side and implement the corresponding API on the server side. -#### Synchronous vs. Async -Synchronous RPC calls, that block till a response arrives from the server, are +#### Synchronous vs. asynchronous +Synchronous RPC calls, that block until a response arrives from the server, are the closest approximation to the abstraction of a procedure call that RPC aspires to. @@ -44,8 +44,8 @@ On the other hand, networks are inherently asynchronous and in many scenarios, it is desirable to have the ability to start RPCs without blocking the current thread. -The gRPC programming surface in most languages comes in both Synchronous and -async flavors. +The gRPC programming surface in most languages comes in both synchronous and +asynchronous flavors. ## Streaming From 51fca2da4b6974d3f56d894f1caa98fd8d32f120 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 23 Jan 2015 10:53:51 -0800 Subject: [PATCH 092/143] Made node library buildable from source tree --- src/node/binding.gyp | 35 +++++++++++++++++++++++++++++------ tools/run_tests/build_node.sh | 20 ++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) create mode 100755 tools/run_tests/build_node.sh diff --git a/src/node/binding.gyp b/src/node/binding.gyp index da4a9434910..fe4b5da9c8f 100644 --- a/src/node/binding.gyp +++ b/src/node/binding.gyp @@ -1,8 +1,13 @@ { + "variables" : { + 'no_install': " Date: Fri, 23 Jan 2015 11:08:45 -0800 Subject: [PATCH 093/143] Skiped currently failing test --- src/node/test/interop_sanity_test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index 410b050e8d2..3c062b97882 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -52,7 +52,8 @@ describe('Interop tests', function() { it('should pass empty_unary', function(done) { interop_client.runTest(port, name_override, 'empty_unary', true, done); }); - it('should pass large_unary', function(done) { + // This fails due to an unknown bug + it.skip('should pass large_unary', function(done) { interop_client.runTest(port, name_override, 'large_unary', true, done); }); it('should pass client_streaming', function(done) { @@ -64,7 +65,6 @@ describe('Interop tests', function() { it('should pass ping_pong', function(done) { interop_client.runTest(port, name_override, 'ping_pong', true, done); }); - // This depends on the new invoke API it.skip('should pass empty_stream', function(done) { interop_client.runTest(port, name_override, 'empty_stream', true, done); }); From c0fc6a1f1fe54f0b83a7089bf65cff39d91e611f Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 11:12:46 -0800 Subject: [PATCH 094/143] Add gpr_ltoa --- include/grpc/support/string.h | 7 +++++++ src/core/support/string.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/include/grpc/support/string.h b/include/grpc/support/string.h index 68e7452a7fa..2ce19036f2e 100644 --- a/include/grpc/support/string.h +++ b/include/grpc/support/string.h @@ -60,6 +60,13 @@ char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags); int gpr_parse_bytes_to_uint32(const char *data, size_t length, gpr_uint32 *result); +/* Convert a long to a string in base 10; returns the length of the + output string (or 0 on failure) */ +int gpr_ltoa(long value, char *output); + +/* Reverse a run of bytes */ +void gpr_reverse_bytes(char *str, int len); + /* printf to a newly-allocated string. The set of supported formats may vary between platforms. diff --git a/src/core/support/string.c b/src/core/support/string.c index 7960547735b..7e84437fac7 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -122,3 +122,33 @@ int gpr_parse_bytes_to_uint32(const char *buf, size_t len, gpr_uint32 *result) { *result = out; return 1; } + +void gpr_reverse_bytes(char *str, int len) { + char *p1, *p2; + for (p1 = str, p2 = str + len - 1; p2 > p1; ++p1, --p2) { + char temp = *p1; + *p1 = *p2; + *p2 = temp; + } +} + +int gpr_ltoa(long value, char *string) { + int i = 0; + int neg = value < 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + if (neg) value = -value; + while (value) { + string[i++] = '0' + value % 10; + value /= 10; + } + if (neg) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} From 985463d3a3082f1096ca34e669d12fc13b8e354b Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 11:13:10 -0800 Subject: [PATCH 095/143] Remove sprintf from timeout_encoding --- src/core/transport/chttp2/timeout_encoding.c | 38 ++++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/core/transport/chttp2/timeout_encoding.c b/src/core/transport/chttp2/timeout_encoding.c index 2706c369a6d..7217afa478c 100644 --- a/src/core/transport/chttp2/timeout_encoding.c +++ b/src/core/transport/chttp2/timeout_encoding.c @@ -36,6 +36,8 @@ #include #include +#include + static int round_up(int x, int divisor) { return (x / divisor + (x % divisor != 0)) * divisor; } @@ -53,15 +55,21 @@ static int round_up_to_three_sig_figs(int x) { } /* encode our minimum viable timeout value */ -static void enc_tiny(char *buffer) { strcpy(buffer, "1n"); } +static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); } + +static void enc_ext(char *buffer, long value, char ext) { + int n = gpr_ltoa(value, buffer); + buffer[n] = ext; + buffer[n+1] = 0; +} static void enc_seconds(char *buffer, long sec) { if (sec % 3600 == 0) { - sprintf(buffer, "%ldH", sec / 3600); + enc_ext(buffer, sec / 3600, 'H'); } else if (sec % 60 == 0) { - sprintf(buffer, "%ldM", sec / 60); + enc_ext(buffer, sec / 60, 'M'); } else { - sprintf(buffer, "%ldS", sec); + enc_ext(buffer, sec, 'S'); } } @@ -69,23 +77,23 @@ static void enc_nanos(char *buffer, int x) { x = round_up_to_three_sig_figs(x); if (x < 100000) { if (x % 1000 == 0) { - sprintf(buffer, "%du", x / 1000); + enc_ext(buffer, x / 1000, 'u'); } else { - sprintf(buffer, "%dn", x); + enc_ext(buffer, x, 'n'); } } else if (x < 100000000) { if (x % 1000000 == 0) { - sprintf(buffer, "%dm", x / 1000000); + enc_ext(buffer, x / 1000000, 'm'); } else { - sprintf(buffer, "%du", x / 1000); + enc_ext(buffer, x / 1000, 'u'); } } else if (x < 1000000000) { - sprintf(buffer, "%dm", x / 1000000); + enc_ext(buffer, x / 1000000, 'm'); } else { /* note that this is only ever called with times of less than one second, so if we reach here the time must have been rounded up to a whole second (and no more) */ - strcpy(buffer, "1S"); + memcpy(buffer, "1S", 3); } } @@ -93,18 +101,18 @@ static void enc_micros(char *buffer, int x) { x = round_up_to_three_sig_figs(x); if (x < 100000) { if (x % 1000 == 0) { - sprintf(buffer, "%dm", x / 1000); + enc_ext(buffer, x / 1000, 'm'); } else { - sprintf(buffer, "%du", x); + enc_ext(buffer, x, 'u'); } } else if (x < 100000000) { if (x % 1000000 == 0) { - sprintf(buffer, "%dS", x / 1000000); + enc_ext(buffer, x / 1000000, 'S'); } else { - sprintf(buffer, "%dm", x / 1000); + enc_ext(buffer, x / 1000, 'm'); } } else { - sprintf(buffer, "%dS", x / 1000000); + enc_ext(buffer, x / 1000000, 'S'); } } From 4113ce75b1748c2853cc1ee82fa0bfe7a5b524c3 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 11:22:28 -0800 Subject: [PATCH 096/143] Remove use of printf --- src/core/surface/call.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 46502fb6b14..f293bc2741c 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -761,7 +761,7 @@ grpc_call_error grpc_call_start_write_status(grpc_call *call, { grpc_mdelem *md; char buffer[32]; - sprintf(buffer, "%d", status); + gpr_ltoa(status, buffer); md = grpc_mdelem_from_strings(call->metadata_context, "grpc-status", buffer); From 87d427532dfc048740ee4bae4dde0cd61125e128 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 11:27:42 -0800 Subject: [PATCH 097/143] Remove use of sprintf --- src/core/channel/client_channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index fa75561c78b..a1ca851b5fe 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -425,7 +425,7 @@ static void init_channel_elem(grpc_channel_element *elem, chand->transport_setup_initiated = 0; chand->args = grpc_channel_args_copy(args); - sprintf(temp, "%d", GRPC_STATUS_CANCELLED); + gpr_ltoa(GRPC_STATUS_CANCELLED, temp); chand->cancel_status = grpc_mdelem_from_strings(metadata_context, "grpc-status", temp); } From a7ed5d9c4c603e5da99de22707919ae5dd91a872 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 11:30:16 -0800 Subject: [PATCH 098/143] Remove use of sprintf --- src/core/transport/chttp2_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 17b37d6d4ae..c273dd79991 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -1015,7 +1015,7 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id, s->cancelled = 1; stream_list_join(t, s, CANCELLED); - sprintf(buffer, "%d", local_status); + gpr_ltoa(local_status, buffer); grpc_sopb_add_metadata( &s->parser.incoming_sopb, grpc_mdelem_from_strings(t->metadata_context, "grpc-status", buffer)); From d56602133caa7326acc209b87ebcc9fa5eb5de2c Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Fri, 23 Jan 2015 20:35:49 +0100 Subject: [PATCH 099/143] OpenSSL 1.0.2 is out of beta - let's update our own repositories to reflect that. --- INSTALL | 4 ++-- third_party/openssl | 2 +- vsprojects/third_party/openssl/OpenSSL.mak | 17 +++++++---------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/INSTALL b/INSTALL index 48511aff7df..bba923ccae3 100644 --- a/INSTALL +++ b/INSTALL @@ -58,7 +58,7 @@ for that particular dependency if you want to reduce the libraries' size. The recommended version of OpenSSL that provides ALPN support is available at this URL: - https://www.openssl.org/source/openssl-1.0.2-beta3.tar.gz + https://www.openssl.org/source/openssl-1.0.2.tar.gz Dependencies to compile and run the tests @@ -101,7 +101,7 @@ A word on OpenSSL Secure HTTP2 requires to have the TLS extension ALPN (see rfc 7301 and http://http2.github.io/http2-spec/ section 3.3). Our HTTP2 implementation -relies on OpenSSL's implementation. OpenSSL 1.0.2beta3 is the first version +relies on OpenSSL's implementation. OpenSSL 1.0.2 is the first released version of OpenSSL that has ALPN support, and this explains our dependency on it. Note that the Makefile supports compiling only the unsecure elements of grpc, diff --git a/third_party/openssl b/third_party/openssl index 2c5db8dac3a..4ac03295828 160000 --- a/third_party/openssl +++ b/third_party/openssl @@ -1 +1 @@ -Subproject commit 2c5db8dac3a06fe5b2c889838a606138ee3542ed +Subproject commit 4ac0329582829f5378d8078c8d314ad37db87736 diff --git a/vsprojects/third_party/openssl/OpenSSL.mak b/vsprojects/third_party/openssl/OpenSSL.mak index 09344e8ae10..8b1167094aa 100644 --- a/vsprojects/third_party/openssl/OpenSSL.mak +++ b/vsprojects/third_party/openssl/OpenSSL.mak @@ -206,13 +206,13 @@ SSLOBJ=$(OBJ_D)\s2_meth.obj \ $(OBJ_D)\t1_lib.obj $(OBJ_D)\t1_enc.obj $(OBJ_D)\t1_ext.obj \ $(OBJ_D)\d1_meth.obj $(OBJ_D)\d1_srvr.obj $(OBJ_D)\d1_clnt.obj \ $(OBJ_D)\d1_lib.obj $(OBJ_D)\d1_pkt.obj $(OBJ_D)\d1_both.obj \ - $(OBJ_D)\d1_enc.obj $(OBJ_D)\d1_srtp.obj $(OBJ_D)\ssl_lib.obj \ - $(OBJ_D)\ssl_err2.obj $(OBJ_D)\ssl_cert.obj $(OBJ_D)\ssl_sess.obj \ - $(OBJ_D)\ssl_ciph.obj $(OBJ_D)\ssl_stat.obj $(OBJ_D)\ssl_rsa.obj \ - $(OBJ_D)\ssl_asn1.obj $(OBJ_D)\ssl_txt.obj $(OBJ_D)\ssl_algs.obj \ - $(OBJ_D)\ssl_conf.obj $(OBJ_D)\bio_ssl.obj $(OBJ_D)\ssl_err.obj \ - $(OBJ_D)\kssl.obj $(OBJ_D)\t1_reneg.obj $(OBJ_D)\tls_srp.obj \ - $(OBJ_D)\t1_trce.obj $(OBJ_D)\ssl_utst.obj + $(OBJ_D)\d1_srtp.obj $(OBJ_D)\ssl_lib.obj $(OBJ_D)\ssl_err2.obj \ + $(OBJ_D)\ssl_cert.obj $(OBJ_D)\ssl_sess.obj $(OBJ_D)\ssl_ciph.obj \ + $(OBJ_D)\ssl_stat.obj $(OBJ_D)\ssl_rsa.obj $(OBJ_D)\ssl_asn1.obj \ + $(OBJ_D)\ssl_txt.obj $(OBJ_D)\ssl_algs.obj $(OBJ_D)\ssl_conf.obj \ + $(OBJ_D)\bio_ssl.obj $(OBJ_D)\ssl_err.obj $(OBJ_D)\kssl.obj \ + $(OBJ_D)\t1_reneg.obj $(OBJ_D)\tls_srp.obj $(OBJ_D)\t1_trce.obj \ + $(OBJ_D)\ssl_utst.obj CRYPTOOBJ=$(OBJ_D)\cryptlib.obj \ $(OBJ_D)\mem.obj $(OBJ_D)\mem_dbg.obj $(OBJ_D)\cversion.obj \ @@ -1277,9 +1277,6 @@ $(OBJ_D)\d1_pkt.obj: $(SRC_D)\ssl\d1_pkt.c $(OBJ_D)\d1_both.obj: $(SRC_D)\ssl\d1_both.c $(CC) /Fo$(OBJ_D)\d1_both.obj $(LIB_CFLAGS) -c $(SRC_D)\ssl\d1_both.c -$(OBJ_D)\d1_enc.obj: $(SRC_D)\ssl\d1_enc.c - $(CC) /Fo$(OBJ_D)\d1_enc.obj $(LIB_CFLAGS) -c $(SRC_D)\ssl\d1_enc.c - $(OBJ_D)\d1_srtp.obj: $(SRC_D)\ssl\d1_srtp.c $(CC) /Fo$(OBJ_D)\d1_srtp.obj $(LIB_CFLAGS) -c $(SRC_D)\ssl\d1_srtp.c From 1fa4e15f7e935b06f1fc6bb3613ab1eab7028c61 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 12:00:11 -0800 Subject: [PATCH 100/143] Add define --- include/grpc/support/string.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/grpc/support/string.h b/include/grpc/support/string.h index 2ce19036f2e..71bd3f84e30 100644 --- a/include/grpc/support/string.h +++ b/include/grpc/support/string.h @@ -60,6 +60,9 @@ char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags); int gpr_parse_bytes_to_uint32(const char *data, size_t length, gpr_uint32 *result); +/* minimum buffer size for calling ltoa */ +#define GPR_LTOA_MIN_BUFSIZE (3 * sizeof(long)) + /* Convert a long to a string in base 10; returns the length of the output string (or 0 on failure) */ int gpr_ltoa(long value, char *output); From 485d77628d0ce8889a420576d5bc41ccbbf48963 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 12:54:05 -0800 Subject: [PATCH 101/143] Move string.h to internal code --- src/core/channel/call_op_string.c | 2 +- src/core/channel/channel_args.c | 2 +- src/core/channel/client_channel.c | 2 +- src/core/channel/connected_channel.c | 2 +- src/core/httpcli/httpcli.c | 2 +- src/core/httpcli/httpcli_security_context.c | 2 +- src/core/iomgr/resolve_address_posix.c | 2 +- src/core/iomgr/sockaddr_utils.c | 2 +- src/core/iomgr/socket_utils_common_posix.c | 2 +- src/core/iomgr/tcp_posix.c | 2 +- src/core/security/credentials.c | 2 +- src/core/security/json_token.c | 2 +- src/core/security/secure_endpoint.c | 2 +- src/core/security/security_context.c | 2 +- src/core/statistics/census_rpc_stats.c | 2 +- src/core/statistics/census_tracing.c | 2 +- src/core/support/cmdline.c | 2 +- src/core/support/host_port.c | 2 +- src/core/support/string.c | 2 +- {include/grpc => src/core}/support/string.h | 0 src/core/surface/call.c | 2 +- src/core/surface/channel_create.c | 2 +- src/core/surface/client.c | 2 +- src/core/surface/completion_queue.c | 2 +- src/core/surface/event_string.c | 2 +- src/core/surface/lame_client.c | 2 +- src/core/surface/secure_channel_create.c | 2 +- src/core/surface/server.c | 2 +- src/core/transport/chttp2/frame_data.c | 2 +- src/core/transport/chttp2/hpack_parser.c | 2 +- src/core/transport/chttp2/timeout_encoding.c | 2 +- src/core/transport/chttp2_transport.c | 2 +- test/core/echo/echo_test.c | 2 +- test/core/echo/server.c | 2 +- test/core/end2end/cq_verifier.c | 2 +- test/core/fling/fling_stream_test.c | 2 +- test/core/fling/fling_test.c | 2 +- test/core/security/credentials_test.c | 2 +- test/core/support/string_test.c | 2 +- test/core/transport/chttp2/bin_encoder_test.c | 2 +- test/core/transport/chttp2/stream_encoder_test.c | 2 +- test/core/transport/transport_end2end_tests.c | 2 +- 42 files changed, 41 insertions(+), 41 deletions(-) rename {include/grpc => src/core}/support/string.h (100%) diff --git a/src/core/channel/call_op_string.c b/src/core/channel/call_op_string.c index 9a7838ce2fc..789913901ad 100644 --- a/src/core/channel/call_op_string.c +++ b/src/core/channel/call_op_string.c @@ -37,8 +37,8 @@ #include #include +#include "src/core/support/string.h" #include -#include #include #define MAX_APPEND 1024 diff --git a/src/core/channel/channel_args.c b/src/core/channel/channel_args.c index 36312e54de8..04ce519ff37 100644 --- a/src/core/channel/channel_args.c +++ b/src/core/channel/channel_args.c @@ -33,9 +33,9 @@ #include #include "src/core/channel/channel_args.h" +#include "src/core/support/string.h" #include -#include #include diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index fa75561c78b..f6078aa8080 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -40,9 +40,9 @@ #include "src/core/channel/connected_channel.h" #include "src/core/channel/metadata_buffer.h" #include "src/core/iomgr/iomgr.h" +#include "src/core/support/string.h" #include #include -#include #include #include diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index e01cb81a890..cfa76f9e488 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -37,12 +37,12 @@ #include #include +#include "src/core/support/string.h" #include "src/core/transport/transport.h" #include #include #include #include -#include #define MAX_BUFFER_LENGTH 8192 /* the protobuf library will (by default) start warning at 100megs */ diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c index 2143eeb63d0..d6fd8ca8658 100644 --- a/src/core/httpcli/httpcli.c +++ b/src/core/httpcli/httpcli.c @@ -44,9 +44,9 @@ #include "src/core/security/security_context.h" #include "src/core/security/google_root_certs.h" #include "src/core/security/secure_transport_setup.h" +#include "src/core/support/string.h" #include #include -#include typedef struct { gpr_slice request_text; diff --git a/src/core/httpcli/httpcli_security_context.c b/src/core/httpcli/httpcli_security_context.c index c7b9b330f0d..d074e163f16 100644 --- a/src/core/httpcli/httpcli_security_context.c +++ b/src/core/httpcli/httpcli_security_context.c @@ -36,9 +36,9 @@ #include #include "src/core/security/secure_transport_setup.h" +#include "src/core/support/string.h" #include #include -#include #include "src/core/tsi/ssl_transport_security.h" typedef struct { diff --git a/src/core/iomgr/resolve_address_posix.c b/src/core/iomgr/resolve_address_posix.c index c9c2c5378a5..f80eea7f46e 100644 --- a/src/core/iomgr/resolve_address_posix.c +++ b/src/core/iomgr/resolve_address_posix.c @@ -44,9 +44,9 @@ #include "src/core/iomgr/iomgr_internal.h" #include "src/core/iomgr/sockaddr_utils.h" #include "src/core/iomgr/socket_utils_posix.h" +#include "src/core/support/string.h" #include #include -#include #include #include diff --git a/src/core/iomgr/sockaddr_utils.c b/src/core/iomgr/sockaddr_utils.c index eca14a4f398..5bb11242843 100644 --- a/src/core/iomgr/sockaddr_utils.c +++ b/src/core/iomgr/sockaddr_utils.c @@ -37,8 +37,8 @@ #include #include +#include "src/core/support/string.h" #include -#include #include #include diff --git a/src/core/iomgr/socket_utils_common_posix.c b/src/core/iomgr/socket_utils_common_posix.c index 3a0639f3567..1854285b5a4 100644 --- a/src/core/iomgr/socket_utils_common_posix.c +++ b/src/core/iomgr/socket_utils_common_posix.c @@ -50,8 +50,8 @@ #include #include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/support/string.h" #include -#include #include #include #include diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c index 64996bd07d1..a9b59df8854 100644 --- a/src/core/iomgr/tcp_posix.c +++ b/src/core/iomgr/tcp_posix.c @@ -44,10 +44,10 @@ #include #include +#include "src/core/support/string.h" #include #include #include -#include #include #include diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index 628963e46cd..db73c01c1de 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -36,9 +36,9 @@ #include "src/core/httpcli/httpcli.h" #include "src/core/iomgr/iomgr.h" #include "src/core/security/json_token.h" +#include "src/core/support/string.h" #include #include -#include #include #include diff --git a/src/core/security/json_token.c b/src/core/security/json_token.c index 14ee758e8bf..82bd9b505a2 100644 --- a/src/core/security/json_token.c +++ b/src/core/security/json_token.c @@ -37,9 +37,9 @@ #include #include -#include #include "src/core/security/base64.h" +#include "src/core/support/string.h" #include #include diff --git a/src/core/security/secure_endpoint.c b/src/core/security/secure_endpoint.c index e73767c1aad..9f12cf5d60f 100644 --- a/src/core/security/secure_endpoint.c +++ b/src/core/security/secure_endpoint.c @@ -32,11 +32,11 @@ */ #include "src/core/security/secure_endpoint.h" +#include "src/core/support/string.h" #include #include #include #include -#include #include #include "src/core/tsi/transport_security_interface.h" diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c index cce3c7fe04f..58cd458415d 100644 --- a/src/core/security/security_context.c +++ b/src/core/security/security_context.c @@ -39,12 +39,12 @@ #include "src/core/channel/http_client_filter.h" #include "src/core/security/credentials.h" #include "src/core/security/secure_endpoint.h" +#include "src/core/support/string.h" #include "src/core/surface/lame_client.h" #include "src/core/transport/chttp2/alpn.h" #include #include #include -#include #include "src/core/tsi/fake_transport_security.h" #include "src/core/tsi/ssl_transport_security.h" diff --git a/src/core/statistics/census_rpc_stats.c b/src/core/statistics/census_rpc_stats.c index 39094b5f65a..dd3c07e80b3 100644 --- a/src/core/statistics/census_rpc_stats.c +++ b/src/core/statistics/census_rpc_stats.c @@ -39,9 +39,9 @@ #include "src/core/statistics/census_tracing.h" #include "src/core/statistics/window_stats.h" #include "src/core/support/murmur_hash.h" +#include "src/core/support/string.h" #include #include -#include #include #define NUM_INTERVALS 3 diff --git a/src/core/statistics/census_tracing.c b/src/core/statistics/census_tracing.c index 1e616020715..3c4ba66f5f4 100644 --- a/src/core/statistics/census_tracing.c +++ b/src/core/statistics/census_tracing.c @@ -38,10 +38,10 @@ #include "src/core/statistics/census_rpc_stats.h" #include "src/core/statistics/hash_table.h" +#include "src/core/support/string.h" #include #include #include -#include #include #include diff --git a/src/core/support/cmdline.c b/src/core/support/cmdline.c index ff163a1f6cc..a55da9dd188 100644 --- a/src/core/support/cmdline.c +++ b/src/core/support/cmdline.c @@ -37,9 +37,9 @@ #include #include +#include "src/core/support/string.h" #include #include -#include typedef enum { ARGTYPE_INT, ARGTYPE_BOOL, ARGTYPE_STRING } argtype; diff --git a/src/core/support/host_port.c b/src/core/support/host_port.c index 02500551fcd..446c11ebec3 100644 --- a/src/core/support/host_port.c +++ b/src/core/support/host_port.c @@ -35,8 +35,8 @@ #include +#include "src/core/support/string.h" #include -#include int gpr_join_host_port(char **out, const char *host, int port) { if (host[0] != '[' && strchr(host, ':') != NULL) { diff --git a/src/core/support/string.c b/src/core/support/string.c index 7e84437fac7..9b5cac75966 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -31,7 +31,7 @@ * */ -#include +#include "src/core/support/string.h" #include #include diff --git a/include/grpc/support/string.h b/src/core/support/string.h similarity index 100% rename from include/grpc/support/string.h rename to src/core/support/string.h diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 46502fb6b14..1786523db27 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -35,11 +35,11 @@ #include "src/core/channel/channel_stack.h" #include "src/core/channel/metadata_buffer.h" #include "src/core/iomgr/alarm.h" +#include "src/core/support/string.h" #include "src/core/surface/channel.h" #include "src/core/surface/completion_queue.h" #include #include -#include #include #include diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c index 41093d78eff..6939b92c60c 100644 --- a/src/core/surface/channel_create.c +++ b/src/core/surface/channel_create.c @@ -48,10 +48,10 @@ #include "src/core/iomgr/tcp_client.h" #include "src/core/surface/channel.h" #include "src/core/surface/client.h" +#include "src/core/support/string.h" #include "src/core/transport/chttp2_transport.h" #include #include -#include #include #include diff --git a/src/core/surface/client.c b/src/core/surface/client.c index 74c79bdf9b1..fe3a81f1b9f 100644 --- a/src/core/surface/client.c +++ b/src/core/surface/client.c @@ -34,9 +34,9 @@ #include "src/core/surface/client.h" #include "src/core/surface/call.h" +#include "src/core/support/string.h" #include #include -#include typedef struct { void *unused; } call_data; diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c index 652f23e8889..e1cc114cda6 100644 --- a/src/core/surface/completion_queue.c +++ b/src/core/surface/completion_queue.c @@ -37,13 +37,13 @@ #include #include "src/core/iomgr/pollset.h" +#include "src/core/support/string.h" #include "src/core/surface/call.h" #include "src/core/surface/event_string.h" #include "src/core/surface/surface_trace.h" #include #include #include -#include #define NUM_TAG_BUCKETS 31 diff --git a/src/core/surface/event_string.c b/src/core/surface/event_string.c index 8ae2af74726..e38ef06c9ff 100644 --- a/src/core/surface/event_string.c +++ b/src/core/surface/event_string.c @@ -35,7 +35,7 @@ #include -#include +#include "src/core/support/string.h" #include static size_t addhdr(char *p, grpc_event *ev) { diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c index a5244dbe61f..056c98646b8 100644 --- a/src/core/surface/lame_client.c +++ b/src/core/surface/lame_client.c @@ -36,11 +36,11 @@ #include #include "src/core/channel/channel_stack.h" +#include "src/core/support/string.h" #include "src/core/surface/channel.h" #include "src/core/surface/call.h" #include #include -#include typedef struct { void *unused; } call_data; diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c index 3d5727927d0..a231b2708e6 100644 --- a/src/core/surface/secure_channel_create.c +++ b/src/core/surface/secure_channel_create.c @@ -48,13 +48,13 @@ #include "src/core/security/auth.h" #include "src/core/security/security_context.h" #include "src/core/security/secure_transport_setup.h" +#include "src/core/support/string.h" #include "src/core/surface/channel.h" #include "src/core/surface/client.h" #include "src/core/transport/chttp2_transport.h" #include #include #include -#include #include #include #include "src/core/tsi/transport_security_interface.h" diff --git a/src/core/surface/server.c b/src/core/surface/server.c index cbdd3bfa308..9585e4e8ea6 100644 --- a/src/core/surface/server.c +++ b/src/core/surface/server.c @@ -40,12 +40,12 @@ #include "src/core/channel/channel_args.h" #include "src/core/channel/connected_channel.h" #include "src/core/iomgr/iomgr.h" +#include "src/core/support/string.h" #include "src/core/surface/call.h" #include "src/core/surface/channel.h" #include "src/core/surface/completion_queue.h" #include #include -#include #include typedef enum { PENDING_START, ALL_CALLS, CALL_LIST_COUNT } call_list; diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c index 00b020b31b1..dee61cee500 100644 --- a/src/core/transport/chttp2/frame_data.c +++ b/src/core/transport/chttp2/frame_data.c @@ -35,9 +35,9 @@ #include +#include "src/core/support/string.h" #include #include -#include #include #include "src/core/transport/transport.h" diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c index 64e08ffac72..c98b90e5d11 100644 --- a/src/core/transport/chttp2/hpack_parser.c +++ b/src/core/transport/chttp2/hpack_parser.c @@ -38,10 +38,10 @@ #include #include "src/core/transport/chttp2/bin_encoder.h" +#include "src/core/support/string.h" #include #include #include -#include #include typedef enum { diff --git a/src/core/transport/chttp2/timeout_encoding.c b/src/core/transport/chttp2/timeout_encoding.c index 7217afa478c..23c4554cf27 100644 --- a/src/core/transport/chttp2/timeout_encoding.c +++ b/src/core/transport/chttp2/timeout_encoding.c @@ -36,7 +36,7 @@ #include #include -#include +#include "src/core/support/string.h" static int round_up(int x, int divisor) { return (x / divisor + (x % divisor != 0)) * divisor; diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 17b37d6d4ae..6dfffa1fc50 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -37,6 +37,7 @@ #include #include +#include "src/core/support/string.h" #include "src/core/transport/chttp2/frame_data.h" #include "src/core/transport/chttp2/frame_goaway.h" #include "src/core/transport/chttp2/frame_ping.h" @@ -53,7 +54,6 @@ #include #include #include -#include #include #define DEFAULT_WINDOW 65535 diff --git a/test/core/echo/echo_test.c b/test/core/echo/echo_test.c index 16d381fb654..6449b2414f3 100644 --- a/test/core/echo/echo_test.c +++ b/test/core/echo/echo_test.c @@ -42,10 +42,10 @@ #include #include "src/core/iomgr/socket_utils_posix.h" +#include "src/core/support/string.h" #include #include #include -#include #include "test/core/util/port.h" int test_client(const char *root, const char *host, int port) { diff --git a/test/core/echo/server.c b/test/core/echo/server.c index 35f118dc9b1..57b083779c3 100644 --- a/test/core/echo/server.c +++ b/test/core/echo/server.c @@ -39,11 +39,11 @@ #include #include +#include "src/core/support/string.h" #include "test/core/util/test_config.h" #include #include #include -#include #include #include "test/core/util/port.h" diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c index 7a7f197907d..37a7f7e32c2 100644 --- a/test/core/end2end/cq_verifier.c +++ b/test/core/end2end/cq_verifier.c @@ -45,10 +45,10 @@ #include #include "src/core/surface/event_string.h" +#include "src/core/support/string.h" #include #include #include -#include #include #include diff --git a/test/core/fling/fling_stream_test.c b/test/core/fling/fling_stream_test.c index f6fe69824b3..7f52fb1bad1 100644 --- a/test/core/fling/fling_stream_test.c +++ b/test/core/fling/fling_stream_test.c @@ -41,9 +41,9 @@ #include #include +#include "src/core/support/string.h" #include #include -#include #include "test/core/util/port.h" int main(int argc, char **argv) { diff --git a/test/core/fling/fling_test.c b/test/core/fling/fling_test.c index 4607aa5f988..b2272f20c8e 100644 --- a/test/core/fling/fling_test.c +++ b/test/core/fling/fling_test.c @@ -43,7 +43,7 @@ #include #include -#include +#include "src/core/support/string.h" #include "test/core/util/port.h" int main(int argc, char **argv) { diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c index 9c60f4c2338..ec21e0d42f7 100644 --- a/test/core/security/credentials_test.c +++ b/test/core/security/credentials_test.c @@ -37,9 +37,9 @@ #include "src/core/httpcli/httpcli.h" #include "src/core/security/json_token.h" +#include "src/core/support/string.h" #include #include -#include #include #include "test/core/util/test_config.h" #include diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c index e87a606aba2..a01ec6f87f3 100644 --- a/test/core/support/string_test.c +++ b/test/core/support/string_test.c @@ -31,7 +31,7 @@ * */ -#include +#include "src/core/support/string.h" #include #include diff --git a/test/core/transport/chttp2/bin_encoder_test.c b/test/core/transport/chttp2/bin_encoder_test.c index ea24f5cbd7a..048ed7edd3c 100644 --- a/test/core/transport/chttp2/bin_encoder_test.c +++ b/test/core/transport/chttp2/bin_encoder_test.c @@ -35,9 +35,9 @@ #include +#include "src/core/support/string.h" #include #include -#include static int all_ok = 1; diff --git a/test/core/transport/chttp2/stream_encoder_test.c b/test/core/transport/chttp2/stream_encoder_test.c index cebc2634fba..eb0f688f58e 100644 --- a/test/core/transport/chttp2/stream_encoder_test.c +++ b/test/core/transport/chttp2/stream_encoder_test.c @@ -35,10 +35,10 @@ #include +#include "src/core/support/string.h" #include "src/core/transport/chttp2/hpack_parser.h" #include #include -#include #include "test/core/util/parse_hexstring.h" #include "test/core/util/slice_splitter.h" #include "test/core/util/test_config.h" diff --git a/test/core/transport/transport_end2end_tests.c b/test/core/transport/transport_end2end_tests.c index 5d26ef53b90..8e9b4a2cc9e 100644 --- a/test/core/transport/transport_end2end_tests.c +++ b/test/core/transport/transport_end2end_tests.c @@ -37,10 +37,10 @@ #include #include +#include "src/core/support/string.h" #include "src/core/transport/transport.h" #include #include -#include #include #include From fcd6c0c9227dac16065c245477cfd949d1ecf621 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Fri, 23 Jan 2015 20:54:58 +0000 Subject: [PATCH 102/143] Correct an out-of-date path in a comment. --- src/python/_framework/foundation/_logging_pool_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/_framework/foundation/_logging_pool_test.py b/src/python/_framework/foundation/_logging_pool_test.py index ffe07c788da..f2224d80e5c 100644 --- a/src/python/_framework/foundation/_logging_pool_test.py +++ b/src/python/_framework/foundation/_logging_pool_test.py @@ -27,7 +27,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""Tests for google3.net.rpc.python.framework.foundation.logging_pool.""" +"""Tests for _framework.foundation.logging_pool.""" import unittest From b1979c73af1f9eb14b3e4c5137768620118f05f8 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:00:00 -0800 Subject: [PATCH 103/143] Remove uses of sprintf --- src/core/channel/connected_channel.c | 38 +++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index cfa76f9e488..47c0ed3b882 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -384,23 +384,25 @@ static void recv_batch(void *user_data, grpc_transport *transport, case GRPC_OP_BEGIN_MESSAGE: /* can't begin a message when we're still reading a message */ if (calld->reading_message) { - char message[128]; - sprintf(message, - "Message terminated early; read %d bytes, expected %d", - (int)calld->incoming_message.length, - (int)calld->incoming_message_length); + char *message = NULL; + gpr_asprintf(&message, + "Message terminated early; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); recv_error(chand, calld, __LINE__, message); + gpr_free(message); return; } /* stash away parameters, and prepare for incoming slices */ length = stream_op->data.begin_message.length; if (length > calld->max_message_length) { - char message[128]; - sprintf( - message, + char *message = NULL; + gpr_asprintf( + &message, "Maximum message length of %d exceeded by a message of length %d", calld->max_message_length, length); recv_error(chand, calld, __LINE__, message); + gpr_free(message); } else if (length > 0) { calld->reading_message = 1; calld->incoming_message_length = length; @@ -423,12 +425,13 @@ static void recv_batch(void *user_data, grpc_transport *transport, gpr_slice_buffer_add(&calld->incoming_message, stream_op->data.slice); if (calld->incoming_message.length > calld->incoming_message_length) { /* if we got too many bytes, complain */ - char message[128]; - sprintf(message, - "Receiving message overflow; read %d bytes, expected %d", - (int)calld->incoming_message.length, - (int)calld->incoming_message_length); + char *message = NULL; + gpr_asprintf(&message, + "Receiving message overflow; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); recv_error(chand, calld, __LINE__, message); + gpr_free(message); return; } else if (calld->incoming_message.length == calld->incoming_message_length) { @@ -441,10 +444,11 @@ static void recv_batch(void *user_data, grpc_transport *transport, final_state == GRPC_STREAM_CLOSED)) { calld->got_read_close = 1; if (calld->reading_message) { - char message[128]; - sprintf(message, "Last message truncated; read %d bytes, expected %d", - (int)calld->incoming_message.length, - (int)calld->incoming_message_length); + char *message = NULL; + gpr_asprintf(&message, + "Last message truncated; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); recv_error(chand, calld, __LINE__, message); } call_op.type = GRPC_RECV_HALF_CLOSE; From 78d241cb3a5c367956a55a2409d5f9dedc86555c Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:02:30 -0800 Subject: [PATCH 104/143] Remove uses of sprintf --- src/core/security/credentials.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index db73c01c1de..b5ff42b337d 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -157,7 +157,7 @@ static void ssl_server_destroy(grpc_server_credentials *creds) { if (c->config.pem_private_keys[i] != NULL) { gpr_free(c->config.pem_private_keys[i]); } - if (c->config.pem_cert_chains[i]!= NULL) { + if (c->config.pem_cert_chains[i] != NULL) { gpr_free(c->config.pem_cert_chains[i]); } } @@ -354,7 +354,6 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( cJSON *access_token = NULL; cJSON *token_type = NULL; cJSON *expires_in = NULL; - size_t new_access_token_size = 0; json = cJSON_Parse(null_terminated_body); if (json == NULL) { gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body); @@ -384,12 +383,8 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( status = GRPC_CREDENTIALS_ERROR; goto end; } - new_access_token_size = strlen(token_type->valuestring) + 1 + - strlen(access_token->valuestring) + 1; - new_access_token = gpr_malloc(new_access_token_size); - /* C89 does not have snprintf :(. */ - sprintf(new_access_token, "%s %s", token_type->valuestring, - access_token->valuestring); + gpr_asprintf(&new_access_token, "%s %s", token_type->valuestring, + access_token->valuestring); token_lifetime->tv_sec = expires_in->valueint; token_lifetime->tv_nsec = 0; if (*token_elem != NULL) grpc_mdelem_unref(*token_elem); From be6a3550de0b8b0aa82a67e8194f1758287ddce1 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:04:45 -0800 Subject: [PATCH 105/143] Remove uses of sprintf --- src/core/security/credentials.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index b5ff42b337d..2f75556e7bd 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -534,9 +534,7 @@ static void service_account_fetch_oauth2( response_cb(metadata_req, &response); return; } - body = gpr_malloc(strlen(GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX) + - strlen(jwt) + 1); - sprintf(body, "%s%s", GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX, jwt); + gpr_asprintf(&body, "%s%s", GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX, jwt); memset(&request, 0, sizeof(grpc_httpcli_request)); request.host = GRPC_SERVICE_ACCOUNT_HOST; request.path = GRPC_SERVICE_ACCOUNT_TOKEN_PATH; From d09f8806a690c8f7c17d10667b066e6d927cf407 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:09:21 -0800 Subject: [PATCH 106/143] Remove uses of sprintf --- src/core/surface/completion_queue.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c index e1cc114cda6..2bf31c50a8f 100644 --- a/src/core/surface/completion_queue.c +++ b/src/core/surface/completion_queue.c @@ -396,12 +396,13 @@ void grpc_event_finish(grpc_event *base) { void grpc_cq_dump_pending_ops(grpc_completion_queue *cc) { #ifndef NDEBUG - char tmp[256]; + char tmp[GRPC_COMPLETION_DO_NOT_USE * (1 + GPR_LTOA_MIN_BUFSIZE)]; char *p = tmp; int i; for (i = 0; i < GRPC_COMPLETION_DO_NOT_USE; i++) { - p += sprintf(p, " %d", (int)cc->pending_op_count[i]); + *p++ = ' '; + p += gpr_ltoa(cc->pending_op_count[i], p); } gpr_log(GPR_INFO, "pending ops:%s", tmp); From 34986de3b885d81eb9d6479a0ada3fea91684163 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 23 Jan 2015 13:27:20 -0800 Subject: [PATCH 107/143] Fixed node interop server --- src/node/interop/interop_server.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js index 6d2bd7ae0de..ebf847876cc 100644 --- a/src/node/interop/interop_server.js +++ b/src/node/interop/interop_server.js @@ -194,7 +194,8 @@ if (require.main === module) { string: ['port', 'use_tls'] }); var server_obj = getServer(argv.port, argv.use_tls === 'true'); - server_obj.server.start(); + console.log('Server attaching to port ' + argv.port); + server_obj.server.listen(); } /** From ab1103fbb35b5e8faef3d4696fbcd6c504dc93e8 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 23 Jan 2015 13:28:08 -0800 Subject: [PATCH 108/143] Removed unnecessary test file --- .../core/end2end/dualstack_socket_test.c.orig | 213 ------------------ 1 file changed, 213 deletions(-) delete mode 100644 test/core/end2end/dualstack_socket_test.c.orig diff --git a/test/core/end2end/dualstack_socket_test.c.orig b/test/core/end2end/dualstack_socket_test.c.orig deleted file mode 100644 index b443caa2a67..00000000000 --- a/test/core/end2end/dualstack_socket_test.c.orig +++ /dev/null @@ -1,213 +0,0 @@ -/* - * - * Copyright 2014, 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. - * - */ - -#include "src/core/iomgr/socket_utils_posix.h" -#include -#include -#include -#include -#include "test/core/end2end/cq_verifier.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -/* This test exercises IPv4, IPv6, and dualstack sockets in various ways. */ - -static void *tag(gpr_intptr i) { return (void *)i; } - -static gpr_timespec ms_from_now(int ms) { - return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_MS * ms)); -} - -static void drain_cq(grpc_completion_queue *cq) { - grpc_event *ev; - grpc_completion_type type; - do { - ev = grpc_completion_queue_next(cq, ms_from_now(5000)); - GPR_ASSERT(ev); - type = ev->type; - grpc_event_finish(ev); - gpr_log(GPR_INFO, "Drained event type %d", type); - } while (type != GRPC_QUEUE_SHUTDOWN); -} - -void test_connect(const char *server_host, const char *client_host, int port, - int expect_ok) { - char *client_hostport; - char *server_hostport; - grpc_channel *client; - grpc_server *server; - grpc_completion_queue *client_cq; - grpc_completion_queue *server_cq; - grpc_call *c; - grpc_call *s; - cq_verifier *v_client; - cq_verifier *v_server; - gpr_timespec deadline; - - gpr_join_host_port(&client_hostport, client_host, port); - gpr_join_host_port(&server_hostport, server_host, port); - gpr_log(GPR_INFO, "Testing with server=%s client=%s (expecting %s)", - server_hostport, client_hostport, expect_ok ? "success" : "failure"); - - /* Create server. */ - server_cq = grpc_completion_queue_create(); - server = grpc_server_create(server_cq, NULL); - GPR_ASSERT(grpc_server_add_http2_port(server, server_hostport)); - grpc_server_start(server); - gpr_free(server_hostport); - v_server = cq_verifier_create(server_cq); - - /* Create client. */ - client_cq = grpc_completion_queue_create(); - client = grpc_channel_create(client_hostport, NULL); - gpr_free(client_hostport); - v_client = cq_verifier_create(client_cq); - - if (expect_ok) { - /* Normal deadline, shouldn't be reached. */ - deadline = ms_from_now(60000); - } else { - /* Give up faster when failure is expected. - BUG: Setting this to 1000 reveals a memory leak (b/18608927). */ - deadline = ms_from_now(1500); - } - - /* Send a trivial request. */ - c = grpc_channel_create_call(client, "/foo", "test.google.com", deadline); - GPR_ASSERT(c); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_invoke(c, client_cq, tag(1), tag(2), tag(3), 0)); - if (expect_ok) { - /* Check for a successful request. */ - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK); - cq_verify(v_client); - - GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4))); - cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK); - cq_verify(v_client); - - GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(server, tag(100))); - cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com", - deadline, NULL); - cq_verify(v_server); - - GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, server_cq, tag(102), 0)); - cq_expect_client_metadata_read(v_client, tag(2), NULL); - cq_verify(v_client); - - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_write_status(s, GRPC_STATUS_UNIMPLEMENTED, "xyz", - tag(5))); - cq_expect_finished_with_status(v_client, tag(3), GRPC_STATUS_UNIMPLEMENTED, - "xyz", NULL); - cq_verify(v_client); - - cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK); - cq_verify(v_server); - cq_expect_finished(v_server, tag(102), NULL); - cq_verify(v_server); - - grpc_call_destroy(c); - grpc_call_destroy(s); - } else { - /* Check for a failed connection. */ - cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_ERROR); - cq_expect_client_metadata_read(v_client, tag(2), NULL); - cq_expect_finished_with_status(v_client, tag(3), GRPC_STATUS_CANCELLED, - NULL, NULL); - cq_verify(v_client); - - grpc_call_destroy(c); - } - - cq_verifier_destroy(v_client); - cq_verifier_destroy(v_server); - - /* Destroy client. */ - grpc_channel_destroy(client); - grpc_completion_queue_shutdown(client_cq); - drain_cq(client_cq); - grpc_completion_queue_destroy(client_cq); - - /* Destroy server. */ - grpc_server_shutdown(server); - grpc_server_destroy(server); - grpc_completion_queue_shutdown(server_cq); - drain_cq(server_cq); - grpc_completion_queue_destroy(server_cq); -} - -int main(int argc, char **argv) { - int do_ipv6 = 1; - int i; - int port = grpc_pick_unused_port_or_die(); - - grpc_test_init(argc, argv); - grpc_init(); - - if (!grpc_ipv6_loopback_available()) { - gpr_log(GPR_INFO, "Can't bind to ::1. Skipping IPv6 tests."); - do_ipv6 = 0; - } - - for (i = 0; i <= 1; i++) { - /* For coverage, test with and without dualstack sockets. */ - grpc_forbid_dualstack_sockets_for_testing = i; - - /* :: and 0.0.0.0 are handled identically. */ - test_connect("::", "127.0.0.1", port, 1); - test_connect("::", "::ffff:127.0.0.1", port, 1); - test_connect("::", "localhost", port, 1); - test_connect("0.0.0.0", "127.0.0.1", port, 1); - test_connect("0.0.0.0", "::ffff:127.0.0.1", port, 1); - test_connect("0.0.0.0", "localhost", port, 1); - if (do_ipv6) { - test_connect("::", "::1", port, 1); - test_connect("0.0.0.0", "::1", port, 1); - } - - /* These only work when the families agree. */ - test_connect("127.0.0.1", "127.0.0.1", port, 1); - if (do_ipv6) { - test_connect("::1", "::1", port, 1); - test_connect("::1", "127.0.0.1", port, 0); - test_connect("127.0.0.1", "::1", port, 0); - } - - } - - grpc_shutdown(); - - return 0; -} From b68f3d1746ccd70f041887d74acc166cb7c3c5fd Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Fri, 23 Jan 2015 21:32:47 +0000 Subject: [PATCH 109/143] Fill out the foundation package. --- .../_framework/foundation/_later_test.py | 145 +++++++++++++++ .../_framework/foundation/_timer_future.py | 156 ++++++++++++++++ .../_framework/foundation/abandonment.py | 38 ++++ .../_framework/foundation/callable_util.py | 78 ++++++++ src/python/_framework/foundation/future.py | 172 ++++++++++++++++++ src/python/_framework/foundation/later.py | 51 ++++++ src/python/_framework/foundation/stream.py | 60 ++++++ .../_framework/foundation/stream_testing.py | 73 ++++++++ .../_framework/foundation/stream_util.py | 160 ++++++++++++++++ 9 files changed, 933 insertions(+) create mode 100644 src/python/_framework/foundation/_later_test.py create mode 100644 src/python/_framework/foundation/_timer_future.py create mode 100644 src/python/_framework/foundation/abandonment.py create mode 100644 src/python/_framework/foundation/callable_util.py create mode 100644 src/python/_framework/foundation/future.py create mode 100644 src/python/_framework/foundation/later.py create mode 100644 src/python/_framework/foundation/stream.py create mode 100644 src/python/_framework/foundation/stream_testing.py create mode 100644 src/python/_framework/foundation/stream_util.py diff --git a/src/python/_framework/foundation/_later_test.py b/src/python/_framework/foundation/_later_test.py new file mode 100644 index 00000000000..fbd17a4ad9e --- /dev/null +++ b/src/python/_framework/foundation/_later_test.py @@ -0,0 +1,145 @@ +# Copyright 2015, 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. + +"""Tests of the later module.""" + +import threading +import time +import unittest + +from _framework.foundation import future +from _framework.foundation import later + +TICK = 0.1 + + +class LaterTest(unittest.TestCase): + + def test_simple_delay(self): + lock = threading.Lock() + cell = [0] + def increment_cell(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, increment_cell) + self.assertFalse(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + time.sleep(TICK) + self.assertFalse(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + with lock: + self.assertEqual(0, cell[0]) + time.sleep(TICK * 2) + self.assertTrue(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + with lock: + self.assertEqual(1, cell[0]) + outcome = computation_future.outcome() + self.assertEqual(future.RETURNED, outcome.category) + + def test_callback(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + outcome_passed_to_callback = [None] + def increment_cell(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, increment_cell) + def callback(outcome): + with lock: + callback_called[0] = True + outcome_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + time.sleep(TICK) + with lock: + self.assertFalse(callback_called[0]) + time.sleep(TICK * 2) + with lock: + self.assertTrue(callback_called[0]) + self.assertEqual(future.RETURNED, outcome_passed_to_callback[0].category) + + callback_called[0] = False + outcome_passed_to_callback[0] = None + + computation_future.add_done_callback(callback) + with lock: + self.assertTrue(callback_called[0]) + self.assertEqual(future.RETURNED, outcome_passed_to_callback[0].category) + + def test_cancel(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + outcome_passed_to_callback = [None] + def increment_cell(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, increment_cell) + def callback(outcome): + with lock: + callback_called[0] = True + outcome_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + time.sleep(TICK) + with lock: + self.assertFalse(callback_called[0]) + computation_future.cancel() + self.assertTrue(computation_future.cancelled()) + self.assertFalse(computation_future.done()) + self.assertEqual(future.ABORTED, computation_future.outcome().category) + with lock: + self.assertTrue(callback_called[0]) + self.assertEqual(future.ABORTED, outcome_passed_to_callback[0].category) + + def test_outcome(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + outcome_passed_to_callback = [None] + def increment_cell(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, increment_cell) + def callback(outcome): + with lock: + callback_called[0] = True + outcome_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + returned_outcome = computation_future.outcome() + self.assertEqual(future.RETURNED, returned_outcome.category) + + # The callback may not yet have been called! Sleep a tick. + time.sleep(TICK) + with lock: + self.assertTrue(callback_called[0]) + self.assertEqual(future.RETURNED, outcome_passed_to_callback[0].category) + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/_framework/foundation/_timer_future.py b/src/python/_framework/foundation/_timer_future.py new file mode 100644 index 00000000000..86bc073d562 --- /dev/null +++ b/src/python/_framework/foundation/_timer_future.py @@ -0,0 +1,156 @@ +# Copyright 2015, 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. + +"""Affords a Future implementation based on Python's threading.Timer.""" + +import threading +import time + +from _framework.foundation import future + + +class TimerFuture(future.Future): + """A Future implementation based around Timer objects.""" + + def __init__(self, compute_time, computation): + """Constructor. + + Args: + compute_time: The time after which to begin this future's computation. + computation: The computation to be performed within this Future. + """ + self._lock = threading.Lock() + self._compute_time = compute_time + self._computation = computation + self._timer = None + self._computing = False + self._computed = False + self._cancelled = False + self._outcome = None + self._waiting = [] + + def _compute(self): + """Performs the computation embedded in this Future. + + Or doesn't, if the time to perform it has not yet arrived. + """ + with self._lock: + time_remaining = self._compute_time - time.time() + if 0 < time_remaining: + self._timer = threading.Timer(time_remaining, self._compute) + self._timer.start() + return + else: + self._computing = True + + try: + returned_value = self._computation() + outcome = future.returned(returned_value) + except Exception as e: # pylint: disable=broad-except + outcome = future.raised(e) + + with self._lock: + self._computing = False + self._computed = True + self._outcome = outcome + waiting = self._waiting + + for callback in waiting: + callback(outcome) + + def start(self): + """Starts this Future. + + This must be called exactly once, immediately after construction. + """ + with self._lock: + self._timer = threading.Timer( + self._compute_time - time.time(), self._compute) + self._timer.start() + + def cancel(self): + """See future.Future.cancel for specification.""" + with self._lock: + if self._computing or self._computed: + return False + elif self._cancelled: + return True + else: + self._timer.cancel() + self._cancelled = True + self._outcome = future.aborted() + outcome = self._outcome + waiting = self._waiting + + for callback in waiting: + try: + callback(outcome) + except Exception: # pylint: disable=broad-except + pass + + return True + + def cancelled(self): + """See future.Future.cancelled for specification.""" + with self._lock: + return self._cancelled + + def done(self): + """See future.Future.done for specification.""" + with self._lock: + return self._computed + + def outcome(self): + """See future.Future.outcome for specification.""" + with self._lock: + if self._computed or self._cancelled: + return self._outcome + + condition = threading.Condition() + def notify_condition(unused_outcome): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait() + + with self._lock: + return self._outcome + + def add_done_callback(self, callback): + """See future.Future.add_done_callback for specification.""" + with self._lock: + if not self._computed and not self._cancelled: + self._waiting.append(callback) + return + else: + outcome = self._outcome + + callback(outcome) diff --git a/src/python/_framework/foundation/abandonment.py b/src/python/_framework/foundation/abandonment.py new file mode 100644 index 00000000000..960b4d06b49 --- /dev/null +++ b/src/python/_framework/foundation/abandonment.py @@ -0,0 +1,38 @@ +# Copyright 2015, 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. + +"""Utilities for indicating abandonment of computation.""" + + +class Abandoned(Exception): + """Indicates that some computation is being abandoned. + + Abandoning a computation is different than returning a value or raising + an exception indicating some operational or programming defect. + """ diff --git a/src/python/_framework/foundation/callable_util.py b/src/python/_framework/foundation/callable_util.py new file mode 100644 index 00000000000..1f7546cb76e --- /dev/null +++ b/src/python/_framework/foundation/callable_util.py @@ -0,0 +1,78 @@ +# Copyright 2015, 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. + +"""Utilities for working with callables.""" + +import functools +import logging + +from _framework.foundation import future + + +def _call_logging_exceptions(behavior, message, *args, **kwargs): + try: + return future.returned(behavior(*args, **kwargs)) + except Exception as e: # pylint: disable=broad-except + logging.exception(message) + return future.raised(e) + + +def with_exceptions_logged(behavior, message): + """Wraps a callable in a try-except that logs any exceptions it raises. + + Args: + behavior: Any callable. + message: A string to log if the behavior raises an exception. + + Returns: + A callable that when executed invokes the given behavior. The returned + callable takes the same arguments as the given behavior but returns a + future.Outcome describing whether the given behavior returned a value or + raised an exception. + """ + @functools.wraps(behavior) + def wrapped_behavior(*args, **kwargs): + return _call_logging_exceptions(behavior, message, *args, **kwargs) + return wrapped_behavior + + +def call_logging_exceptions(behavior, message, *args, **kwargs): + """Calls a behavior in a try-except that logs any exceptions it raises. + + Args: + behavior: Any callable. + message: A string to log if the behavior raises an exception. + *args: Positional arguments to pass to the given behavior. + **kwargs: Keyword arguments to pass to the given behavior. + + Returns: + A future.Outcome describing whether the given behavior returned a value or + raised an exception. + """ + return _call_logging_exceptions(behavior, message, *args, **kwargs) diff --git a/src/python/_framework/foundation/future.py b/src/python/_framework/foundation/future.py new file mode 100644 index 00000000000..f00c503257a --- /dev/null +++ b/src/python/_framework/foundation/future.py @@ -0,0 +1,172 @@ +# Copyright 2015, 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. + +"""The Future interface missing from Python's standard library. + +Python's concurrent.futures library defines a Future class very much like the +Future defined here, but since that class is concrete and without construction +semantics it is only available within the concurrent.futures library itself. +The Future class defined here is an entirely abstract interface that anyone may +implement and use. +""" + +import abc +import collections + +RETURNED = object() +RAISED = object() +ABORTED = object() + + +class Outcome(object): + """A sum type describing the outcome of some computation. + + Attributes: + category: One of RETURNED, RAISED, or ABORTED, respectively indicating + that the computation returned a value, raised an exception, or was + aborted. + return_value: The value returned by the computation. Must be present if + category is RETURNED. + exception: The exception raised by the computation. Must be present if + category is RAISED. + """ + __metaclass__ = abc.ABCMeta + + +class _EasyOutcome( + collections.namedtuple('_EasyOutcome', + ['category', 'return_value', 'exception']), + Outcome): + """A trivial implementation of Outcome.""" + +# All Outcomes describing abortion are indistinguishable so there might as well +# be only one. +_ABORTED_OUTCOME = _EasyOutcome(ABORTED, None, None) + + +def aborted(): + """Returns an Outcome indicating that a computation was aborted. + + Returns: + An Outcome indicating that a computation was aborted. + """ + return _ABORTED_OUTCOME + + +def raised(exception): + """Returns an Outcome indicating that a computation raised an exception. + + Args: + exception: The exception raised by the computation. + + Returns: + An Outcome indicating that a computation raised the given exception. + """ + return _EasyOutcome(RAISED, None, exception) + + +def returned(value): + """Returns an Outcome indicating that a computation returned a value. + + Args: + value: The value returned by the computation. + + Returns: + An Outcome indicating that a computation returned the given value. + """ + return _EasyOutcome(RETURNED, value, None) + + +class Future(object): + """A representation of a computation happening in another control flow. + + Computations represented by a Future may have already completed, may be + ongoing, or may be yet to be begun. + + Computations represented by a Future are considered uninterruptable; once + started they will be allowed to terminate either by returning or raising + an exception. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Attempts to cancel the computation. + + Returns: + True if the computation will not be allowed to take place or False if + the computation has already taken place or is currently taking place. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cancelled(self): + """Describes whether the computation was cancelled. + + Returns: + True if the computation was cancelled and did not take place or False + if the computation took place, is taking place, or is scheduled to + take place in the future. + """ + raise NotImplementedError() + + @abc.abstractmethod + def done(self): + """Describes whether the computation has taken place. + + Returns: + True if the computation took place; False otherwise. + """ + raise NotImplementedError() + + @abc.abstractmethod + def outcome(self): + """Accesses the outcome of the computation. + + If the computation has not yet completed, this method blocks until it has. + + Returns: + An Outcome describing the outcome of the computation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_done_callback(self, callback): + """Adds a function to be called at completion of the computation. + + The callback will be passed an Outcome object describing the outcome of + the computation. + + If the computation has already completed, the callback will be called + immediately. + + Args: + callback: A callable taking an Outcome as its single parameter. + """ + raise NotImplementedError() diff --git a/src/python/_framework/foundation/later.py b/src/python/_framework/foundation/later.py new file mode 100644 index 00000000000..fc2cf578d0b --- /dev/null +++ b/src/python/_framework/foundation/later.py @@ -0,0 +1,51 @@ +# Copyright 2015, 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. + +"""Enables scheduling execution at a later time.""" + +import time + +from _framework.foundation import _timer_future + + +def later(delay, computation): + """Schedules later execution of a callable. + + Args: + delay: Any numeric value. Represents the minimum length of time in seconds + to allow to pass before beginning the computation. No guarantees are made + about the maximum length of time that will pass. + computation: A callable that accepts no arguments. + + Returns: + A Future representing the scheduled computation. + """ + timer_future = _timer_future.TimerFuture(time.time() + delay, computation) + timer_future.start() + return timer_future diff --git a/src/python/_framework/foundation/stream.py b/src/python/_framework/foundation/stream.py new file mode 100644 index 00000000000..75c0cf145b4 --- /dev/null +++ b/src/python/_framework/foundation/stream.py @@ -0,0 +1,60 @@ +# Copyright 2015, 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. + +"""Interfaces related to streams of values or objects.""" + +import abc + + +class Consumer(object): + """Interface for consumers of finite streams of values or objects.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def consume(self, value): + """Accepts a value. + + Args: + value: Any value accepted by this Consumer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates to this Consumer that no more values will be supplied.""" + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, value): + """Supplies a value and signals that no more values will be supplied. + + Args: + value: Any value accepted by this Consumer. + """ + raise NotImplementedError() diff --git a/src/python/_framework/foundation/stream_testing.py b/src/python/_framework/foundation/stream_testing.py new file mode 100644 index 00000000000..c1acedc5c6e --- /dev/null +++ b/src/python/_framework/foundation/stream_testing.py @@ -0,0 +1,73 @@ +# Copyright 2015, 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. + +"""Utilities for testing stream-related code.""" + +from _framework.foundation import stream + + +class TestConsumer(stream.Consumer): + """A stream.Consumer instrumented for testing. + + Attributes: + calls: A sequence of value-termination pairs describing the history of calls + made on this object. + """ + + def __init__(self): + self.calls = [] + + def consume(self, value): + """See stream.Consumer.consume for specification.""" + self.calls.append((value, False)) + + def terminate(self): + """See stream.Consumer.terminate for specification.""" + self.calls.append((None, True)) + + def consume_and_terminate(self, value): + """See stream.Consumer.consume_and_terminate for specification.""" + self.calls.append((value, True)) + + def is_legal(self): + """Reports whether or not a legal sequence of calls has been made.""" + terminated = False + for value, terminal in self.calls: + if terminated: + return False + elif terminal: + terminated = True + elif value is None: + return False + else: # pylint: disable=useless-else-on-loop + return True + + def values(self): + """Returns the sequence of values that have been passed to this Consumer.""" + return [value for value, _ in self.calls if value] diff --git a/src/python/_framework/foundation/stream_util.py b/src/python/_framework/foundation/stream_util.py new file mode 100644 index 00000000000..3a9c0433169 --- /dev/null +++ b/src/python/_framework/foundation/stream_util.py @@ -0,0 +1,160 @@ +# Copyright 2015, 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. + +"""Helpful utilities related to the stream module.""" + +import logging +import threading + +from _framework.foundation import stream + +_NO_VALUE = object() + + +class TransformingConsumer(stream.Consumer): + """A stream.Consumer that passes a transformation of its input to another.""" + + def __init__(self, transformation, downstream): + self._transformation = transformation + self._downstream = downstream + + def consume(self, value): + self._downstream.consume(self._transformation(value)) + + def terminate(self): + self._downstream.terminate() + + def consume_and_terminate(self, value): + self._downstream.consume_and_terminate(self._transformation(value)) + + +class IterableConsumer(stream.Consumer): + """A Consumer that when iterated over emits the values it has consumed.""" + + def __init__(self): + self._condition = threading.Condition() + self._values = [] + self._active = True + + def consume(self, stock_reply): + with self._condition: + if self._active: + self._values.append(stock_reply) + self._condition.notify() + + def terminate(self): + with self._condition: + self._active = False + self._condition.notify() + + def consume_and_terminate(self, stock_reply): + with self._condition: + if self._active: + self._values.append(stock_reply) + self._active = False + self._condition.notify() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while self._active and not self._values: + self._condition.wait() + if self._values: + return self._values.pop(0) + else: + raise StopIteration() + + +class ThreadSwitchingConsumer(stream.Consumer): + """A Consumer decorator that affords serialization and asynchrony.""" + + def __init__(self, sink, pool): + self._lock = threading.Lock() + self._sink = sink + self._pool = pool + # True if self._spin has been submitted to the pool to be called once and + # that call has not yet returned, False otherwise. + self._spinning = False + self._values = [] + self._active = True + + def _spin(self, sink, value, terminate): + while True: + try: + if value is _NO_VALUE: + sink.terminate() + elif terminate: + sink.consume_and_terminate(value) + else: + sink.consume(value) + except Exception as e: # pylint:disable=broad-except + logging.exception(e) + + with self._lock: + if terminate: + self._spinning = False + return + elif self._values: + value = self._values.pop(0) + terminate = not self._values and not self._active + elif not self._active: + value = _NO_VALUE + terminate = True + else: + self._spinning = False + return + + def consume(self, value): + with self._lock: + if self._active: + if self._spinning: + self._values.append(value) + else: + self._pool.submit(self._spin, self._sink, value, False) + self._spinning = True + + def terminate(self): + with self._lock: + if self._active: + self._active = False + if not self._spinning: + self._pool.submit(self._spin, self._sink, _NO_VALUE, True) + self._spinning = True + + def consume_and_terminate(self, value): + with self._lock: + if self._active: + self._active = False + if self._spinning: + self._values.append(value) + else: + self._pool.submit(self._spin, self._sink, value, True) + self._spinning = True From 61b5a81a1e401b7670fcf003533de0b938caf363 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Fri, 23 Jan 2015 21:42:39 +0000 Subject: [PATCH 110/143] Add the _framework.common package. It's rather unimpressive at the moment with just one module. --- src/python/_framework/common/__init__.py | 0 src/python/_framework/common/cardinality.py | 42 +++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/python/_framework/common/__init__.py create mode 100644 src/python/_framework/common/cardinality.py diff --git a/src/python/_framework/common/__init__.py b/src/python/_framework/common/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_framework/common/cardinality.py b/src/python/_framework/common/cardinality.py new file mode 100644 index 00000000000..610425e8032 --- /dev/null +++ b/src/python/_framework/common/cardinality.py @@ -0,0 +1,42 @@ +# Copyright 2015, 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. + +"""Defines an enum for classifying RPC methods by streaming semantics.""" + +import enum + + +@enum.unique +class Cardinality(enum.Enum): + """Describes the streaming semantics of an RPC method.""" + + UNARY_UNARY = 'request-unary/response-unary' + UNARY_STREAM = 'request-unary/response-streaming' + STREAM_UNARY = 'request-streaming/response-unary' + STREAM_STREAM = 'request-streaming/response-streaming' From 913d026e00cb4be1a4f58a2c34ad6cf152e9bb8d Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:47:56 -0800 Subject: [PATCH 111/143] src --- src/core/support/string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/support/string.h b/src/core/support/string.h index 71bd3f84e30..2ce4b7f7856 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -60,7 +60,7 @@ char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags); int gpr_parse_bytes_to_uint32(const char *data, size_t length, gpr_uint32 *result); -/* minimum buffer size for calling ltoa */ +/* Minimum buffer size for calling ltoa */ #define GPR_LTOA_MIN_BUFSIZE (3 * sizeof(long)) /* Convert a long to a string in base 10; returns the length of the From 672cd42a04aa74d86f8ffbc1aa44b662ea7f6f1b Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:48:49 -0800 Subject: [PATCH 112/143] Formatting --- src/core/support/string.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/support/string.h b/src/core/support/string.h index 2ce4b7f7856..28b7029ecd6 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -64,7 +64,8 @@ int gpr_parse_bytes_to_uint32(const char *data, size_t length, #define GPR_LTOA_MIN_BUFSIZE (3 * sizeof(long)) /* Convert a long to a string in base 10; returns the length of the - output string (or 0 on failure) */ + output string (or 0 on failure). + output must be at least GPR_LTOA_MIN_BUFSIZE bytes long. */ int gpr_ltoa(long value, char *output); /* Reverse a run of bytes */ From b7b9c758ed04a2de87822b5f37a993e529b84c28 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:52:43 -0800 Subject: [PATCH 113/143] Add constant for min bufsize for timeout encoding --- src/core/transport/chttp2/timeout_encoding.h | 3 +++ test/core/transport/chttp2/timeout_encoding_test.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/transport/chttp2/timeout_encoding.h b/src/core/transport/chttp2/timeout_encoding.h index a4582566adc..d1e47760324 100644 --- a/src/core/transport/chttp2/timeout_encoding.h +++ b/src/core/transport/chttp2/timeout_encoding.h @@ -34,8 +34,11 @@ #ifndef __GRPC_INTERNAL_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H_ #define __GRPC_INTERNAL_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H_ +#include "src/core/support/string.h" #include +#define GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1) + /* Encode/decode timeouts to the GRPC over HTTP2 format; encoding may round up arbitrarily */ void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer); diff --git a/test/core/transport/chttp2/timeout_encoding_test.c b/test/core/transport/chttp2/timeout_encoding_test.c index 4bb84e3f0bf..ffa0070e341 100644 --- a/test/core/transport/chttp2/timeout_encoding_test.c +++ b/test/core/transport/chttp2/timeout_encoding_test.c @@ -43,7 +43,7 @@ #define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__) static void assert_encodes_as(gpr_timespec ts, const char *s) { - char buffer[32]; + char buffer[GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE]; grpc_chttp2_encode_timeout(ts, buffer); gpr_log(GPR_INFO, "check '%s' == '%s'", buffer, s); GPR_ASSERT(0 == strcmp(buffer, s)); From 8b433a2d1d9c2eb7d76e453cd3f0322adfce1f24 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 14:47:07 -0800 Subject: [PATCH 114/143] Use symbolic constant --- src/core/channel/client_channel.c | 2 +- src/core/surface/call.c | 2 +- src/core/transport/chttp2_transport.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index c315dd6c1e0..f9b42db419d 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -410,7 +410,7 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_mdctx *metadata_context, int is_first, int is_last) { channel_data *chand = elem->channel_data; - char temp[16]; + char temp[GPR_LTOA_MIN_BUFSIZE]; GPR_ASSERT(!is_first); GPR_ASSERT(is_last); diff --git a/src/core/surface/call.c b/src/core/surface/call.c index a9510097174..1d8315a4b66 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -760,7 +760,7 @@ grpc_call_error grpc_call_start_write_status(grpc_call *call, /* always send status */ { grpc_mdelem *md; - char buffer[32]; + char buffer[GPR_LTOA_MIN_BUFSIZE]; gpr_ltoa(status, buffer); md = grpc_mdelem_from_strings(call->metadata_context, "grpc-status", buffer); diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 36358089ec5..531a53b9848 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -1002,7 +1002,7 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id, grpc_chttp2_error_code error_code, int send_rst) { int had_outgoing; - char buffer[32]; + char buffer[GPR_LTOA_MIN_BUFSIZE]; if (s) { /* clear out any unreported input & output: nobody cares anymore */ From c92499d1d91091d4a2cd437f46534a353fb0bee6 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 23 Jan 2015 14:52:01 -0800 Subject: [PATCH 115/143] Removed some duplicate stream code --- src/node/client.js | 6 +-- src/node/server.js | 2 +- src/node/surface_client.js | 56 +++++++++------------ src/node/surface_server.js | 73 ++++++++++------------------ src/node/test/interop_sanity_test.js | 2 +- 5 files changed, 56 insertions(+), 83 deletions(-) diff --git a/src/node/client.js b/src/node/client.js index 2fefd14bbc5..7007852b939 100644 --- a/src/node/client.js +++ b/src/node/client.js @@ -105,7 +105,7 @@ function GrpcClientStream(call, serialize, deserialize) { return; } var data = event.data; - if (self.push(data) && data != null) { + if (self.push(self.deserialize(data)) && data != null) { self._call.startRead(readCallback); } else { reading = false; @@ -155,7 +155,7 @@ GrpcClientStream.prototype._read = function(size) { */ GrpcClientStream.prototype._write = function(chunk, encoding, callback) { var self = this; - self._call.startWrite(chunk, function(event) { + self._call.startWrite(self.serialize(chunk), function(event) { callback(); }, 0); }; @@ -185,7 +185,7 @@ function makeRequest(channel, if (metadata) { call.addMetadata(metadata); } - return new GrpcClientStream(call); + return new GrpcClientStream(call, serialize, deserialize); } /** diff --git a/src/node/server.js b/src/node/server.js index eca20aa5fd0..fc7144a9c14 100644 --- a/src/node/server.js +++ b/src/node/server.js @@ -151,7 +151,7 @@ function GrpcServerStream(call, serialize, deserialize) { return; } var data = event.data; - if (self.push(deserialize(data)) && data != null) { + if (self.push(self.deserialize(data)) && data != null) { self._call.startRead(readCallback); } else { reading = false; diff --git a/src/node/surface_client.js b/src/node/surface_client.js index 996e3d101fc..8996f0be40e 100644 --- a/src/node/surface_client.js +++ b/src/node/surface_client.js @@ -63,18 +63,16 @@ util.inherits(ClientReadableObjectStream, Readable); * client side. Extends from stream.Readable. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(Buffer)} deserialize Function for deserializing binary data - * @param {object} options Stream options */ -function ClientReadableObjectStream(stream, deserialize, options) { - options = _.extend(options, {objectMode: true}); +function ClientReadableObjectStream(stream) { + var options = {objectMode: true}; Readable.call(this, options); this._stream = stream; var self = this; forwardEvent(stream, this, 'status'); forwardEvent(stream, this, 'metadata'); this._stream.on('data', function forwardData(chunk) { - if (!self.push(deserialize(chunk))) { + if (!self.push(chunk)) { self._stream.pause(); } }); @@ -88,14 +86,11 @@ util.inherits(ClientWritableObjectStream, Writable); * client side. Extends from stream.Writable. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(*):Buffer} serialize Function for serializing objects - * @param {object} options Stream options */ -function ClientWritableObjectStream(stream, serialize, options) { - options = _.extend(options, {objectMode: true}); +function ClientWritableObjectStream(stream) { + var options = {objectMode: true}; Writable.call(this, options); this._stream = stream; - this._serialize = serialize; forwardEvent(stream, this, 'status'); forwardEvent(stream, this, 'metadata'); this.on('finish', function() { @@ -111,20 +106,16 @@ util.inherits(ClientBidiObjectStream, Duplex); * client side. Extends from stream.Duplex. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(*):Buffer} serialize Function for serializing objects - * @param {function(Buffer)} deserialize Function for deserializing binary data - * @param {object} options Stream options */ -function ClientBidiObjectStream(stream, serialize, deserialize, options) { - options = _.extend(options, {objectMode: true}); +function ClientBidiObjectStream(stream) { + var options = {objectMode: true}; Duplex.call(this, options); this._stream = stream; - this._serialize = serialize; var self = this; forwardEvent(stream, this, 'status'); forwardEvent(stream, this, 'metadata'); this._stream.on('data', function forwardData(chunk) { - if (!self.push(deserialize(chunk))) { + if (!self.push(chunk)) { self._stream.pause(); } }); @@ -160,7 +151,7 @@ ClientBidiObjectStream.prototype._read = _read; * @param {function(Error)} callback Callback to call when finished writing */ function _write(chunk, encoding, callback) { - this._stream.write(this._serialize(chunk), encoding, callback); + this._stream.write(chunk, encoding, callback); } /** @@ -196,15 +187,16 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeUnaryRequest(argument, callback, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, metadata, deadline); + var stream = client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); var emitter = new EventEmitter(); forwardEvent(stream, emitter, 'status'); forwardEvent(stream, emitter, 'metadata'); - stream.write(serialize(argument)); + stream.write(argument); stream.end(); stream.on('data', function forwardData(chunk) { try { - callback(null, deserialize(chunk)); + callback(null, chunk); } catch (e) { callback(e); } @@ -236,11 +228,12 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeClientStreamRequest(callback, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, metadata, deadline); - var obj_stream = new ClientWritableObjectStream(stream, serialize, {}); + var stream = client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); + var obj_stream = new ClientWritableObjectStream(stream); stream.on('data', function forwardData(chunk) { try { - callback(null, deserialize(chunk)); + callback(null, chunk); } catch (e) { callback(e); } @@ -272,9 +265,10 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeServerStreamRequest(argument, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, metadata, deadline); - var obj_stream = new ClientReadableObjectStream(stream, deserialize, {}); - stream.write(serialize(argument)); + var stream = client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); + var obj_stream = new ClientReadableObjectStream(stream); + stream.write(argument); stream.end(); return obj_stream; } @@ -301,11 +295,9 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeBidiStreamRequest(metadata, deadline) { - var stream = client.makeRequest(this.channel, method, metadata, deadline); - var obj_stream = new ClientBidiObjectStream(stream, - serialize, - deserialize, - {}); + var stream = client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); + var obj_stream = new ClientBidiObjectStream(stream); return obj_stream; } return makeBidiStreamRequest; diff --git a/src/node/surface_server.js b/src/node/surface_server.js index bc688839fe5..28af5ab42f9 100644 --- a/src/node/surface_server.js +++ b/src/node/surface_server.js @@ -54,11 +54,9 @@ util.inherits(ServerReadableObjectStream, Readable); * server side. Extends from stream.Readable. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(Buffer)} deserialize Function for deserializing binary data - * @param {object} options Stream options */ -function ServerReadableObjectStream(stream, deserialize, options) { - options = _.extend(options, {objectMode: true}); +function ServerReadableObjectStream(stream) { + var options = {objectMode: true}; Readable.call(this, options); this._stream = stream; Object.defineProperty(this, 'cancelled', { @@ -66,7 +64,7 @@ function ServerReadableObjectStream(stream, deserialize, options) { }); var self = this; this._stream.on('data', function forwardData(chunk) { - if (!self.push(deserialize(chunk))) { + if (!self.push(chunk)) { self._stream.pause(); } }); @@ -83,14 +81,11 @@ util.inherits(ServerWritableObjectStream, Writable); * server side. Extends from stream.Writable. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(*):Buffer} serialize Function for serializing objects - * @param {object} options Stream options */ -function ServerWritableObjectStream(stream, serialize, options) { - options = _.extend(options, {objectMode: true}); +function ServerWritableObjectStream(stream) { + var options = {objectMode: true}; Writable.call(this, options); this._stream = stream; - this._serialize = serialize; this.on('finish', function() { this._stream.end(); }); @@ -103,18 +98,14 @@ util.inherits(ServerBidiObjectStream, Duplex); * server side. Extends from stream.Duplex. * @constructor * @param {stream} stream Underlying binary Duplex stream for the call - * @param {function(*):Buffer} serialize Function for serializing objects - * @param {function(Buffer)} deserialize Function for deserializing binary data - * @param {object} options Stream options */ -function ServerBidiObjectStream(stream, serialize, deserialize, options) { - options = _.extend(options, {objectMode: true}); +function ServerBidiObjectStream(stream) { + var options = {objectMode: true}; Duplex.call(this, options); this._stream = stream; - this._serialize = serialize; var self = this; this._stream.on('data', function forwardData(chunk) { - if (!self.push(deserialize(chunk))) { + if (!self.push(chunk)) { self._stream.pause(); } }); @@ -153,7 +144,7 @@ ServerBidiObjectStream.prototype._read = _read; * @param {function(Error)} callback Callback to call when finished writing */ function _write(chunk, encoding, callback) { - this._stream.write(this._serialize(chunk), encoding, callback); + this._stream.write(chunk, encoding, callback); } /** @@ -168,11 +159,9 @@ ServerBidiObjectStream.prototype._write = _write; /** * Creates a binary stream handler function from a unary handler function * @param {function(Object, function(Error, *))} handler Unary call handler - * @param {function(*):Buffer} serialize Serialization function - * @param {function(Buffer):*} deserialize Deserialization function * @return {function(stream)} Binary stream handler */ -function makeUnaryHandler(handler, serialize, deserialize) { +function makeUnaryHandler(handler) { /** * Handles a stream by reading a single data value, passing it to the handler, * and writing the response back to the stream. @@ -180,7 +169,7 @@ function makeUnaryHandler(handler, serialize, deserialize) { */ return function handleUnaryCall(stream) { stream.on('data', function handleUnaryData(value) { - var call = {request: deserialize(value)}; + var call = {request: value}; Object.defineProperty(call, 'cancelled', { get: function() { return stream.cancelled;} }); @@ -188,7 +177,7 @@ function makeUnaryHandler(handler, serialize, deserialize) { if (err) { stream.emit('error', err); } else { - stream.write(serialize(value)); + stream.write(value); stream.end(); } }); @@ -201,23 +190,21 @@ function makeUnaryHandler(handler, serialize, deserialize) { * function * @param {function(Readable, function(Error, *))} handler Client stream call * handler - * @param {function(*):Buffer} serialize Serialization function - * @param {function(Buffer):*} deserialize Deserialization function * @return {function(stream)} Binary stream handler */ -function makeClientStreamHandler(handler, serialize, deserialize) { +function makeClientStreamHandler(handler) { /** * Handles a stream by passing a deserializing stream to the handler and * writing the response back to the stream. * @param {stream} stream Binary data stream */ return function handleClientStreamCall(stream) { - var object_stream = new ServerReadableObjectStream(stream, deserialize, {}); + var object_stream = new ServerReadableObjectStream(stream); handler(object_stream, function sendClientStreamData(err, value) { if (err) { stream.emit('error', err); } else { - stream.write(serialize(value)); + stream.write(value); stream.end(); } }); @@ -228,11 +215,9 @@ function makeClientStreamHandler(handler, serialize, deserialize) { * Creates a binary stream handler function from a server stream handler * function * @param {function(Writable)} handler Server stream call handler - * @param {function(*):Buffer} serialize Serialization function - * @param {function(Buffer):*} deserialize Deserialization function * @return {function(stream)} Binary stream handler */ -function makeServerStreamHandler(handler, serialize, deserialize) { +function makeServerStreamHandler(handler) { /** * Handles a stream by attaching it to a serializing stream, and passing it to * the handler. @@ -240,10 +225,8 @@ function makeServerStreamHandler(handler, serialize, deserialize) { */ return function handleServerStreamCall(stream) { stream.on('data', function handleClientData(value) { - var object_stream = new ServerWritableObjectStream(stream, - serialize, - {}); - object_stream.request = deserialize(value); + var object_stream = new ServerWritableObjectStream(stream); + object_stream.request = value; handler(object_stream); }); }; @@ -252,21 +235,16 @@ function makeServerStreamHandler(handler, serialize, deserialize) { /** * Creates a binary stream handler function from a bidi stream handler function * @param {function(Duplex)} handler Unary call handler - * @param {function(*):Buffer} serialize Serialization function - * @param {function(Buffer):*} deserialize Deserialization function * @return {function(stream)} Binary stream handler */ -function makeBidiStreamHandler(handler, serialize, deserialize) { +function makeBidiStreamHandler(handler) { /** * Handles a stream by wrapping it in a serializing and deserializing object * stream, and passing it to the handler. * @param {stream} stream Binary data stream */ return function handleBidiStreamCall(stream) { - var object_stream = new ServerBidiObjectStream(stream, - serialize, - deserialize, - {}); + var object_stream = new ServerBidiObjectStream(stream); handler(object_stream); }; } @@ -341,10 +319,13 @@ function makeServerConstructor(services) { common.fullyQualifiedName(method) + ' not provided.'); } var binary_handler = handler_makers[method_type]( - service_handlers[service_name][decapitalize(method.name)], - common.serializeCls(method.resolvedResponseType.build()), - common.deserializeCls(method.resolvedRequestType.build())); - server.register(prefix + capitalize(method.name), binary_handler); + service_handlers[service_name][decapitalize(method.name)]); + var serialize = common.serializeCls( + method.resolvedResponseType.build()); + var deserialize = common.deserializeCls( + method.resolvedRequestType.build()); + server.register(prefix + capitalize(method.name), binary_handler, + serialize, deserialize); }); }, this); } diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index 3c062b97882..8ea48c359f8 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -65,7 +65,7 @@ describe('Interop tests', function() { it('should pass ping_pong', function(done) { interop_client.runTest(port, name_override, 'ping_pong', true, done); }); - it.skip('should pass empty_stream', function(done) { + it('should pass empty_stream', function(done) { interop_client.runTest(port, name_override, 'empty_stream', true, done); }); }); From 46c2461c8bfc60311f2eb607c35389f6437a02b1 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 23 Jan 2015 15:00:38 -0800 Subject: [PATCH 116/143] Removed unnecessary bidi stream wrappers --- src/node/surface_client.js | 42 +++------------------------------ src/node/surface_server.js | 48 ++------------------------------------ 2 files changed, 5 insertions(+), 85 deletions(-) diff --git a/src/node/surface_client.js b/src/node/surface_client.js index 8996f0be40e..abec999cabb 100644 --- a/src/node/surface_client.js +++ b/src/node/surface_client.js @@ -98,36 +98,9 @@ function ClientWritableObjectStream(stream) { }); } - -util.inherits(ClientBidiObjectStream, Duplex); - -/** - * Class for representing a gRPC bidi streaming call as a Node stream on the - * client side. Extends from stream.Duplex. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ClientBidiObjectStream(stream) { - var options = {objectMode: true}; - Duplex.call(this, options); - this._stream = stream; - var self = this; - forwardEvent(stream, this, 'status'); - forwardEvent(stream, this, 'metadata'); - this._stream.on('data', function forwardData(chunk) { - if (!self.push(chunk)) { - self._stream.pause(); - } - }); - this._stream.pause(); - this.on('finish', function() { - this._stream.end(); - }); -} - /** * _read implementation for both types of streams that allow reading. - * @this {ClientReadableObjectStream|ClientBidiObjectStream} + * @this {ClientReadableObjectStream} * @param {number} size Ignored */ function _read(size) { @@ -138,14 +111,10 @@ function _read(size) { * See docs for _read */ ClientReadableObjectStream.prototype._read = _read; -/** - * See docs for _read - */ -ClientBidiObjectStream.prototype._read = _read; /** * _write implementation for both types of streams that allow writing - * @this {ClientWritableObjectStream|ClientBidiObjectStream} + * @this {ClientWritableObjectStream} * @param {*} chunk The value to write to the stream * @param {string} encoding Ignored * @param {function(Error)} callback Callback to call when finished writing @@ -158,10 +127,6 @@ function _write(chunk, encoding, callback) { * See docs for _write */ ClientWritableObjectStream.prototype._write = _write; -/** - * See docs for _write - */ -ClientBidiObjectStream.prototype._write = _write; /** * Get a function that can make unary requests to the specified method. @@ -297,8 +262,7 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) { function makeBidiStreamRequest(metadata, deadline) { var stream = client.makeRequest(this.channel, method, serialize, deserialize, metadata, deadline); - var obj_stream = new ClientBidiObjectStream(stream); - return obj_stream; + return stream; } return makeBidiStreamRequest; } diff --git a/src/node/surface_server.js b/src/node/surface_server.js index 28af5ab42f9..e3c48b13e19 100644 --- a/src/node/surface_server.js +++ b/src/node/surface_server.js @@ -90,34 +90,6 @@ function ServerWritableObjectStream(stream) { this._stream.end(); }); } - -util.inherits(ServerBidiObjectStream, Duplex); - -/** - * Class for representing a gRPC bidi streaming call as a Node stream on the - * server side. Extends from stream.Duplex. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ServerBidiObjectStream(stream) { - var options = {objectMode: true}; - Duplex.call(this, options); - this._stream = stream; - var self = this; - this._stream.on('data', function forwardData(chunk) { - if (!self.push(chunk)) { - self._stream.pause(); - } - }); - this._stream.on('end', function forwardEnd() { - self.push(null); - }); - this._stream.pause(); - this.on('finish', function() { - this._stream.end(); - }); -} - /** * _read implementation for both types of streams that allow reading. * @this {ServerReadableObjectStream|ServerBidiObjectStream} @@ -131,14 +103,10 @@ function _read(size) { * See docs for _read */ ServerReadableObjectStream.prototype._read = _read; -/** - * See docs for _read - */ -ServerBidiObjectStream.prototype._read = _read; /** * _write implementation for both types of streams that allow writing - * @this {ServerWritableObjectStream|ServerBidiObjectStream} + * @this {ServerWritableObjectStream} * @param {*} chunk The value to write to the stream * @param {string} encoding Ignored * @param {function(Error)} callback Callback to call when finished writing @@ -151,10 +119,6 @@ function _write(chunk, encoding, callback) { * See docs for _write */ ServerWritableObjectStream.prototype._write = _write; -/** - * See docs for _write - */ -ServerBidiObjectStream.prototype._write = _write; /** * Creates a binary stream handler function from a unary handler function @@ -238,15 +202,7 @@ function makeServerStreamHandler(handler) { * @return {function(stream)} Binary stream handler */ function makeBidiStreamHandler(handler) { - /** - * Handles a stream by wrapping it in a serializing and deserializing object - * stream, and passing it to the handler. - * @param {stream} stream Binary data stream - */ - return function handleBidiStreamCall(stream) { - var object_stream = new ServerBidiObjectStream(stream); - handler(object_stream); - }; + return handler; } /** From 2ed57b48588270d37846c35694b0d484e912ff4b Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 23 Jan 2015 15:15:46 -0800 Subject: [PATCH 117/143] Moved some code around for clarity --- src/node/surface_client.js | 28 ++++++++++++++-------------- src/node/surface_server.js | 27 ++++++++++++++------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/node/surface_client.js b/src/node/surface_client.js index abec999cabb..aba8feeada9 100644 --- a/src/node/surface_client.js +++ b/src/node/surface_client.js @@ -79,6 +79,20 @@ function ClientReadableObjectStream(stream) { this._stream.pause(); } +/** + * _read implementation for both types of streams that allow reading. + * @this {ClientReadableObjectStream} + * @param {number} size Ignored + */ +function _read(size) { + this._stream.resume(); +} + +/** + * See docs for _read + */ +ClientReadableObjectStream.prototype._read = _read; + util.inherits(ClientWritableObjectStream, Writable); /** @@ -98,20 +112,6 @@ function ClientWritableObjectStream(stream) { }); } -/** - * _read implementation for both types of streams that allow reading. - * @this {ClientReadableObjectStream} - * @param {number} size Ignored - */ -function _read(size) { - this._stream.resume(); -} - -/** - * See docs for _read - */ -ClientReadableObjectStream.prototype._read = _read; - /** * _write implementation for both types of streams that allow writing * @this {ClientWritableObjectStream} diff --git a/src/node/surface_server.js b/src/node/surface_server.js index e3c48b13e19..07c5339f62f 100644 --- a/src/node/surface_server.js +++ b/src/node/surface_server.js @@ -74,6 +74,20 @@ function ServerReadableObjectStream(stream) { this._stream.pause(); } +/** + * _read implementation for both types of streams that allow reading. + * @this {ServerReadableObjectStream|ServerBidiObjectStream} + * @param {number} size Ignored + */ +function _read(size) { + this._stream.resume(); +} + +/** + * See docs for _read + */ +ServerReadableObjectStream.prototype._read = _read; + util.inherits(ServerWritableObjectStream, Writable); /** @@ -90,19 +104,6 @@ function ServerWritableObjectStream(stream) { this._stream.end(); }); } -/** - * _read implementation for both types of streams that allow reading. - * @this {ServerReadableObjectStream|ServerBidiObjectStream} - * @param {number} size Ignored - */ -function _read(size) { - this._stream.resume(); -} - -/** - * See docs for _read - */ -ServerReadableObjectStream.prototype._read = _read; /** * _write implementation for both types of streams that allow writing From 21f29c6bf70d81ab2a16deb5cda13cecf9452d9a Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Sat, 24 Jan 2015 00:05:22 +0000 Subject: [PATCH 118/143] Add the _framework.base package. --- src/python/_framework/base/__init__.py | 0 src/python/_framework/base/exceptions.py | 34 ++ src/python/_framework/base/interfaces.py | 229 +++++++++ src/python/_framework/base/interfaces_test.py | 299 ++++++++++++ .../_framework/base/packets/__init__.py | 0 .../_framework/base/packets/_cancellation.py | 64 +++ .../_framework/base/packets/_constants.py | 32 ++ .../_framework/base/packets/_context.py | 99 ++++ .../_framework/base/packets/_emission.py | 126 +++++ src/python/_framework/base/packets/_ends.py | 408 ++++++++++++++++ .../_framework/base/packets/_expiration.py | 158 +++++++ .../_framework/base/packets/_ingestion.py | 440 ++++++++++++++++++ .../_framework/base/packets/_interfaces.py | 269 +++++++++++ .../_framework/base/packets/_reception.py | 394 ++++++++++++++++ .../_framework/base/packets/_termination.py | 201 ++++++++ .../_framework/base/packets/_transmission.py | 393 ++++++++++++++++ .../base/packets/implementations.py | 77 +++ .../base/packets/implementations_test.py | 80 ++++ .../_framework/base/packets/in_memory.py | 108 +++++ .../_framework/base/packets/interfaces.py | 84 ++++ src/python/_framework/base/packets/null.py | 56 +++ src/python/_framework/base/packets/packets.py | 112 +++++ src/python/_framework/base/util.py | 91 ++++ 23 files changed, 3754 insertions(+) create mode 100644 src/python/_framework/base/__init__.py create mode 100644 src/python/_framework/base/exceptions.py create mode 100644 src/python/_framework/base/interfaces.py create mode 100644 src/python/_framework/base/interfaces_test.py create mode 100644 src/python/_framework/base/packets/__init__.py create mode 100644 src/python/_framework/base/packets/_cancellation.py create mode 100644 src/python/_framework/base/packets/_constants.py create mode 100644 src/python/_framework/base/packets/_context.py create mode 100644 src/python/_framework/base/packets/_emission.py create mode 100644 src/python/_framework/base/packets/_ends.py create mode 100644 src/python/_framework/base/packets/_expiration.py create mode 100644 src/python/_framework/base/packets/_ingestion.py create mode 100644 src/python/_framework/base/packets/_interfaces.py create mode 100644 src/python/_framework/base/packets/_reception.py create mode 100644 src/python/_framework/base/packets/_termination.py create mode 100644 src/python/_framework/base/packets/_transmission.py create mode 100644 src/python/_framework/base/packets/implementations.py create mode 100644 src/python/_framework/base/packets/implementations_test.py create mode 100644 src/python/_framework/base/packets/in_memory.py create mode 100644 src/python/_framework/base/packets/interfaces.py create mode 100644 src/python/_framework/base/packets/null.py create mode 100644 src/python/_framework/base/packets/packets.py create mode 100644 src/python/_framework/base/util.py diff --git a/src/python/_framework/base/__init__.py b/src/python/_framework/base/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_framework/base/exceptions.py b/src/python/_framework/base/exceptions.py new file mode 100644 index 00000000000..b8f47521847 --- /dev/null +++ b/src/python/_framework/base/exceptions.py @@ -0,0 +1,34 @@ +# Copyright 2015, 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. + +"""Exceptions defined and used by the base layer of RPC Framework.""" + + +class NoSuchMethodError(Exception): + """Indicates that an operation with an unrecognized name has been called.""" diff --git a/src/python/_framework/base/interfaces.py b/src/python/_framework/base/interfaces.py new file mode 100644 index 00000000000..de7137cbf75 --- /dev/null +++ b/src/python/_framework/base/interfaces.py @@ -0,0 +1,229 @@ +# Copyright 2015, 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. + +"""Interfaces defined and used by the base layer of RPC Framework.""" + +# TODO(nathaniel): Use Python's new enum library for enumerated types rather +# than constants merely placed close together. + +import abc + +# stream is referenced from specification in this module. +from _framework.foundation import stream # pylint: disable=unused-import + +# Operation outcomes. +COMPLETED = 'completed' +CANCELLED = 'cancelled' +EXPIRED = 'expired' +RECEPTION_FAILURE = 'reception failure' +TRANSMISSION_FAILURE = 'transmission failure' +SERVICER_FAILURE = 'servicer failure' +SERVICED_FAILURE = 'serviced failure' + +# Subscription categories. +FULL = 'full' +TERMINATION_ONLY = 'termination only' +NONE = 'none' + + +class OperationContext(object): + """Provides operation-related information and action. + + Attributes: + trace_id: A uuid.UUID identifying a particular set of related operations. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the operation is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def add_termination_callback(self, callback): + """Adds a function to be called upon operation termination. + + Args: + callback: A callable that will be passed one of COMPLETED, CANCELLED, + EXPIRED, RECEPTION_FAILURE, TRANSMISSION_FAILURE, SERVICER_FAILURE, or + SERVICED_FAILURE. + """ + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the operation. + + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the operation to complete before it is considered to have + timed out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def fail(self, exception): + """Indicates that the operation has failed. + + Args: + exception: An exception germane to the operation failure. May be None. + """ + raise NotImplementedError() + + +class Servicer(object): + """Interface for service implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, name, context, output_consumer): + """Services an operation. + + Args: + name: The name of the operation. + context: A ServicerContext object affording contextual information and + actions. + output_consumer: A stream.Consumer that will accept output values of + the operation. + + Returns: + A stream.Consumer that will accept input values for the operation. + + Raises: + exceptions.NoSuchMethodError: If this Servicer affords no method with the + given name. + abandonment.Abandoned: If the operation has been aborted and there no + longer is any reason to service the operation. + """ + raise NotImplementedError() + + +class Operation(object): + """Representation of an in-progress operation. + + Attributes: + consumer: A stream.Consumer into which payloads constituting the operation's + input may be passed. + context: An OperationContext affording information and action about the + operation. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Cancels this operation.""" + raise NotImplementedError() + + +class ServicedIngestor(object): + """Responsible for accepting the result of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def consumer(self, operation_context): + """Affords a consumer to which operation results will be passed. + + Args: + operation_context: An OperationContext object for the current operation. + + Returns: + A stream.Consumer to which the results of the current operation will be + passed. + + Raises: + abandonment.Abandoned: If the operation has been aborted and there no + longer is any reason to service the operation. + """ + raise NotImplementedError() + + +class ServicedSubscription(object): + """A sum type representing a serviced's interest in an operation. + + Attributes: + category: One of FULL, TERMINATION_ONLY, or NONE. + ingestor: A ServicedIngestor. Must be present if category is FULL. + """ + __metaclass__ = abc.ABCMeta + + +class End(object): + """Common type for entry-point objects on both sides of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def operation_stats(self): + """Reports the number of terminated operations broken down by outcome. + + Returns: + A dictionary from operation outcome constant (COMPLETED, CANCELLED, + EXPIRED, and so on) to an integer representing the number of operations + that terminated with that outcome. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_idle_action(self, action): + """Adds an action to be called when this End has no ongoing operations. + + Args: + action: A callable that accepts no arguments. + """ + raise NotImplementedError() + + +class Front(End): + """Clientish objects that afford the invocation of operations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def operate( + self, name, payload, complete, timeout, subscription, trace_id): + """Commences an operation. + + Args: + name: The name of the method invoked for the operation. + payload: An initial payload for the operation. May be None. + complete: A boolean indicating whether or not additional payloads to be + sent to the servicer may be supplied after this call. + timeout: A length of time in seconds to allow for the operation. + subscription: A ServicedSubscription for the operation. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + + Returns: + An Operation object affording information and action about the operation + in progress. + """ + raise NotImplementedError() + + +class Back(End): + """Serverish objects that perform the work of operations.""" + __metaclass__ = abc.ABCMeta diff --git a/src/python/_framework/base/interfaces_test.py b/src/python/_framework/base/interfaces_test.py new file mode 100644 index 00000000000..6eb07ea5056 --- /dev/null +++ b/src/python/_framework/base/interfaces_test.py @@ -0,0 +1,299 @@ +# Copyright 2015, 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. + +"""Abstract tests against the interfaces of the base layer of RPC Framework.""" + +import threading +import time + +from _framework.base import interfaces +from _framework.base import util +from _framework.foundation import stream +from _framework.foundation import stream_testing +from _framework.foundation import stream_util + +TICK = 0.1 +SMALL_TIMEOUT = TICK * 50 +STREAM_LENGTH = 100 + +SYNCHRONOUS_ECHO = 'synchronous echo' +ASYNCHRONOUS_ECHO = 'asynchronous echo' +IMMEDIATE_FAILURE = 'immediate failure' +TRIGGERED_FAILURE = 'triggered failure' +WAIT_ON_CONDITION = 'wait on condition' + +EMPTY_OUTCOME_DICT = { + interfaces.COMPLETED: 0, + interfaces.CANCELLED: 0, + interfaces.EXPIRED: 0, + interfaces.RECEPTION_FAILURE: 0, + interfaces.TRANSMISSION_FAILURE: 0, + interfaces.SERVICER_FAILURE: 0, + interfaces.SERVICED_FAILURE: 0, + } + + +def _synchronous_echo(output_consumer): + return stream_util.TransformingConsumer(lambda x: x, output_consumer) + + +class AsynchronousEcho(stream.Consumer): + """A stream.Consumer that echoes its input to another stream.Consumer.""" + + def __init__(self, output_consumer, pool): + self._lock = threading.Lock() + self._output_consumer = output_consumer + self._pool = pool + + self._queue = [] + self._spinning = False + + def _spin(self, value, complete): + while True: + if value: + if complete: + self._output_consumer.consume_and_terminate(value) + else: + self._output_consumer.consume(value) + elif complete: + self._output_consumer.terminate() + with self._lock: + if self._queue: + value, complete = self._queue.pop(0) + else: + self._spinning = False + return + + def consume(self, value): + with self._lock: + if self._spinning: + self._queue.append((value, False)) + else: + self._spinning = True + self._pool.submit(self._spin, value, False) + + def terminate(self): + with self._lock: + if self._spinning: + self._queue.append((None, True)) + else: + self._spinning = True + self._pool.submit(self._spin, None, True) + + def consume_and_terminate(self, value): + with self._lock: + if self._spinning: + self._queue.append((value, True)) + else: + self._spinning = True + self._pool.submit(self._spin, value, True) + + +class TestServicer(interfaces.Servicer): + """An interfaces.Servicer with instrumented for testing.""" + + def __init__(self, pool): + self._pool = pool + self.condition = threading.Condition() + self._released = False + + def service(self, name, context, output_consumer): + if name == SYNCHRONOUS_ECHO: + return _synchronous_echo(output_consumer) + elif name == ASYNCHRONOUS_ECHO: + return AsynchronousEcho(output_consumer, self._pool) + elif name == IMMEDIATE_FAILURE: + raise ValueError() + elif name == TRIGGERED_FAILURE: + raise NotImplementedError + elif name == WAIT_ON_CONDITION: + with self.condition: + while not self._released: + self.condition.wait() + return _synchronous_echo(output_consumer) + else: + raise NotImplementedError() + + def release(self): + with self.condition: + self._released = True + self.condition.notify_all() + + +class EasyServicedIngestor(interfaces.ServicedIngestor): + """A trivial implementation of interfaces.ServicedIngestor.""" + + def __init__(self, consumer): + self._consumer = consumer + + def consumer(self, operation_context): + """See interfaces.ServicedIngestor.consumer for specification.""" + return self._consumer + + +class FrontAndBackTest(object): + """A test suite usable against any joined Front and Back.""" + + # Pylint doesn't know that this is a unittest.TestCase mix-in. + # pylint: disable=invalid-name + + def testSimplestCall(self): + """Tests the absolute simplest call - a one-packet fire-and-forget.""" + self.front.operate( + SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT, + util.none_serviced_subscription(), 'test trace ID') + util.wait_for_idle(self.front) + self.assertEqual(1, self.front.operation_stats()[interfaces.COMPLETED]) + + # Assuming nothing really pathological (such as pauses on the order of + # SMALL_TIMEOUT interfering with this test) there are a two different ways + # the back could have experienced execution up to this point: + # (1) The packet is still either in the front waiting to be transmitted + # or is somewhere on the link between the front and the back. The back has + # no idea that this test is even happening. Calling wait_for_idle on it + # would do no good because in this case the back is idle and the call would + # return with the packet bound for it still in the front or on the link. + back_operation_stats = self.back.operation_stats() + first_back_possibility = EMPTY_OUTCOME_DICT + # (2) The packet arrived at the back and the back completed the operation. + second_back_possibility = dict(EMPTY_OUTCOME_DICT) + second_back_possibility[interfaces.COMPLETED] = 1 + self.assertIn( + back_operation_stats, (first_back_possibility, second_back_possibility)) + # It's true that if the packet had arrived at the back and the back had + # begun processing that wait_for_idle could hold test execution until the + # back completed the operation, but that doesn't really collapse the + # possibility space down to one solution. + + def testEntireEcho(self): + """Tests a very simple one-packet-each-way round-trip.""" + test_payload = 'test payload' + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + self.front.operate( + ASYNCHRONOUS_ECHO, test_payload, True, SMALL_TIMEOUT, subscription, + 'test trace ID') + + util.wait_for_idle(self.front) + util.wait_for_idle(self.back) + self.assertEqual(1, self.front.operation_stats()[interfaces.COMPLETED]) + self.assertEqual(1, self.back.operation_stats()[interfaces.COMPLETED]) + self.assertListEqual([(test_payload, True)], test_consumer.calls) + + def testBidirectionalStreamingEcho(self): + """Tests sending multiple packets each way.""" + test_payload_template = 'test_payload: %03d' + test_payloads = [test_payload_template % i for i in range(STREAM_LENGTH)] + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + operation = self.front.operate( + SYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, + 'test trace ID') + + for test_payload in test_payloads: + operation.consumer.consume(test_payload) + operation.consumer.terminate() + + util.wait_for_idle(self.front) + util.wait_for_idle(self.back) + self.assertEqual(1, self.front.operation_stats()[interfaces.COMPLETED]) + self.assertEqual(1, self.back.operation_stats()[interfaces.COMPLETED]) + self.assertListEqual(test_payloads, test_consumer.values()) + + def testCancellation(self): + """Tests cancelling a long-lived operation.""" + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + operation = self.front.operate( + ASYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, + 'test trace ID') + operation.cancel() + + util.wait_for_idle(self.front) + self.assertEqual(1, self.front.operation_stats()[interfaces.CANCELLED]) + util.wait_for_idle(self.back) + self.assertListEqual([], test_consumer.calls) + + # Assuming nothing really pathological (such as pauses on the order of + # SMALL_TIMEOUT interfering with this test) there are a two different ways + # the back could have experienced execution up to this point: + # (1) Both packets are still either in the front waiting to be transmitted + # or are somewhere on the link between the front and the back. The back has + # no idea that this test is even happening. Calling wait_for_idle on it + # would do no good because in this case the back is idle and the call would + # return with the packets bound for it still in the front or on the link. + back_operation_stats = self.back.operation_stats() + first_back_possibility = EMPTY_OUTCOME_DICT + # (2) Both packets arrived within SMALL_TIMEOUT of one another at the back. + # The back started processing based on the first packet and then stopped + # upon receiving the cancellation packet. + second_back_possibility = dict(EMPTY_OUTCOME_DICT) + second_back_possibility[interfaces.CANCELLED] = 1 + self.assertIn( + back_operation_stats, (first_back_possibility, second_back_possibility)) + + def testExpiration(self): + """Tests that operations time out.""" + timeout = TICK * 2 + allowance = TICK # How much extra time to + condition = threading.Condition() + test_payload = 'test payload' + subscription = util.termination_only_serviced_subscription() + start_time = time.time() + + outcome_cell = [None] + termination_time_cell = [None] + def termination_action(outcome): + with condition: + outcome_cell[0] = outcome + termination_time_cell[0] = time.time() + condition.notify() + + with condition: + operation = self.front.operate( + SYNCHRONOUS_ECHO, test_payload, False, timeout, subscription, + 'test trace ID') + operation.context.add_termination_callback(termination_action) + while outcome_cell[0] is None: + condition.wait() + + duration = termination_time_cell[0] - start_time + self.assertLessEqual(timeout, duration) + self.assertLess(duration, timeout + allowance) + self.assertEqual(interfaces.EXPIRED, outcome_cell[0]) + util.wait_for_idle(self.front) + self.assertEqual(1, self.front.operation_stats()[interfaces.EXPIRED]) + util.wait_for_idle(self.back) + self.assertLessEqual(1, self.back.operation_stats()[interfaces.EXPIRED]) diff --git a/src/python/_framework/base/packets/__init__.py b/src/python/_framework/base/packets/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_framework/base/packets/_cancellation.py b/src/python/_framework/base/packets/_cancellation.py new file mode 100644 index 00000000000..49172d1b974 --- /dev/null +++ b/src/python/_framework/base/packets/_cancellation.py @@ -0,0 +1,64 @@ +# Copyright 2015, 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. + +"""State and behavior for operation cancellation.""" + +from _framework.base.packets import _interfaces +from _framework.base.packets import packets + + +class CancellationManager(_interfaces.CancellationManager): + """An implementation of _interfaces.CancellationManager.""" + + def __init__( + self, lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def cancel(self): + """See _interfaces.CancellationManager.cancel for specification.""" + with self._lock: + self._termination_manager.abort(packets.Kind.CANCELLATION) + self._transmission_manager.abort(packets.Kind.CANCELLATION) + self._ingestion_manager.abort() + self._expiration_manager.abort() diff --git a/src/python/_framework/base/packets/_constants.py b/src/python/_framework/base/packets/_constants.py new file mode 100644 index 00000000000..8fbdc82782a --- /dev/null +++ b/src/python/_framework/base/packets/_constants.py @@ -0,0 +1,32 @@ +# Copyright 2015, 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. + +"""Private constants for the package.""" + +INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Base) internal error! :-(' diff --git a/src/python/_framework/base/packets/_context.py b/src/python/_framework/base/packets/_context.py new file mode 100644 index 00000000000..be390364b0a --- /dev/null +++ b/src/python/_framework/base/packets/_context.py @@ -0,0 +1,99 @@ +# Copyright 2015, 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. + +"""State and behavior for operation context.""" + +import time + +# _interfaces and packets are referenced from specification in this module. +from _framework.base import interfaces as base_interfaces +from _framework.base.packets import _interfaces # pylint: disable=unused-import +from _framework.base.packets import packets # pylint: disable=unused-import + + +class OperationContext(base_interfaces.OperationContext): + """An implementation of base_interfaces.OperationContext.""" + + def __init__( + self, lock, operation_id, local_failure, termination_manager, + transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + operation_id: An object identifying the operation. + local_failure: Whichever one of packets.Kind.SERVICED_FAILURE or + packets.Kind.SERVICER_FAILURE describes local failure of customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._local_failure = local_failure + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = None + self._expiration_manager = None + + self.operation_id = operation_id + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """Sets managers with which this OperationContext cooperates. + + Args: + ingestion_manager: The _interfaces.IngestionManager for the operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def is_active(self): + """See base_interfaces.OperationContext.is_active for specification.""" + with self._lock: + return self._termination_manager.is_active() + + def add_termination_callback(self, callback): + """See base_interfaces.OperationContext.add_termination_callback.""" + with self._lock: + self._termination_manager.add_callback(callback) + + def time_remaining(self): + """See interfaces.OperationContext.time_remaining for specification.""" + with self._lock: + deadline = self._expiration_manager.deadline() + return max(0.0, deadline - time.time()) + + def fail(self, exception): + """See interfaces.OperationContext.fail for specification.""" + with self._lock: + self._termination_manager.abort(self._local_failure) + self._transmission_manager.abort(self._local_failure) + self._ingestion_manager.abort() + self._expiration_manager.abort() diff --git a/src/python/_framework/base/packets/_emission.py b/src/python/_framework/base/packets/_emission.py new file mode 100644 index 00000000000..b4be5eb0ff9 --- /dev/null +++ b/src/python/_framework/base/packets/_emission.py @@ -0,0 +1,126 @@ +# Copyright 2015, 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. + +"""State and behavior for handling emitted values.""" + +# packets is referenced from specifications in this module. +from _framework.base.packets import _interfaces +from _framework.base.packets import packets # pylint: disable=unused-import + + +class _EmissionManager(_interfaces.EmissionManager): + """An implementation of _interfaces.EmissionManager.""" + + def __init__( + self, lock, failure_kind, termination_manager, transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + failure_kind: Whichever one of packets.Kind.SERVICED_FAILURE or + packets.Kind.SERVICER_FAILURE describes this object's methods being + called inappropriately by customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._failure_kind = failure_kind + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = None + self._expiration_manager = None + + self._emission_complete = False + + def set_ingestion_manager_and_expiration_manager( + self, ingestion_manager, expiration_manager): + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def _abort(self): + self._termination_manager.abort(self._failure_kind) + self._transmission_manager.abort(self._failure_kind) + self._ingestion_manager.abort() + self._expiration_manager.abort() + + def consume(self, value): + with self._lock: + if self._emission_complete: + self._abort() + else: + self._transmission_manager.inmit(value, False) + + def terminate(self): + with self._lock: + if not self._emission_complete: + self._termination_manager.emission_complete() + self._transmission_manager.inmit(None, True) + self._emission_complete = True + + def consume_and_terminate(self, value): + with self._lock: + if self._emission_complete: + self._abort() + else: + self._termination_manager.emission_complete() + self._transmission_manager.inmit(value, True) + self._emission_complete = True + + +def front_emission_manager(lock, termination_manager, transmission_manager): + """Creates an _interfaces.EmissionManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the operation. + + Returns: + An _interfaces.EmissionManager appropriate for front-side use. + """ + return _EmissionManager( + lock, packets.Kind.SERVICED_FAILURE, termination_manager, + transmission_manager) + + +def back_emission_manager(lock, termination_manager, transmission_manager): + """Creates an _interfaces.EmissionManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the operation. + + Returns: + An _interfaces.EmissionManager appropriate for back-side use. + """ + return _EmissionManager( + lock, packets.Kind.SERVICER_FAILURE, termination_manager, + transmission_manager) diff --git a/src/python/_framework/base/packets/_ends.py b/src/python/_framework/base/packets/_ends.py new file mode 100644 index 00000000000..baaf5cacf9a --- /dev/null +++ b/src/python/_framework/base/packets/_ends.py @@ -0,0 +1,408 @@ +# Copyright 2015, 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. + +"""Implementations of Fronts and Backs.""" + +import collections +import threading +import uuid + +# _interfaces and packets are referenced from specification in this module. +from _framework.base import interfaces as base_interfaces +from _framework.base.packets import _cancellation +from _framework.base.packets import _context +from _framework.base.packets import _emission +from _framework.base.packets import _expiration +from _framework.base.packets import _ingestion +from _framework.base.packets import _interfaces # pylint: disable=unused-import +from _framework.base.packets import _reception +from _framework.base.packets import _termination +from _framework.base.packets import _transmission +from _framework.base.packets import interfaces +from _framework.base.packets import packets # pylint: disable=unused-import +from _framework.foundation import callable_util + +_IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!' + +_OPERATION_OUTCOMES = ( + base_interfaces.COMPLETED, + base_interfaces.CANCELLED, + base_interfaces.EXPIRED, + base_interfaces.RECEPTION_FAILURE, + base_interfaces.TRANSMISSION_FAILURE, + base_interfaces.SERVICER_FAILURE, + base_interfaces.SERVICED_FAILURE, + ) + + +class _EasyOperation(base_interfaces.Operation): + """A trivial implementation of base_interfaces.Operation.""" + + def __init__(self, emission_manager, context, cancellation_manager): + """Constructor. + + Args: + emission_manager: The _interfaces.EmissionManager for the operation that + will accept values emitted by customer code. + context: The base_interfaces.OperationContext for use by the customer + during the operation. + cancellation_manager: The _interfaces.CancellationManager for the + operation. + """ + self.consumer = emission_manager + self.context = context + self._cancellation_manager = cancellation_manager + + def cancel(self): + self._cancellation_manager.cancel() + + +class _Endlette(object): + """Utility for stateful behavior common to Fronts and Backs.""" + + def __init__(self, pool): + """Constructor. + + Args: + pool: A thread pool to use when calling registered idle actions. + """ + self._lock = threading.Lock() + self._pool = pool + # Dictionary from operation IDs to ReceptionManager-or-None. A None value + # indicates an in-progress fire-and-forget operation for which the customer + # has chosen to ignore results. + self._operations = {} + self._stats = {outcome: 0 for outcome in _OPERATION_OUTCOMES} + self._idle_actions = [] + + def terminal_action(self, operation_id): + """Constructs the termination action for a single operation. + + Args: + operation_id: An operation ID. + + Returns: + A callable that takes an operation outcome for an argument to be used as + the termination action for the operation associated with the given + operation ID. + """ + def termination_action(outcome): + with self._lock: + self._stats[outcome] += 1 + self._operations.pop(operation_id, None) + if not self._operations: + for action in self._idle_actions: + self._pool.submit(callable_util.with_exceptions_logged( + action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) + self._idle_actions = [] + return termination_action + + def __enter__(self): + self._lock.acquire() + + def __exit__(self, exc_type, exc_val, exc_tb): + self._lock.release() + + def get_operation(self, operation_id): + return self._operations.get(operation_id, None) + + def add_operation(self, operation_id, operation_reception_manager): + self._operations[operation_id] = operation_reception_manager + + def operation_stats(self): + with self._lock: + return dict(self._stats) + + def add_idle_action(self, action): + with self._lock: + if self._operations: + self._idle_actions.append(action) + else: + self._pool.submit(callable_util.with_exceptions_logged( + action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) + + +class _FrontManagement( + collections.namedtuple( + '_FrontManagement', + ('reception', 'emission', 'operation', 'cancellation'))): + """Just a trivial helper class to bundle four fellow-traveling objects.""" + + +def _front_operate( + callback, work_pool, transmission_pool, utility_pool, + termination_action, operation_id, name, payload, complete, timeout, + subscription, trace_id): + """Constructs objects necessary for front-side operation management. + + Args: + callback: A callable that accepts packets.FrontToBackPackets and delivers + them to the other side of the operation. Execution of this callable may + take any arbitrary length of time. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + termination_action: A no-arg behavior to be called upon operation + completion. + operation_id: An object identifying the operation. + name: The name of the method being called during the operation. + payload: The first customer-significant value to be transmitted to the other + side. May be None if there is no such value or if the customer chose not + to pass it at operation invocation. + complete: A boolean indicating whether or not additional payloads will be + supplied by the customer. + timeout: A length of time in seconds to allow for the operation. + subscription: A base_interfaces.ServicedSubscription describing the + customer's interest in the results of the operation. + trace_id: A uuid.UUID identifying a set of related operations to which this + operation belongs. May be None. + + Returns: + A _FrontManagement object bundling together the + _interfaces.ReceptionManager, _interfaces.EmissionManager, + _context.OperationContext, and _interfaces.CancellationManager for the + operation. + """ + lock = threading.Lock() + with lock: + termination_manager = _termination.front_termination_manager( + work_pool, utility_pool, termination_action, subscription.category) + transmission_manager = _transmission.front_transmission_manager( + lock, transmission_pool, callback, operation_id, name, + subscription.category, trace_id, timeout, termination_manager) + operation_context = _context.OperationContext( + lock, operation_id, packets.Kind.SERVICED_FAILURE, + termination_manager, transmission_manager) + emission_manager = _emission.front_emission_manager( + lock, termination_manager, transmission_manager) + ingestion_manager = _ingestion.front_ingestion_manager( + lock, work_pool, subscription, termination_manager, + transmission_manager, operation_context) + expiration_manager = _expiration.front_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout) + reception_manager = _reception.front_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + cancellation_manager = _cancellation.CancellationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + + transmission_manager.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + operation_context.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + emission_manager.set_ingestion_manager_and_expiration_manager( + ingestion_manager, expiration_manager) + ingestion_manager.set_expiration_manager(expiration_manager) + + transmission_manager.inmit(payload, complete) + + returned_reception_manager = ( + None if subscription.category == base_interfaces.NONE + else reception_manager) + + return _FrontManagement( + returned_reception_manager, emission_manager, operation_context, + cancellation_manager) + + +class Front(interfaces.Front): + """An implementation of interfaces.Front.""" + + def __init__(self, work_pool, transmission_pool, utility_pool): + """Constructor. + + Args: + work_pool: A thread pool to be used for executing customer code. + transmission_pool: A thread pool to be used for transmitting values to + the other side of the operation. + utility_pool: A thread pool to be used for utility tasks. + """ + self._endlette = _Endlette(utility_pool) + self._work_pool = work_pool + self._transmission_pool = transmission_pool + self._utility_pool = utility_pool + self._callback = None + + self._operations = {} + + def join_rear_link(self, rear_link): + """See interfaces.ForeLink.join_rear_link for specification.""" + with self._endlette: + self._callback = rear_link.accept_front_to_back_ticket + + def operation_stats(self): + """See base_interfaces.End.operation_stats for specification.""" + return self._endlette.operation_stats() + + def add_idle_action(self, action): + """See base_interfaces.End.add_idle_action for specification.""" + self._endlette.add_idle_action(action) + + def operate( + self, name, payload, complete, timeout, subscription, trace_id): + """See base_interfaces.Front.operate for specification.""" + operation_id = uuid.uuid4() + with self._endlette: + management = _front_operate( + self._callback, self._work_pool, self._transmission_pool, + self._utility_pool, self._endlette.terminal_action(operation_id), + operation_id, name, payload, complete, timeout, subscription, + trace_id) + self._endlette.add_operation(operation_id, management.reception) + return _EasyOperation( + management.emission, management.operation, management.cancellation) + + def accept_back_to_front_ticket(self, ticket): + """See interfaces.End.act for specification.""" + with self._endlette: + reception_manager = self._endlette.get_operation(ticket.operation_id) + if reception_manager: + reception_manager.receive_packet(ticket) + + +def _back_operate( + servicer, callback, work_pool, transmission_pool, utility_pool, + termination_action, ticket, default_timeout, maximum_timeout): + """Constructs objects necessary for back-side operation management. + + Also begins back-side operation by feeding the first received ticket into the + constructed _interfaces.ReceptionManager. + + Args: + servicer: An interfaces.Servicer for servicing operations. + callback: A callable that accepts packets.BackToFrontPackets and delivers + them to the other side of the operation. Execution of this callable may + take any arbitrary length of time. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + termination_action: A no-arg behavior to be called upon operation + completion. + ticket: The first packets.FrontToBackPacket received for the operation. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + + Returns: + The _interfaces.ReceptionManager to be used for the operation. + """ + lock = threading.Lock() + with lock: + termination_manager = _termination.back_termination_manager( + work_pool, utility_pool, termination_action, ticket.subscription) + transmission_manager = _transmission.back_transmission_manager( + lock, transmission_pool, callback, ticket.operation_id, + termination_manager, ticket.subscription) + operation_context = _context.OperationContext( + lock, ticket.operation_id, packets.Kind.SERVICER_FAILURE, + termination_manager, transmission_manager) + emission_manager = _emission.back_emission_manager( + lock, termination_manager, transmission_manager) + ingestion_manager = _ingestion.back_ingestion_manager( + lock, work_pool, servicer, termination_manager, + transmission_manager, operation_context, emission_manager) + expiration_manager = _expiration.back_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + ticket.timeout, default_timeout, maximum_timeout) + reception_manager = _reception.back_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + + transmission_manager.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + operation_context.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + emission_manager.set_ingestion_manager_and_expiration_manager( + ingestion_manager, expiration_manager) + ingestion_manager.set_expiration_manager(expiration_manager) + + reception_manager.receive_packet(ticket) + + return reception_manager + + +class Back(interfaces.Back): + """An implementation of interfaces.Back.""" + + def __init__( + self, servicer, work_pool, transmission_pool, utility_pool, + default_timeout, maximum_timeout): + """Constructor. + + Args: + servicer: An interfaces.Servicer for servicing operations. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + """ + self._endlette = _Endlette(utility_pool) + self._servicer = servicer + self._work_pool = work_pool + self._transmission_pool = transmission_pool + self._utility_pool = utility_pool + self._default_timeout = default_timeout + self._maximum_timeout = maximum_timeout + self._callback = None + + def join_fore_link(self, fore_link): + """See interfaces.RearLink.join_fore_link for specification.""" + with self._endlette: + self._callback = fore_link.accept_back_to_front_ticket + + def accept_front_to_back_ticket(self, ticket): + """See interfaces.RearLink.accept_front_to_back_ticket for specification.""" + with self._endlette: + reception_manager = self._endlette.get_operation(ticket.operation_id) + if reception_manager is None: + reception_manager = _back_operate( + self._servicer, self._callback, self._work_pool, + self._transmission_pool, self._utility_pool, + self._endlette.terminal_action(ticket.operation_id), ticket, + self._default_timeout, self._maximum_timeout) + self._endlette.add_operation(ticket.operation_id, reception_manager) + else: + reception_manager.receive_packet(ticket) + + def operation_stats(self): + """See base_interfaces.End.operation_stats for specification.""" + return self._endlette.operation_stats() + + def add_idle_action(self, action): + """See base_interfaces.End.add_idle_action for specification.""" + self._endlette.add_idle_action(action) diff --git a/src/python/_framework/base/packets/_expiration.py b/src/python/_framework/base/packets/_expiration.py new file mode 100644 index 00000000000..772e15f08c8 --- /dev/null +++ b/src/python/_framework/base/packets/_expiration.py @@ -0,0 +1,158 @@ +# Copyright 2015, 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. + +"""State and behavior for operation expiration.""" + +import time + +from _framework.base.packets import _interfaces +from _framework.base.packets import packets +from _framework.foundation import later + + +class _ExpirationManager(_interfaces.ExpirationManager): + """An implementation of _interfaces.ExpirationManager.""" + + def __init__( + self, lock, termination_manager, transmission_manager, ingestion_manager, + commencement, timeout, maximum_timeout): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + commencement: The time in seconds since the epoch at which the operation + began. + timeout: A length of time in seconds to allow for the operation to run. + maximum_timeout: The maximum length of time in seconds to allow for the + operation to run despite what is requested via this object's + change_timout method. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._commencement = commencement + self._maximum_timeout = maximum_timeout + + self._timeout = timeout + self._deadline = commencement + timeout + self._index = None + self._future = None + + def _expire(self, index): + with self._lock: + if self._future is not None and index == self._index: + self._future = None + self._termination_manager.abort(packets.Kind.EXPIRATION) + self._transmission_manager.abort(packets.Kind.EXPIRATION) + self._ingestion_manager.abort() + + def start(self): + self._index = 0 + self._future = later.later(self._timeout, lambda: self._expire(0)) + + def change_timeout(self, timeout): + if self._future is not None and timeout != self._timeout: + self._future.cancel() + new_timeout = min(timeout, self._maximum_timeout) + new_index = self._index + 1 + self._timeout = new_timeout + self._deadline = self._commencement + new_timeout + self._index = new_index + delay = self._deadline - time.time() + self._future = later.later( + delay, lambda: self._expire(new_index)) + + def deadline(self): + return self._deadline + + def abort(self): + if self._future: + self._future.cancel() + self._future = None + self._deadline_index = None + + +def front_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout): + """Creates an _interfaces.ExpirationManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + timeout: A length of time in seconds to allow for the operation to run. + + Returns: + An _interfaces.ExpirationManager appropriate for front-side use. + """ + commencement = time.time() + expiration_manager = _ExpirationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + commencement, timeout, timeout) + expiration_manager.start() + return expiration_manager + + +def back_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout, default_timeout, maximum_timeout): + """Creates an _interfaces.ExpirationManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + timeout: A length of time in seconds to allow for the operation to run. May + be None in which case default_timeout will be used. + default_timeout: The default length of time in seconds to allow for the + operation to run if the front-side customer has not specified such a value + (or if the value they specified is not yet known). + maximum_timeout: The maximum length of time in seconds to allow for the + operation to run. + + Returns: + An _interfaces.ExpirationManager appropriate for back-side use. + """ + commencement = time.time() + expiration_manager = _ExpirationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + commencement, default_timeout if timeout is None else timeout, + maximum_timeout) + expiration_manager.start() + return expiration_manager diff --git a/src/python/_framework/base/packets/_ingestion.py b/src/python/_framework/base/packets/_ingestion.py new file mode 100644 index 00000000000..ad5ed4cadab --- /dev/null +++ b/src/python/_framework/base/packets/_ingestion.py @@ -0,0 +1,440 @@ +# Copyright 2015, 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. + +"""State and behavior for ingestion during an operation.""" + +import abc +import collections + +from _framework.base import exceptions +from _framework.base import interfaces +from _framework.base.packets import _constants +from _framework.base.packets import _interfaces +from _framework.base.packets import packets +from _framework.foundation import abandonment +from _framework.foundation import callable_util +from _framework.foundation import stream + +_CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE = 'Exception initializing ingestion!' +_CONSUME_EXCEPTION_LOG_MESSAGE = 'Exception during ingestion!' + + +class _ConsumerCreation(collections.namedtuple( + '_ConsumerCreation', ('consumer', 'remote_error', 'abandoned'))): + """A sum type for the outcome of ingestion initialization. + + Either consumer will be non-None, remote_error will be True, or abandoned will + be True. + + Attributes: + consumer: A stream.Consumer for ingesting payloads. + remote_error: A boolean indicating that the consumer could not be created + due to an error on the remote side of the operation. + abandoned: A boolean indicating that the consumer creation was abandoned. + """ + + +class _EmptyConsumer(stream.Consumer): + """A no-operative stream.Consumer that ignores all inputs and calls.""" + + def consume(self, value): + """See stream.Consumer.consume for specification.""" + + def terminate(self): + """See stream.Consumer.terminate for specification.""" + + def consume_and_terminate(self, value): + """See stream.Consumer.consume_and_terminate for specification.""" + + +class _ConsumerCreator(object): + """Common specification of different consumer-creating behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def create_consumer(self, requirement): + """Creates the stream.Consumer to which customer payloads will be delivered. + + Any exceptions raised by this method should be attributed to and treated as + defects in the serviced or servicer code called by this method. + + Args: + requirement: A value required by this _ConsumerCreator for consumer + creation. + + Returns: + A _ConsumerCreation describing the result of consumer creation. + """ + raise NotImplementedError() + + +class _FrontConsumerCreator(_ConsumerCreator): + """A _ConsumerCreator appropriate for front-side use.""" + + def __init__(self, subscription, operation_context): + """Constructor. + + Args: + subscription: The serviced's interfaces.ServicedSubscription for the + operation. + operation_context: The interfaces.OperationContext object for the + operation. + """ + self._subscription = subscription + self._operation_context = operation_context + + def create_consumer(self, requirement): + """See _ConsumerCreator.create_consumer for specification.""" + if self._subscription.category == interfaces.FULL: + try: + return _ConsumerCreation( + self._subscription.ingestor.consumer(self._operation_context), + False, False) + except abandonment.Abandoned: + return _ConsumerCreation(None, False, True) + else: + return _ConsumerCreation(_EmptyConsumer(), False, False) + + +class _BackConsumerCreator(_ConsumerCreator): + """A _ConsumerCreator appropriate for back-side use.""" + + def __init__(self, servicer, operation_context, emission_consumer): + """Constructor. + + Args: + servicer: The interfaces.Servicer that will service the operation. + operation_context: The interfaces.OperationContext object for the + operation. + emission_consumer: The stream.Consumer object to which payloads emitted + from the operation will be passed. + """ + self._servicer = servicer + self._operation_context = operation_context + self._emission_consumer = emission_consumer + + def create_consumer(self, requirement): + """See _ConsumerCreator.create_consumer for full specification. + + Args: + requirement: The name of the Servicer method to be called during this + operation. + + Returns: + A _ConsumerCreation describing the result of consumer creation. + """ + try: + return _ConsumerCreation( + self._servicer.service( + requirement, self._operation_context, self._emission_consumer), + False, False) + except exceptions.NoSuchMethodError: + return _ConsumerCreation(None, True, False) + except abandonment.Abandoned: + return _ConsumerCreation(None, False, True) + + +class _WrappedConsumer(object): + """Wraps a consumer to catch the exceptions that it is allowed to throw.""" + + def __init__(self, consumer): + """Constructor. + + Args: + consumer: A stream.Consumer that may raise abandonment.Abandoned from any + of its methods. + """ + self._consumer = consumer + + def moar(self, payload, complete): + """Makes progress with the wrapped consumer. + + This method catches all exceptions allowed to be thrown by the wrapped + consumer. Any exceptions raised by this method should be blamed on the + customer-supplied consumer. + + Args: + payload: A customer-significant payload object. May be None only if + complete is True. + complete: Whether or not the end of the payload sequence has been reached. + May be False only if payload is not None. + + Returns: + True if the wrapped consumer made progress or False if the wrapped + consumer raised abandonment.Abandoned to indicate its abandonment of + progress. + """ + try: + if payload: + if complete: + self._consumer.consume_and_terminate(payload) + else: + self._consumer.consume(payload) + else: + self._consumer.terminate() + return True + except abandonment.Abandoned: + return False + + +class _IngestionManager(_interfaces.IngestionManager): + """An implementation of _interfaces.IngestionManager.""" + + def __init__( + self, lock, pool, consumer_creator, failure_kind, termination_manager, + transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + consumer_creator: A _ConsumerCreator wrapping the portion of customer code + that when called returns the stream.Consumer with which the customer + code will ingest payload values. + failure_kind: Whichever one of packets.Kind.SERVICED_FAILURE or + packets.Kind.SERVICER_FAILURE describes local failure of customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._pool = pool + self._consumer_creator = consumer_creator + self._failure_kind = failure_kind + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = None + + self._wrapped_ingestion_consumer = None + self._pending_ingestion = [] + self._ingestion_complete = False + self._processing = False + + def set_expiration_manager(self, expiration_manager): + self._expiration_manager = expiration_manager + + def _abort_internal_only(self): + self._wrapped_ingestion_consumer = None + self._pending_ingestion = None + + def _abort_and_notify(self, outcome): + self._abort_internal_only() + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.abort() + + def _next(self): + """Computes the next step for ingestion. + + Returns: + A payload, complete, continue triplet indicating what payload (if any) is + available to feed into customer code, whether or not the sequence of + payloads has terminated, and whether or not there is anything + immediately actionable to call customer code to do. + """ + if self._pending_ingestion is None: + return None, False, False + elif self._pending_ingestion: + payload = self._pending_ingestion.pop(0) + complete = self._ingestion_complete and not self._pending_ingestion + return payload, complete, True + elif self._ingestion_complete: + return None, True, True + else: + return None, False, False + + def _process(self, wrapped_ingestion_consumer, payload, complete): + """A method to call to execute customer code. + + This object's lock must *not* be held when calling this method. + + Args: + wrapped_ingestion_consumer: The _WrappedConsumer with which to pass + payloads to customer code. + payload: A customer payload. May be None only if complete is True. + complete: Whether or not the sequence of payloads to pass to the customer + has concluded. + """ + while True: + consumption_outcome = callable_util.call_logging_exceptions( + wrapped_ingestion_consumer.moar, _CONSUME_EXCEPTION_LOG_MESSAGE, + payload, complete) + if consumption_outcome.exception is None: + if consumption_outcome.return_value: + with self._lock: + if complete: + self._pending_ingestion = None + self._termination_manager.ingestion_complete() + return + else: + payload, complete, moar = self._next() + if not moar: + self._processing = False + return + else: + with self._lock: + if self._pending_ingestion is not None: + self._abort_and_notify(self._failure_kind) + self._processing = False + return + else: + with self._lock: + self._abort_and_notify(self._failure_kind) + self._processing = False + return + + def start(self, requirement): + if self._pending_ingestion is not None: + def initialize(): + consumer_creation_outcome = callable_util.call_logging_exceptions( + self._consumer_creator.create_consumer, + _CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE, requirement) + if consumer_creation_outcome.return_value is None: + with self._lock: + self._abort_and_notify(self._failure_kind) + self._processing = False + elif consumer_creation_outcome.return_value.remote_error: + with self._lock: + self._abort_and_notify(packets.Kind.RECEPTION_FAILURE) + self._processing = False + elif consumer_creation_outcome.return_value.abandoned: + with self._lock: + if self._pending_ingestion is not None: + self._abort_and_notify(self._failure_kind) + self._processing = False + else: + wrapped_ingestion_consumer = _WrappedConsumer( + consumer_creation_outcome.return_value.consumer) + with self._lock: + self._wrapped_ingestion_consumer = wrapped_ingestion_consumer + payload, complete, moar = self._next() + if not moar: + self._processing = False + return + + self._process(wrapped_ingestion_consumer, payload, complete) + + self._pool.submit( + callable_util.with_exceptions_logged( + initialize, _constants.INTERNAL_ERROR_LOG_MESSAGE)) + self._processing = True + + def consume(self, payload): + if self._ingestion_complete: + self._abort_and_notify(self._failure_kind) + elif self._pending_ingestion is not None: + if self._processing: + self._pending_ingestion.append(payload) + else: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, payload, False) + self._processing = True + + def terminate(self): + if self._ingestion_complete: + self._abort_and_notify(self._failure_kind) + else: + self._ingestion_complete = True + if self._pending_ingestion is not None and not self._processing: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, None, True) + self._processing = True + + def consume_and_terminate(self, payload): + if self._ingestion_complete: + self._abort_and_notify(self._failure_kind) + else: + self._ingestion_complete = True + if self._pending_ingestion is not None: + if self._processing: + self._pending_ingestion.append(payload) + else: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, payload, True) + self._processing = True + + def abort(self): + """See _interfaces.IngestionManager.abort for specification.""" + self._abort_internal_only() + + +def front_ingestion_manager( + lock, pool, subscription, termination_manager, transmission_manager, + operation_context): + """Creates an IngestionManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + subscription: A base_interfaces.ServicedSubscription indicating the + customer's interest in the results of the operation. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + operation_context: A base_interfaces.OperationContext for the operation. + + Returns: + An IngestionManager appropriate for front-side use. + """ + ingestion_manager = _IngestionManager( + lock, pool, _FrontConsumerCreator(subscription, operation_context), + packets.Kind.SERVICED_FAILURE, termination_manager, transmission_manager) + ingestion_manager.start(None) + return ingestion_manager + + +def back_ingestion_manager( + lock, pool, servicer, termination_manager, transmission_manager, + operation_context, emission_consumer): + """Creates an IngestionManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + servicer: A base_interfaces.Servicer for servicing the operation. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + operation_context: A base_interfaces.OperationContext for the operation. + emission_consumer: The _interfaces.EmissionConsumer for the operation. + + Returns: + An IngestionManager appropriate for back-side use. + """ + ingestion_manager = _IngestionManager( + lock, pool, _BackConsumerCreator( + servicer, operation_context, emission_consumer), + packets.Kind.SERVICER_FAILURE, termination_manager, transmission_manager) + return ingestion_manager diff --git a/src/python/_framework/base/packets/_interfaces.py b/src/python/_framework/base/packets/_interfaces.py new file mode 100644 index 00000000000..5f6c0593d0a --- /dev/null +++ b/src/python/_framework/base/packets/_interfaces.py @@ -0,0 +1,269 @@ +# Copyright 2015, 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. + +"""Package-internal interfaces.""" + +import abc + +# base_interfaces and packets are referenced from specification in this module. +from _framework.base import interfaces as base_interfaces # pylint: disable=unused-import +from _framework.base.packets import packets # pylint: disable=unused-import +from _framework.foundation import stream + + +class TerminationManager(object): + """An object responsible for handling the termination of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Reports whether or not the operation is active. + + Returns: + True if the operation is active or False if the operation has terminated. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_callback(self, callback): + """Registers a callback to be called on operation termination. + + If the operation has already terminated, the callback will be called + immediately. + + Args: + callback: A callable that will be passed one of base_interfaces.COMPLETED, + base_interfaces.CANCELLED, base_interfaces.EXPIRED, + base_interfaces.RECEPTION_FAILURE, base_interfaces.TRANSMISSION_FAILURE, + base_interfaces.SERVICER_FAILURE, or base_interfaces.SERVICED_FAILURE. + """ + raise NotImplementedError() + + @abc.abstractmethod + def emission_complete(self): + """Indicates that emissions from customer code have completed.""" + raise NotImplementedError() + + @abc.abstractmethod + def transmission_complete(self): + """Indicates that transmissions to the remote end are complete.""" + raise NotImplementedError() + + @abc.abstractmethod + def ingestion_complete(self): + """Indicates that customer code ingestion of received values is complete.""" + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, kind): + """Indicates that the operation must abort for the indicated reason. + + Args: + kind: A value of packets.Kind indicating operation abortion. + """ + raise NotImplementedError() + + +class TransmissionManager(object): + """A manager responsible for transmitting to the other end of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def inmit(self, emission, complete): + """Accepts a value for transmission to the other end of the operation. + + Args: + emission: A value of some significance to the customer to be transmitted + to the other end of the operation. May be None only if complete is True. + complete: A boolean that if True indicates that customer code has emitted + all values it intends to emit. + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, kind): + """Indicates that the operation has aborted for the indicated reason. + + Args: + kind: A value of packets.Kind indicating operation abortion. + """ + raise NotImplementedError() + + +class EmissionManager(stream.Consumer): + """A manager of values emitted by customer code.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_ingestion_manager_and_expiration_manager( + self, ingestion_manager, expiration_manager): + """Sets two other objects with which this EmissionManager will cooperate. + + Args: + ingestion_manager: The IngestionManager for the operation. + expiration_manager: The ExpirationManager for the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume(self, value): + """Accepts a value emitted by customer code. + + This method should only be called by customer code. + + Args: + value: Any value of significance to the customer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates that no more values will be emitted by customer code. + + This method should only be called by customer code. + + Implementations of this method may be idempotent and forgive customer code + calling this method more than once. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, value): + """Accepts the last value emitted by customer code. + + This method should only be called by customer code. + + Args: + value: Any value of significance to the customer. + """ + raise NotImplementedError() + + +class IngestionManager(stream.Consumer): + """A manager responsible for executing customer code.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_expiration_manager(self, expiration_manager): + """Sets the ExpirationManager with which this object will cooperate.""" + + @abc.abstractmethod + def start(self, requirement): + """Commences execution of customer code. + + Args: + requirement: Some value unavailable at the time of this object's + construction that is required to begin executing customer code. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume(self, payload): + """Accepts a customer-significant value to be supplied to customer code. + + Args: + payload: Some customer-significant value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates the end of values to be supplied to customer code.""" + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, payload): + """Accepts the last value to be supplied to customer code. + + Args: + payload: Some customer-significant value (and the last such value). + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self): + """Indicates to this manager that the operation has aborted.""" + raise NotImplementedError() + + +class ExpirationManager(object): + """A manager responsible for aborting the operation if it runs out of time.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def change_timeout(self, timeout): + """Changes the timeout allotted for the operation. + + Operation duration is always measure from the beginning of the operation; + calling this method changes the operation's allotted time to timeout total + seconds, not timeout seconds from the time of this method call. + + Args: + timeout: A length of time in seconds to allow for the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deadline(self): + """Returns the time until which the operation is allowed to run. + + Returns: + The time (seconds since the epoch) at which the operation will expire. + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self): + """Indicates to this manager that the operation has aborted.""" + raise NotImplementedError() + + +class ReceptionManager(object): + """A manager responsible for receiving packets from the other end.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def receive_packet(self, packet): + """Handle a packet from the other side of the operation. + + Args: + packet: A packets.BackToFrontPacket or packets.FrontToBackPacket + appropriate to this end of the operation and this object. + """ + raise NotImplementedError() + + +class CancellationManager(object): + """A manager of operation cancellation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Cancels the operation.""" + raise NotImplementedError() diff --git a/src/python/_framework/base/packets/_reception.py b/src/python/_framework/base/packets/_reception.py new file mode 100644 index 00000000000..a2a3823d28c --- /dev/null +++ b/src/python/_framework/base/packets/_reception.py @@ -0,0 +1,394 @@ +# Copyright 2015, 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. + +"""State and behavior for packet reception.""" + +import abc + +from _framework.base.packets import _interfaces +from _framework.base.packets import packets + + +class _Receiver(object): + """Common specification of different packet-handling behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def abort_if_abortive(self, packet): + """Aborts the operation if the packet is abortive. + + Args: + packet: A just-arrived packet. + + Returns: + A boolean indicating whether or not this Receiver aborted the operation + based on the packet. + """ + raise NotImplementedError() + + @abc.abstractmethod + def receive(self, packet): + """Handles a just-arrived packet. + + Args: + packet: A just-arrived packet. + + Returns: + A boolean indicating whether or not the packet was terminal (i.e. whether + or not non-abortive packets are legal after this one). + """ + raise NotImplementedError() + + @abc.abstractmethod + def reception_failure(self): + """Aborts the operation with an indication of reception failure.""" + raise NotImplementedError() + + +def _abort( + category, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Indicates abortion with the given category to the given managers.""" + termination_manager.abort(category) + transmission_manager.abort(category) + ingestion_manager.abort() + expiration_manager.abort() + + +def _abort_if_abortive( + packet, abortive, termination_manager, transmission_manager, + ingestion_manager, expiration_manager): + """Determines a packet's being abortive and if so aborts the operation. + + Args: + packet: A just-arrived packet. + abortive: A callable that takes a packet and returns an operation category + indicating that the operation should be aborted or None indicating that + the operation should not be aborted. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + True if the operation was aborted; False otherwise. + """ + abort_category = abortive(packet) + if abort_category is None: + return False + else: + _abort( + abort_category, termination_manager, transmission_manager, + ingestion_manager, expiration_manager) + return True + + +def _reception_failure( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Aborts the operation with an indication of reception failure.""" + _abort( + packets.Kind.RECEPTION_FAILURE, termination_manager, transmission_manager, + ingestion_manager, expiration_manager) + + +class _BackReceiver(_Receiver): + """Packet-handling specific to the back side of an operation.""" + + def __init__( + self, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + """ + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + self._first_packet_seen = False + self._last_packet_seen = False + + def _abortive(self, packet): + """Determines whether or not (and if so, how) a packet is abortive. + + Args: + packet: A just-arrived packet. + + Returns: + One of packets.Kind.CANCELLATION, packets.Kind.SERVICED_FAILURE, or + packets.Kind.RECEPTION_FAILURE, indicating that the packet is abortive + and how, or None, indicating that the packet is not abortive. + """ + if packet.kind is packets.Kind.CANCELLATION: + return packets.Kind.CANCELLATION + elif packet.kind is packets.Kind.EXPIRATION: + return packets.Kind.EXPIRATION + elif packet.kind is packets.Kind.SERVICED_FAILURE: + return packets.Kind.SERVICED_FAILURE + elif packet.kind is packets.Kind.RECEPTION_FAILURE: + return packets.Kind.SERVICED_FAILURE + elif (packet.kind in (packets.Kind.COMMENCEMENT, packets.Kind.ENTIRE) and + self._first_packet_seen): + return packets.Kind.RECEPTION_FAILURE + elif self._last_packet_seen: + return packets.Kind.RECEPTION_FAILURE + else: + return None + + def abort_if_abortive(self, packet): + """See _Receiver.abort_if_abortive for specification.""" + return _abort_if_abortive( + packet, self._abortive, self._termination_manager, + self._transmission_manager, self._ingestion_manager, + self._expiration_manager) + + def receive(self, packet): + """See _Receiver.receive for specification.""" + if packet.timeout is not None: + self._expiration_manager.change_timeout(packet.timeout) + + if packet.kind is packets.Kind.COMMENCEMENT: + self._first_packet_seen = True + self._ingestion_manager.start(packet.name) + if packet.payload is not None: + self._ingestion_manager.consume(packet.payload) + elif packet.kind is packets.Kind.CONTINUATION: + self._ingestion_manager.consume(packet.payload) + elif packet.kind is packets.Kind.COMPLETION: + self._last_packet_seen = True + if packet.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(packet.payload) + else: + self._first_packet_seen = True + self._last_packet_seen = True + self._ingestion_manager.start(packet.name) + if packet.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(packet.payload) + + def reception_failure(self): + """See _Receiver.reception_failure for specification.""" + _reception_failure( + self._termination_manager, self._transmission_manager, + self._ingestion_manager, self._expiration_manager) + + +class _FrontReceiver(_Receiver): + """Packet-handling specific to the front side of an operation.""" + + def __init__( + self, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + """ + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + self._last_packet_seen = False + + def _abortive(self, packet): + """Determines whether or not (and if so, how) a packet is abortive. + + Args: + packet: A just-arrived packet. + + Returns: + One of packets.Kind.EXPIRATION, packets.Kind.SERVICER_FAILURE, or + packets.Kind.RECEPTION_FAILURE, indicating that the packet is abortive + and how, or None, indicating that the packet is not abortive. + """ + if packet.kind is packets.Kind.EXPIRATION: + return packets.Kind.EXPIRATION + elif packet.kind is packets.Kind.SERVICER_FAILURE: + return packets.Kind.SERVICER_FAILURE + elif packet.kind is packets.Kind.RECEPTION_FAILURE: + return packets.Kind.SERVICER_FAILURE + elif self._last_packet_seen: + return packets.Kind.RECEPTION_FAILURE + else: + return None + + def abort_if_abortive(self, packet): + """See _Receiver.abort_if_abortive for specification.""" + return _abort_if_abortive( + packet, self._abortive, self._termination_manager, + self._transmission_manager, self._ingestion_manager, + self._expiration_manager) + + def receive(self, packet): + """See _Receiver.receive for specification.""" + if packet.kind is packets.Kind.CONTINUATION: + self._ingestion_manager.consume(packet.payload) + elif packet.kind is packets.Kind.COMPLETION: + self._last_packet_seen = True + if packet.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(packet.payload) + + def reception_failure(self): + """See _Receiver.reception_failure for specification.""" + _reception_failure( + self._termination_manager, self._transmission_manager, + self._ingestion_manager, self._expiration_manager) + + +class _ReceptionManager(_interfaces.ReceptionManager): + """A ReceptionManager based around a _Receiver passed to it.""" + + def __init__(self, lock, receiver): + """Constructor. + + Args: + lock: The operation-servicing-wide lock object. + receiver: A _Receiver responsible for handling received packets. + """ + self._lock = lock + self._receiver = receiver + + self._lowest_unseen_sequence_number = 0 + self._out_of_sequence_packets = {} + self._completed_sequence_number = None + self._aborted = False + + def _sequence_failure(self, packet): + """Determines a just-arrived packet's sequential legitimacy. + + Args: + packet: A just-arrived packet. + + Returns: + True if the packet is sequentially legitimate; False otherwise. + """ + if packet.sequence_number < self._lowest_unseen_sequence_number: + return True + elif packet.sequence_number in self._out_of_sequence_packets: + return True + elif (self._completed_sequence_number is not None and + self._completed_sequence_number <= packet.sequence_number): + return True + else: + return False + + def _process(self, packet): + """Process those packets ready to be processed. + + Args: + packet: A just-arrived packet the sequence number of which matches this + _ReceptionManager's _lowest_unseen_sequence_number field. + """ + while True: + completed = self._receiver.receive(packet) + if completed: + self._out_of_sequence_packets.clear() + self._completed_sequence_number = packet.sequence_number + self._lowest_unseen_sequence_number = packet.sequence_number + 1 + return + else: + next_packet = self._out_of_sequence_packets.pop( + packet.sequence_number + 1, None) + if next_packet is None: + self._lowest_unseen_sequence_number = packet.sequence_number + 1 + return + else: + packet = next_packet + + def receive_packet(self, packet): + """See _interfaces.ReceptionManager.receive_packet for specification.""" + with self._lock: + if self._aborted: + return + elif self._sequence_failure(packet): + self._receiver.reception_failure() + self._aborted = True + elif self._receiver.abort_if_abortive(packet): + self._aborted = True + elif packet.sequence_number == self._lowest_unseen_sequence_number: + self._process(packet) + else: + self._out_of_sequence_packets[packet.sequence_number] = packet + + +def front_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Creates a _interfaces.ReceptionManager for front-side use. + + Args: + lock: The operation-servicing-wide lock object. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + A _interfaces.ReceptionManager appropriate for front-side use. + """ + return _ReceptionManager( + lock, _FrontReceiver( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager)) + + +def back_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Creates a _interfaces.ReceptionManager for back-side use. + + Args: + lock: The operation-servicing-wide lock object. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + A _interfaces.ReceptionManager appropriate for back-side use. + """ + return _ReceptionManager( + lock, _BackReceiver( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager)) diff --git a/src/python/_framework/base/packets/_termination.py b/src/python/_framework/base/packets/_termination.py new file mode 100644 index 00000000000..d586c2167b8 --- /dev/null +++ b/src/python/_framework/base/packets/_termination.py @@ -0,0 +1,201 @@ +# Copyright 2015, 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. + +"""State and behavior for operation termination.""" + +from _framework.base import interfaces +from _framework.base.packets import _constants +from _framework.base.packets import _interfaces +from _framework.base.packets import packets +from _framework.foundation import callable_util + +_CALLBACK_EXCEPTION_LOG_MESSAGE = 'Exception calling termination callback!' + +# TODO(nathaniel): enum module. +_EMISSION = 'emission' +_TRANSMISSION = 'transmission' +_INGESTION = 'ingestion' + +_FRONT_NOT_LISTENING_REQUIREMENTS = (_TRANSMISSION,) +_BACK_NOT_LISTENING_REQUIREMENTS = (_EMISSION, _INGESTION,) +_LISTENING_REQUIREMENTS = (_TRANSMISSION, _INGESTION,) + +_KINDS_TO_OUTCOMES = { + packets.Kind.COMPLETION: interfaces.COMPLETED, + packets.Kind.CANCELLATION: interfaces.CANCELLED, + packets.Kind.EXPIRATION: interfaces.EXPIRED, + packets.Kind.RECEPTION_FAILURE: interfaces.RECEPTION_FAILURE, + packets.Kind.TRANSMISSION_FAILURE: interfaces.TRANSMISSION_FAILURE, + packets.Kind.SERVICER_FAILURE: interfaces.SERVICER_FAILURE, + packets.Kind.SERVICED_FAILURE: interfaces.SERVICED_FAILURE, + } + + +class _TerminationManager(_interfaces.TerminationManager): + """An implementation of _interfaces.TerminationManager.""" + + def __init__( + self, work_pool, utility_pool, action, requirements, local_failure): + """Constructor. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + requirements: A combination of _EMISSION, _TRANSMISSION, and _INGESTION + identifying what must finish for the operation to be considered + completed. + local_failure: A packets.Kind specifying what constitutes local failure of + customer work. + """ + self._work_pool = work_pool + self._utility_pool = utility_pool + self._action = action + self._local_failure = local_failure + self._has_locally_failed = False + + self._outstanding_requirements = set(requirements) + self._kind = None + self._callbacks = [] + + def _terminate(self, kind): + """Terminates the operation. + + Args: + kind: One of packets.Kind.COMPLETION, packets.Kind.CANCELLATION, + packets.Kind.EXPIRATION, packets.Kind.RECEPTION_FAILURE, + packets.Kind.TRANSMISSION_FAILURE, packets.Kind.SERVICER_FAILURE, or + packets.Kind.SERVICED_FAILURE. + """ + self._outstanding_requirements = None + callbacks = list(self._callbacks) + self._callbacks = None + self._kind = kind + outcome = _KINDS_TO_OUTCOMES[kind] + + act = callable_util.with_exceptions_logged( + self._action, _constants.INTERNAL_ERROR_LOG_MESSAGE) + + if self._has_locally_failed: + self._utility_pool.submit(act, outcome) + else: + def call_callbacks_and_act(callbacks, outcome): + for callback in callbacks: + callback_outcome = callable_util.call_logging_exceptions( + callback, _CALLBACK_EXCEPTION_LOG_MESSAGE, outcome) + if callback_outcome.exception is not None: + outcome = _KINDS_TO_OUTCOMES[self._local_failure] + break + self._utility_pool.submit(act, outcome) + + self._work_pool.submit(callable_util.with_exceptions_logged( + call_callbacks_and_act, + _constants.INTERNAL_ERROR_LOG_MESSAGE), + callbacks, outcome) + + def is_active(self): + """See _interfaces.TerminationManager.is_active for specification.""" + return self._outstanding_requirements is not None + + def add_callback(self, callback): + """See _interfaces.TerminationManager.add_callback for specification.""" + if not self._has_locally_failed: + if self._outstanding_requirements is None: + self._work_pool.submit( + callable_util.with_exceptions_logged( + callback, _CALLBACK_EXCEPTION_LOG_MESSAGE), + _KINDS_TO_OUTCOMES[self._kind]) + else: + self._callbacks.append(callback) + + def emission_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_EMISSION) + if not self._outstanding_requirements: + self._terminate(packets.Kind.COMPLETION) + + def transmission_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_TRANSMISSION) + if not self._outstanding_requirements: + self._terminate(packets.Kind.COMPLETION) + + def ingestion_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_INGESTION) + if not self._outstanding_requirements: + self._terminate(packets.Kind.COMPLETION) + + def abort(self, kind): + """See _interfaces.TerminationManager.abort for specification.""" + if kind == self._local_failure: + self._has_failed_locally = True + if self._outstanding_requirements is not None: + self._terminate(kind) + + +def front_termination_manager(work_pool, utility_pool, action, subscription): + """Creates a TerminationManager appropriate for front-side use. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + subscription: One of interfaces.FULL, interfaces.termination_only, or + interfaces.NONE. + + Returns: + A TerminationManager appropriate for front-side use. + """ + return _TerminationManager( + work_pool, utility_pool, action, + _FRONT_NOT_LISTENING_REQUIREMENTS if subscription == interfaces.NONE else + _LISTENING_REQUIREMENTS, packets.Kind.SERVICED_FAILURE) + + +def back_termination_manager(work_pool, utility_pool, action, subscription): + """Creates a TerminationManager appropriate for back-side use. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + subscription: One of interfaces.FULL, interfaces.termination_only, or + interfaces.NONE. + + Returns: + A TerminationManager appropriate for back-side use. + """ + return _TerminationManager( + work_pool, utility_pool, action, + _BACK_NOT_LISTENING_REQUIREMENTS if subscription == interfaces.NONE else + _LISTENING_REQUIREMENTS, packets.Kind.SERVICER_FAILURE) diff --git a/src/python/_framework/base/packets/_transmission.py b/src/python/_framework/base/packets/_transmission.py new file mode 100644 index 00000000000..006128774d5 --- /dev/null +++ b/src/python/_framework/base/packets/_transmission.py @@ -0,0 +1,393 @@ +# Copyright 2015, 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. + +"""State and behavior for packet transmission during an operation.""" + +import abc + +from _framework.base import interfaces +from _framework.base.packets import _constants +from _framework.base.packets import _interfaces +from _framework.base.packets import packets +from _framework.foundation import callable_util + +_TRANSMISSION_EXCEPTION_LOG_MESSAGE = 'Exception during transmission!' + +_FRONT_TO_BACK_NO_TRANSMISSION_KINDS = ( + packets.Kind.SERVICER_FAILURE, + ) +_BACK_TO_FRONT_NO_TRANSMISSION_KINDS = ( + packets.Kind.CANCELLATION, + packets.Kind.SERVICED_FAILURE, + ) + + +class _Packetizer(object): + """Common specification of different packet-creating behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def packetize(self, operation_id, sequence_number, payload, complete): + """Creates a packet indicating ordinary operation progress. + + Args: + operation_id: The operation ID for the current operation. + sequence_number: A sequence number for the packet. + payload: A customer payload object. May be None if sequence_number is + zero or complete is true. + complete: A boolean indicating whether or not the packet should describe + itself as (but for a later indication of operation abortion) the last + packet to be sent. + + Returns: + An object of an appropriate type suitable for transmission to the other + side of the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def packetize_abortion(self, operation_id, sequence_number, kind): + """Creates a packet indicating that the operation is aborted. + + Args: + operation_id: The operation ID for the current operation. + sequence_number: A sequence number for the packet. + kind: One of the values of packets.Kind indicating operational abortion. + + Returns: + An object of an appropriate type suitable for transmission to the other + side of the operation, or None if transmission is not appropriate for + the given kind. + """ + raise NotImplementedError() + + +class _FrontPacketizer(_Packetizer): + """Front-side packet-creating behavior.""" + + def __init__(self, name, subscription, trace_id, timeout): + """Constructor. + + Args: + name: The name of the operation. + subscription: One of interfaces.FULL, interfaces.TERMINATION_ONLY, or + interfaces.NONE describing the interest the front has in packets sent + from the back. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + timeout: A length of time in seconds to allow for the entire operation. + """ + self._name = name + self._subscription = subscription + self._trace_id = trace_id + self._timeout = timeout + + def packetize(self, operation_id, sequence_number, payload, complete): + """See _Packetizer.packetize for specification.""" + if sequence_number: + return packets.FrontToBackPacket( + operation_id, sequence_number, + packets.Kind.COMPLETION if complete else packets.Kind.CONTINUATION, + self._name, self._subscription, self._trace_id, payload, + self._timeout) + else: + return packets.FrontToBackPacket( + operation_id, 0, + packets.Kind.ENTIRE if complete else packets.Kind.COMMENCEMENT, + self._name, self._subscription, self._trace_id, payload, + self._timeout) + + def packetize_abortion(self, operation_id, sequence_number, kind): + """See _Packetizer.packetize_abortion for specification.""" + if kind in _FRONT_TO_BACK_NO_TRANSMISSION_KINDS: + return None + else: + return packets.FrontToBackPacket( + operation_id, sequence_number, kind, None, None, None, None, None) + + +class _BackPacketizer(_Packetizer): + """Back-side packet-creating behavior.""" + + def packetize(self, operation_id, sequence_number, payload, complete): + """See _Packetizer.packetize for specification.""" + return packets.BackToFrontPacket( + operation_id, sequence_number, + packets.Kind.COMPLETION if complete else packets.Kind.CONTINUATION, + payload) + + def packetize_abortion(self, operation_id, sequence_number, kind): + """See _Packetizer.packetize_abortion for specification.""" + if kind in _BACK_TO_FRONT_NO_TRANSMISSION_KINDS: + return None + else: + return packets.BackToFrontPacket( + operation_id, sequence_number, kind, None) + + +class TransmissionManager(_interfaces.TransmissionManager): + """A _interfaces.TransmissionManager on which other managers may be set.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """Sets two of the other managers with which this manager may interact. + + Args: + ingestion_manager: The _interfaces.IngestionManager associated with the + current operation. + expiration_manager: The _interfaces.ExpirationManager associated with the + current operation. + """ + raise NotImplementedError() + + +class _EmptyTransmissionManager(TransmissionManager): + """A completely no-operative _interfaces.TransmissionManager.""" + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """See overriden method for specification.""" + + def inmit(self, emission, complete): + """See _interfaces.TransmissionManager.inmit for specification.""" + + def abort(self, category): + """See _interfaces.TransmissionManager.abort for specification.""" + + +class _TransmittingTransmissionManager(TransmissionManager): + """A TransmissionManager implementation that sends packets.""" + + def __init__( + self, lock, pool, callback, operation_id, packetizer, + termination_manager): + """Constructor. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting packets will be + performed. + callback: A callable that accepts packets and sends them to the other side + of the operation. + operation_id: The operation's ID. + packetizer: A _Packetizer for packet creation. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + """ + self._lock = lock + self._pool = pool + self._callback = callback + self._operation_id = operation_id + self._packetizer = packetizer + self._termination_manager = termination_manager + self._ingestion_manager = None + self._expiration_manager = None + + self._emissions = [] + self._emission_complete = False + self._kind = None + self._lowest_unused_sequence_number = 0 + self._transmitting = False + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """See overridden method for specification.""" + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def _lead_packet(self, emission, complete): + """Creates a packet suitable for leading off the transmission loop. + + Args: + emission: A customer payload object to be sent to the other side of the + operation. + complete: Whether or not the sequence of customer payloads ends with + the passed object. + + Returns: + A packet with which to lead off the transmission loop. + """ + sequence_number = self._lowest_unused_sequence_number + self._lowest_unused_sequence_number += 1 + return self._packetizer.packetize( + self._operation_id, sequence_number, emission, complete) + + def _abortive_response_packet(self, kind): + """Creates a packet indicating operation abortion. + + Args: + kind: One of the values of packets.Kind indicating operational abortion. + + Returns: + A packet indicating operation abortion. + """ + packet = self._packetizer.packetize_abortion( + self._operation_id, self._lowest_unused_sequence_number, kind) + if packet is None: + return None + else: + self._lowest_unused_sequence_number += 1 + return packet + + def _next_packet(self): + """Creates the next packet to be sent to the other side of the operation. + + Returns: + A (completed, packet) tuple comprised of a boolean indicating whether or + not the sequence of packets has completed normally and a packet to send + to the other side if the sequence of packets hasn't completed. The tuple + will never have both a True first element and a non-None second element. + """ + if self._emissions is None: + return False, None + elif self._kind is None: + if self._emissions: + payload = self._emissions.pop(0) + complete = self._emission_complete and not self._emissions + sequence_number = self._lowest_unused_sequence_number + self._lowest_unused_sequence_number += 1 + return complete, self._packetizer.packetize( + self._operation_id, sequence_number, payload, complete) + else: + return self._emission_complete, None + else: + packet = self._abortive_response_packet(self._kind) + self._emissions = None + return False, None if packet is None else packet + + def _transmit(self, packet): + """Commences the transmission loop sending packets. + + Args: + packet: A packet to be sent to the other side of the operation. + """ + def transmit(packet): + while True: + transmission_outcome = callable_util.call_logging_exceptions( + self._callback, _TRANSMISSION_EXCEPTION_LOG_MESSAGE, packet) + if transmission_outcome.exception is None: + with self._lock: + complete, packet = self._next_packet() + if packet is None: + if complete: + self._termination_manager.transmission_complete() + self._transmitting = False + return + else: + with self._lock: + self._emissions = None + self._termination_manager.abort(packets.Kind.TRANSMISSION_FAILURE) + self._ingestion_manager.abort() + self._expiration_manager.abort() + self._transmitting = False + return + + self._pool.submit(callable_util.with_exceptions_logged( + transmit, _constants.INTERNAL_ERROR_LOG_MESSAGE), packet) + self._transmitting = True + + def inmit(self, emission, complete): + """See _interfaces.TransmissionManager.inmit for specification.""" + if self._emissions is not None and self._kind is None: + self._emission_complete = complete + if self._transmitting: + self._emissions.append(emission) + else: + self._transmit(self._lead_packet(emission, complete)) + + def abort(self, kind): + """See _interfaces.TransmissionManager.abort for specification.""" + if self._emissions is not None and self._kind is None: + self._kind = kind + if not self._transmitting: + packet = self._abortive_response_packet(kind) + self._emissions = None + if packet is not None: + self._transmit(packet) + + +def front_transmission_manager( + lock, pool, callback, operation_id, name, subscription, trace_id, timeout, + termination_manager): + """Creates a TransmissionManager appropriate for front-side use. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting packets will be + performed. + callback: A callable that accepts packets and sends them to the other side + of the operation. + operation_id: The operation's ID. + name: The name of the operation. + subscription: One of interfaces.FULL, interfaces.TERMINATION_ONLY, or + interfaces.NONE describing the interest the front has in packets sent + from the back. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + timeout: A length of time in seconds to allow for the entire operation. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + + Returns: + A TransmissionManager appropriate for front-side use. + """ + return _TransmittingTransmissionManager( + lock, pool, callback, operation_id, _FrontPacketizer( + name, subscription, trace_id, timeout), + termination_manager) + + +def back_transmission_manager( + lock, pool, callback, operation_id, termination_manager, subscription): + """Creates a TransmissionManager appropriate for back-side use. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting packets will be + performed. + callback: A callable that accepts packets and sends them to the other side + of the operation. + operation_id: The operation's ID. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + subscription: One of interfaces.FULL, interfaces.TERMINATION_ONLY, or + interfaces.NONE describing the interest the front has in packets sent from + the back. + + Returns: + A TransmissionManager appropriate for back-side use. + """ + if subscription == interfaces.NONE: + return _EmptyTransmissionManager() + else: + return _TransmittingTransmissionManager( + lock, pool, callback, operation_id, _BackPacketizer(), + termination_manager) diff --git a/src/python/_framework/base/packets/implementations.py b/src/python/_framework/base/packets/implementations.py new file mode 100644 index 00000000000..2f07054d4d2 --- /dev/null +++ b/src/python/_framework/base/packets/implementations.py @@ -0,0 +1,77 @@ +# Copyright 2015, 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. + +"""Entry points into the packet-exchange-based implementation the base layer.""" + +# interfaces is referenced from specification in this module. +from _framework.base.packets import _ends +from _framework.base.packets import interfaces # pylint: disable=unused-import + + +def front(work_pool, transmission_pool, utility_pool): + """Factory function for creating interfaces.Fronts. + + Args: + work_pool: A thread pool to be used for doing work within the created Front + object. + transmission_pool: A thread pool to be used within the created Front object + for transmitting values to some Back object. + utility_pool: A thread pool to be used within the created Front object for + utility tasks. + + Returns: + An interfaces.Front. + """ + return _ends.Front(work_pool, transmission_pool, utility_pool) + + +def back( + servicer, work_pool, transmission_pool, utility_pool, default_timeout, + maximum_timeout): + """Factory function for creating interfaces.Backs. + + Args: + servicer: An interfaces.Servicer for servicing operations. + work_pool: A thread pool to be used for doing work within the created Back + object. + transmission_pool: A thread pool to be used within the created Back object + for transmitting values to some Front object. + utility_pool: A thread pool to be used within the created Back object for + utility tasks. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + + Returns: + An interfaces.Back. + """ + return _ends.Back( + servicer, work_pool, transmission_pool, utility_pool, default_timeout, + maximum_timeout) diff --git a/src/python/_framework/base/packets/implementations_test.py b/src/python/_framework/base/packets/implementations_test.py new file mode 100644 index 00000000000..8bb53531760 --- /dev/null +++ b/src/python/_framework/base/packets/implementations_test.py @@ -0,0 +1,80 @@ +# Copyright 2015, 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. + +"""Tests for _framework.base.packets.implementations.""" + +import unittest + +from _framework.base import interfaces_test +from _framework.base import util +from _framework.base.packets import implementations +from _framework.foundation import logging_pool + +POOL_MAX_WORKERS = 100 +DEFAULT_TIMEOUT = 30 +MAXIMUM_TIMEOUT = 60 + + +class ImplementationsTest( + interfaces_test.FrontAndBackTest, unittest.TestCase): + + def setUp(self): + self.memory_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_work_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_work_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.test_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.test_servicer = interfaces_test.TestServicer(self.test_pool) + self.front = implementations.front( + self.front_work_pool, self.front_transmission_pool, + self.front_utility_pool) + self.back = implementations.back( + self.test_servicer, self.back_work_pool, self.back_transmission_pool, + self.back_utility_pool, DEFAULT_TIMEOUT, MAXIMUM_TIMEOUT) + self.front.join_rear_link(self.back) + self.back.join_fore_link(self.front) + + def tearDown(self): + util.wait_for_idle(self.back) + util.wait_for_idle(self.front) + self.memory_transmission_pool.shutdown(wait=True) + self.front_work_pool.shutdown(wait=True) + self.front_transmission_pool.shutdown(wait=True) + self.front_utility_pool.shutdown(wait=True) + self.back_work_pool.shutdown(wait=True) + self.back_transmission_pool.shutdown(wait=True) + self.back_utility_pool.shutdown(wait=True) + self.test_pool.shutdown(wait=True) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/_framework/base/packets/in_memory.py b/src/python/_framework/base/packets/in_memory.py new file mode 100644 index 00000000000..17daf3acf7b --- /dev/null +++ b/src/python/_framework/base/packets/in_memory.py @@ -0,0 +1,108 @@ +# Copyright 2015, 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. + +"""Entry points into the packet-exchange-based implementation the base layer.""" + +import threading + +from _framework.base.packets import _constants +from _framework.base.packets import interfaces +from _framework.foundation import callable_util + + +class _Serializer(object): + """A utility for serializing values that may arrive concurrently.""" + + def __init__(self, pool): + self._lock = threading.Lock() + self._pool = pool + self._sink = None + self._spinning = False + self._values = [] + + def _spin(self, sink, value): + while True: + sink(value) + with self._lock: + if self._sink is None or not self._values: + self._spinning = False + return + else: + sink, value = self._sink, self._values.pop(0) + + def set_sink(self, sink): + with self._lock: + self._sink = sink + if sink is not None and self._values and not self._spinning: + self._spinning = True + self._pool.submit( + callable_util.with_exceptions_logged( + self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), + sink, self._values.pop(0)) + + def add_value(self, value): + with self._lock: + if self._sink and not self._spinning: + self._spinning = True + self._pool.submit( + callable_util.with_exceptions_logged( + self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._sink, value) + else: + self._values.append(value) + + +class Link(interfaces.ForeLink, interfaces.RearLink): + """A trivial implementation of interfaces.ForeLink and interfaces.RearLink.""" + + def __init__(self, pool): + """Constructor. + + Args: + pool: A thread pool to be used for serializing ticket exchange in each + direction. + """ + self._front_to_back = _Serializer(pool) + self._back_to_front = _Serializer(pool) + + def join_fore_link(self, fore_link): + """See interfaces.RearLink.join_fore_link for specification.""" + self._back_to_front.set_sink(fore_link.accept_back_to_front_ticket) + + def join_rear_link(self, rear_link): + """See interfaces.ForeLink.join_rear_link for specification.""" + self._front_to_back.set_sink(rear_link.accept_front_to_back_ticket) + + def accept_front_to_back_ticket(self, ticket): + """See interfaces.ForeLink.accept_front_to_back_ticket for specification.""" + self._front_to_back.add_value(ticket) + + def accept_back_to_front_ticket(self, ticket): + """See interfaces.RearLink.accept_back_to_front_ticket for specification.""" + self._back_to_front.add_value(ticket) diff --git a/src/python/_framework/base/packets/interfaces.py b/src/python/_framework/base/packets/interfaces.py new file mode 100644 index 00000000000..99f9e877726 --- /dev/null +++ b/src/python/_framework/base/packets/interfaces.py @@ -0,0 +1,84 @@ +# Copyright 2015, 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. + +"""Interfaces defined and used by the base layer of RPC Framework.""" + +import abc + +# packets is referenced from specifications in this module. +from _framework.base import interfaces +from _framework.base.packets import packets # pylint: disable=unused-import + + +class ForeLink(object): + """Accepts back-to-front tickets and emits front-to-back tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_back_to_front_ticket(self, ticket): + """Accept a packets.BackToFrontPacket. + + Args: + ticket: Any packets.BackToFrontPacket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_rear_link(self, rear_link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() + + +class RearLink(object): + """Accepts front-to-back tickets and emits back-to-front tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_front_to_back_ticket(self, ticket): + """Accepts a packets.FrontToBackPacket. + + Args: + ticket: Any packets.FrontToBackPacket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_fore_link(self, fore_link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() + + +class Front(ForeLink, interfaces.Front): + """Clientish objects that operate by sending and receiving tickets.""" + __metaclass__ = abc.ABCMeta + + +class Back(RearLink, interfaces.Back): + """Serverish objects that operate by sending and receiving tickets.""" + __metaclass__ = abc.ABCMeta diff --git a/src/python/_framework/base/packets/null.py b/src/python/_framework/base/packets/null.py new file mode 100644 index 00000000000..9b40a005052 --- /dev/null +++ b/src/python/_framework/base/packets/null.py @@ -0,0 +1,56 @@ +# Copyright 2015, 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. + +"""Null links that ignore tickets passed to them.""" + +from _framework.base.packets import interfaces + + +class _NullForeLink(interfaces.ForeLink): + """A do-nothing ForeLink.""" + + def accept_back_to_front_ticket(self, ticket): + pass + + def join_rear_link(self, rear_link): + raise NotImplementedError() + + +class _NullRearLink(interfaces.RearLink): + """A do-nothing RearLink.""" + + def accept_front_to_back_ticket(self, ticket): + pass + + def join_fore_link(self, fore_link): + raise NotImplementedError() + + +NULL_FORE_LINK = _NullForeLink() +NULL_REAR_LINK = _NullRearLink() diff --git a/src/python/_framework/base/packets/packets.py b/src/python/_framework/base/packets/packets.py new file mode 100644 index 00000000000..1315ca650e1 --- /dev/null +++ b/src/python/_framework/base/packets/packets.py @@ -0,0 +1,112 @@ +# Copyright 2015, 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. + +"""Packets used between fronts and backs.""" + +import collections +import enum + +# interfaces is referenced from specifications in this module. +from _framework.base import interfaces # pylint: disable=unused-import + + +@enum.unique +class Kind(enum.Enum): + """Identifies the overall kind of a ticket.""" + + COMMENCEMENT = 'commencement' + CONTINUATION = 'continuation' + COMPLETION = 'completion' + ENTIRE = 'entire' + CANCELLATION = 'cancellation' + EXPIRATION = 'expiration' + SERVICER_FAILURE = 'servicer failure' + SERVICED_FAILURE = 'serviced failure' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + + +class FrontToBackPacket( + collections.namedtuple( + 'FrontToBackPacket', + ['operation_id', 'sequence_number', 'kind', 'name', 'subscription', + 'trace_id', 'payload', 'timeout'])): + """A sum type for all values sent from a front to a back. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + packet's place among all the packets sent from front to back for this + particular operation. Must be zero if kind is Kind.COMMENCEMENT or + Kind.ENTIRE. Must be positive for any other kind. + kind: One of Kind.COMMENCEMENT, Kind.CONTINUATION, Kind.COMPLETION, + Kind.ENTIRE, Kind.CANCELLATION, Kind.EXPIRATION, Kind.SERVICED_FAILURE, + Kind.RECEPTION_FAILURE, or Kind.TRANSMISSION_FAILURE. + name: The name of an operation. Must be present if kind is Kind.COMMENCEMENT + or Kind.ENTIRE. Must be None for any other kind. + subscription: One of interfaces.FULL, interfaces.TERMINATION_ONLY, or + interfaces.NONE describing the interest the front has in packets sent from + the back. Must be present if kind is Kind.COMMENCEMENT or Kind.ENTIRE. + Must be None for any other kind. + trace_id: A uuid.UUID identifying a set of related operations to which this + operation belongs. May be None. + payload: A customer payload object. Must be present if kind is + Kind.CONTINUATION. Must be None if kind is Kind.CANCELLATION. May be None + for any other kind. + timeout: An optional length of time (measured from the beginning of the + operation) to allow for the entire operation. If None, a default value on + the back will be used. If present and excessively large, the back may + limit the operation to a smaller duration of its choice. May be present + for any ticket kind; setting a value on a later ticket allows fronts + to request time extensions (or even time reductions!) on in-progress + operations. + """ + + +class BackToFrontPacket( + collections.namedtuple( + 'BackToFrontPacket', + ['operation_id', 'sequence_number', 'kind', 'payload'])): + """A sum type for all values sent from a back to a front. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + packet's place among all the packets sent from back to front for this + particular operation. + kind: One of Kind.CONTINUATION, Kind.COMPLETION, Kind.EXPIRATION, + Kind.SERVICER_FAILURE, Kind.RECEPTION_FAILURE, or + Kind.TRANSMISSION_FAILURE. + payload: A customer payload object. Must be present if kind is + Kind.CONTINUATION. May be None if kind is Kind.COMPLETION. Must be None if + kind is Kind.EXPIRATION, Kind.SERVICER_FAILURE, Kind.RECEPTION_FAILURE, or + Kind.TRANSMISSION_FAILURE. + """ diff --git a/src/python/_framework/base/util.py b/src/python/_framework/base/util.py new file mode 100644 index 00000000000..6bbd18a59a8 --- /dev/null +++ b/src/python/_framework/base/util.py @@ -0,0 +1,91 @@ +# Copyright 2015, 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. + +"""Utilities helpful for working with the base layer of RPC Framework.""" + +import collections +import threading + +from _framework.base import interfaces + + +class _ServicedSubscription( + collections.namedtuple('_ServicedSubscription', ['category', 'ingestor']), + interfaces.ServicedSubscription): + """See interfaces.ServicedSubscription for specification.""" + +_NONE_SUBSCRIPTION = _ServicedSubscription(interfaces.NONE, None) +_TERMINATION_ONLY_SUBSCRIPTION = _ServicedSubscription( + interfaces.TERMINATION_ONLY, None) + + +def none_serviced_subscription(): + """Creates a "none" interfaces.ServicedSubscription object. + + Returns: + An interfaces.ServicedSubscription indicating no subscription to an + operation's results (such as would be the case for a fire-and-forget + operation invocation). + """ + return _NONE_SUBSCRIPTION + + +def termination_only_serviced_subscription(): + """Creates a "termination only" interfaces.ServicedSubscription object. + + Returns: + An interfaces.ServicedSubscription indicating that the front-side customer + is interested only in the overall termination outcome of the operation + (such as completion or expiration) and would ignore the actual results of + the operation. + """ + return _TERMINATION_ONLY_SUBSCRIPTION + + +def full_serviced_subscription(ingestor): + """Creates a "full" interfaces.ServicedSubscription object. + + Args: + ingestor: A ServicedIngestor. + + Returns: + A ServicedSubscription object indicating a full subscription. + """ + return _ServicedSubscription(interfaces.FULL, ingestor) + + +def wait_for_idle(end): + """Waits for an interfaces.End to complete all operations. + + Args: + end: Any interfaces.End. + """ + event = threading.Event() + end.add_idle_action(event.set) + event.wait() From f06fd4c4dd3b21fba6592e6458529e47cbf63393 Mon Sep 17 00:00:00 2001 From: Donna Dionne Date: Fri, 23 Jan 2015 15:59:10 -0800 Subject: [PATCH 119/143] Adding Docker support for cpp --- tools/dockerfile/grpc_cxx/Dockerfile | 13 ++++++++++--- tools/gce_setup/grpc_docker.sh | 12 ++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/tools/dockerfile/grpc_cxx/Dockerfile b/tools/dockerfile/grpc_cxx/Dockerfile index ea3a1dba8f1..8d8a1cf3ac6 100644 --- a/tools/dockerfile/grpc_cxx/Dockerfile +++ b/tools/dockerfile/grpc_cxx/Dockerfile @@ -1,6 +1,8 @@ # Dockerfile for gRPC C++ FROM grpc/base +RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev + # Get the source from GitHub RUN git clone git@github.com:google/grpc.git /var/local/git/grpc RUN cd /var/local/git/grpc && \ @@ -12,7 +14,12 @@ RUN cd /var/local/git/grpc/third_party/protobuf && \ ./autogen.sh && \ ./configure --prefix=/usr && \ make -j12 && make check && make install && make clean -RUN make install -C /var/local/git/grpc +#RUN make install -C /var/local/git/grpc + +RUN cd /var/local/git/grpc && ls \ + && make clean \ + && make gens/test/cpp/util/messages.pb.cc \ + && make interop_client \ + && make interop_server -# Define the default command. -CMD ["bash"] +CMD ["/var/local/git/grpc/bins/opt/interop_server", "--enable_ssl", "--port=8010"] diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index e61c83b36f0..145685305c3 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -676,5 +676,17 @@ grpc_interop_gen_php_cmd() { echo $the_cmd } +# constructs the full dockerized cpp interop test cmd. +# +# +# call-seq: +# flags= .... # generic flags to include the command +# cmd=$($grpc_gen_test_cmd $flags) +grpc_interop_gen_cxx_cmd() { + local cmd_prefix="sudo docker run grpc/cxx"; + local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl"; + local the_cmd="$cmd_prefix $test_script $@"; + echo $the_cmd +} # TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs From aed5642d00aa164f2789eab2314f16bd0248ad07 Mon Sep 17 00:00:00 2001 From: Donna Dionne Date: Fri, 23 Jan 2015 16:04:29 -0800 Subject: [PATCH 120/143] Adding docker cpp support removing commented out line --- tools/dockerfile/grpc_cxx/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/dockerfile/grpc_cxx/Dockerfile b/tools/dockerfile/grpc_cxx/Dockerfile index 8d8a1cf3ac6..141a20a8815 100644 --- a/tools/dockerfile/grpc_cxx/Dockerfile +++ b/tools/dockerfile/grpc_cxx/Dockerfile @@ -14,7 +14,6 @@ RUN cd /var/local/git/grpc/third_party/protobuf && \ ./autogen.sh && \ ./configure --prefix=/usr && \ make -j12 && make check && make install && make clean -#RUN make install -C /var/local/git/grpc RUN cd /var/local/git/grpc && ls \ && make clean \ From b6b0486829c6c685e92aa8001b9e001f133a1a0d Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 11:22:28 -0800 Subject: [PATCH 121/143] Remove use of printf --- src/core/surface/call.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/surface/call.c b/src/core/surface/call.c index bb5aa41f11b..06474e9bb8e 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -823,7 +823,7 @@ grpc_call_error grpc_call_start_write_status(grpc_call *call, { grpc_mdelem *md; char buffer[32]; - sprintf(buffer, "%d", status); + gpr_ltoa(status, buffer); md = grpc_mdelem_from_strings(call->metadata_context, "grpc-status", buffer); From 8f3bda2ad28676f0638ad11c26c95af1db144a6b Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 11:27:42 -0800 Subject: [PATCH 122/143] Remove use of sprintf --- src/core/channel/client_channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index f6078aa8080..c315dd6c1e0 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -425,7 +425,7 @@ static void init_channel_elem(grpc_channel_element *elem, chand->transport_setup_initiated = 0; chand->args = grpc_channel_args_copy(args); - sprintf(temp, "%d", GRPC_STATUS_CANCELLED); + gpr_ltoa(GRPC_STATUS_CANCELLED, temp); chand->cancel_status = grpc_mdelem_from_strings(metadata_context, "grpc-status", temp); } From 0e5b306a5f2acf7afe27a1634a1ce70ba763d037 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 11:30:16 -0800 Subject: [PATCH 123/143] Remove use of sprintf --- src/core/transport/chttp2_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 6dfffa1fc50..36358089ec5 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -1015,7 +1015,7 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id, s->cancelled = 1; stream_list_join(t, s, CANCELLED); - sprintf(buffer, "%d", local_status); + gpr_ltoa(local_status, buffer); grpc_sopb_add_metadata( &s->parser.incoming_sopb, grpc_mdelem_from_strings(t->metadata_context, "grpc-status", buffer)); From c46afa6d02b731dd54aa3bba46ea5ee69045b458 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:00:00 -0800 Subject: [PATCH 124/143] Remove uses of sprintf --- src/core/channel/connected_channel.c | 38 +++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index cfa76f9e488..47c0ed3b882 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -384,23 +384,25 @@ static void recv_batch(void *user_data, grpc_transport *transport, case GRPC_OP_BEGIN_MESSAGE: /* can't begin a message when we're still reading a message */ if (calld->reading_message) { - char message[128]; - sprintf(message, - "Message terminated early; read %d bytes, expected %d", - (int)calld->incoming_message.length, - (int)calld->incoming_message_length); + char *message = NULL; + gpr_asprintf(&message, + "Message terminated early; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); recv_error(chand, calld, __LINE__, message); + gpr_free(message); return; } /* stash away parameters, and prepare for incoming slices */ length = stream_op->data.begin_message.length; if (length > calld->max_message_length) { - char message[128]; - sprintf( - message, + char *message = NULL; + gpr_asprintf( + &message, "Maximum message length of %d exceeded by a message of length %d", calld->max_message_length, length); recv_error(chand, calld, __LINE__, message); + gpr_free(message); } else if (length > 0) { calld->reading_message = 1; calld->incoming_message_length = length; @@ -423,12 +425,13 @@ static void recv_batch(void *user_data, grpc_transport *transport, gpr_slice_buffer_add(&calld->incoming_message, stream_op->data.slice); if (calld->incoming_message.length > calld->incoming_message_length) { /* if we got too many bytes, complain */ - char message[128]; - sprintf(message, - "Receiving message overflow; read %d bytes, expected %d", - (int)calld->incoming_message.length, - (int)calld->incoming_message_length); + char *message = NULL; + gpr_asprintf(&message, + "Receiving message overflow; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); recv_error(chand, calld, __LINE__, message); + gpr_free(message); return; } else if (calld->incoming_message.length == calld->incoming_message_length) { @@ -441,10 +444,11 @@ static void recv_batch(void *user_data, grpc_transport *transport, final_state == GRPC_STREAM_CLOSED)) { calld->got_read_close = 1; if (calld->reading_message) { - char message[128]; - sprintf(message, "Last message truncated; read %d bytes, expected %d", - (int)calld->incoming_message.length, - (int)calld->incoming_message_length); + char *message = NULL; + gpr_asprintf(&message, + "Last message truncated; read %d bytes, expected %d", + (int)calld->incoming_message.length, + (int)calld->incoming_message_length); recv_error(chand, calld, __LINE__, message); } call_op.type = GRPC_RECV_HALF_CLOSE; From b4fb262d9daf081b6b669d30cf65ef998ece5463 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:02:30 -0800 Subject: [PATCH 125/143] Remove uses of sprintf --- src/core/security/credentials.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index db73c01c1de..b5ff42b337d 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -157,7 +157,7 @@ static void ssl_server_destroy(grpc_server_credentials *creds) { if (c->config.pem_private_keys[i] != NULL) { gpr_free(c->config.pem_private_keys[i]); } - if (c->config.pem_cert_chains[i]!= NULL) { + if (c->config.pem_cert_chains[i] != NULL) { gpr_free(c->config.pem_cert_chains[i]); } } @@ -354,7 +354,6 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( cJSON *access_token = NULL; cJSON *token_type = NULL; cJSON *expires_in = NULL; - size_t new_access_token_size = 0; json = cJSON_Parse(null_terminated_body); if (json == NULL) { gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body); @@ -384,12 +383,8 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( status = GRPC_CREDENTIALS_ERROR; goto end; } - new_access_token_size = strlen(token_type->valuestring) + 1 + - strlen(access_token->valuestring) + 1; - new_access_token = gpr_malloc(new_access_token_size); - /* C89 does not have snprintf :(. */ - sprintf(new_access_token, "%s %s", token_type->valuestring, - access_token->valuestring); + gpr_asprintf(&new_access_token, "%s %s", token_type->valuestring, + access_token->valuestring); token_lifetime->tv_sec = expires_in->valueint; token_lifetime->tv_nsec = 0; if (*token_elem != NULL) grpc_mdelem_unref(*token_elem); From 170a60a60e770696efa982899096c2e677035174 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:04:45 -0800 Subject: [PATCH 126/143] Remove uses of sprintf --- src/core/security/credentials.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index b5ff42b337d..2f75556e7bd 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -534,9 +534,7 @@ static void service_account_fetch_oauth2( response_cb(metadata_req, &response); return; } - body = gpr_malloc(strlen(GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX) + - strlen(jwt) + 1); - sprintf(body, "%s%s", GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX, jwt); + gpr_asprintf(&body, "%s%s", GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX, jwt); memset(&request, 0, sizeof(grpc_httpcli_request)); request.host = GRPC_SERVICE_ACCOUNT_HOST; request.path = GRPC_SERVICE_ACCOUNT_TOKEN_PATH; From aade4ed5a0e5e1c3d751baeff61c2ac5819b6446 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 13:09:21 -0800 Subject: [PATCH 127/143] Remove uses of sprintf --- src/core/surface/completion_queue.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c index e1cc114cda6..2bf31c50a8f 100644 --- a/src/core/surface/completion_queue.c +++ b/src/core/surface/completion_queue.c @@ -396,12 +396,13 @@ void grpc_event_finish(grpc_event *base) { void grpc_cq_dump_pending_ops(grpc_completion_queue *cc) { #ifndef NDEBUG - char tmp[256]; + char tmp[GRPC_COMPLETION_DO_NOT_USE * (1 + GPR_LTOA_MIN_BUFSIZE)]; char *p = tmp; int i; for (i = 0; i < GRPC_COMPLETION_DO_NOT_USE; i++) { - p += sprintf(p, " %d", (int)cc->pending_op_count[i]); + *p++ = ' '; + p += gpr_ltoa(cc->pending_op_count[i], p); } gpr_log(GPR_INFO, "pending ops:%s", tmp); From e93084a7b32c76ebd07190c49c1b76897dd1ef29 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 23 Jan 2015 14:47:07 -0800 Subject: [PATCH 128/143] Use symbolic constant --- src/core/channel/client_channel.c | 2 +- src/core/surface/call.c | 2 +- src/core/transport/chttp2_transport.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index c315dd6c1e0..f9b42db419d 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -410,7 +410,7 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_mdctx *metadata_context, int is_first, int is_last) { channel_data *chand = elem->channel_data; - char temp[16]; + char temp[GPR_LTOA_MIN_BUFSIZE]; GPR_ASSERT(!is_first); GPR_ASSERT(is_last); diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 06474e9bb8e..14d990df6a1 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -822,7 +822,7 @@ grpc_call_error grpc_call_start_write_status(grpc_call *call, /* always send status */ { grpc_mdelem *md; - char buffer[32]; + char buffer[GPR_LTOA_MIN_BUFSIZE]; gpr_ltoa(status, buffer); md = grpc_mdelem_from_strings(call->metadata_context, "grpc-status", buffer); diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 36358089ec5..531a53b9848 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -1002,7 +1002,7 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id, grpc_chttp2_error_code error_code, int send_rst) { int had_outgoing; - char buffer[32]; + char buffer[GPR_LTOA_MIN_BUFSIZE]; if (s) { /* clear out any unreported input & output: nobody cares anymore */ From 1de8ddb224b4fe1c856722ec6d18c9076b802736 Mon Sep 17 00:00:00 2001 From: Yang Gao Date: Fri, 23 Jan 2015 16:07:48 -0800 Subject: [PATCH 129/143] move proto files in dependency order --- build.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.json b/build.json index cacbfe2ae50..1884ed22cb8 100644 --- a/build.json +++ b/build.json @@ -405,9 +405,9 @@ "build": "private", "language": "c++", "src": [ + "test/cpp/util/messages.proto", "test/cpp/util/echo.proto", "test/cpp/util/echo_duplicate.proto", - "test/cpp/util/messages.proto", "test/cpp/end2end/async_test_server.cc", "test/cpp/util/create_test_channel.cc" ] From ed3ed701ce6ffb8aae181c7f362dcce6a89c00bd Mon Sep 17 00:00:00 2001 From: Yang Gao Date: Fri, 23 Jan 2015 16:09:48 -0800 Subject: [PATCH 130/143] run generate_projects.sh since I changed build.json --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 1d1aff2a6a6..dff032fa65c 100644 --- a/Makefile +++ b/Makefile @@ -2133,9 +2133,9 @@ objs/$(CONFIG)/src/cpp/util/time.o: LIBGRPC++_TEST_UTIL_SRC = \ + gens/test/cpp/util/messages.pb.cc \ gens/test/cpp/util/echo.pb.cc \ gens/test/cpp/util/echo_duplicate.pb.cc \ - gens/test/cpp/util/messages.pb.cc \ test/cpp/end2end/async_test_server.cc \ test/cpp/util/create_test_channel.cc \ @@ -2152,9 +2152,9 @@ libs/$(CONFIG)/libgrpc++_test_util.a: openssl_dep_error else ifneq ($(OPENSSL_DEP),) +test/cpp/util/messages.proto: $(OPENSSL_DEP) test/cpp/util/echo.proto: $(OPENSSL_DEP) test/cpp/util/echo_duplicate.proto: $(OPENSSL_DEP) -test/cpp/util/messages.proto: $(OPENSSL_DEP) test/cpp/end2end/async_test_server.cc: $(OPENSSL_DEP) test/cpp/util/create_test_channel.cc: $(OPENSSL_DEP) endif @@ -2183,8 +2183,8 @@ endif -objs/$(CONFIG)/test/cpp/end2end/async_test_server.o: gens/test/cpp/util/echo.pb.cc gens/test/cpp/util/echo_duplicate.pb.cc gens/test/cpp/util/messages.pb.cc -objs/$(CONFIG)/test/cpp/util/create_test_channel.o: gens/test/cpp/util/echo.pb.cc gens/test/cpp/util/echo_duplicate.pb.cc gens/test/cpp/util/messages.pb.cc +objs/$(CONFIG)/test/cpp/end2end/async_test_server.o: gens/test/cpp/util/messages.pb.cc gens/test/cpp/util/echo.pb.cc gens/test/cpp/util/echo_duplicate.pb.cc +objs/$(CONFIG)/test/cpp/util/create_test_channel.o: gens/test/cpp/util/messages.pb.cc gens/test/cpp/util/echo.pb.cc gens/test/cpp/util/echo_duplicate.pb.cc LIBTIPS_CLIENT_LIB_SRC = \ From dab7095fc5510c78eef150346cda15cc090fd689 Mon Sep 17 00:00:00 2001 From: Yang Gao Date: Fri, 23 Jan 2015 16:06:36 -0800 Subject: [PATCH 131/143] Make interop server listen on 0.0.0.0. --- test/cpp/interop/server.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cpp/interop/server.cc b/test/cpp/interop/server.cc index 5b5c35416ca..8a6be579294 100644 --- a/test/cpp/interop/server.cc +++ b/test/cpp/interop/server.cc @@ -192,7 +192,7 @@ class TestServiceImpl : public TestService::Service { void RunServer() { std::ostringstream server_address; - server_address << "localhost:" << FLAGS_port; + server_address << "0.0.0.0:" << FLAGS_port; TestServiceImpl service; SimpleRequest request; From 7a3c38bed9ea9e69ec748b9ed08cdf16cb6ac501 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Sat, 24 Jan 2015 00:47:35 +0000 Subject: [PATCH 132/143] Add the _framework.face package. --- src/python/_framework/face/__init__.py | 0 src/python/_framework/face/_calls.py | 310 ++++++++++ src/python/_framework/face/_control.py | 194 +++++++ src/python/_framework/face/_service.py | 189 ++++++ src/python/_framework/face/_test_case.py | 81 +++ ...blocking_invocation_inline_service_test.py | 46 ++ src/python/_framework/face/demonstration.py | 118 ++++ ...vocation_synchronous_event_service_test.py | 46 ++ src/python/_framework/face/exceptions.py | 77 +++ ...ocation_asynchronous_event_service_test.py | 46 ++ src/python/_framework/face/implementations.py | 246 ++++++++ src/python/_framework/face/interfaces.py | 545 ++++++++++++++++++ .../_framework/face/testing/__init__.py | 0 .../_framework/face/testing/base_util.py | 102 ++++ ...ing_invocation_inline_service_test_case.py | 223 +++++++ .../_framework/face/testing/callback.py | 94 +++ src/python/_framework/face/testing/control.py | 87 +++ .../_framework/face/testing/coverage.py | 123 ++++ src/python/_framework/face/testing/digest.py | 446 ++++++++++++++ ...ion_synchronous_event_service_test_case.py | 367 ++++++++++++ ...on_asynchronous_event_service_test_case.py | 377 ++++++++++++ .../_framework/face/testing/interfaces.py | 117 ++++ src/python/_framework/face/testing/serial.py | 70 +++ src/python/_framework/face/testing/service.py | 337 +++++++++++ .../_framework/face/testing/stock_service.py | 374 ++++++++++++ .../_framework/face/testing/test_case.py | 111 ++++ src/python/_junkdrawer/__init__.py | 0 src/python/_junkdrawer/stock_pb2.py | 152 +++++ tools/run_tests/run_python.sh | 5 +- 29 files changed, 4881 insertions(+), 2 deletions(-) create mode 100644 src/python/_framework/face/__init__.py create mode 100644 src/python/_framework/face/_calls.py create mode 100644 src/python/_framework/face/_control.py create mode 100644 src/python/_framework/face/_service.py create mode 100644 src/python/_framework/face/_test_case.py create mode 100644 src/python/_framework/face/blocking_invocation_inline_service_test.py create mode 100644 src/python/_framework/face/demonstration.py create mode 100644 src/python/_framework/face/event_invocation_synchronous_event_service_test.py create mode 100644 src/python/_framework/face/exceptions.py create mode 100644 src/python/_framework/face/future_invocation_asynchronous_event_service_test.py create mode 100644 src/python/_framework/face/implementations.py create mode 100644 src/python/_framework/face/interfaces.py create mode 100644 src/python/_framework/face/testing/__init__.py create mode 100644 src/python/_framework/face/testing/base_util.py create mode 100644 src/python/_framework/face/testing/blocking_invocation_inline_service_test_case.py create mode 100644 src/python/_framework/face/testing/callback.py create mode 100644 src/python/_framework/face/testing/control.py create mode 100644 src/python/_framework/face/testing/coverage.py create mode 100644 src/python/_framework/face/testing/digest.py create mode 100644 src/python/_framework/face/testing/event_invocation_synchronous_event_service_test_case.py create mode 100644 src/python/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py create mode 100644 src/python/_framework/face/testing/interfaces.py create mode 100644 src/python/_framework/face/testing/serial.py create mode 100644 src/python/_framework/face/testing/service.py create mode 100644 src/python/_framework/face/testing/stock_service.py create mode 100644 src/python/_framework/face/testing/test_case.py create mode 100644 src/python/_junkdrawer/__init__.py create mode 100644 src/python/_junkdrawer/stock_pb2.py diff --git a/src/python/_framework/face/__init__.py b/src/python/_framework/face/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_framework/face/_calls.py b/src/python/_framework/face/_calls.py new file mode 100644 index 00000000000..ab58e6378b1 --- /dev/null +++ b/src/python/_framework/face/_calls.py @@ -0,0 +1,310 @@ +# Copyright 2015, 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. + +"""Utility functions for invoking RPCs.""" + +import threading + +from _framework.base import interfaces as base_interfaces +from _framework.base import util as base_util +from _framework.face import _control +from _framework.face import interfaces +from _framework.foundation import callable_util +from _framework.foundation import future + +_ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!' +_DONE_CALLBACK_LOG_MESSAGE = 'Exception calling Future "done" callback!' + + +class _RendezvousServicedIngestor(base_interfaces.ServicedIngestor): + + def __init__(self, rendezvous): + self._rendezvous = rendezvous + + def consumer(self, operation_context): + return self._rendezvous + + +class _EventServicedIngestor(base_interfaces.ServicedIngestor): + + def __init__(self, result_consumer, abortion_callback): + self._result_consumer = result_consumer + self._abortion_callback = abortion_callback + + def consumer(self, operation_context): + operation_context.add_termination_callback( + _control.as_operation_termination_callback(self._abortion_callback)) + return self._result_consumer + + +def _rendezvous_subscription(rendezvous): + return base_util.full_serviced_subscription( + _RendezvousServicedIngestor(rendezvous)) + + +def _unary_event_subscription(completion_callback, abortion_callback): + return base_util.full_serviced_subscription( + _EventServicedIngestor( + _control.UnaryConsumer(completion_callback), abortion_callback)) + + +def _stream_event_subscription(result_consumer, abortion_callback): + return base_util.full_serviced_subscription( + _EventServicedIngestor(result_consumer, abortion_callback)) + + +class _OperationCancellableIterator(interfaces.CancellableIterator): + """An interfaces.CancellableIterator for response-streaming operations.""" + + def __init__(self, rendezvous, operation): + self._rendezvous = rendezvous + self._operation = operation + + def __iter__(self): + return self + + def next(self): + return next(self._rendezvous) + + def cancel(self): + self._operation.cancel() + self._rendezvous.set_outcome(base_interfaces.CANCELLED) + + +class _OperationFuture(future.Future): + """A future.Future interface to an operation.""" + + def __init__(self, rendezvous, operation): + self._condition = threading.Condition() + self._rendezvous = rendezvous + self._operation = operation + + self._outcome = None + self._callbacks = [] + + def cancel(self): + """See future.Future.cancel for specification.""" + with self._condition: + if self._outcome is None: + self._operation.cancel() + self._outcome = future.aborted() + self._condition.notify_all() + return False + + def cancelled(self): + """See future.Future.cancelled for specification.""" + return False + + def done(self): + """See future.Future.done for specification.""" + with self._condition: + return (self._outcome is not None and + self._outcome.category is not future.ABORTED) + + def outcome(self): + """See future.Future.outcome for specification.""" + with self._condition: + while self._outcome is None: + self._condition.wait() + return self._outcome + + def add_done_callback(self, callback): + """See future.Future.add_done_callback for specification.""" + with self._condition: + if self._callbacks is not None: + self._callbacks.add(callback) + return + + outcome = self._outcome + + callable_util.call_logging_exceptions( + callback, _DONE_CALLBACK_LOG_MESSAGE, outcome) + + def on_operation_termination(self, operation_outcome): + """Indicates to this object that the operation has terminated. + + Args: + operation_outcome: One of base_interfaces.COMPLETED, + base_interfaces.CANCELLED, base_interfaces.EXPIRED, + base_interfaces.RECEPTION_FAILURE, base_interfaces.TRANSMISSION_FAILURE, + base_interfaces.SERVICED_FAILURE, or base_interfaces.SERVICER_FAILURE + indicating the categorical outcome of the operation. + """ + with self._condition: + if (self._outcome is None and + operation_outcome != base_interfaces.COMPLETED): + self._outcome = future.raised( + _control.abortion_outcome_to_exception(operation_outcome)) + self._condition.notify_all() + + outcome = self._outcome + rendezvous = self._rendezvous + callbacks = list(self._callbacks) + self._callbacks = None + + if outcome is None: + try: + return_value = next(rendezvous) + except Exception as e: # pylint: disable=broad-except + outcome = future.raised(e) + else: + outcome = future.returned(return_value) + with self._condition: + if self._outcome is None: + self._outcome = outcome + self._condition.notify_all() + else: + outcome = self._outcome + + for callback in callbacks: + callable_util.call_logging_exceptions( + callback, _DONE_CALLBACK_LOG_MESSAGE, outcome) + + +class _Call(interfaces.Call): + + def __init__(self, operation): + self._operation = operation + self.context = _control.RpcContext(operation.context) + + def cancel(self): + self._operation.cancel() + + +def blocking_value_in_value_out(front, name, payload, timeout, trace_id): + """Services in a blocking fashion a value-in value-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + return next(rendezvous) + + +def future_value_in_value_out(front, name, payload, timeout, trace_id): + """Services a value-in value-out servicer method by returning a Future.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + operation_future = _OperationFuture(rendezvous, operation) + operation.context.add_termination_callback( + operation_future.on_operation_termination) + return operation_future + + +def inline_value_in_stream_out(front, name, payload, timeout, trace_id): + """Services a value-in stream-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + return _OperationCancellableIterator(rendezvous, operation) + + +def blocking_stream_in_value_out( + front, name, payload_iterator, timeout, trace_id): + """Services in a blocking fashion a stream-in value-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + for payload in payload_iterator: + operation.consumer.consume(payload) + operation.consumer.terminate() + return next(rendezvous) + + +def future_stream_in_value_out( + front, name, payload_iterator, timeout, trace_id, pool): + """Services a stream-in value-out servicer method by returning a Future.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + pool.submit( + callable_util.with_exceptions_logged( + _control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), + payload_iterator, operation.consumer, lambda: True, True) + operation_future = _OperationFuture(rendezvous, operation) + operation.context.add_termination_callback( + operation_future.on_operation_termination) + return operation_future + + +def inline_stream_in_stream_out( + front, name, payload_iterator, timeout, trace_id, pool): + """Services a stream-in stream-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + pool.submit( + callable_util.with_exceptions_logged( + _control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), + payload_iterator, operation.consumer, lambda: True, True) + return _OperationCancellableIterator(rendezvous, operation) + + +def event_value_in_value_out( + front, name, payload, completion_callback, abortion_callback, timeout, + trace_id): + subscription = _unary_event_subscription( + completion_callback, abortion_callback) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + return _Call(operation) + + +def event_value_in_stream_out( + front, name, payload, result_payload_consumer, abortion_callback, timeout, + trace_id): + subscription = _stream_event_subscription( + result_payload_consumer, abortion_callback) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + return _Call(operation) + + +def event_stream_in_value_out( + front, name, completion_callback, abortion_callback, timeout, trace_id): + subscription = _unary_event_subscription( + completion_callback, abortion_callback) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + return _Call(operation), operation.consumer + + +def event_stream_in_stream_out( + front, name, result_payload_consumer, abortion_callback, timeout, trace_id): + subscription = _stream_event_subscription( + result_payload_consumer, abortion_callback) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + return _Call(operation), operation.consumer diff --git a/src/python/_framework/face/_control.py b/src/python/_framework/face/_control.py new file mode 100644 index 00000000000..2c221321d69 --- /dev/null +++ b/src/python/_framework/face/_control.py @@ -0,0 +1,194 @@ +# Copyright 2015, 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. + +"""State and behavior for translating between sync and async control flow.""" + +import threading + +from _framework.base import interfaces as base_interfaces +from _framework.face import exceptions +from _framework.face import interfaces +from _framework.foundation import abandonment +from _framework.foundation import stream + +INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Face) Internal Error! :-(' + +_OPERATION_OUTCOME_TO_RPC_ABORTION = { + base_interfaces.CANCELLED: interfaces.CANCELLED, + base_interfaces.EXPIRED: interfaces.EXPIRED, + base_interfaces.RECEPTION_FAILURE: interfaces.NETWORK_FAILURE, + base_interfaces.TRANSMISSION_FAILURE: interfaces.NETWORK_FAILURE, + base_interfaces.SERVICED_FAILURE: interfaces.SERVICED_FAILURE, + base_interfaces.SERVICER_FAILURE: interfaces.SERVICER_FAILURE, + } + + +def _as_operation_termination_callback(rpc_abortion_callback): + def operation_termination_callback(operation_outcome): + rpc_abortion = _OPERATION_OUTCOME_TO_RPC_ABORTION.get( + operation_outcome, None) + if rpc_abortion is not None: + rpc_abortion_callback(rpc_abortion) + return operation_termination_callback + + +def _abortion_outcome_to_exception(abortion_outcome): + if abortion_outcome == base_interfaces.CANCELLED: + return exceptions.CancellationError() + elif abortion_outcome == base_interfaces.EXPIRED: + return exceptions.ExpirationError() + elif abortion_outcome == base_interfaces.SERVICER_FAILURE: + return exceptions.ServicerError() + elif abortion_outcome == base_interfaces.SERVICED_FAILURE: + return exceptions.ServicedError() + else: + return exceptions.NetworkError() + + +class UnaryConsumer(stream.Consumer): + """A stream.Consumer that should only ever be passed one value.""" + + def __init__(self, on_termination): + self._on_termination = on_termination + self._value = None + + def consume(self, value): + self._value = value + + def terminate(self): + self._on_termination(self._value) + + def consume_and_terminate(self, value): + self._on_termination(value) + + +class Rendezvous(stream.Consumer): + """A rendez-vous with stream.Consumer and iterator interfaces.""" + + def __init__(self): + self._condition = threading.Condition() + self._values = [] + self._values_completed = False + self._abortion = None + + def consume(self, value): + with self._condition: + self._values.append(value) + self._condition.notify() + + def terminate(self): + with self._condition: + self._values_completed = True + self._condition.notify() + + def consume_and_terminate(self, value): + with self._condition: + self._values.append(value) + self._values_completed = True + self._condition.notify() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while ((self._abortion is None) and + (not self._values) and + (not self._values_completed)): + self._condition.wait() + if self._abortion is not None: + raise _abortion_outcome_to_exception(self._abortion) + elif self._values: + return self._values.pop(0) + elif self._values_completed: + raise StopIteration() + else: + raise AssertionError('Unreachable code reached!') + + def set_outcome(self, outcome): + with self._condition: + if outcome != base_interfaces.COMPLETED: + self._abortion = outcome + self._condition.notify() + + +class RpcContext(interfaces.RpcContext): + """A wrapped base_interfaces.OperationContext.""" + + def __init__(self, operation_context): + self._operation_context = operation_context + + def is_active(self): + return self._operation_context.is_active() + + def time_remaining(self): + return self._operation_context.time_remaining() + + def add_abortion_callback(self, abortion_callback): + self._operation_context.add_termination_callback( + _as_operation_termination_callback(abortion_callback)) + + +def pipe_iterator_to_consumer(iterator, consumer, active, terminate): + """Pipes values emitted from an iterator to a stream.Consumer. + + Args: + iterator: An iterator from which values will be emitted. + consumer: A stream.Consumer to which values will be passed. + active: A no-argument callable that returns True if the work being done by + this function is still valid and should not be abandoned and False if the + work being done by this function should be abandoned. + terminate: A boolean indicating whether or not this function should + terminate the given consumer after passing to it all values emitted by the + given iterator. + + Raises: + abandonment.Abandoned: If this function quits early after seeing False + returned by the active function passed to it. + Exception: This function raises whatever exceptions are raised by iterating + over the given iterator. + """ + for element in iterator: + if not active(): + raise abandonment.Abandoned() + + consumer.consume(element) + + if not active(): + raise abandonment.Abandoned() + if terminate: + consumer.terminate() + + +def abortion_outcome_to_exception(abortion_outcome): + return _abortion_outcome_to_exception(abortion_outcome) + + +def as_operation_termination_callback(rpc_abortion_callback): + return _as_operation_termination_callback(rpc_abortion_callback) diff --git a/src/python/_framework/face/_service.py b/src/python/_framework/face/_service.py new file mode 100644 index 00000000000..d758c2f1480 --- /dev/null +++ b/src/python/_framework/face/_service.py @@ -0,0 +1,189 @@ +# Copyright 2015, 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. + +"""Behaviors for servicing RPCs.""" + +# base_interfaces and interfaces are referenced from specification in this +# module. +from _framework.base import interfaces as base_interfaces # pylint: disable=unused-import +from _framework.face import _control +from _framework.face import exceptions +from _framework.face import interfaces # pylint: disable=unused-import +from _framework.foundation import abandonment +from _framework.foundation import callable_util +from _framework.foundation import stream +from _framework.foundation import stream_util + + +class _ValueInStreamOutConsumer(stream.Consumer): + """A stream.Consumer that maps inputs one-to-many onto outputs.""" + + def __init__(self, behavior, context, downstream): + """Constructor. + + Args: + behavior: A callable that takes a single value and an + interfaces.RpcContext and returns a generator of arbitrarily many + values. + context: An interfaces.RpcContext. + downstream: A stream.Consumer to which to pass the values generated by the + given behavior. + """ + self._behavior = behavior + self._context = context + self._downstream = downstream + + def consume(self, value): + _control.pipe_iterator_to_consumer( + self._behavior(value, self._context), self._downstream, + self._context.is_active, False) + + def terminate(self): + self._downstream.terminate() + + def consume_and_terminate(self, value): + _control.pipe_iterator_to_consumer( + self._behavior(value, self._context), self._downstream, + self._context.is_active, True) + + +def _pool_wrap(behavior, operation_context): + """Wraps an operation-related behavior so that it may be called in a pool. + + Args: + behavior: A callable related to carrying out an operation. + operation_context: A base_interfaces.OperationContext for the operation. + + Returns: + A callable that when called carries out the behavior of the given callable + and handles whatever exceptions it raises appropriately. + """ + def translation(*args): + try: + behavior(*args) + except ( + abandonment.Abandoned, + exceptions.ExpirationError, + exceptions.CancellationError, + exceptions.ServicedError, + exceptions.NetworkError) as e: + if operation_context.is_active(): + operation_context.fail(e) + except Exception as e: + operation_context.fail(e) + return callable_util.with_exceptions_logged( + translation, _control.INTERNAL_ERROR_LOG_MESSAGE) + + +def adapt_inline_value_in_value_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return stream_util.TransformingConsumer( + lambda request: method.service(request, rpc_context), response_consumer) + return adaptation + + +def adapt_inline_value_in_stream_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return _ValueInStreamOutConsumer( + method.service, rpc_context, response_consumer) + return adaptation + + +def adapt_inline_stream_in_value_out(method, pool): + def adaptation(response_consumer, operation_context): + rendezvous = _control.Rendezvous() + operation_context.add_termination_callback(rendezvous.set_outcome) + def in_pool_thread(): + response_consumer.consume_and_terminate( + method.service(rendezvous, _control.RpcContext(operation_context))) + pool.submit(_pool_wrap(in_pool_thread, operation_context)) + return rendezvous + return adaptation + + +def adapt_inline_stream_in_stream_out(method, pool): + """Adapts an interfaces.InlineStreamInStreamOutMethod for use with Consumers. + + RPCs may be serviced by calling the return value of this function, passing + request values to the stream.Consumer returned from that call, and receiving + response values from the stream.Consumer passed to that call. + + Args: + method: An interfaces.InlineStreamInStreamOutMethod. + pool: A thread pool. + + Returns: + A callable that takes a stream.Consumer and a + base_interfaces.OperationContext and returns a stream.Consumer. + """ + def adaptation(response_consumer, operation_context): + rendezvous = _control.Rendezvous() + operation_context.add_termination_callback(rendezvous.set_outcome) + def in_pool_thread(): + _control.pipe_iterator_to_consumer( + method.service(rendezvous, _control.RpcContext(operation_context)), + response_consumer, operation_context.is_active, True) + pool.submit(_pool_wrap(in_pool_thread, operation_context)) + return rendezvous + return adaptation + + +def adapt_event_value_in_value_out(method): + def adaptation(response_consumer, operation_context): + def on_payload(payload): + method.service( + payload, response_consumer.consume_and_terminate, + _control.RpcContext(operation_context)) + return _control.UnaryConsumer(on_payload) + return adaptation + + +def adapt_event_value_in_stream_out(method): + def adaptation(response_consumer, operation_context): + def on_payload(payload): + method.service( + payload, response_consumer, _control.RpcContext(operation_context)) + return _control.UnaryConsumer(on_payload) + return adaptation + + +def adapt_event_stream_in_value_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return method.service(response_consumer.consume_and_terminate, rpc_context) + return adaptation + + +def adapt_event_stream_in_stream_out(method): + def adaptation(response_consumer, operation_context): + return method.service( + response_consumer, _control.RpcContext(operation_context)) + return adaptation diff --git a/src/python/_framework/face/_test_case.py b/src/python/_framework/face/_test_case.py new file mode 100644 index 00000000000..50b55c389f3 --- /dev/null +++ b/src/python/_framework/face/_test_case.py @@ -0,0 +1,81 @@ +# Copyright 2015, 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. + +"""Common lifecycle code for in-memory-ticket-exchange Face-layer tests.""" + +from _framework.face import implementations +from _framework.face.testing import base_util +from _framework.face.testing import test_case +from _framework.foundation import logging_pool + +_TIMEOUT = 3 +_MAXIMUM_POOL_SIZE = 100 + + +class FaceTestCase(test_case.FaceTestCase): + """Provides abstract Face-layer tests an in-memory implementation.""" + + def set_up_implementation( + self, + name, + methods, + inline_value_in_value_out_methods, + inline_value_in_stream_out_methods, + inline_stream_in_value_out_methods, + inline_stream_in_stream_out_methods, + event_value_in_value_out_methods, + event_value_in_stream_out_methods, + event_stream_in_value_out_methods, + event_stream_in_stream_out_methods, + multi_method): + servicer_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + stub_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + + servicer = implementations.servicer( + servicer_pool, + inline_value_in_value_out_methods=inline_value_in_value_out_methods, + inline_value_in_stream_out_methods=inline_value_in_stream_out_methods, + inline_stream_in_value_out_methods=inline_stream_in_value_out_methods, + inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods, + event_value_in_value_out_methods=event_value_in_value_out_methods, + event_value_in_stream_out_methods=event_value_in_stream_out_methods, + event_stream_in_value_out_methods=event_stream_in_value_out_methods, + event_stream_in_stream_out_methods=event_stream_in_stream_out_methods, + multi_method=multi_method) + + linked_pair = base_util.linked_pair(servicer, _TIMEOUT) + server = implementations.server() + stub = implementations.stub(linked_pair.front, stub_pool) + return server, stub, (servicer_pool, stub_pool, linked_pair) + + def tear_down_implementation(self, memo): + servicer_pool, stub_pool, linked_pair = memo + linked_pair.shut_down() + stub_pool.shutdown(wait=True) + servicer_pool.shutdown(wait=True) diff --git a/src/python/_framework/face/blocking_invocation_inline_service_test.py b/src/python/_framework/face/blocking_invocation_inline_service_test.py new file mode 100644 index 00000000000..96563c94eeb --- /dev/null +++ b/src/python/_framework/face/blocking_invocation_inline_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from _framework.face import _test_case +from _framework.face.testing import blocking_invocation_inline_service_test_case as test_case + + +class BlockingInvocationInlineServiceTest( + _test_case.FaceTestCase, + test_case.BlockingInvocationInlineServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/_framework/face/demonstration.py b/src/python/_framework/face/demonstration.py new file mode 100644 index 00000000000..501ec6b3f83 --- /dev/null +++ b/src/python/_framework/face/demonstration.py @@ -0,0 +1,118 @@ +# Copyright 2015, 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. + +"""Demonstration-suitable implementation of the face layer of RPC Framework.""" + +from _framework.base import util as _base_util +from _framework.base.packets import implementations as _tickets_implementations +from _framework.face import implementations +from _framework.foundation import logging_pool + +_POOL_SIZE_LIMIT = 20 + +_MAXIMUM_TIMEOUT = 90 + + +class LinkedPair(object): + """A Server and Stub that are linked to one another. + + Attributes: + server: A Server. + stub: A Stub. + """ + + def shut_down(self): + """Shuts down this object and releases its resources.""" + raise NotImplementedError() + + +class _LinkedPair(LinkedPair): + + def __init__(self, server, stub, front, back, pools): + self.server = server + self.stub = stub + self._front = front + self._back = back + self._pools = pools + + def shut_down(self): + _base_util.wait_for_idle(self._front) + _base_util.wait_for_idle(self._back) + + for pool in self._pools: + pool.shutdown(wait=True) + + +def server_and_stub( + default_timeout, + inline_value_in_value_out_methods=None, + inline_value_in_stream_out_methods=None, + inline_stream_in_value_out_methods=None, + inline_stream_in_stream_out_methods=None, + event_value_in_value_out_methods=None, + event_value_in_stream_out_methods=None, + event_stream_in_value_out_methods=None, + event_stream_in_stream_out_methods=None, + multi_method=None): + """Creates a Server and Stub linked together for use.""" + front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + stub_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + pools = ( + front_work_pool, front_transmission_pool, front_utility_pool, + back_work_pool, back_transmission_pool, back_utility_pool, + stub_pool) + + servicer = implementations.servicer( + back_work_pool, + inline_value_in_value_out_methods=inline_value_in_value_out_methods, + inline_value_in_stream_out_methods=inline_value_in_stream_out_methods, + inline_stream_in_value_out_methods=inline_stream_in_value_out_methods, + inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods, + event_value_in_value_out_methods=event_value_in_value_out_methods, + event_value_in_stream_out_methods=event_value_in_stream_out_methods, + event_stream_in_value_out_methods=event_stream_in_value_out_methods, + event_stream_in_stream_out_methods=event_stream_in_stream_out_methods, + multi_method=multi_method) + + front = _tickets_implementations.front( + front_work_pool, front_transmission_pool, front_utility_pool) + back = _tickets_implementations.back( + servicer, back_work_pool, back_transmission_pool, back_utility_pool, + default_timeout, _MAXIMUM_TIMEOUT) + front.join_rear_link(back) + back.join_fore_link(front) + + stub = implementations.stub(front, stub_pool) + + return _LinkedPair(implementations.server(), stub, front, back, pools) diff --git a/src/python/_framework/face/event_invocation_synchronous_event_service_test.py b/src/python/_framework/face/event_invocation_synchronous_event_service_test.py new file mode 100644 index 00000000000..48e05b2478e --- /dev/null +++ b/src/python/_framework/face/event_invocation_synchronous_event_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from _framework.face import _test_case +from _framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case + + +class EventInvocationSynchronousEventServiceTest( + _test_case.FaceTestCase, + test_case.EventInvocationSynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/_framework/face/exceptions.py b/src/python/_framework/face/exceptions.py new file mode 100644 index 00000000000..f112df70bc6 --- /dev/null +++ b/src/python/_framework/face/exceptions.py @@ -0,0 +1,77 @@ +# Copyright 2015, 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. + +"""Exceptions used in the Face layer of RPC Framework.""" + +import abc + + +class NoSuchMethodError(Exception): + """Raised by customer code to indicate an unrecognized RPC method name. + + Attributes: + name: The unrecognized name. + """ + + def __init__(self, name): + """Constructor. + + Args: + name: The unrecognized RPC method name. + """ + super(NoSuchMethodError, self).__init__() + self.name = name + + +class RpcError(Exception): + """Common super type for all exceptions raised by the Face layer. + + Only RPC Framework should instantiate and raise these exceptions. + """ + __metaclass__ = abc.ABCMeta + + +class CancellationError(RpcError): + """Indicates that an RPC has been cancelled.""" + + +class ExpirationError(RpcError): + """Indicates that an RPC has expired ("timed out").""" + + +class NetworkError(RpcError): + """Indicates that some error occurred on the network.""" + + +class ServicedError(RpcError): + """Indicates that the Serviced failed in the course of an RPC.""" + + +class ServicerError(RpcError): + """Indicates that the Servicer failed in the course of servicing an RPC.""" diff --git a/src/python/_framework/face/future_invocation_asynchronous_event_service_test.py b/src/python/_framework/face/future_invocation_asynchronous_event_service_test.py new file mode 100644 index 00000000000..96f5fe85d3d --- /dev/null +++ b/src/python/_framework/face/future_invocation_asynchronous_event_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from _framework.face import _test_case +from _framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case + + +class FutureInvocationAsynchronousEventServiceTest( + _test_case.FaceTestCase, + test_case.FutureInvocationAsynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/_framework/face/implementations.py b/src/python/_framework/face/implementations.py new file mode 100644 index 00000000000..94362e20071 --- /dev/null +++ b/src/python/_framework/face/implementations.py @@ -0,0 +1,246 @@ +# Copyright 2015, 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. + +"""Entry points into the Face layer of RPC Framework.""" + +from _framework.base import exceptions as _base_exceptions +from _framework.base import interfaces as base_interfaces +from _framework.face import _calls +from _framework.face import _service +from _framework.face import exceptions +from _framework.face import interfaces + + +class _BaseServicer(base_interfaces.Servicer): + + def __init__(self, methods, multi_method): + self._methods = methods + self._multi_method = multi_method + + def service(self, name, context, output_consumer): + method = self._methods.get(name, None) + if method is not None: + return method(output_consumer, context) + elif self._multi_method is not None: + try: + return self._multi_method.service(name, output_consumer, context) + except exceptions.NoSuchMethodError: + raise _base_exceptions.NoSuchMethodError() + else: + raise _base_exceptions.NoSuchMethodError() + + +class _Server(interfaces.Server): + """An interfaces.Server implementation.""" + + +class _Stub(interfaces.Stub): + """An interfaces.Stub implementation.""" + + def __init__(self, front, pool): + self._front = front + self._pool = pool + + def blocking_value_in_value_out(self, name, request, timeout): + return _calls.blocking_value_in_value_out( + self._front, name, request, timeout, 'unused trace ID') + + def future_value_in_value_out(self, name, request, timeout): + return _calls.future_value_in_value_out( + self._front, name, request, timeout, 'unused trace ID') + + def inline_value_in_stream_out(self, name, request, timeout): + return _calls.inline_value_in_stream_out( + self._front, name, request, timeout, 'unused trace ID') + + def blocking_stream_in_value_out(self, name, request_iterator, timeout): + return _calls.blocking_stream_in_value_out( + self._front, name, request_iterator, timeout, 'unused trace ID') + + def future_stream_in_value_out(self, name, request_iterator, timeout): + return _calls.future_stream_in_value_out( + self._front, name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def inline_stream_in_stream_out(self, name, request_iterator, timeout): + return _calls.inline_stream_in_stream_out( + self._front, name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def event_value_in_value_out( + self, name, request, response_callback, abortion_callback, timeout): + return _calls.event_value_in_value_out( + self._front, name, request, response_callback, abortion_callback, + timeout, 'unused trace ID') + + def event_value_in_stream_out( + self, name, request, response_consumer, abortion_callback, timeout): + return _calls.event_value_in_stream_out( + self._front, name, request, response_consumer, abortion_callback, + timeout, 'unused trace ID') + + def event_stream_in_value_out( + self, name, response_callback, abortion_callback, timeout): + return _calls.event_stream_in_value_out( + self._front, name, response_callback, abortion_callback, timeout, + 'unused trace ID') + + def event_stream_in_stream_out( + self, name, response_consumer, abortion_callback, timeout): + return _calls.event_stream_in_stream_out( + self._front, name, response_consumer, abortion_callback, timeout, + 'unused trace ID') + + +def _aggregate_methods( + pool, + inline_value_in_value_out_methods, + inline_value_in_stream_out_methods, + inline_stream_in_value_out_methods, + inline_stream_in_stream_out_methods, + event_value_in_value_out_methods, + event_value_in_stream_out_methods, + event_stream_in_value_out_methods, + event_stream_in_stream_out_methods): + """Aggregates methods coded in according to different interfaces.""" + methods = {} + + def adapt_unpooled_methods(adapted_methods, unadapted_methods, adaptation): + if unadapted_methods is not None: + for name, unadapted_method in unadapted_methods.iteritems(): + adapted_methods[name] = adaptation(unadapted_method) + + def adapt_pooled_methods(adapted_methods, unadapted_methods, adaptation): + if unadapted_methods is not None: + for name, unadapted_method in unadapted_methods.iteritems(): + adapted_methods[name] = adaptation(unadapted_method, pool) + + adapt_unpooled_methods( + methods, inline_value_in_value_out_methods, + _service.adapt_inline_value_in_value_out) + adapt_unpooled_methods( + methods, inline_value_in_stream_out_methods, + _service.adapt_inline_value_in_stream_out) + adapt_pooled_methods( + methods, inline_stream_in_value_out_methods, + _service.adapt_inline_stream_in_value_out) + adapt_pooled_methods( + methods, inline_stream_in_stream_out_methods, + _service.adapt_inline_stream_in_stream_out) + adapt_unpooled_methods( + methods, event_value_in_value_out_methods, + _service.adapt_event_value_in_value_out) + adapt_unpooled_methods( + methods, event_value_in_stream_out_methods, + _service.adapt_event_value_in_stream_out) + adapt_unpooled_methods( + methods, event_stream_in_value_out_methods, + _service.adapt_event_stream_in_value_out) + adapt_unpooled_methods( + methods, event_stream_in_stream_out_methods, + _service.adapt_event_stream_in_stream_out) + + return methods + + +def servicer( + pool, + inline_value_in_value_out_methods=None, + inline_value_in_stream_out_methods=None, + inline_stream_in_value_out_methods=None, + inline_stream_in_stream_out_methods=None, + event_value_in_value_out_methods=None, + event_value_in_stream_out_methods=None, + event_stream_in_value_out_methods=None, + event_stream_in_stream_out_methods=None, + multi_method=None): + """Creates a base_interfaces.Servicer. + + The key sets of the passed dictionaries must be disjoint. It is guaranteed + that any passed MultiMethod implementation will only be called to service an + RPC if the RPC method name is not present in the key sets of the passed + dictionaries. + + Args: + pool: A thread pool. + inline_value_in_value_out_methods: A dictionary mapping method names to + interfaces.InlineValueInValueOutMethod implementations. + inline_value_in_stream_out_methods: A dictionary mapping method names to + interfaces.InlineValueInStreamOutMethod implementations. + inline_stream_in_value_out_methods: A dictionary mapping method names to + interfaces.InlineStreamInValueOutMethod implementations. + inline_stream_in_stream_out_methods: A dictionary mapping method names to + interfaces.InlineStreamInStreamOutMethod implementations. + event_value_in_value_out_methods: A dictionary mapping method names to + interfaces.EventValueInValueOutMethod implementations. + event_value_in_stream_out_methods: A dictionary mapping method names to + interfaces.EventValueInStreamOutMethod implementations. + event_stream_in_value_out_methods: A dictionary mapping method names to + interfaces.EventStreamInValueOutMethod implementations. + event_stream_in_stream_out_methods: A dictionary mapping method names to + interfaces.EventStreamInStreamOutMethod implementations. + multi_method: An implementation of interfaces.MultiMethod. + + Returns: + A base_interfaces.Servicer that services RPCs via the given implementations. + """ + methods = _aggregate_methods( + pool, + inline_value_in_value_out_methods, + inline_value_in_stream_out_methods, + inline_stream_in_value_out_methods, + inline_stream_in_stream_out_methods, + event_value_in_value_out_methods, + event_value_in_stream_out_methods, + event_stream_in_value_out_methods, + event_stream_in_stream_out_methods) + + return _BaseServicer(methods, multi_method) + + +def server(): + """Creates an interfaces.Server. + + Returns: + An interfaces.Server. + """ + return _Server() + + +def stub(front, pool): + """Creates an interfaces.Stub. + + Args: + front: A base_interfaces.Front. + pool: A futures.ThreadPoolExecutor. + + Returns: + An interfaces.Stub that performs RPCs via the given base_interfaces.Front. + """ + return _Stub(front, pool) diff --git a/src/python/_framework/face/interfaces.py b/src/python/_framework/face/interfaces.py new file mode 100644 index 00000000000..0cc7c70df37 --- /dev/null +++ b/src/python/_framework/face/interfaces.py @@ -0,0 +1,545 @@ +# Copyright 2015, 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. + +"""Interfaces for the face layer of RPC Framework.""" + +import abc + +# exceptions, abandonment, and future are referenced from specification in this +# module. +from _framework.face import exceptions # pylint: disable=unused-import +from _framework.foundation import abandonment # pylint: disable=unused-import +from _framework.foundation import future # pylint: disable=unused-import + + +class CancellableIterator(object): + """Implements the Iterator protocol and affords a cancel method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __iter__(self): + """Returns the self object in accordance with the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def next(self): + """Returns a value or raises StopIteration per the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Requests cancellation of whatever computation underlies this iterator.""" + raise NotImplementedError() + + +# Constants that categorize RPC abortion. +# TODO(nathaniel): Learn and use Python's enum library for this de facto +# enumerated type +CANCELLED = 'abortion: cancelled' +EXPIRED = 'abortion: expired' +NETWORK_FAILURE = 'abortion: network failure' +SERVICED_FAILURE = 'abortion: serviced failure' +SERVICER_FAILURE = 'abortion: servicer failure' + + +class RpcContext(object): + """Provides RPC-related information and action.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the RPC is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the RPC. + + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the RPC to complete before it is considered to have timed + out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_abortion_callback(self, abortion_callback): + """Registers a callback to be called if the RPC is aborted. + + Args: + abortion_callback: A callable to be called and passed one of CANCELLED, + EXPIRED, NETWORK_FAILURE, SERVICED_FAILURE, or SERVICER_FAILURE in the + event of RPC abortion. + """ + raise NotImplementedError() + + +class InlineValueInValueOutMethod(object): + """A type for inline unary-request-unary-response RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, context): + """Services an RPC that accepts one value and produces one value. + + Args: + request: The single request value for the RPC. + context: An RpcContext object. + + Returns: + The single response value for the RPC. + + Raises: + abandonment.Abandoned: If no response is necessary because the RPC has + been aborted. + """ + raise NotImplementedError() + + +class InlineValueInStreamOutMethod(object): + """A type for inline unary-request-stream-response RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, context): + """Services an RPC that accepts one value and produces a stream of values. + + Args: + request: The single request value for the RPC. + context: An RpcContext object. + + Yields: + The values that comprise the response stream of the RPC. + + Raises: + abandonment.Abandoned: If completing the response stream is not necessary + because the RPC has been aborted. + """ + raise NotImplementedError() + + +class InlineStreamInValueOutMethod(object): + """A type for inline stream-request-unary-response RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request_iterator, context): + """Services an RPC that accepts a stream of values and produces one value. + + Args: + request_iterator: An iterator that yields the request values of the RPC. + Drawing values from this iterator may also raise exceptions.RpcError to + indicate abortion of the RPC. + context: An RpcContext object. + + Yields: + The values that comprise the response stream of the RPC. + + Raises: + abandonment.Abandoned: If no response is necessary because the RPC has + been aborted. + exceptions.RpcError: Implementations of this method must not deliberately + raise exceptions.RpcError but may allow such errors raised by the + request_iterator passed to them to propagate through their bodies + uncaught. + """ + raise NotImplementedError() + + +class InlineStreamInStreamOutMethod(object): + """A type for inline stream-request-stream-response RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request_iterator, context): + """Services an RPC that accepts and produces streams of values. + + Args: + request_iterator: An iterator that yields the request values of the RPC. + Drawing values from this iterator may also raise exceptions.RpcError to + indicate abortion of the RPC. + context: An RpcContext object. + + Yields: + The values that comprise the response stream of the RPC. + + Raises: + abandonment.Abandoned: If completing the response stream is not necessary + because the RPC has been aborted. + exceptions.RpcError: Implementations of this method must not deliberately + raise exceptions.RpcError but may allow such errors raised by the + request_iterator passed to them to propagate through their bodies + uncaught. + """ + raise NotImplementedError() + + +class EventValueInValueOutMethod(object): + """A type for event-driven unary-request-unary-response RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_callback, context): + """Services an RPC that accepts one value and produces one value. + + Args: + request: The single request value for the RPC. + response_callback: A callback to be called to accept the response value of + the RPC. + context: An RpcContext object. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class EventValueInStreamOutMethod(object): + """A type for event-driven unary-request-stream-response RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_consumer, context): + """Services an RPC that accepts one value and produces a stream of values. + + Args: + request: The single request value for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + context: An RpcContext object. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class EventStreamInValueOutMethod(object): + """A type for event-driven stream-request-unary-response RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_callback, context): + """Services an RPC that accepts a stream of values and produces one value. + + Args: + response_callback: A callback to be called to accept the response value of + the RPC. + context: An RpcContext object. + + Returns: + A stream.Consumer with which to accept the request values of the RPC. The + consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing values to this object. Implementations must not assume that this + object will be called to completion of the request stream or even called + at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class EventStreamInStreamOutMethod(object): + """A type for event-driven stream-request-stream-response RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_consumer, context): + """Services an RPC that accepts and produces streams of values. + + Args: + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + context: An RpcContext object. + + Returns: + A stream.Consumer with which to accept the request values of the RPC. The + consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing values to this object. Implementations must not assume that this + object will be called to completion of the request stream or even called + at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class MultiMethod(object): + """A general type able to service many RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, name, response_consumer, context): + """Services an RPC. + + Args: + name: The RPC method name. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + context: An RpcContext object. + + Returns: + A stream.Consumer with which to accept the request values of the RPC. The + consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing values to this object. Implementations must not assume that this + object will be called to completion of the request stream or even called + at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + exceptions.NoSuchMethodError: If this MultiMethod does not recognize the + given RPC method name and is not able to service the RPC. + """ + raise NotImplementedError() + + +class Server(object): + """Specification of a running server that services RPCs.""" + __metaclass__ = abc.ABCMeta + + +class Call(object): + """Invocation-side representation of an RPC. + + Attributes: + context: An RpcContext affording information about the RPC. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Requests cancellation of the RPC.""" + raise NotImplementedError() + + +class Stub(object): + """Affords RPC methods to callers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def blocking_value_in_value_out(self, name, request, timeout): + """Invokes a unary-request-unary-response RPC method. + + This method blocks until either returning the response value of the RPC + (in the event of RPC completion) or raising an exception (in the event of + RPC abortion). + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future_value_in_value_out(self, name, request, timeout): + """Invokes a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future will return an outcome indicating that the RPC returned + the response value of the RPC. In the event of RPC abortion, the + returned Future will return an outcome indicating that the RPC raised + an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def inline_value_in_stream_out(self, name, request, timeout): + """Invokes a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion of + the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def blocking_stream_in_value_out(self, name, request_iterator, timeout): + """Invokes a stream-request-unary-response RPC method. + + This method blocks until either returning the response value of the RPC + (in the event of RPC completion) or raising an exception (in the event of + RPC abortion). + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future_stream_in_value_out(self, name, request_iterator, timeout): + """Invokes a stream-request-unary-response RPC method. + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future will return an outcome indicating that the RPC returned + the response value of the RPC. In the event of RPC abortion, the + returned Future will return an outcome indicating that the RPC raised + an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def inline_stream_in_stream_out(self, name, request_iterator, timeout): + """Invokes a stream-request-stream-response RPC method. + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion of + the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_value_in_value_out( + self, name, request, response_callback, abortion_callback, timeout): + """Event-driven invocation of a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + response_callback: A callback to be called to accept the response value + of the RPC. + abortion_callback: A callback to be called to accept one of CANCELLED, + EXPIRED, NETWORK_FAILURE, or SERVICER_FAILURE in the event of RPC + abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_value_in_stream_out( + self, name, request, response_consumer, abortion_callback, timeout): + """Event-driven invocation of a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + abortion_callback: A callback to be called to accept one of CANCELLED, + EXPIRED, NETWORK_FAILURE, or SERVICER_FAILURE in the event of RPC + abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_stream_in_value_out( + self, name, response_callback, abortion_callback, timeout): + """Event-driven invocation of a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + response_callback: A callback to be called to accept the response value + of the RPC. + abortion_callback: A callback to be called to accept one of CANCELLED, + EXPIRED, NETWORK_FAILURE, or SERVICER_FAILURE in the event of RPC + abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_stream_in_stream_out( + self, name, response_consumer, abortion_callback, timeout): + """Event-driven invocation of a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + abortion_callback: A callback to be called to accept one of CANCELLED, + EXPIRED, NETWORK_FAILURE, or SERVICER_FAILURE in the event of RPC + abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() diff --git a/src/python/_framework/face/testing/__init__.py b/src/python/_framework/face/testing/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_framework/face/testing/base_util.py b/src/python/_framework/face/testing/base_util.py new file mode 100644 index 00000000000..d9ccb3af8fc --- /dev/null +++ b/src/python/_framework/face/testing/base_util.py @@ -0,0 +1,102 @@ +# Copyright 2015, 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. + +"""Utilities for creating Base-layer objects for use in Face-layer tests.""" + +import abc + +# interfaces is referenced from specification in this module. +from _framework.base import util as _base_util +from _framework.base.packets import implementations +from _framework.base.packets import in_memory +from _framework.base.packets import interfaces # pylint: disable=unused-import +from _framework.foundation import logging_pool + +_POOL_SIZE_LIMIT = 20 + +_MAXIMUM_TIMEOUT = 90 + + +class LinkedPair(object): + """A Front and Back that are linked to one another. + + Attributes: + front: An interfaces.Front. + back: An interfaces.Back. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def shut_down(self): + """Shuts down this object and releases its resources.""" + raise NotImplementedError() + + +class _LinkedPair(LinkedPair): + + def __init__(self, front, back, pools): + self.front = front + self.back = back + self._pools = pools + + def shut_down(self): + _base_util.wait_for_idle(self.front) + _base_util.wait_for_idle(self.back) + + for pool in self._pools: + pool.shutdown(wait=True) + + +def linked_pair(servicer, default_timeout): + """Creates a Server and Stub linked together for use.""" + link_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + pools = ( + link_pool, + front_work_pool, front_transmission_pool, front_utility_pool, + back_work_pool, back_transmission_pool, back_utility_pool) + + link = in_memory.Link(link_pool) + front = implementations.front( + front_work_pool, front_transmission_pool, front_utility_pool) + back = implementations.back( + servicer, back_work_pool, back_transmission_pool, back_utility_pool, + default_timeout, _MAXIMUM_TIMEOUT) + front.join_rear_link(link) + link.join_fore_link(front) + back.join_fore_link(link) + link.join_rear_link(back) + + return _LinkedPair(front, back, pools) diff --git a/src/python/_framework/face/testing/blocking_invocation_inline_service_test_case.py b/src/python/_framework/face/testing/blocking_invocation_inline_service_test_case.py new file mode 100644 index 00000000000..0b1a2f0bd21 --- /dev/null +++ b/src/python/_framework/face/testing/blocking_invocation_inline_service_test_case.py @@ -0,0 +1,223 @@ +# Copyright 2015, 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +# unittest is referenced from specification in this module. +import abc +import unittest # pylint: disable=unused-import + +from _framework.face import exceptions +from _framework.face.testing import control +from _framework.face.testing import coverage +from _framework.face.testing import digest +from _framework.face.testing import stock_service +from _framework.face.testing import test_case + +_TIMEOUT = 3 + + +class BlockingInvocationInlineServiceTestCase( + test_case.FaceTestCase, coverage.BlockingCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, None) + + self.server, self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.inline_unary_unary_methods, + self.digest.inline_unary_stream_methods, + self.digest.inline_stream_unary_methods, + self.digest.inline_stream_stream_methods, + {}, {}, {}, {}, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response = self.stub.blocking_value_in_value_out( + name, request, _TIMEOUT) + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response = self.stub.blocking_stream_in_value_out( + name, iter(requests), _TIMEOUT) + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response = self.stub.blocking_value_in_value_out( + name, first_request, _TIMEOUT) + + test_messages.verify(first_request, first_response, self) + + second_response = self.stub.blocking_value_in_value_out( + name, second_request, _TIMEOUT) + + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + self.stub.blocking_value_in_value_out(name, request, _TIMEOUT) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + self.stub.blocking_stream_in_value_out(name, iter(requests), _TIMEOUT) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + self.stub.blocking_value_in_value_out(name, request, _TIMEOUT) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + self.stub.blocking_stream_in_value_out(name, iter(requests), _TIMEOUT) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) diff --git a/src/python/_framework/face/testing/callback.py b/src/python/_framework/face/testing/callback.py new file mode 100644 index 00000000000..7a20869abe9 --- /dev/null +++ b/src/python/_framework/face/testing/callback.py @@ -0,0 +1,94 @@ +# Copyright 2015, 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. + +"""A utility useful in tests of asynchronous, event-driven interfaces.""" + +import threading + +from _framework.foundation import stream + + +class Callback(stream.Consumer): + """A utility object useful in tests of asynchronous code.""" + + def __init__(self): + self._condition = threading.Condition() + self._unary_response = None + self._streamed_responses = [] + self._completed = False + self._abortion = None + + def abort(self, abortion): + with self._condition: + self._abortion = abortion + self._condition.notify_all() + + def complete(self, unary_response): + with self._condition: + self._unary_response = unary_response + self._completed = True + self._condition.notify_all() + + def consume(self, streamed_response): + with self._condition: + self._streamed_responses.append(streamed_response) + + def terminate(self): + with self._condition: + self._completed = True + self._condition.notify_all() + + def consume_and_terminate(self, streamed_response): + with self._condition: + self._streamed_responses.append(streamed_response) + self._completed = True + self._condition.notify_all() + + def block_until_terminated(self): + with self._condition: + while self._abortion is None and not self._completed: + self._condition.wait() + + def response(self): + with self._condition: + if self._abortion is None: + return self._unary_response + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def responses(self): + with self._condition: + if self._abortion is None: + return list(self._streamed_responses) + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def abortion(self): + with self._condition: + return self._abortion diff --git a/src/python/_framework/face/testing/control.py b/src/python/_framework/face/testing/control.py new file mode 100644 index 00000000000..3960c4e6495 --- /dev/null +++ b/src/python/_framework/face/testing/control.py @@ -0,0 +1,87 @@ +# Copyright 2015, 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. + +"""Code for instructing systems under test to block or fail.""" + +import abc +import contextlib +import threading + + +class Control(object): + """An object that accepts program control from a system under test. + + Systems under test passed a Control should call its control() method + frequently during execution. The control() method may block, raise an + exception, or do nothing, all according to the enclosing test's desire for + the system under test to simulate hanging, failing, or functioning. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def control(self): + """Potentially does anything.""" + raise NotImplementedError() + + +class PauseFailControl(Control): + """A Control that can be used to pause or fail code under control.""" + + def __init__(self): + self._condition = threading.Condition() + self._paused = False + self._fail = False + + def control(self): + with self._condition: + if self._fail: + raise ValueError() + + while self._paused: + self._condition.wait() + + @contextlib.contextmanager + def pause(self): + """Pauses code under control while controlling code is in context.""" + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): + """Fails code under control while controlling code is in context.""" + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False diff --git a/src/python/_framework/face/testing/coverage.py b/src/python/_framework/face/testing/coverage.py new file mode 100644 index 00000000000..f3aca113fe7 --- /dev/null +++ b/src/python/_framework/face/testing/coverage.py @@ -0,0 +1,123 @@ +# Copyright 2015, 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. + +"""Governs coverage for the tests of the Face layer of RPC Framework.""" + +import abc + +# These classes are only valid when inherited by unittest.TestCases. +# pylint: disable=invalid-name + + +class BlockingCoverage(object): + """Specification of test coverage for blocking behaviors.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testSuccessfulUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSequentialInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() + + +class FullCoverage(BlockingCoverage): + """Specification of test coverage for non-blocking behaviors.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/_framework/face/testing/digest.py b/src/python/_framework/face/testing/digest.py new file mode 100644 index 00000000000..8d1291c9755 --- /dev/null +++ b/src/python/_framework/face/testing/digest.py @@ -0,0 +1,446 @@ +# Copyright 2015, 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. + +"""Code for making a service.TestService more amenable to use in tests.""" + +import collections +import threading + +# testing_control, interfaces, and testing_service are referenced from +# specification in this module. +from _framework.face import exceptions +from _framework.face import interfaces as face_interfaces +from _framework.face.testing import control as testing_control # pylint: disable=unused-import +from _framework.face.testing import interfaces # pylint: disable=unused-import +from _framework.face.testing import service as testing_service # pylint: disable=unused-import +from _framework.foundation import stream +from _framework.foundation import stream_util + +_IDENTITY = lambda x: x + + +class TestServiceDigest( + collections.namedtuple( + 'TestServiceDigest', + ['name', + 'methods', + 'inline_unary_unary_methods', + 'inline_unary_stream_methods', + 'inline_stream_unary_methods', + 'inline_stream_stream_methods', + 'event_unary_unary_methods', + 'event_unary_stream_methods', + 'event_stream_unary_methods', + 'event_stream_stream_methods', + 'multi_method', + 'unary_unary_messages_sequences', + 'unary_stream_messages_sequences', + 'stream_unary_messages_sequences', + 'stream_stream_messages_sequences'])): + """A transformation of a service.TestService. + + Attributes: + name: The RPC service name to be used in the test. + methods: A sequence of interfaces.Method objects describing the RPC + methods that will be called during the test. + inline_unary_unary_methods: A dict from method name to + face_interfaces.InlineValueInValueOutMethod object to be used in tests of + in-line calls to behaviors under test. + inline_unary_stream_methods: A dict from method name to + face_interfaces.InlineValueInStreamOutMethod object to be used in tests of + in-line calls to behaviors under test. + inline_stream_unary_methods: A dict from method name to + face_interfaces.InlineStreamInValueOutMethod object to be used in tests of + in-line calls to behaviors under test. + inline_stream_stream_methods: A dict from method name to + face_interfaces.InlineStreamInStreamOutMethod object to be used in tests + of in-line calls to behaviors under test. + event_unary_unary_methods: A dict from method name to + face_interfaces.EventValueInValueOutMethod object to be used in tests of + event-driven calls to behaviors under test. + event_unary_stream_methods: A dict from method name to + face_interfaces.EventValueInStreamOutMethod object to be used in tests of + event-driven calls to behaviors under test. + event_stream_unary_methods: A dict from method name to + face_interfaces.EventStreamInValueOutMethod object to be used in tests of + event-driven calls to behaviors under test. + event_stream_stream_methods: A dict from method name to + face_interfaces.EventStreamInStreamOutMethod object to be used in tests of + event-driven calls to behaviors under test. + multi_method: A face_interfaces.MultiMethod to be used in tests of generic + calls to behaviors under test. + unary_unary_messages_sequences: A dict from method name to sequence of + service.UnaryUnaryTestMessages objects to be used to test the method + with the given name. + unary_stream_messages_sequences: A dict from method name to sequence of + service.UnaryStreamTestMessages objects to be used to test the method + with the given name. + stream_unary_messages_sequences: A dict from method name to sequence of + service.StreamUnaryTestMessages objects to be used to test the method + with the given name. + stream_stream_messages_sequences: A dict from method name to sequence of + service.StreamStreamTestMessages objects to be used to test the + method with the given name. + serialization: A serial.Serialization object describing serialization + behaviors for all the RPC methods. + """ + + +class _BufferingConsumer(stream.Consumer): + """A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" + + def __init__(self): + self.consumed = [] + self.terminated = False + + def consume(self, value): + self.consumed.append(value) + + def terminate(self): + self.terminated = True + + def consume_and_terminate(self, value): + self.consumed.append(value) + self.terminated = True + + +class _InlineUnaryUnaryMethod(face_interfaces.InlineValueInValueOutMethod): + + def __init__(self, unary_unary_test_method, control): + self._test_method = unary_unary_test_method + self._control = control + + def service(self, request, context): + response_list = [] + self._test_method.service( + request, response_list.append, context, self._control) + return response_list.pop(0) + + +class _EventUnaryUnaryMethod(face_interfaces.EventValueInValueOutMethod): + + def __init__(self, unary_unary_test_method, control, pool): + self._test_method = unary_unary_test_method + self._control = control + self._pool = pool + + def service(self, request, response_callback, context): + if self._pool is None: + self._test_method.service( + request, response_callback, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_callback, context, + self._control) + + +class _InlineUnaryStreamMethod(face_interfaces.InlineValueInStreamOutMethod): + + def __init__(self, unary_stream_test_method, control): + self._test_method = unary_stream_test_method + self._control = control + + def service(self, request, context): + response_consumer = _BufferingConsumer() + self._test_method.service( + request, response_consumer, context, self._control) + for response in response_consumer.consumed: + yield response + + +class _EventUnaryStreamMethod(face_interfaces.EventValueInStreamOutMethod): + + def __init__(self, unary_stream_test_method, control, pool): + self._test_method = unary_stream_test_method + self._control = control + self._pool = pool + + def service(self, request, response_consumer, context): + if self._pool is None: + self._test_method.service( + request, response_consumer, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_consumer, context, + self._control) + + +class _InlineStreamUnaryMethod(face_interfaces.InlineStreamInValueOutMethod): + + def __init__(self, stream_unary_test_method, control): + self._test_method = stream_unary_test_method + self._control = control + + def service(self, request_iterator, context): + response_list = [] + request_consumer = self._test_method.service( + response_list.append, context, self._control) + for request in request_iterator: + request_consumer.consume(request) + request_consumer.terminate() + return response_list.pop(0) + + +class _EventStreamUnaryMethod(face_interfaces.EventStreamInValueOutMethod): + + def __init__(self, stream_unary_test_method, control, pool): + self._test_method = stream_unary_test_method + self._control = control + self._pool = pool + + def service(self, response_callback, context): + request_consumer = self._test_method.service( + response_callback, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _InlineStreamStreamMethod(face_interfaces.InlineStreamInStreamOutMethod): + + def __init__(self, stream_stream_test_method, control): + self._test_method = stream_stream_test_method + self._control = control + + def service(self, request_iterator, context): + response_consumer = _BufferingConsumer() + request_consumer = self._test_method.service( + response_consumer, context, self._control) + + for request in request_iterator: + request_consumer.consume(request) + while response_consumer.consumed: + yield response_consumer.consumed.pop(0) + response_consumer.terminate() + + +class _EventStreamStreamMethod(face_interfaces.EventStreamInStreamOutMethod): + + def __init__(self, stream_stream_test_method, control, pool): + self._test_method = stream_stream_test_method + self._control = control + self._pool = pool + + def service(self, response_consumer, context): + request_consumer = self._test_method.service( + response_consumer, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _UnaryConsumer(stream.Consumer): + """A Consumer that only allows consumption of exactly one value.""" + + def __init__(self, action): + self._lock = threading.Lock() + self._action = action + self._consumed = False + self._terminated = False + + def consume(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + + self._action(value) + + def terminate(self): + with self._lock: + if not self._consumed: + raise ValueError('Unary consumer hasn\'t yet consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._terminated = True + + def consume_and_terminate(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + self._terminated = True + + self._action(value) + + +class _UnaryUnaryAdaptation(object): + + def __init__(self, unary_unary_test_method): + self._method = unary_unary_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service( + request, response_consumer.consume_and_terminate, context, control) + return _UnaryConsumer(action) + + +class _UnaryStreamAdaptation(object): + + def __init__(self, unary_stream_test_method): + self._method = unary_stream_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service(request, response_consumer, context, control) + return _UnaryConsumer(action) + + +class _StreamUnaryAdaptation(object): + + def __init__(self, stream_unary_test_method): + self._method = stream_unary_test_method + + def service(self, response_consumer, context, control): + return self._method.service( + response_consumer.consume_and_terminate, context, control) + + +class _MultiMethod(face_interfaces.MultiMethod): + + def __init__(self, methods, control, pool): + self._methods = methods + self._control = control + self._pool = pool + + def service(self, name, response_consumer, context): + method = self._methods.get(name, None) + if method is None: + raise exceptions.NoSuchMethodError(name) + elif self._pool is None: + return method(response_consumer, context, self._control) + else: + request_consumer = method(response_consumer, context, self._control) + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _Assembly( + collections.namedtuple( + '_Assembly', + ['methods', 'inlines', 'events', 'adaptations', 'messages'])): + """An intermediate structure created when creating a TestServiceDigest.""" + + +def _assemble( + scenarios, names, inline_method_constructor, event_method_constructor, + adapter, control, pool): + """Creates an _Assembly from the given scenarios.""" + methods = [] + inlines = {} + events = {} + adaptations = {} + messages = {} + for name, scenario in scenarios.iteritems(): + if name in names: + raise ValueError('Repeated name "%s"!' % name) + + test_method = scenario[0] + inline_method = inline_method_constructor(test_method, control) + event_method = event_method_constructor(test_method, control, pool) + adaptation = adapter(test_method) + + methods.append(test_method) + inlines[name] = inline_method + events[name] = event_method + adaptations[name] = adaptation + messages[name] = scenario[1] + + return _Assembly(methods, inlines, events, adaptations, messages) + + +def digest(service, control, pool): + """Creates a TestServiceDigest from a TestService. + + Args: + service: A testing_service.TestService. + control: A testing_control.Control. + pool: If RPC methods should be serviced in a separate thread, a thread pool. + None if RPC methods should be serviced in the thread belonging to the + run-time that calls for their service. + + Returns: + A TestServiceDigest synthesized from the given service.TestService. + """ + names = set() + + unary_unary = _assemble( + service.unary_unary_scenarios(), names, _InlineUnaryUnaryMethod, + _EventUnaryUnaryMethod, _UnaryUnaryAdaptation, control, pool) + names.update(set(unary_unary.inlines)) + + unary_stream = _assemble( + service.unary_stream_scenarios(), names, _InlineUnaryStreamMethod, + _EventUnaryStreamMethod, _UnaryStreamAdaptation, control, pool) + names.update(set(unary_stream.inlines)) + + stream_unary = _assemble( + service.stream_unary_scenarios(), names, _InlineStreamUnaryMethod, + _EventStreamUnaryMethod, _StreamUnaryAdaptation, control, pool) + names.update(set(stream_unary.inlines)) + + stream_stream = _assemble( + service.stream_stream_scenarios(), names, _InlineStreamStreamMethod, + _EventStreamStreamMethod, _IDENTITY, control, pool) + names.update(set(stream_stream.inlines)) + + methods = list(unary_unary.methods) + methods.extend(unary_stream.methods) + methods.extend(stream_unary.methods) + methods.extend(stream_stream.methods) + adaptations = dict(unary_unary.adaptations) + adaptations.update(unary_stream.adaptations) + adaptations.update(stream_unary.adaptations) + adaptations.update(stream_stream.adaptations) + + return TestServiceDigest( + service.name(), + methods, + unary_unary.inlines, + unary_stream.inlines, + stream_unary.inlines, + stream_stream.inlines, + unary_unary.events, + unary_stream.events, + stream_unary.events, + stream_stream.events, + _MultiMethod(adaptations, control, pool), + unary_unary.messages, + unary_stream.messages, + stream_unary.messages, + stream_stream.messages) diff --git a/src/python/_framework/face/testing/event_invocation_synchronous_event_service_test_case.py b/src/python/_framework/face/testing/event_invocation_synchronous_event_service_test_case.py new file mode 100644 index 00000000000..dba73a93686 --- /dev/null +++ b/src/python/_framework/face/testing/event_invocation_synchronous_event_service_test_case.py @@ -0,0 +1,367 @@ +# Copyright 2015, 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +import abc +import unittest + +from _framework.face import interfaces +from _framework.face.testing import callback as testing_callback +from _framework.face.testing import control +from _framework.face.testing import coverage +from _framework.face.testing import digest +from _framework.face.testing import stock_service +from _framework.face.testing import test_case + +_TIMEOUT = 3 + + +class EventInvocationSynchronousEventServiceTestCase( + test_case.FaceTestCase, coverage.FullCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, None) + + self.server, self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + {}, {}, {}, {}, + self.digest.event_unary_unary_methods, + self.digest.event_unary_stream_methods, + self.digest.event_stream_unary_methods, + self.digest.event_stream_stream_methods, + None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + response = callback.response() + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + responses = callback.responses() + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + response = callback.response() + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + responses = callback.responses() + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + # pylint: disable=cell-var-from-loop + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + first_callback = testing_callback.Callback() + second_callback = testing_callback.Callback() + + def make_second_invocation(first_response): + first_callback.complete(first_response) + self.stub.event_value_in_value_out( + name, second_request, second_callback.complete, + second_callback.abort, _TIMEOUT) + + self.stub.event_value_in_value_out( + name, first_request, make_second_invocation, first_callback.abort, + _TIMEOUT) + second_callback.block_until_terminated() + + first_response = first_callback.response() + second_response = second_callback.response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.EXPIRED, callback.abortion()) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.EXPIRED, callback.abortion()) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + callback = testing_callback.Callback() + + self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.EXPIRED, callback.abortion()) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + callback.block_until_terminated() + + self.assertEqual(interfaces.EXPIRED, callback.abortion()) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.fail(): + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.SERVICER_FAILURE, callback.abortion()) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.fail(): + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.SERVICER_FAILURE, callback.abortion()) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + with self.control.fail(): + unused_call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + + self.assertEqual(interfaces.SERVICER_FAILURE, callback.abortion()) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + with self.control.fail(): + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + + self.assertEqual(interfaces.SERVICER_FAILURE, callback.abortion()) + + def testParallelInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + first_callback = testing_callback.Callback() + second_request = test_messages.request() + second_callback = testing_callback.Callback() + + self.stub.event_value_in_value_out( + name, first_request, first_callback.complete, first_callback.abort, + _TIMEOUT) + self.stub.event_value_in_value_out( + name, second_request, second_callback.complete, + second_callback.abort, _TIMEOUT) + first_callback.block_until_terminated() + second_callback.block_until_terminated() + + first_response = first_callback.response() + second_response = second_callback.response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + call = self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.CANCELLED, callback.abortion()) + + def testCancelledUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + call = self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.CANCELLED, callback.abortion()) + + def testCancelledStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.CANCELLED, callback.abortion()) + + def testCancelledStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + callback = testing_callback.Callback() + + call, unused_request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.CANCELLED, callback.abortion()) diff --git a/src/python/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py new file mode 100644 index 00000000000..cf8b2eeb959 --- /dev/null +++ b/src/python/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py @@ -0,0 +1,377 @@ +# Copyright 2015, 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +import abc +import contextlib +import threading +import unittest + +from _framework.face import exceptions +from _framework.face.testing import control +from _framework.face.testing import coverage +from _framework.face.testing import digest +from _framework.face.testing import stock_service +from _framework.face.testing import test_case +from _framework.foundation import future +from _framework.foundation import logging_pool + +_TIMEOUT = 3 +_MAXIMUM_POOL_SIZE = 100 + + +class _PauseableIterator(object): + + def __init__(self, upstream): + self._upstream = upstream + self._condition = threading.Condition() + self._paused = False + + @contextlib.contextmanager + def pause(self): + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while self._paused: + self._condition.wait() + return next(self._upstream) + + +class FutureInvocationAsynchronousEventServiceTestCase( + test_case.FaceTestCase, coverage.FullCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, self.digest_pool) + + self.server, self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + {}, {}, {}, {}, + self.digest.event_unary_unary_methods, + self.digest.event_unary_stream_methods, + self.digest.event_stream_unary_methods, + self.digest.event_stream_stream_methods, + None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + self.digest_pool.shutdown(wait=True) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + response = response_future.outcome().return_value + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_future = self.stub.future_stream_in_value_out( + name, request_iterator, _TIMEOUT) + response = response_future.outcome().return_value + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, request_iterator, _TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self.stub.future_value_in_value_out( + name, first_request, _TIMEOUT) + first_response = first_response_future.outcome().return_value + + test_messages.verify(first_request, first_response, self) + + second_response_future = self.stub.future_value_in_value_out( + name, second_request, _TIMEOUT) + second_response = second_response_future.outcome().return_value + + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + outcome = response_future.outcome() + + self.assertIsInstance( + outcome.exception, exceptions.ExpirationError) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), _TIMEOUT) + outcome = response_future.outcome() + + self.assertIsInstance( + outcome.exception, exceptions.ExpirationError) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(): + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + outcome = response_future.outcome() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_callback before the + # expiration of the RPC. + self.assertIsInstance(outcome.exception, exceptions.ExpirationError) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self.control.fail(), self.assertRaises(exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), _TIMEOUT) + outcome = response_future.outcome() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_callback before the + # expiration of the RPC. + self.assertIsInstance(outcome.exception, exceptions.ExpirationError) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self.control.fail(), self.assertRaises(exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) + + def testParallelInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self.stub.future_value_in_value_out( + name, first_request, _TIMEOUT) + second_response_future = self.stub.future_value_in_value_out( + name, second_request, _TIMEOUT) + first_response = first_response_future.outcome().return_value + second_response = second_response_future.outcome().return_value + + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + cancelled = response_future.cancel() + + self.assertFalse(cancelled) + self.assertEqual(future.ABORTED, response_future.outcome().category) + + def testCancelledUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(exceptions.CancellationError): + next(response_iterator) + + def testCancelledStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), _TIMEOUT) + cancelled = response_future.cancel() + + self.assertFalse(cancelled) + self.assertEqual(future.ABORTED, response_future.outcome().category) + + def testCancelledStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(exceptions.CancellationError): + next(response_iterator) diff --git a/src/python/_framework/face/testing/interfaces.py b/src/python/_framework/face/testing/interfaces.py new file mode 100644 index 00000000000..253f6f118df --- /dev/null +++ b/src/python/_framework/face/testing/interfaces.py @@ -0,0 +1,117 @@ +# Copyright 2015, 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. + +"""Interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# cardinality is referenced from specification in this module. +from _framework.common import cardinality # pylint: disable=unused-import + + +class Method(object): + """An RPC method to be used in tests of RPC implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Identify the name of the method. + + Returns: + The name of the method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cardinality(self): + """Identify the cardinality of the method. + + Returns: + A cardinality.Cardinality value describing the streaming semantics of the + method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def request_class(self): + """Identify the class used for the method's request objects. + + Returns: + The class object of the class to which the method's request objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_class(self): + """Identify the class used for the method's response objects. + + Returns: + The class object of the class to which the method's response objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize the given request object. + + Args: + request: A request object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """Synthesize a request object from a given bytestring. + + Args: + serialized_request: A bytestring deserializable into a request object + appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize the given response object. + + Args: + response: A response object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """Synthesize a response object from a given bytestring. + + Args: + serialized_response: A bytestring deserializable into a response object + appropriate for this method. + """ + raise NotImplementedError() diff --git a/src/python/_framework/face/testing/serial.py b/src/python/_framework/face/testing/serial.py new file mode 100644 index 00000000000..47fc5822de9 --- /dev/null +++ b/src/python/_framework/face/testing/serial.py @@ -0,0 +1,70 @@ +# Copyright 2015, 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. + +"""Utility for serialization in the context of test RPC services.""" + +import collections + + +class Serialization( + collections.namedtuple( + '_Serialization', + ['request_serializers', + 'request_deserializers', + 'response_serializers', + 'response_deserializers'])): + """An aggregation of serialization behaviors for an RPC service. + + Attributes: + request_serializers: A dict from method name to request object serializer + behavior. + request_deserializers: A dict from method name to request object + deserializer behavior. + response_serializers: A dict from method name to response object serializer + behavior. + response_deserializers: A dict from method name to response object + deserializer behavior. + """ + + +def serialization(methods): + """Creates a Serialization from a sequences of interfaces.Method objects.""" + request_serializers = {} + request_deserializers = {} + response_serializers = {} + response_deserializers = {} + for method in methods: + name = method.name() + request_serializers[name] = method.serialize_request + request_deserializers[name] = method.deserialize_request + response_serializers[name] = method.serialize_response + response_deserializers[name] = method.deserialize_response + return Serialization( + request_serializers, request_deserializers, response_serializers, + response_deserializers) diff --git a/src/python/_framework/face/testing/service.py b/src/python/_framework/face/testing/service.py new file mode 100644 index 00000000000..771346ec2e1 --- /dev/null +++ b/src/python/_framework/face/testing/service.py @@ -0,0 +1,337 @@ +# Copyright 2015, 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. + +"""Private interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# interfaces is referenced from specification in this module. +from _framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from _framework.face.testing import interfaces + + +class UnaryUnaryTestMethod(interfaces.Method): + """Like face_interfaces.EventValueInValueOutMethod but with a control.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_callback, context, control): + """Services an RPC that accepts one message and produces one message. + + Args: + request: The single request message for the RPC. + response_callback: A callback to be called to accept the response message + of the RPC. + context: An face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryUnaryTestMessages(object): + """A type for unary-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, response, test_case): + """Verifies that the computed response matches the given request. + + Args: + request: A request message. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class UnaryStreamTestMethod(interfaces.Method): + """Like face_interfaces.EventValueInStreamOutMethod but with a control.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_consumer, context, control): + """Services an RPC that takes one message and produces a stream of messages. + + Args: + request: The single request message for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: An RpcContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryStreamTestMessages(object): + """A type for unary-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, responses, test_case): + """Verifies that the computed responses match the given request. + + Args: + request: A request message. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and responses do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamUnaryTestMethod(interfaces.Method): + """Like face_interfaces.EventStreamInValueOutMethod but with a control.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_callback, context, control): + """Services an RPC that takes a stream of messages and produces one message. + + Args: + response_callback: A callback to be called to accept the response message + of the RPC. + context: An RpcContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamUnaryTestMessages(object): + """A type for stream-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, response, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamStreamTestMethod(interfaces.Method): + """Like face_interfaces.EventStreamInStreamOutMethod but with a control.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_consumer, context, control): + """Services an RPC that accepts and produces streams of messages. + + Args: + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: An RpcContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamStreamTestMessages(object): + """A type for stream-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, responses, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and responses do not match, indicating + that there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class TestService(object): + """A specification of implemented RPC methods to use in tests.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Identifies the RPC service name used during the test. + + Returns: + The RPC service name to be used for the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_unary_scenarios(self): + """Affords unary-request-unary-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair + is a UnaryUnaryTestMethod object and the second element is a sequence + of UnaryUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_stream_scenarios(self): + """Affords unary-request-stream-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + UnaryStreamTestMethod object and the second element is a sequence of + UnaryStreamTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_unary_scenarios(self): + """Affords stream-request-unary-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + StreamUnaryTestMethod object and the second element is a sequence of + StreamUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_stream_scenarios(self): + """Affords stream-request-stream-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + StreamStreamTestMethod object and the second element is a sequence of + StreamStreamTestMethodMessages objects. + """ + raise NotImplementedError() diff --git a/src/python/_framework/face/testing/stock_service.py b/src/python/_framework/face/testing/stock_service.py new file mode 100644 index 00000000000..bd82877e838 --- /dev/null +++ b/src/python/_framework/face/testing/stock_service.py @@ -0,0 +1,374 @@ +# Copyright 2015, 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. + +"""Examples of Python implementations of the stock.proto Stock service.""" + +from _framework.common import cardinality +from _framework.face.testing import service +from _framework.foundation import abandonment +from _framework.foundation import stream +from _framework.foundation import stream_util +from _junkdrawer import stock_pb2 + +SYMBOL_FORMAT = 'test symbol:%03d' +STREAM_LENGTH = 400 + +# A test-appropriate security-pricing function. :-P +_price = lambda symbol_name: float(hash(symbol_name) % 4096) + + +def _get_last_trade_price(stock_request, stock_reply_callback, control, active): + """A unary-request, unary-response test method.""" + control.control() + if active(): + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol))) + else: + raise abandonment.Abandoned() + + +def _get_last_trade_price_multiple(stock_reply_consumer, control, active): + """A stream-request, stream-response test method.""" + def stock_reply_for_stock_request(stock_request): + control.control() + if active(): + return stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol)) + else: + raise abandonment.Abandoned() + return stream_util.TransformingConsumer( + stock_reply_for_stock_request, stock_reply_consumer) + + +def _watch_future_trades(stock_request, stock_reply_consumer, control, active): + """A unary-request, stream-response test method.""" + base_price = _price(stock_request.symbol) + for index in range(stock_request.num_trades_to_watch): + control.control() + if active(): + stock_reply_consumer.consume( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=base_price + index)) + else: + raise abandonment.Abandoned() + stock_reply_consumer.terminate() + + +def _get_highest_trade_price(stock_reply_callback, control, active): + """A stream-request, unary-response test method.""" + + class StockRequestConsumer(stream.Consumer): + """Keeps an ongoing record of the most valuable symbol yet consumed.""" + + def __init__(self): + self._symbol = None + self._price = None + + def consume(self, stock_request): + control.control() + if active(): + if self._price is None: + self._symbol = stock_request.symbol + self._price = _price(stock_request.symbol) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + self._symbol = stock_request.symbol + self._price = candidate_price + + def terminate(self): + control.control() + if active(): + if self._symbol is None: + raise ValueError() + else: + stock_reply_callback( + stock_pb2.StockReply(symbol=self._symbol, price=self._price)) + self._symbol = None + self._price = None + + def consume_and_terminate(self, stock_request): + control.control() + if active(): + if self._price is None: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, + price=_price(stock_request.symbol))) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=candidate_price)) + else: + stock_reply_callback( + stock_pb2.StockReply( + symbol=self._symbol, price=self._price)) + + self._symbol = None + self._price = None + + return StockRequestConsumer() + + +class GetLastTradePrice(service.UnaryUnaryTestMethod): + """GetLastTradePrice for use in tests.""" + + def name(self): + return 'GetLastTradePrice' + + def cardinality(self): + return cardinality.Cardinality.UNARY_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_callback, context, control): + _get_last_trade_price( + request, response_callback, control, context.is_active) + + +class GetLastTradePriceMessages(service.UnaryUnaryTestMessages): + + def __init__(self): + self._index = 0 + + def request(self): + symbol = SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest(symbol=symbol) + + def verify(self, request, response, test_case): + test_case.assertEqual(request.symbol, response.symbol) + test_case.assertEqual(_price(request.symbol), response.price) + + +class GetLastTradePriceMultiple(service.StreamStreamTestMethod): + """GetLastTradePriceMultiple for use in tests.""" + + def name(self): + return 'GetLastTradePriceMultiple' + + def cardinality(self): + return cardinality.Cardinality.STREAM_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_consumer, context, control): + return _get_last_trade_price_multiple( + response_consumer, control, context.is_active) + + +class GetLastTradePriceMultipleMessages(service.StreamStreamTestMessages): + """Pairs of message streams for use with GetLastTradePriceMultiple.""" + + def __init__(self): + self._index = 0 + + def requests(self): + base_index = self._index + self._index += 1 + return [ + stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % (base_index + index)) + for index in range(STREAM_LENGTH)] + + def verify(self, requests, responses, test_case): + test_case.assertEqual(len(requests), len(responses)) + for stock_request, stock_reply in zip(requests, responses): + test_case.assertEqual(stock_request.symbol, stock_reply.symbol) + test_case.assertEqual(_price(stock_request.symbol), stock_reply.price) + + +class WatchFutureTrades(service.UnaryStreamTestMethod): + """WatchFutureTrades for use in tests.""" + + def name(self): + return 'WatchFutureTrades' + + def cardinality(self): + return cardinality.Cardinality.UNARY_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_consumer, context, control): + _watch_future_trades(request, response_consumer, control, context.is_active) + + +class WatchFutureTradesMessages(service.UnaryStreamTestMessages): + """Pairs of a single request message and a sequence of response messages.""" + + def __init__(self): + self._index = 0 + + def request(self): + symbol = SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest( + symbol=symbol, num_trades_to_watch=STREAM_LENGTH) + + def verify(self, request, responses, test_case): + test_case.assertEqual(STREAM_LENGTH, len(responses)) + base_price = _price(request.symbol) + for index, response in enumerate(responses): + test_case.assertEqual(base_price + index, response.price) + + +class GetHighestTradePrice(service.StreamUnaryTestMethod): + """GetHighestTradePrice for use in tests.""" + + def name(self): + return 'GetHighestTradePrice' + + def cardinality(self): + return cardinality.Cardinality.STREAM_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_callback, context, control): + return _get_highest_trade_price( + response_callback, control, context.is_active) + + +class GetHighestTradePriceMessages(service.StreamUnaryTestMessages): + + def requests(self): + return [ + stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % index) + for index in range(STREAM_LENGTH)] + + def verify(self, requests, response, test_case): + price = None + symbol = None + for stock_request in requests: + current_symbol = stock_request.symbol + current_price = _price(current_symbol) + if price is None or price < current_price: + price = current_price + symbol = current_symbol + test_case.assertEqual(price, response.price) + test_case.assertEqual(symbol, response.symbol) + + +class StockTestService(service.TestService): + """A corpus of test data with one method of each RPC cardinality.""" + + def name(self): + return 'Stock' + + def unary_unary_scenarios(self): + return { + 'GetLastTradePrice': ( + GetLastTradePrice(), [GetLastTradePriceMessages()]), + } + + def unary_stream_scenarios(self): + return { + 'WatchFutureTrades': ( + WatchFutureTrades(), [WatchFutureTradesMessages()]), + } + + def stream_unary_scenarios(self): + return { + 'GetHighestTradePrice': ( + GetHighestTradePrice(), [GetHighestTradePriceMessages()]) + } + + def stream_stream_scenarios(self): + return { + 'GetLastTradePriceMultiple': ( + GetLastTradePriceMultiple(), [GetLastTradePriceMultipleMessages()]), + } + + +STOCK_TEST_SERVICE = StockTestService() diff --git a/src/python/_framework/face/testing/test_case.py b/src/python/_framework/face/testing/test_case.py new file mode 100644 index 00000000000..09b5a67f5ad --- /dev/null +++ b/src/python/_framework/face/testing/test_case.py @@ -0,0 +1,111 @@ +# Copyright 2015, 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. + +"""Tools for creating tests of implementations of the Face layer.""" + +import abc + +# face_interfaces and interfaces are referenced in specification in this module. +from _framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from _framework.face.testing import interfaces # pylint: disable=unused-import + + +class FaceTestCase(object): + """Describes a test of the Face Layer of RPC Framework. + + Concrete subclasses must also inherit from unittest.TestCase and from at least + one class that defines test methods. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_up_implementation( + self, + name, + methods, + inline_value_in_value_out_methods, + inline_value_in_stream_out_methods, + inline_stream_in_value_out_methods, + inline_stream_in_stream_out_methods, + event_value_in_value_out_methods, + event_value_in_stream_out_methods, + event_stream_in_value_out_methods, + event_stream_in_stream_out_methods, + multi_method): + """Instantiates the Face Layer implementation under test. + + Args: + name: The service name to be used in the test. + methods: A sequence of interfaces.Method objects describing the RPC + methods that will be called during the test. + inline_value_in_value_out_methods: A dictionary from string method names + to face_interfaces.InlineValueInValueOutMethod implementations of those + methods. + inline_value_in_stream_out_methods: A dictionary from string method names + to face_interfaces.InlineValueInStreamOutMethod implementations of those + methods. + inline_stream_in_value_out_methods: A dictionary from string method names + to face_interfaces.InlineStreamInValueOutMethod implementations of those + methods. + inline_stream_in_stream_out_methods: A dictionary from string method names + to face_interfaces.InlineStreamInStreamOutMethod implementations of + those methods. + event_value_in_value_out_methods: A dictionary from string method names + to face_interfaces.EventValueInValueOutMethod implementations of those + methods. + event_value_in_stream_out_methods: A dictionary from string method names + to face_interfaces.EventValueInStreamOutMethod implementations of those + methods. + event_stream_in_value_out_methods: A dictionary from string method names + to face_interfaces.EventStreamInValueOutMethod implementations of those + methods. + event_stream_in_stream_out_methods: A dictionary from string method names + to face_interfaces.EventStreamInStreamOutMethod implementations of those + methods. + multi_method: An face_interfaces.MultiMethod, or None. + + Returns: + A sequence of length three the first element of which is a + face_interfaces.Server, the second element of which is a + face_interfaces.Stub, (both of which are backed by the given method + implementations), and the third element of which is an arbitrary memo + object to be kept and passed to tearDownImplementation at the conclusion + of the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def tear_down_implementation(self, memo): + """Destroys the Face layer implementation under test. + + Args: + memo: The object from the third position of the return value of + set_up_implementation. + """ + raise NotImplementedError() diff --git a/src/python/_junkdrawer/__init__.py b/src/python/_junkdrawer/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/_junkdrawer/stock_pb2.py b/src/python/_junkdrawer/stock_pb2.py new file mode 100644 index 00000000000..eef18f82d65 --- /dev/null +++ b/src/python/_junkdrawer/stock_pb2.py @@ -0,0 +1,152 @@ +# Copyright 2015, 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. + +# TODO(nathaniel): Remove this from source control after having made +# generation from the stock.proto source part of GRPC's build-and-test +# process. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: stock.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='stock.proto', + package='stock', + serialized_pb=_b('\n\x0bstock.proto\x12\x05stock\">\n\x0cStockRequest\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x1e\n\x13num_trades_to_watch\x18\x02 \x01(\x05:\x01\x30\"+\n\nStockReply\x12\r\n\x05price\x18\x01 \x01(\x02\x12\x0e\n\x06symbol\x18\x02 \x01(\t2\x96\x02\n\x05Stock\x12=\n\x11GetLastTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x12I\n\x19GetLastTradePriceMultiple\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01\x30\x01\x12?\n\x11WatchFutureTrades\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x30\x01\x12\x42\n\x14GetHighestTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_STOCKREQUEST = _descriptor.Descriptor( + name='StockRequest', + full_name='stock.StockRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='symbol', full_name='stock.StockRequest.symbol', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='num_trades_to_watch', full_name='stock.StockRequest.num_trades_to_watch', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=84, +) + + +_STOCKREPLY = _descriptor.Descriptor( + name='StockReply', + full_name='stock.StockReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='price', full_name='stock.StockReply.price', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='symbol', full_name='stock.StockReply.symbol', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=86, + serialized_end=129, +) + +DESCRIPTOR.message_types_by_name['StockRequest'] = _STOCKREQUEST +DESCRIPTOR.message_types_by_name['StockReply'] = _STOCKREPLY + +StockRequest = _reflection.GeneratedProtocolMessageType('StockRequest', (_message.Message,), dict( + DESCRIPTOR = _STOCKREQUEST, + __module__ = 'stock_pb2' + # @@protoc_insertion_point(class_scope:stock.StockRequest) + )) +_sym_db.RegisterMessage(StockRequest) + +StockReply = _reflection.GeneratedProtocolMessageType('StockReply', (_message.Message,), dict( + DESCRIPTOR = _STOCKREPLY, + __module__ = 'stock_pb2' + # @@protoc_insertion_point(class_scope:stock.StockReply) + )) +_sym_db.RegisterMessage(StockReply) + + +# @@protoc_insertion_point(module_scope) diff --git a/tools/run_tests/run_python.sh b/tools/run_tests/run_python.sh index 0d5ed0238db..ef40602eb3b 100755 --- a/tools/run_tests/run_python.sh +++ b/tools/run_tests/run_python.sh @@ -6,5 +6,6 @@ set -ex cd $(dirname $0)/../.. root=`pwd` -python2.7_virtual_environment/bin/python2.7 -B -m unittest discover -s src/python -p '*.py' -python3.4 -B -m unittest discover -s src/python -p '*.py' +PYTHONPATH=third_party/protobuf/python python2.7_virtual_environment/bin/python2.7 -B -m unittest discover -s src/python -p '*.py' +# TODO(nathaniel): Get this working again (requires 3.X-friendly protobuf) +# python3.4 -B -m unittest discover -s src/python -p '*.py' From 1ea024dbe26420dc3a9f66e5add922dfd91095d1 Mon Sep 17 00:00:00 2001 From: Nicolas 'Pixel' Noble Date: Sat, 24 Jan 2015 00:37:10 -0800 Subject: [PATCH 133/143] Adding Visual Studio filters. --- .../vs2013/gpr.vcxproj.filters.template | 2 + .../vs2013/grpc.vcxproj.filters.template | 2 + .../grpc_unsecure.vcxproj.filters.template | 2 + .../vs2013/vcxproj.filters_defs.include | 64 ++ vsprojects/vs2013/gpr.vcxproj.filters | 193 ++++++ vsprojects/vs2013/grpc.vcxproj.filters | 628 ++++++++++++++++++ .../vs2013/grpc_unsecure.vcxproj.filters | 547 +++++++++++++++ 7 files changed, 1438 insertions(+) create mode 100644 templates/vsprojects/vs2013/gpr.vcxproj.filters.template create mode 100644 templates/vsprojects/vs2013/grpc.vcxproj.filters.template create mode 100644 templates/vsprojects/vs2013/grpc_unsecure.vcxproj.filters.template create mode 100644 templates/vsprojects/vs2013/vcxproj.filters_defs.include create mode 100644 vsprojects/vs2013/gpr.vcxproj.filters create mode 100644 vsprojects/vs2013/grpc.vcxproj.filters create mode 100644 vsprojects/vs2013/grpc_unsecure.vcxproj.filters diff --git a/templates/vsprojects/vs2013/gpr.vcxproj.filters.template b/templates/vsprojects/vs2013/gpr.vcxproj.filters.template new file mode 100644 index 00000000000..0ed1ed85d0d --- /dev/null +++ b/templates/vsprojects/vs2013/gpr.vcxproj.filters.template @@ -0,0 +1,2 @@ +<%namespace file="vcxproj.filters_defs.include" import="gen_project"/>\ +${gen_project('gpr', libs, targets)} diff --git a/templates/vsprojects/vs2013/grpc.vcxproj.filters.template b/templates/vsprojects/vs2013/grpc.vcxproj.filters.template new file mode 100644 index 00000000000..0327c9422d9 --- /dev/null +++ b/templates/vsprojects/vs2013/grpc.vcxproj.filters.template @@ -0,0 +1,2 @@ +<%namespace file="vcxproj.filters_defs.include" import="gen_project"/>\ +${gen_project('grpc', libs, targets)} diff --git a/templates/vsprojects/vs2013/grpc_unsecure.vcxproj.filters.template b/templates/vsprojects/vs2013/grpc_unsecure.vcxproj.filters.template new file mode 100644 index 00000000000..48cbbd30bb6 --- /dev/null +++ b/templates/vsprojects/vs2013/grpc_unsecure.vcxproj.filters.template @@ -0,0 +1,2 @@ +<%namespace file="vcxproj.filters_defs.include" import="gen_project"/>\ +${gen_project('grpc_unsecure', libs, targets)} diff --git a/templates/vsprojects/vs2013/vcxproj.filters_defs.include b/templates/vsprojects/vs2013/vcxproj.filters_defs.include new file mode 100644 index 00000000000..c25718b8025 --- /dev/null +++ b/templates/vsprojects/vs2013/vcxproj.filters_defs.include @@ -0,0 +1,64 @@ +<%! + import re + import hashlib + + def calc_to_filter(path): + return '\\'.join(path.split('/')[:-1]) +%>\ +<%def name="to_windows_path(path)">${path.replace('/','\\')}\ +<%def name="to_filter(path)">${calc_to_filter(path)}\ +<%def name="filter_to_guid(proj, filter)">${re.sub('(........)(....)(....)(....)', r'\1-\2-\3-\4-', hashlib.md5(''.join([filter, proj])).hexdigest())}\ +<%def name="gen_project(name, libs, targets)">\ +% for project in vsprojects: + % if project.name == name: + + + % if project.get('src',[]): + + % for src_name in project.src: + + ${to_filter(src_name)} + + % endfor + + % endif + % if project.get('public_headers',[]): + + % for public_header in project.public_headers: + + ${to_filter(public_header)} + + % endfor + + % endif + % if project.get('headers',[]): + + % for header in project.headers: + + ${to_filter(header)} + + % endfor + + % endif +<% + filters = set() + files = project.get('src', []) + project.get('public_headers', []) + project.get('headers', []) + for file in files: + filter = calc_to_filter(file) + paths = filter.split('\\') + for i in range(len(paths)): + filters.add('\\'.join(paths[:i + 1])) + + filters = sorted(filters) +%> + + % for filter in filters: + + {${filter_to_guid(project.name, filter)}} + + % endfor + + + % endif +% endfor +\ \ No newline at end of file diff --git a/vsprojects/vs2013/gpr.vcxproj.filters b/vsprojects/vs2013/gpr.vcxproj.filters new file mode 100644 index 00000000000..c1fccfff3be --- /dev/null +++ b/vsprojects/vs2013/gpr.vcxproj.filters @@ -0,0 +1,193 @@ + + + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + src\core\support + + + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + include\grpc\support + + + + + src\core\support + + + src\core\support + + + src\core\support + + + + + + {9ea89137-2bf7-b6d9-b7af-7cb4d1b74928} + + + {e6957ec1-85ba-6515-03c0-e12878045b1f} + + + {31c42000-3ed7-95e1-d076-df814b72cdee} + + + {60eb2826-e58b-cb10-a98d-fe04727398a2} + + + {c5e1baa7-de77-beb1-9675-942261648f79} + + + {bb116f2a-ea2a-c233-82da-0c54e3cbfec1} + + + + diff --git a/vsprojects/vs2013/grpc.vcxproj.filters b/vsprojects/vs2013/grpc.vcxproj.filters new file mode 100644 index 00000000000..ad3f1aece0b --- /dev/null +++ b/vsprojects/vs2013/grpc.vcxproj.filters @@ -0,0 +1,628 @@ + + + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\tsi + + + src\core\tsi + + + src\core\tsi + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\compression + + + src\core\compression + + + src\core\httpcli + + + src\core\httpcli + + + src\core\httpcli + + + src\core\httpcli + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + third_party\cJSON + + + + + include\grpc + + + include\grpc + + + include\grpc + + + include\grpc + + + include\grpc + + + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\security + + + src\core\tsi + + + src\core\tsi + + + src\core\tsi + + + src\core\tsi + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\compression + + + src\core\compression + + + src\core\httpcli + + + src\core\httpcli + + + src\core\httpcli + + + src\core\httpcli + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + + + + {968de0a1-346d-b75a-6f19-6a55119b8235} + + + {880c644d-b84f-cfca-98bd-e145f36232ab} + + + {d538af37-07b2-062b-fa2a-d9f882cb2737} + + + {ea745680-21ea-9c5e-679b-64dc40562d08} + + + {d897b6c3-c555-234e-a589-b4f008063615} + + + {263cb913-dfe6-42a4-096b-cac231f76305} + + + {a9bc00ad-835f-c625-c6d9-6a1324f98b9f} + + + {1baf3894-af37-e647-bdbc-95dc17ed0073} + + + {1d850ac6-e639-4eab-5338-4ba40272fcc9} + + + {0ef49896-2313-4a3f-1ce2-716fa0e5c6ca} + + + {aeb18e82-5d25-0aad-8b02-a0a3470073ce} + + + {168fa1b1-1c18-eb55-9a4d-746bc58df2c1} + + + {b8b623c3-a168-a2b1-0d5f-b70a1f1cd8d2} + + + {0b0f9ab1-efa4-7f03-e446-6fb9b5227e84} + + + {aaab30a4-2a15-732e-c141-3fbc0f0f5a7a} + + + {332d0840-2c7a-bb09-8e58-585a6fb3959f} + + + + diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj.filters b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters new file mode 100644 index 00000000000..636e95fbacc --- /dev/null +++ b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters @@ -0,0 +1,547 @@ + + + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\compression + + + src\core\compression + + + src\core\httpcli + + + src\core\httpcli + + + src\core\httpcli + + + src\core\httpcli + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + third_party\cJSON + + + + + include\grpc + + + include\grpc + + + include\grpc + + + include\grpc + + + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\channel + + + src\core\compression + + + src\core\compression + + + src\core\httpcli + + + src\core\httpcli + + + src\core\httpcli + + + src\core\httpcli + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\iomgr + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\statistics + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\surface + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport\chttp2 + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + src\core\transport + + + + + + {10076c7e-7c8e-8005-0c81-64454af2cbc8} + + + {77b9717b-b8d8-dd5f-14bb-a3e96809a70a} + + + {aaf326a1-c884-46ea-875a-cbbd9983e539} + + + {88491077-386b-2039-d14c-0c40136b5f7a} + + + {cc102c4b-66ff-cf4c-2288-d76327e1a183} + + + {2e3aca1d-223d-10a1-b282-7f9fc68ee6f5} + + + {1ba3a245-47e7-89b5-b0c9-aca758bd0277} + + + {a9df8b24-ecea-ff6d-8999-d8fa54cd70bf} + + + {e084164c-a069-00e3-db35-4e0b1cd6f0b7} + + + {6cd0127e-c24b-d43c-38f5-198db8d4322a} + + + {6687ff98-e36e-c0b1-2756-1bc79edec406} + + + {5fcd6206-f774-9ae6-4b85-305d6a723843} + + + {025c051e-8eba-125b-67f9-173f95176eb2} + + + {7d75397e-988a-baac-897e-2ea7b43d5dd9} + + + + From d5a99855229542c149a91e4bb688442488f3c8c1 Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Sat, 24 Jan 2015 01:27:48 -0800 Subject: [PATCH 134/143] Fixing a few winsocket misuses. --- Makefile | 10 +++++----- build.json | 2 +- src/core/httpcli/httpcli.c | 1 + .../{resolve_address_posix.c => resolve_address.c} | 5 +---- src/core/iomgr/sockaddr_posix.h | 4 ++++ src/core/iomgr/sockaddr_utils.c | 1 - src/core/iomgr/sockaddr_win32.h | 2 ++ src/core/surface/channel_create.c | 2 ++ src/core/surface/secure_channel_create.c | 2 ++ src/core/transport/metadata.c | 1 + vsprojects/vs2013/grpc.vcxproj | 2 +- vsprojects/vs2013/grpc.vcxproj.filters | 2 +- vsprojects/vs2013/grpc_unsecure.vcxproj | 2 +- vsprojects/vs2013/grpc_unsecure.vcxproj.filters | 2 +- 14 files changed, 23 insertions(+), 15 deletions(-) rename src/core/iomgr/{resolve_address_posix.c => resolve_address.c} (98%) diff --git a/Makefile b/Makefile index dff032fa65c..f4bb942b527 100644 --- a/Makefile +++ b/Makefile @@ -1396,7 +1396,7 @@ LIBGRPC_SRC = \ src/core/iomgr/pollset_multipoller_with_poll_posix.c \ src/core/iomgr/pollset_posix.c \ src/core/iomgr/pollset_windows.c \ - src/core/iomgr/resolve_address_posix.c \ + src/core/iomgr/resolve_address.c \ src/core/iomgr/sockaddr_utils.c \ src/core/iomgr/socket_utils_common_posix.c \ src/core/iomgr/socket_utils_linux.c \ @@ -1515,7 +1515,7 @@ src/core/iomgr/pollset_kick_posix.c: $(OPENSSL_DEP) src/core/iomgr/pollset_multipoller_with_poll_posix.c: $(OPENSSL_DEP) src/core/iomgr/pollset_posix.c: $(OPENSSL_DEP) src/core/iomgr/pollset_windows.c: $(OPENSSL_DEP) -src/core/iomgr/resolve_address_posix.c: $(OPENSSL_DEP) +src/core/iomgr/resolve_address.c: $(OPENSSL_DEP) src/core/iomgr/sockaddr_utils.c: $(OPENSSL_DEP) src/core/iomgr/socket_utils_common_posix.c: $(OPENSSL_DEP) src/core/iomgr/socket_utils_linux.c: $(OPENSSL_DEP) @@ -1655,7 +1655,7 @@ objs/$(CONFIG)/src/core/iomgr/pollset_kick_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_multipoller_with_poll_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_windows.o: -objs/$(CONFIG)/src/core/iomgr/resolve_address_posix.o: +objs/$(CONFIG)/src/core/iomgr/resolve_address.o: objs/$(CONFIG)/src/core/iomgr/sockaddr_utils.o: objs/$(CONFIG)/src/core/iomgr/socket_utils_common_posix.o: objs/$(CONFIG)/src/core/iomgr/socket_utils_linux.o: @@ -1815,7 +1815,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/iomgr/pollset_multipoller_with_poll_posix.c \ src/core/iomgr/pollset_posix.c \ src/core/iomgr/pollset_windows.c \ - src/core/iomgr/resolve_address_posix.c \ + src/core/iomgr/resolve_address.c \ src/core/iomgr/sockaddr_utils.c \ src/core/iomgr/socket_utils_common_posix.c \ src/core/iomgr/socket_utils_linux.c \ @@ -1938,7 +1938,7 @@ objs/$(CONFIG)/src/core/iomgr/pollset_kick_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_multipoller_with_poll_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_posix.o: objs/$(CONFIG)/src/core/iomgr/pollset_windows.o: -objs/$(CONFIG)/src/core/iomgr/resolve_address_posix.o: +objs/$(CONFIG)/src/core/iomgr/resolve_address.o: objs/$(CONFIG)/src/core/iomgr/sockaddr_utils.o: objs/$(CONFIG)/src/core/iomgr/socket_utils_common_posix.o: objs/$(CONFIG)/src/core/iomgr/socket_utils_linux.o: diff --git a/build.json b/build.json index 1884ed22cb8..4416c3fdd49 100644 --- a/build.json +++ b/build.json @@ -129,7 +129,7 @@ "src/core/iomgr/pollset_multipoller_with_poll_posix.c", "src/core/iomgr/pollset_posix.c", "src/core/iomgr/pollset_windows.c", - "src/core/iomgr/resolve_address_posix.c", + "src/core/iomgr/resolve_address.c", "src/core/iomgr/sockaddr_utils.c", "src/core/iomgr/socket_utils_common_posix.c", "src/core/iomgr/socket_utils_linux.c", diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c index d6fd8ca8658..acd9fa7b55b 100644 --- a/src/core/httpcli/httpcli.c +++ b/src/core/httpcli/httpcli.c @@ -31,6 +31,7 @@ * */ +#include "src/core/iomgr/sockaddr.h" #include "src/core/httpcli/httpcli.h" #include diff --git a/src/core/iomgr/resolve_address_posix.c b/src/core/iomgr/resolve_address.c similarity index 98% rename from src/core/iomgr/resolve_address_posix.c rename to src/core/iomgr/resolve_address.c index f80eea7f46e..01681168ce4 100644 --- a/src/core/iomgr/resolve_address_posix.c +++ b/src/core/iomgr/resolve_address.c @@ -33,17 +33,14 @@ #define _POSIX_SOURCE +#include "src/core/iomgr/sockaddr.h" #include "src/core/iomgr/resolve_address.h" #include -#include -#include -#include #include #include "src/core/iomgr/iomgr_internal.h" #include "src/core/iomgr/sockaddr_utils.h" -#include "src/core/iomgr/socket_utils_posix.h" #include "src/core/support/string.h" #include #include diff --git a/src/core/iomgr/sockaddr_posix.h b/src/core/iomgr/sockaddr_posix.h index 79ef3ca3cf3..53c80386d43 100644 --- a/src/core/iomgr/sockaddr_posix.h +++ b/src/core/iomgr/sockaddr_posix.h @@ -34,7 +34,11 @@ #ifndef __GRPC_INTERNAL_IOMGR_SOCKADDR_POSIX_H_ #define __GRPC_INTERNAL_IOMGR_SOCKADDR_POSIX_H_ +#include #include +#include #include +#include +#include #endif /* __GRPC_INTERNAL_IOMGR_SOCKADDR_POSIX_H_ */ diff --git a/src/core/iomgr/sockaddr_utils.c b/src/core/iomgr/sockaddr_utils.c index 5bb11242843..07bf7b3a35c 100644 --- a/src/core/iomgr/sockaddr_utils.c +++ b/src/core/iomgr/sockaddr_utils.c @@ -33,7 +33,6 @@ #include "src/core/iomgr/sockaddr_utils.h" -#include #include #include diff --git a/src/core/iomgr/sockaddr_win32.h b/src/core/iomgr/sockaddr_win32.h index 751ac3d2e7b..cdea33fec07 100644 --- a/src/core/iomgr/sockaddr_win32.h +++ b/src/core/iomgr/sockaddr_win32.h @@ -34,4 +34,6 @@ #ifndef __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_ #define __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_ +#include + #endif // __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_ diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c index 6939b92c60c..d3faf0c996a 100644 --- a/src/core/surface/channel_create.c +++ b/src/core/surface/channel_create.c @@ -31,6 +31,8 @@ * */ +#include "src/core/iomgr/sockaddr.h" + #include #include diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c index a231b2708e6..defee797666 100644 --- a/src/core/surface/secure_channel_create.c +++ b/src/core/surface/secure_channel_create.c @@ -31,6 +31,8 @@ * */ +#include "src/core/iomgr/sockaddr.h" + #include #include diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c index 6881b871ecb..74bbb02134d 100644 --- a/src/core/transport/metadata.c +++ b/src/core/transport/metadata.c @@ -31,6 +31,7 @@ * */ +#include "src/core/iomgr/sockaddr.h" #include "src/core/transport/metadata.h" #include diff --git a/vsprojects/vs2013/grpc.vcxproj b/vsprojects/vs2013/grpc.vcxproj index 8249272f78c..be72b6c8212 100644 --- a/vsprojects/vs2013/grpc.vcxproj +++ b/vsprojects/vs2013/grpc.vcxproj @@ -258,7 +258,7 @@ - + diff --git a/vsprojects/vs2013/grpc.vcxproj.filters b/vsprojects/vs2013/grpc.vcxproj.filters index ad3f1aece0b..4f03c14b767 100644 --- a/vsprojects/vs2013/grpc.vcxproj.filters +++ b/vsprojects/vs2013/grpc.vcxproj.filters @@ -130,7 +130,7 @@ src\core\iomgr - + src\core\iomgr diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj b/vsprojects/vs2013/grpc_unsecure.vcxproj index 8249272f78c..be72b6c8212 100644 --- a/vsprojects/vs2013/grpc_unsecure.vcxproj +++ b/vsprojects/vs2013/grpc_unsecure.vcxproj @@ -258,7 +258,7 @@ - + diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj.filters b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters index 636e95fbacc..a7ba173dcc0 100644 --- a/vsprojects/vs2013/grpc_unsecure.vcxproj.filters +++ b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters @@ -91,7 +91,7 @@ src\core\iomgr - + src\core\iomgr From 9074794e39c5ea4b555efc7c7b4870da46ca5b6f Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Sun, 25 Jan 2015 00:01:06 +0100 Subject: [PATCH 135/143] Fixing an extremely suspicious-looking paradigm in murmur_hash.c The current code is using the same variable name twice, k1, in two different scopes. A cursory inspection of the code looks like one is trying to sneak a vulnerability by masking off the reset of the k1 variable out of the loop; which is, in fact, what the official algorithm does explicitely. I guess this is a result of the C89-ization of the official algorithm, with the second k1 variable usually defined AFTER the loop. This change brings more sanity to the algorithm, making it much better-looking by removing all ambiguity in what we're doing. The end result is exactly the same, but at least we're explicit about it. --- src/core/support/murmur_hash.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/support/murmur_hash.c b/src/core/support/murmur_hash.c index 08b1eb80d8f..892e360968f 100644 --- a/src/core/support/murmur_hash.c +++ b/src/core/support/murmur_hash.c @@ -52,7 +52,7 @@ gpr_uint32 gpr_murmur_hash3(const void *key, size_t len, gpr_uint32 seed) { int i; gpr_uint32 h1 = seed; - gpr_uint32 k1 = 0; + gpr_uint32 k1; const gpr_uint32 c1 = 0xcc9e2d51; const gpr_uint32 c2 = 0x1b873593; @@ -62,7 +62,7 @@ gpr_uint32 gpr_murmur_hash3(const void *key, size_t len, gpr_uint32 seed) { /* body */ for (i = -nblocks; i; i++) { - gpr_uint32 k1 = GETBLOCK32(blocks, i); + k1 = GETBLOCK32(blocks, i); k1 *= c1; k1 = ROTL32(k1, 15); @@ -73,6 +73,8 @@ gpr_uint32 gpr_murmur_hash3(const void *key, size_t len, gpr_uint32 seed) { h1 = h1 * 5 + 0xe6546b64; } + k1 = 0; + /* tail */ switch (len & 3) { case 3: From 4097c1d2010ef378b8bdea65a5e7e4fd6788a698 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 26 Jan 2015 09:21:22 -0800 Subject: [PATCH 136/143] Shortened a function --- src/node/surface_client.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/node/surface_client.js b/src/node/surface_client.js index aba8feeada9..b63ae13e8dc 100644 --- a/src/node/surface_client.js +++ b/src/node/surface_client.js @@ -260,9 +260,8 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeBidiStreamRequest(metadata, deadline) { - var stream = client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - return stream; + return client.makeRequest(this.channel, method, serialize, + deserialize, metadata, deadline); } return makeBidiStreamRequest; } From 7eb6bb99d29c373994759494698a1a3f41fc5b46 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 26 Jan 2015 10:13:03 -0800 Subject: [PATCH 137/143] Removed all instances of == in js files --- src/node/examples/math_server.js | 7 ++++--- src/node/interop/interop_client.js | 2 +- src/node/server.js | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/node/examples/math_server.js b/src/node/examples/math_server.js index d649b4fd6d0..e65cfe30027 100644 --- a/src/node/examples/math_server.js +++ b/src/node/examples/math_server.js @@ -52,7 +52,8 @@ var Server = grpc.buildServer([math.Math.service]); */ function mathDiv(call, cb) { var req = call.request; - if (req.divisor == 0) { + // Unary + is explicit coersion to integer + if (+req.divisor === 0) { cb(new Error('cannot divide by zero')); } cb(null, { @@ -89,7 +90,7 @@ function mathSum(call, cb) { // Here, call is a standard readable Node object Stream var sum = 0; call.on('data', function(data) { - sum += data.num | 0; + sum += (+data.num); }); call.on('end', function() { cb(null, {num: sum}); @@ -104,7 +105,7 @@ function mathDivMany(stream) { Transform.call(this, options); } DivTransform.prototype._transform = function(div_args, encoding, callback) { - if (div_args.divisor == 0) { + if (+div_args.divisor === 0) { callback(new Error('cannot divide by zero')); } callback(null, { diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js index cf75b9a77ac..9306317b68f 100644 --- a/src/node/interop/interop_client.js +++ b/src/node/interop/interop_client.js @@ -183,7 +183,7 @@ function pingPong(client, done) { assert.equal(response.payload.body.limit - response.payload.body.offset, response_sizes[index]); index += 1; - if (index == 4) { + if (index === 4) { call.end(); } else { call.write({ diff --git a/src/node/server.js b/src/node/server.js index fc7144a9c14..fe50acb5a1c 100644 --- a/src/node/server.js +++ b/src/node/server.js @@ -233,7 +233,7 @@ function Server(options) { function handleNewCall(event) { var call = event.call; var data = event.data; - if (data == null) { + if (data === null) { return; } server.requestCall(handleNewCall); From 6ed6036e02c42be3991b971d4018693954bcc281 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Wed, 21 Jan 2015 18:17:25 -0800 Subject: [PATCH 138/143] Updates the Java Dockerfiles to pull source from GitHub --- tools/dockerfile/grpc_java/Dockerfile | 3 --- tools/dockerfile/grpc_java_base/Dockerfile | 14 ++++++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/dockerfile/grpc_java/Dockerfile b/tools/dockerfile/grpc_java/Dockerfile index 78659dedeb2..c51ee1f214a 100644 --- a/tools/dockerfile/grpc_java/Dockerfile +++ b/tools/dockerfile/grpc_java/Dockerfile @@ -1,9 +1,6 @@ # Dockerfile for the gRPC Java dev image FROM grpc/java_base -# Start the daemon that allows access to private git-on-borg repos -RUN /var/local/git/gcompute-tools/git-cookie-authdaemon - RUN cd /var/local/git/grpc-java/lib/okhttp && \ mvn -pl okhttp -am install RUN cd /var/local/git/grpc-java/lib/netty && \ diff --git a/tools/dockerfile/grpc_java_base/Dockerfile b/tools/dockerfile/grpc_java_base/Dockerfile index 44fa52c0e8b..40be7b1c5bd 100644 --- a/tools/dockerfile/grpc_java_base/Dockerfile +++ b/tools/dockerfile/grpc_java_base/Dockerfile @@ -20,14 +20,20 @@ ENV M2_HOME /var/local/apache-maven-3.2.1 ENV PATH $PATH:$JAVA_HOME/bin:$M2_HOME/bin ENV LD_LIBRARY_PATH /usr/local/lib -# Start the daemon that allows access to the protected git-on-borg repos -RUN /var/local/git/gcompute-tools/git-cookie-authdaemon +# Install a GitHub SSH service credential that gives access to the GitHub repo while it's private +# TODO: remove this once the repo is public +ADD .ssh .ssh +RUN chmod 600 .ssh/github.rsa +RUN mkdir -p $HOME/.ssh && echo 'Host github.com' > $HOME/.ssh/config +RUN echo " IdentityFile /.ssh/github.rsa" >> $HOME/.ssh/config +RUN echo 'StrictHostKeyChecking no' >> $HOME/.ssh/config -RUN git clone --recursive https://team.googlesource.com/one-platform-grpc-team/grpc-java /var/local/git/grpc-java +# Get the source from GitHub +RUN git clone --recursive git@github.com:google/grpc-java.git /var/local/git/grpc-java RUN cd /var/local/git/grpc-java/lib/okhttp && \ mvn -pl okhttp -am validate RUN cd /var/local/git/grpc-java/lib/netty && \ mvn -pl codec-http2 -am validate RUN cd /var/local/git/grpc-java && \ - mvn validate \ No newline at end of file + mvn validate From 7d5b1027edccdf9680a8b7c86a587e929ecae2c9 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Thu, 22 Jan 2015 12:09:50 -0800 Subject: [PATCH 139/143] Updates java_base to pull in protobuf --- tools/dockerfile/grpc_java_base/Dockerfile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/dockerfile/grpc_java_base/Dockerfile b/tools/dockerfile/grpc_java_base/Dockerfile index 40be7b1c5bd..91ee218b479 100644 --- a/tools/dockerfile/grpc_java_base/Dockerfile +++ b/tools/dockerfile/grpc_java_base/Dockerfile @@ -28,6 +28,13 @@ RUN mkdir -p $HOME/.ssh && echo 'Host github.com' > $HOME/.ssh/config RUN echo " IdentityFile /.ssh/github.rsa" >> $HOME/.ssh/config RUN echo 'StrictHostKeyChecking no' >> $HOME/.ssh/config +# Get the protobuf source from GitHub and install it +RUN git clone --recursive git@github.com:google/protobuf.git /var/local/git/protobuf +RUN cd /var/local/git/protobuf && \ + ./autogen.sh && \ + ./configure --prefix=/usr && \ + make -j12 && make check && make install && make clean + # Get the source from GitHub RUN git clone --recursive git@github.com:google/grpc-java.git /var/local/git/grpc-java From 41b6d9aad3a22edfdc7a17b3ed0be2483b93ba67 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Sun, 25 Jan 2015 16:25:03 -0800 Subject: [PATCH 140/143] Switched to using protobuf-2.6.1 for now --- tools/dockerfile/grpc_java_base/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/dockerfile/grpc_java_base/Dockerfile b/tools/dockerfile/grpc_java_base/Dockerfile index 91ee218b479..15c7b318167 100644 --- a/tools/dockerfile/grpc_java_base/Dockerfile +++ b/tools/dockerfile/grpc_java_base/Dockerfile @@ -29,7 +29,7 @@ RUN echo " IdentityFile /.ssh/github.rsa" >> $HOME/.ssh/config RUN echo 'StrictHostKeyChecking no' >> $HOME/.ssh/config # Get the protobuf source from GitHub and install it -RUN git clone --recursive git@github.com:google/protobuf.git /var/local/git/protobuf +RUN git clone --recursive --branch v2.6.1 git@github.com:google/protobuf.git /var/local/git/protobuf RUN cd /var/local/git/protobuf && \ ./autogen.sh && \ ./configure --prefix=/usr && \ From 12e12a39ab27a19560377d177041065366f2cdd3 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Wed, 21 Jan 2015 18:17:25 -0800 Subject: [PATCH 141/143] Updates the Java Dockerfiles to pull source from GitHub --- tools/dockerfile/grpc_java_base/Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/dockerfile/grpc_java_base/Dockerfile b/tools/dockerfile/grpc_java_base/Dockerfile index 15c7b318167..3271d1b2c2d 100644 --- a/tools/dockerfile/grpc_java_base/Dockerfile +++ b/tools/dockerfile/grpc_java_base/Dockerfile @@ -35,9 +35,6 @@ RUN cd /var/local/git/protobuf && \ ./configure --prefix=/usr && \ make -j12 && make check && make install && make clean -# Get the source from GitHub -RUN git clone --recursive git@github.com:google/grpc-java.git /var/local/git/grpc-java - RUN cd /var/local/git/grpc-java/lib/okhttp && \ mvn -pl okhttp -am validate RUN cd /var/local/git/grpc-java/lib/netty && \ From 3ff195d885dfb155b5d3112cc30a79a462077b33 Mon Sep 17 00:00:00 2001 From: Tim Emiola Date: Mon, 26 Jan 2015 10:29:46 -0800 Subject: [PATCH 142/143] Updates the java dockerfile and client command to reflect recent flag changes --- tools/dockerfile/grpc_java/Dockerfile | 2 +- tools/gce_setup/grpc_docker.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/dockerfile/grpc_java/Dockerfile b/tools/dockerfile/grpc_java/Dockerfile index c51ee1f214a..f234f514e6f 100644 --- a/tools/dockerfile/grpc_java/Dockerfile +++ b/tools/dockerfile/grpc_java/Dockerfile @@ -10,4 +10,4 @@ RUN cd /var/local/git/grpc-java && \ mvn install # Specify the default command such that the interop server runs on its known testing port -CMD ["/var/local/git/grpc-java/run-test-server.sh", "--transport=HTTP2_NETTY_TLS", "--grpc_version=2", "--port=8030"] +CMD ["/var/local/git/grpc-java/run-test-server.sh", "--use_tls=true", "--port=8030"] diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh index 145685305c3..d97f8294359 100755 --- a/tools/gce_setup/grpc_docker.sh +++ b/tools/gce_setup/grpc_docker.sh @@ -655,7 +655,7 @@ grpc_interop_gen_go_cmd() { grpc_interop_gen_java_cmd() { local cmd_prefix="sudo docker run grpc/java"; local test_script="/var/local/git/grpc-java/run-test-client.sh"; - local test_script+=" --transport=NETTY_TLS --grpc_version=2" + local test_script+=" --server_host_override=foo.test.google.com --use_test_ca=true --use_tls=true" local the_cmd="$cmd_prefix $test_script $@"; echo $the_cmd } @@ -683,7 +683,7 @@ grpc_interop_gen_php_cmd() { # flags= .... # generic flags to include the command # cmd=$($grpc_gen_test_cmd $flags) grpc_interop_gen_cxx_cmd() { - local cmd_prefix="sudo docker run grpc/cxx"; + local cmd_prefix="sudo docker run grpc/cxx"; local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl"; local the_cmd="$cmd_prefix $test_script $@"; echo $the_cmd From 74b3232d395cd1b9fe23bc56f83d726932d57606 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 26 Jan 2015 11:23:16 -0800 Subject: [PATCH 143/143] 1) Add protection header macro 2) remove some falgs with default values --- examples/tips/client.h | 5 +++++ examples/tips/client_main.cc | 17 ++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/tips/client.h b/examples/tips/client.h index 3f4f1fd8369..6ae9d506580 100644 --- a/examples/tips/client.h +++ b/examples/tips/client.h @@ -31,6 +31,9 @@ * */ +#ifndef __GRPCPP_EXAMPLES_TIPS_CLIENT_H_ +#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_ + #include #include @@ -52,3 +55,5 @@ class Client { } // namespace tips } // namespace examples } // namespace grpc + +#endif // __GRPCPP_EXAMPLES_TIPS_CLIENT_H_ diff --git a/examples/tips/client_main.cc b/examples/tips/client_main.cc index 17567b6f17a..f4a3b09601e 100644 --- a/examples/tips/client_main.cc +++ b/examples/tips/client_main.cc @@ -41,30 +41,29 @@ #include "examples/tips/client.h" #include "test/cpp/util/create_test_channel.h" -DEFINE_bool(enable_ssl, true, "Whether to use ssl/tls."); -DEFINE_bool(use_prod_roots, true, "True to use SSL roots for production GFE"); -DEFINE_int32(server_port, 0, "Server port."); -DEFINE_string(server_host, "127.0.0.1", "Server host to connect to"); -DEFINE_string(server_host_override, "foo.test.google.com", - "Override the server host which is sent in HTTP header"); +DEFINE_int32(server_port, 443, "Server port."); +DEFINE_string(server_host, + "pubsub-staging.googleapis.com", "Server host to connect to"); int main(int argc, char** argv) { grpc_init(); google::ParseCommandLineFlags(&argc, &argv, true); gpr_log(GPR_INFO, "Start TIPS client"); - GPR_ASSERT(FLAGS_server_port); const int host_port_buf_size = 1024; char host_port[host_port_buf_size]; snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(), FLAGS_server_port); std::shared_ptr channel( - grpc::CreateTestChannel(host_port, FLAGS_server_host_override, - FLAGS_enable_ssl, FLAGS_use_prod_roots)); + grpc::CreateTestChannel(host_port, + FLAGS_server_host, + true, // enable SSL + true)); // use prod roots grpc::examples::tips::Client client(channel); grpc::Status s = client.CreateTopic("test"); + gpr_log(GPR_INFO, "return code %d", s.code()); GPR_ASSERT(s.IsOk()); channel.reset();