aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--INSTALL6
-rw-r--r--Makefile330
-rw-r--r--README.md73
-rw-r--r--build.json58
-rw-r--r--examples/tips/client.cc60
-rw-r--r--examples/tips/client.h59
-rw-r--r--examples/tips/client_main.cc72
-rw-r--r--examples/tips/client_test.cc106
-rw-r--r--examples/tips/empty.proto13
-rw-r--r--examples/tips/label.proto48
-rw-r--r--examples/tips/pubsub.proto702
-rw-r--r--include/grpc/grpc.h14
-rw-r--r--include/grpc/support/port_platform.h8
-rw-r--r--src/core/channel/call_op_string.c2
-rw-r--r--src/core/channel/channel_args.c2
-rw-r--r--src/core/channel/client_channel.c6
-rw-r--r--src/core/channel/connected_channel.c40
-rw-r--r--src/core/httpcli/httpcli.c3
-rw-r--r--src/core/httpcli/httpcli_security_context.c2
-rw-r--r--src/core/iomgr/endpoint_pair_posix.c6
-rw-r--r--src/core/iomgr/fd_posix.c6
-rw-r--r--src/core/iomgr/pollset.h4
-rw-r--r--src/core/iomgr/pollset_kick.h6
-rw-r--r--src/core/iomgr/pollset_kick_posix.c6
-rw-r--r--src/core/iomgr/pollset_kick_windows.h45
-rw-r--r--src/core/iomgr/pollset_posix.c6
-rw-r--r--src/core/iomgr/pollset_windows.c (renamed from src/node/port_picker.js)20
-rw-r--r--src/core/iomgr/pollset_windows.h54
-rw-r--r--src/core/iomgr/resolve_address.c (renamed from src/core/iomgr/resolve_address_posix.c)7
-rw-r--r--src/core/iomgr/resolve_address.h6
-rw-r--r--src/core/iomgr/sockaddr_posix.h4
-rw-r--r--src/core/iomgr/sockaddr_utils.c3
-rw-r--r--src/core/iomgr/sockaddr_win32.h2
-rw-r--r--src/core/iomgr/socket_utils_common_posix.c12
-rw-r--r--src/core/iomgr/socket_utils_posix.c18
-rw-r--r--src/core/iomgr/tcp_client_posix.c6
-rw-r--r--src/core/iomgr/tcp_posix.c8
-rw-r--r--src/core/iomgr/tcp_server.h5
-rw-r--r--src/core/iomgr/tcp_server_posix.c10
-rw-r--r--src/core/security/credentials.c16
-rw-r--r--src/core/security/json_token.c2
-rw-r--r--src/core/security/secure_endpoint.c2
-rw-r--r--src/core/security/security_context.c2
-rw-r--r--src/core/security/server_secure_chttp2.c16
-rw-r--r--src/core/statistics/census_rpc_stats.c2
-rw-r--r--src/core/statistics/census_tracing.c2
-rw-r--r--src/core/support/cmdline.c2
-rw-r--r--src/core/support/host_port.c2
-rw-r--r--src/core/support/murmur_hash.c6
-rw-r--r--src/core/support/string.c32
-rw-r--r--src/core/support/string.h (renamed from include/grpc/support/string.h)11
-rw-r--r--src/core/support/time_posix.c2
-rw-r--r--src/core/surface/byte_buffer.c11
-rw-r--r--src/core/surface/call.c190
-rw-r--r--src/core/surface/channel_create.c4
-rw-r--r--src/core/surface/client.c2
-rw-r--r--src/core/surface/completion_queue.c7
-rw-r--r--src/core/surface/event_string.c2
-rw-r--r--src/core/surface/lame_client.c2
-rw-r--r--src/core/surface/secure_channel_create.c4
-rw-r--r--src/core/surface/server.c2
-rw-r--r--src/core/transport/chttp2/frame_data.c4
-rw-r--r--src/core/transport/chttp2/hpack_parser.c4
-rw-r--r--src/core/transport/chttp2/timeout_encoding.c38
-rw-r--r--src/core/transport/chttp2/timeout_encoding.h3
-rw-r--r--src/core/transport/chttp2_transport.c8
-rw-r--r--src/core/transport/metadata.c1
-rw-r--r--src/core/transport/stream_op.c2
-rw-r--r--src/cpp/client/channel.cc15
-rw-r--r--src/cpp/stream/stream_context.cc12
-rw-r--r--src/cpp/stream/stream_context.h1
-rw-r--r--src/node/binding.gyp35
-rw-r--r--src/node/call.cc31
-rw-r--r--src/node/call.h2
-rw-r--r--src/node/client.js144
-rw-r--r--src/node/examples/math_server.js7
-rw-r--r--src/node/interop/interop_client.js2
-rw-r--r--src/node/interop/interop_server.js12
-rw-r--r--src/node/node_grpc.cc2
-rw-r--r--src/node/server.cc4
-rw-r--r--src/node/server.js71
-rw-r--r--src/node/surface_client.js113
-rw-r--r--src/node/surface_server.js141
-rw-r--r--src/node/test/call_test.js42
-rw-r--r--src/node/test/client_server_test.js159
-rw-r--r--src/node/test/constant_test.js1
-rw-r--r--src/node/test/end_to_end_test.js243
-rw-r--r--src/node/test/interop_sanity_test.js21
-rw-r--r--src/node/test/math_client_test.js10
-rw-r--r--src/node/test/server_test.js88
-rw-r--r--src/php/ext/grpc/call.c21
-rw-r--r--src/php/ext/grpc/credentials.c2
-rw-r--r--src/php/ext/grpc/php_grpc.c4
-rwxr-xr-xsrc/php/lib/Grpc/ActiveCall.php3
-rwxr-xr-xsrc/php/tests/unit_tests/CallTest.php6
-rwxr-xr-xsrc/php/tests/unit_tests/EndToEndTest.php24
-rwxr-xr-xsrc/php/tests/unit_tests/SecureEndToEndTest.php24
-rw-r--r--src/python/__init__.py0
-rw-r--r--src/python/_framework/__init__.py0
-rw-r--r--src/python/_framework/base/__init__.py0
-rw-r--r--src/python/_framework/base/exceptions.py (renamed from src/ruby/lib/grpc/beefcake.rb)31
-rw-r--r--src/python/_framework/base/interfaces.py229
-rw-r--r--src/python/_framework/base/interfaces_test.py299
-rw-r--r--src/python/_framework/base/packets/__init__.py0
-rw-r--r--src/python/_framework/base/packets/_cancellation.py64
-rw-r--r--src/python/_framework/base/packets/_constants.py32
-rw-r--r--src/python/_framework/base/packets/_context.py99
-rw-r--r--src/python/_framework/base/packets/_emission.py126
-rw-r--r--src/python/_framework/base/packets/_ends.py408
-rw-r--r--src/python/_framework/base/packets/_expiration.py158
-rw-r--r--src/python/_framework/base/packets/_ingestion.py440
-rw-r--r--src/python/_framework/base/packets/_interfaces.py269
-rw-r--r--src/python/_framework/base/packets/_reception.py394
-rw-r--r--src/python/_framework/base/packets/_termination.py201
-rw-r--r--src/python/_framework/base/packets/_transmission.py393
-rw-r--r--src/python/_framework/base/packets/implementations.py77
-rw-r--r--src/python/_framework/base/packets/implementations_test.py80
-rw-r--r--src/python/_framework/base/packets/in_memory.py108
-rw-r--r--src/python/_framework/base/packets/interfaces.py84
-rw-r--r--src/python/_framework/base/packets/null.py56
-rw-r--r--src/python/_framework/base/packets/packets.py112
-rw-r--r--src/python/_framework/base/util.py91
-rw-r--r--src/python/_framework/common/__init__.py0
-rw-r--r--src/python/_framework/common/cardinality.py42
-rw-r--r--src/python/_framework/face/__init__.py0
-rw-r--r--src/python/_framework/face/_calls.py310
-rw-r--r--src/python/_framework/face/_control.py194
-rw-r--r--src/python/_framework/face/_service.py189
-rw-r--r--src/python/_framework/face/_test_case.py81
-rw-r--r--src/python/_framework/face/blocking_invocation_inline_service_test.py46
-rw-r--r--src/python/_framework/face/demonstration.py118
-rw-r--r--src/python/_framework/face/event_invocation_synchronous_event_service_test.py46
-rw-r--r--src/python/_framework/face/exceptions.py77
-rw-r--r--src/python/_framework/face/future_invocation_asynchronous_event_service_test.py46
-rw-r--r--src/python/_framework/face/implementations.py246
-rw-r--r--src/python/_framework/face/interfaces.py545
-rw-r--r--src/python/_framework/face/testing/__init__.py0
-rw-r--r--src/python/_framework/face/testing/base_util.py102
-rw-r--r--src/python/_framework/face/testing/blocking_invocation_inline_service_test_case.py223
-rw-r--r--src/python/_framework/face/testing/callback.py94
-rw-r--r--src/python/_framework/face/testing/control.py87
-rw-r--r--src/python/_framework/face/testing/coverage.py123
-rw-r--r--src/python/_framework/face/testing/digest.py446
-rw-r--r--src/python/_framework/face/testing/event_invocation_synchronous_event_service_test_case.py367
-rw-r--r--src/python/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py377
-rw-r--r--src/python/_framework/face/testing/interfaces.py117
-rw-r--r--src/python/_framework/face/testing/serial.py70
-rw-r--r--src/python/_framework/face/testing/service.py337
-rw-r--r--src/python/_framework/face/testing/stock_service.py374
-rw-r--r--src/python/_framework/face/testing/test_case.py111
-rw-r--r--src/python/_framework/foundation/__init__.py0
-rw-r--r--src/python/_framework/foundation/_later_test.py145
-rw-r--r--src/python/_framework/foundation/_logging_pool_test.py64
-rw-r--r--src/python/_framework/foundation/_timer_future.py156
-rw-r--r--src/python/_framework/foundation/abandonment.py38
-rw-r--r--src/python/_framework/foundation/callable_util.py78
-rw-r--r--src/python/_framework/foundation/future.py172
-rw-r--r--src/python/_framework/foundation/later.py51
-rw-r--r--src/python/_framework/foundation/logging_pool.py83
-rw-r--r--src/python/_framework/foundation/stream.py60
-rw-r--r--src/python/_framework/foundation/stream_testing.py73
-rw-r--r--src/python/_framework/foundation/stream_util.py160
-rw-r--r--src/python/_junkdrawer/__init__.py0
-rw-r--r--src/python/_junkdrawer/stock_pb2.py152
-rwxr-xr-xsrc/ruby/README.md83
-rwxr-xr-xsrc/ruby/bin/interop/README.md11
-rwxr-xr-xsrc/ruby/bin/interop/interop_server.rb4
-rw-r--r--src/ruby/ext/grpc/extconf.rb8
-rw-r--r--src/ruby/ext/grpc/rb_call.c20
-rw-r--r--src/ruby/ext/grpc/rb_completion_queue.c2
-rw-r--r--src/ruby/ext/grpc/rb_event.c6
-rwxr-xr-xsrc/ruby/grpc.gemspec8
-rw-r--r--src/ruby/lib/grpc/generic/active_call.rb28
-rw-r--r--src/ruby/lib/grpc/generic/bidi_call.rb6
-rw-r--r--src/ruby/lib/grpc/generic/rpc_server.rb6
-rw-r--r--src/ruby/lib/grpc/logconfig.rb2
-rw-r--r--src/ruby/spec/call_spec.rb16
-rw-r--r--src/ruby/spec/client_server_spec.rb10
-rw-r--r--src/ruby/spec/event_spec.rb3
-rw-r--r--src/ruby/spec/generic/active_call_spec.rb56
-rwxr-xr-xsrc/ruby/spec/testdata/README3
-rw-r--r--templates/Makefile.template30
-rw-r--r--templates/vsprojects/vs2013/gpr.vcxproj.filters.template2
-rw-r--r--templates/vsprojects/vs2013/grpc.sln.template28
-rw-r--r--templates/vsprojects/vs2013/grpc.vcxproj.filters.template2
-rw-r--r--templates/vsprojects/vs2013/grpc_unsecure.vcxproj.filters.template2
-rw-r--r--templates/vsprojects/vs2013/vcxproj.filters_defs.include64
-rw-r--r--templates/vsprojects/vs2013/vcxproj_defs.include24
-rw-r--r--test/core/echo/client.c7
-rw-r--r--test/core/echo/echo_test.c2
-rw-r--r--test/core/echo/server.c2
-rw-r--r--test/core/end2end/cq_verifier.c25
-rw-r--r--test/core/end2end/cq_verifier.h1
-rw-r--r--test/core/end2end/dualstack_socket_test.c10
-rw-r--r--test/core/end2end/no_server_test.c4
-rw-r--r--test/core/end2end/tests/cancel_after_accept.c4
-rw-r--r--test/core/end2end/tests/cancel_after_accept_and_writes_closed.c4
-rw-r--r--test/core/end2end/tests/cancel_after_invoke.c4
-rw-r--r--test/core/end2end/tests/cancel_before_invoke.c3
-rw-r--r--test/core/end2end/tests/census_simple_request.c4
-rw-r--r--test/core/end2end/tests/disappearing_server.c7
-rw-r--r--test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c4
-rw-r--r--test/core/end2end/tests/graceful_server_shutdown.c4
-rw-r--r--test/core/end2end/tests/invoke_large_request.c4
-rw-r--r--test/core/end2end/tests/max_concurrent_streams.c35
-rw-r--r--test/core/end2end/tests/ping_pong_streaming.c3
-rw-r--r--test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c4
-rw-r--r--test/core/end2end/tests/request_response_with_metadata_and_payload.c4
-rw-r--r--test/core/end2end/tests/request_response_with_payload.c4
-rw-r--r--test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c4
-rw-r--r--test/core/end2end/tests/request_with_large_metadata.c4
-rw-r--r--test/core/end2end/tests/request_with_payload.c4
-rw-r--r--test/core/end2end/tests/simple_delayed_request.c6
-rw-r--r--test/core/end2end/tests/simple_request.c8
-rw-r--r--test/core/end2end/tests/thread_stress.c40
-rw-r--r--test/core/end2end/tests/writes_done_hangs_with_pending_read.c4
-rw-r--r--test/core/fling/client.c11
-rw-r--r--test/core/fling/fling_stream_test.c2
-rw-r--r--test/core/fling/fling_test.c2
-rw-r--r--test/core/iomgr/fd_posix_test.c25
-rw-r--r--test/core/iomgr/resolve_address_test.c5
-rw-r--r--test/core/iomgr/sockaddr_utils_test.c6
-rw-r--r--test/core/iomgr/tcp_client_posix_test.c4
-rw-r--r--test/core/security/credentials_test.c2
-rw-r--r--test/core/support/string_test.c2
-rw-r--r--test/core/surface/lame_client_test.c4
-rw-r--r--test/core/transport/chttp2/bin_encoder_test.c2
-rw-r--r--test/core/transport/chttp2/stream_encoder_test.c2
-rw-r--r--test/core/transport/chttp2/timeout_encoding_test.c2
-rw-r--r--test/core/transport/transport_end2end_tests.c7
-rw-r--r--test/core/util/test_config.c2
-rw-r--r--test/cpp/interop/client.cc4
-rw-r--r--test/cpp/interop/server.cc2
m---------third_party/openssl0
-rwxr-xr-xtools/buildgen/plugins/generate_vsprojects.py38
-rw-r--r--tools/dockerfile/grpc_cxx/Dockerfile12
-rw-r--r--tools/dockerfile/grpc_go/Dockerfile27
-rw-r--r--tools/dockerfile/grpc_go/README.md4
-rw-r--r--tools/dockerfile/grpc_java/Dockerfile5
-rw-r--r--tools/dockerfile/grpc_java_base/Dockerfile18
-rw-r--r--tools/dockerfile/grpc_ruby/Dockerfile3
-rwxr-xr-xtools/gce_setup/grpc_docker.sh96
-rwxr-xr-xtools/gce_setup/shared_startup_funcs.sh21
-rwxr-xr-xtools/run_tests/build_node.sh20
-rwxr-xr-xtools/run_tests/build_python.sh10
-rwxr-xr-xtools/run_tests/jobset.py4
-rwxr-xr-xtools/run_tests/run_python.sh11
-rwxr-xr-xtools/run_tests/run_tests.py18
-rw-r--r--tools/run_tests/tests.json4
-rw-r--r--vsprojects/third_party/openssl/OpenSSL.mak17
-rw-r--r--vsprojects/vs2013/.gitignore5
-rw-r--r--vsprojects/vs2013/gpr.vcxproj.filters193
-rw-r--r--vsprojects/vs2013/grpc.vcxproj6
-rw-r--r--vsprojects/vs2013/grpc.vcxproj.filters628
-rw-r--r--vsprojects/vs2013/grpc_unsecure.vcxproj6
-rw-r--r--vsprojects/vs2013/grpc_unsecure.vcxproj.filters547
257 files changed, 14262 insertions, 1361 deletions
diff --git a/.gitignore b/.gitignore
index 63332d1b16..002e3e661c 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 98c20f5898..bba923ccae 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
*******************************
@@ -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/Makefile b/Makefile
index c68a412baa..03e005856b 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
@@ -372,6 +380,8 @@ credentials_test: bins/$(CONFIG)/credentials_test
end2end_test: bins/$(CONFIG)/end2end_test
interop_client: bins/$(CONFIG)/interop_client
interop_server: bins/$(CONFIG)/interop_server
+tips_client: bins/$(CONFIG)/tips_client
+tips_client_test: bins/$(CONFIG)/tips_client_test
qps_client: bins/$(CONFIG)/qps_client
qps_server: bins/$(CONFIG)/qps_server
ruby_plugin: bins/$(CONFIG)/ruby_plugin
@@ -524,8 +534,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
@@ -547,13 +561,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)/alarm_heap_test bins/$(CONFIG)/alarm_list_test bins/$(CONFIG)/alarm_test bins/$(CONFIG)/alpn_test bins/$(CONFIG)/bin_encoder_test bins/$(CONFIG)/census_hash_table_test bins/$(CONFIG)/census_statistics_multiple_writers_circular_buffer_test bins/$(CONFIG)/census_statistics_multiple_writers_test bins/$(CONFIG)/census_statistics_performance_test bins/$(CONFIG)/census_statistics_quick_test bins/$(CONFIG)/census_statistics_small_log_test bins/$(CONFIG)/census_stub_test bins/$(CONFIG)/census_window_stats_test bins/$(CONFIG)/chttp2_status_conversion_test bins/$(CONFIG)/chttp2_stream_encoder_test bins/$(CONFIG)/chttp2_stream_map_test bins/$(CONFIG)/chttp2_transport_end2end_test bins/$(CONFIG)/dualstack_socket_test bins/$(CONFIG)/echo_client bins/$(CONFIG)/echo_server bins/$(CONFIG)/echo_test bins/$(CONFIG)/fd_posix_test bins/$(CONFIG)/fling_client bins/$(CONFIG)/fling_server bins/$(CONFIG)/fling_stream_test bins/$(CONFIG)/fling_test bins/$(CONFIG)/gpr_cancellable_test bins/$(CONFIG)/gpr_cmdline_test bins/$(CONFIG)/gpr_histogram_test bins/$(CONFIG)/gpr_host_port_test bins/$(CONFIG)/gpr_log_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)/gpr_useful_test bins/$(CONFIG)/grpc_base64_test bins/$(CONFIG)/grpc_byte_buffer_reader_test bins/$(CONFIG)/grpc_channel_stack_test bins/$(CONFIG)/grpc_completion_queue_test bins/$(CONFIG)/grpc_credentials_test bins/$(CONFIG)/grpc_json_token_test bins/$(CONFIG)/grpc_stream_op_test bins/$(CONFIG)/hpack_parser_test bins/$(CONFIG)/hpack_table_test bins/$(CONFIG)/httpcli_format_request_test bins/$(CONFIG)/httpcli_parser_test bins/$(CONFIG)/httpcli_test bins/$(CONFIG)/lame_client_test bins/$(CONFIG)/message_compress_test bins/$(CONFIG)/metadata_buffer_test bins/$(CONFIG)/murmur_hash_test bins/$(CONFIG)/no_server_test bins/$(CONFIG)/poll_kick_test bins/$(CONFIG)/resolve_address_test bins/$(CONFIG)/secure_endpoint_test bins/$(CONFIG)/sockaddr_utils_test bins/$(CONFIG)/tcp_client_posix_test bins/$(CONFIG)/tcp_posix_test bins/$(CONFIG)/tcp_server_posix_test bins/$(CONFIG)/time_averaged_stats_test bins/$(CONFIG)/time_test bins/$(CONFIG)/timeout_encoding_test bins/$(CONFIG)/transport_metadata_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)/channel_arguments_test bins/$(CONFIG)/credentials_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/interop_client bins/$(CONFIG)/interop_server bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test
+buildtests_cxx: privatelibs_cxx bins/$(CONFIG)/channel_arguments_test bins/$(CONFIG)/credentials_test bins/$(CONFIG)/end2end_test bins/$(CONFIG)/interop_client bins/$(CONFIG)/interop_server bins/$(CONFIG)/tips_client bins/$(CONFIG)/tips_client_test bins/$(CONFIG)/qps_client bins/$(CONFIG)/qps_server bins/$(CONFIG)/status_test bins/$(CONFIG)/sync_client_async_server_test bins/$(CONFIG)/thread_pool_test
test: test_c test_cxx
@@ -955,6 +969,8 @@ test_cxx: buildtests_cxx
$(Q) ./bins/$(CONFIG)/credentials_test || ( echo test credentials_test failed ; exit 1 )
$(E) "[RUN] Testing end2end_test"
$(Q) ./bins/$(CONFIG)/end2end_test || ( echo test end2end_test 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 qps_client"
$(Q) ./bins/$(CONFIG)/qps_client || ( echo test qps_client failed ; exit 1 )
$(E) "[RUN] Testing qps_server"
@@ -1008,6 +1024,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 $@`
@@ -1220,7 +1251,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 +1331,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
@@ -1356,7 +1395,8 @@ 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/resolve_address_posix.c \
+ src/core/iomgr/pollset_windows.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 \
@@ -1475,7 +1515,8 @@ 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/resolve_address_posix.c: $(OPENSSL_DEP)
+src/core/iomgr/pollset_windows.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)
@@ -1532,6 +1573,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
@@ -1540,6 +1582,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
@@ -1611,7 +1656,8 @@ 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/resolve_address_posix.o:
+objs/$(CONFIG)/src/core/iomgr/pollset_windows.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:
@@ -1709,7 +1755,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
@@ -1767,7 +1817,8 @@ 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/resolve_address_posix.c \
+ src/core/iomgr/pollset_windows.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 \
@@ -1831,7 +1882,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
@@ -1886,7 +1941,8 @@ 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/resolve_address_posix.o:
+objs/$(CONFIG)/src/core/iomgr/pollset_windows.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:
@@ -2025,7 +2081,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
@@ -2078,9 +2138,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 \
@@ -2097,9 +2157,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
@@ -2107,7 +2167,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
@@ -2124,8 +2188,60 @@ 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 = \
+ 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) 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
+
+
+
+
+
+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 = \
@@ -2150,7 +2266,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
@@ -2189,7 +2309,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
@@ -2228,7 +2352,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
@@ -2267,7 +2395,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
@@ -2306,7 +2438,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
@@ -2345,7 +2481,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
@@ -2371,7 +2511,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
@@ -2393,7 +2537,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
@@ -2415,7 +2563,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
@@ -2437,7 +2589,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
@@ -2459,7 +2615,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
@@ -2481,7 +2641,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
@@ -2503,7 +2667,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
@@ -2525,7 +2693,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
@@ -2547,7 +2719,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
@@ -2569,7 +2745,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
@@ -2591,7 +2771,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
@@ -2613,7 +2797,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
@@ -2635,7 +2823,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
@@ -2657,7 +2849,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
@@ -2679,7 +2875,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
@@ -2701,7 +2901,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
@@ -2723,7 +2927,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
@@ -2745,7 +2953,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
@@ -2767,7 +2979,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
@@ -2789,7 +3005,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
@@ -2811,7 +3031,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
@@ -2833,7 +3057,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
@@ -2874,7 +3102,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
@@ -5318,6 +5550,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
+
+
QPS_CLIENT_SRC = \
gens/test/cpp/qps/qpstest.pb.cc \
test/cpp/qps/client.cc \
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000..fa39d3b308
--- /dev/null
+++ b/README.md
@@ -0,0 +1,73 @@
+[gRPC - An RPC library and framework](http://github.com/google/grpc)
+===================================
+
+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.
+
+
+##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.
+
+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. 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.
+
+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
+asynchronous 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`).
+
+## 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).
+
+## 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.
diff --git a/build.json b/build.json
index 4e4e10307e..18c6b2eac8 100644
--- a/build.json
+++ b/build.json
@@ -48,7 +48,9 @@
"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",
"src/core/iomgr/sockaddr.h",
"src/core/iomgr/sockaddr_posix.h",
@@ -126,7 +128,8 @@
"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/resolve_address_posix.c",
+ "src/core/iomgr/pollset_windows.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",
@@ -403,12 +406,28 @@
"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"
]
+ },
+ {
+ "name": "tips_client_lib",
+ "build": "private",
+ "language": "c++",
+ "src": [
+ "examples/tips/label.proto",
+ "examples/tips/empty.proto",
+ "examples/tips/pubsub.proto",
+ "examples/tips/client.cc"
+ ],
+ "deps": [
+ "grpc++",
+ "grpc",
+ "gpr"
+ ]
}
],
"targets": [
@@ -1495,6 +1514,41 @@
"run": false
},
{
+ "name": "tips_client",
+ "build": "test",
+ "run": false,
+ "language": "c++",
+ "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",
+ "language": "c++",
+ "src": [
+ "examples/tips/client_test.cc"
+ ],
+ "deps": [
+ "tips_client_lib",
+ "grpc++_test_util",
+ "grpc_test_util",
+ "grpc++",
+ "grpc",
+ "gpr_test_util",
+ "gpr"
+ ]
+ },
+ {
"name": "qps_client",
"build": "test",
"language": "c++",
diff --git a/examples/tips/client.cc b/examples/tips/client.cc
new file mode 100644
index 0000000000..695ff80e8a
--- /dev/null
+++ b/examples/tips/client.cc
@@ -0,0 +1,60 @@
+/*
+ *
+ * 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 <grpc++/client_context.h>
+
+#include "examples/tips/client.h"
+
+using tech::pubsub::Topic;
+using tech::pubsub::PublisherService;
+
+namespace grpc {
+namespace examples {
+namespace tips {
+
+Client::Client(std::shared_ptr<ChannelInterface> 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);
+}
+
+} // namespace tips
+} // namespace examples
+} // namespace grpc
diff --git a/examples/tips/client.h b/examples/tips/client.h
new file mode 100644
index 0000000000..6ae9d50658
--- /dev/null
+++ b/examples/tips/client.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * 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 __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
+
+#include <grpc++/channel_interface.h>
+#include <grpc++/status.h>
+
+#include "examples/tips/pubsub.pb.h"
+
+namespace grpc {
+namespace examples {
+namespace tips {
+
+class Client {
+ public:
+ Client(std::shared_ptr<grpc::ChannelInterface> channel);
+ Status CreateTopic(grpc::string topic);
+
+ private:
+ std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_;
+};
+
+} // 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
new file mode 100644
index 0000000000..f4a3b09601
--- /dev/null
+++ b/examples/tips/client_main.cc
@@ -0,0 +1,72 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <google/gflags.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/status.h>
+
+#include "examples/tips/client.h"
+#include "test/cpp/util/create_test_channel.h"
+
+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");
+
+ 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<grpc::ChannelInterface> channel(
+ 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();
+ grpc_shutdown();
+ return 0;
+}
diff --git a/examples/tips/client_test.cc b/examples/tips/client_test.cc
new file mode 100644
index 0000000000..69238f2c6f
--- /dev/null
+++ b/examples/tips/client_test.cc
@@ -0,0 +1,106 @@
+/*
+ *
+ * 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 <grpc++/channel_arguments.h>
+#include <grpc++/channel_interface.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc++/status.h>
+#include <gtest/gtest.h>
+
+#include "examples/tips/client.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+using grpc::ChannelInterface;
+
+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> server_;
+ std::ostringstream server_address_;
+ PublishServiceImpl service_;
+
+ std::shared_ptr<ChannelInterface> 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 0000000000..adf66b5e61
--- /dev/null
+++ b/examples/tips/empty.proto
@@ -0,0 +1,13 @@
+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) { };
+// };
+//
+message Empty {}
diff --git a/examples/tips/label.proto b/examples/tips/label.proto
new file mode 100644
index 0000000000..e93ac9dea3
--- /dev/null
+++ b/examples/tips/label.proto
@@ -0,0 +1,48 @@
+// 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.
+
+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/pubsub.proto b/examples/tips/pubsub.proto
new file mode 100644
index 0000000000..0b3bd5d012
--- /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 <code>ListTopicsResponse</code>
+ // 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 <code>ListTopicsRequest</code>
+ // 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 <code>PushConfig</code> for a specified subscription.
+ // This method can be used to suspend the flow of messages to an endpoint
+ // by clearing the <code>PushConfig</code> 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 <code>query</code> 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
+ // <code>CreateSubscription</code> 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 <code>max_bytes</code> is specified, the system is allowed to drop
+ // old messages to keep the combined size of stored messages under
+ // <code>max_bytes</code>. 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 <code>max_age_seconds</code> 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
+ // <code>ModifyAckDeadline</code>.
+ // 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:<REGID>", where <REGID> 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 <code>ListSubscriptionsResponse</code>
+ // 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
+ // <code>ListSubscriptionsRequest</code> 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 <code>push_config</code> 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 <code>PubsubMessage</code> 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 <code>PubsubMessage</code> 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/include/grpc/grpc.h b/include/grpc/grpc.h
index 3c5b0de195..6c8b168532 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);
@@ -313,18 +314,14 @@ 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
completed (there may be other events for the call pending at this
time) */
-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 grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
+ void *metadata_read_tag, void *finished_tag,
+ gpr_uint32 flags);
/* Accept an incoming RPC, binding a completion queue to it.
To be called before sending or receiving messages.
@@ -428,7 +425,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/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h
index 05a5bbe1bc..118a919aee 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_SOCKET)
+#error Must define GPR_POSIX_SOCKET 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/channel/call_op_string.c b/src/core/channel/call_op_string.c
index 9a7838ce2f..789913901a 100644
--- a/src/core/channel/call_op_string.c
+++ b/src/core/channel/call_op_string.c
@@ -37,8 +37,8 @@
#include <stdio.h>
#include <string.h>
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
-#include <grpc/support/string.h>
#include <grpc/support/useful.h>
#define MAX_APPEND 1024
diff --git a/src/core/channel/channel_args.c b/src/core/channel/channel_args.c
index 36312e54de..04ce519ff3 100644
--- a/src/core/channel/channel_args.c
+++ b/src/core/channel/channel_args.c
@@ -33,9 +33,9 @@
#include <grpc/grpc.h>
#include "src/core/channel/channel_args.h"
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
-#include <grpc/support/string.h>
#include <string.h>
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index fa75561c78..f9b42db419 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/sync.h>
#include <grpc/support/useful.h>
@@ -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);
@@ -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);
}
diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c
index e01cb81a89..47c0ed3b88 100644
--- a/src/core/channel/connected_channel.c
+++ b/src/core/channel/connected_channel.c
@@ -37,12 +37,12 @@
#include <stdio.h>
#include <string.h>
+#include "src/core/support/string.h"
#include "src/core/transport/transport.h"
#include <grpc/byte_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice_buffer.h>
-#include <grpc/support/string.h>
#define MAX_BUFFER_LENGTH 8192
/* the protobuf library will (by default) start warning at 100megs */
@@ -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;
diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c
index 2143eeb63d..acd9fa7b55 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 <string.h>
@@ -44,9 +45,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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
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 c7b9b330f0..d074e163f1 100644
--- a/src/core/httpcli/httpcli_security_context.c
+++ b/src/core/httpcli/httpcli_security_context.c
@@ -36,9 +36,9 @@
#include <string.h>
#include "src/core/security/secure_transport_setup.h"
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include "src/core/tsi/ssl_transport_security.h"
typedef struct {
diff --git a/src/core/iomgr/endpoint_pair_posix.c b/src/core/iomgr/endpoint_pair_posix.c
index f08d1344eb..3f53402cf3 100644
--- a/src/core/iomgr/endpoint_pair_posix.c
+++ b/src/core/iomgr/endpoint_pair_posix.c
@@ -31,6 +31,10 @@
*
*/
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
#include "src/core/iomgr/endpoint_pair.h"
#include <errno.h>
@@ -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 3cd2f9a8e0..9f70a26c64 100644
--- a/src/core/iomgr/fd_posix.c
+++ b/src/core/iomgr/fd_posix.c
@@ -31,6 +31,10 @@
*
*/
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
#include "src/core/iomgr/fd_posix.h"
#include <assert.h>
@@ -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 36d80d5c29..b9fcf45ea6 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 f088818b9a..02f3e41433 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 9f85b6137a..4386cf5a46 100644
--- a/src/core/iomgr/pollset_kick_posix.c
+++ b/src/core/iomgr/pollset_kick_posix.c
@@ -31,6 +31,10 @@
*
*/
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
#include "src/core/iomgr/pollset_kick_posix.h"
#include <errno.h>
@@ -175,3 +179,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 0000000000..243e519dad
--- /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 <grpc/support/sync.h>
+
+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 2555322532..39e2dc4667 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -31,6 +31,10 @@
*
*/
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
#include "src/core/iomgr/pollset_posix.h"
#include <errno.h>
@@ -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/node/port_picker.js b/src/core/iomgr/pollset_windows.c
index ad82f2a7f8..3fb39918b3 100644
--- a/src/node/port_picker.js
+++ b/src/core/iomgr/pollset_windows.c
@@ -31,22 +31,8 @@
*
*/
-var net = require('net');
+#include <grpc/support/port_platform.h>
-/**
- * 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());
- });
- });
-}
+#ifdef GPR_WIN32
-exports.nextAvailablePort = nextAvailablePort;
+#endif /* GPR_WIN32 */
diff --git a/src/core/iomgr/pollset_windows.h b/src/core/iomgr/pollset_windows.h
new file mode 100644
index 0000000000..53b9ffa5ab
--- /dev/null
+++ b/src/core/iomgr/pollset_windows.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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_WINDOWS_H_
+#define __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_
+
+#include <grpc/support/sync.h>
+
+#include "src/core/iomgr/pollset_kick.h"
+
+/* 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 {
+ gpr_mu mu;
+ gpr_cv cv;
+} grpc_pollset;
+
+#define GRPC_POLLSET_MU(pollset) (&(pollset)->mu)
+#define GRPC_POLLSET_CV(pollset) (&(pollset)->cv)
+
+#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_ */
diff --git a/src/core/iomgr/resolve_address_posix.c b/src/core/iomgr/resolve_address.c
index c9c2c5378a..01681168ce 100644
--- a/src/core/iomgr/resolve_address_posix.c
+++ b/src/core/iomgr/resolve_address.c
@@ -33,20 +33,17 @@
#define _POSIX_SOURCE
+#include "src/core/iomgr/sockaddr.h"
#include "src/core/iomgr/resolve_address.h"
#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <unistd.h>
#include <string.h>
#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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/thd.h>
#include <grpc/support/time.h>
diff --git a/src/core/iomgr/resolve_address.h b/src/core/iomgr/resolve_address.h
index 37ec0f0335..7b537b1767 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 <sys/socket.h>
+#include <stddef.h>
+
+#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/sockaddr_posix.h b/src/core/iomgr/sockaddr_posix.h
index 79ef3ca3cf..53c80386d4 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 <arpa/inet.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
#endif /* __GRPC_INTERNAL_IOMGR_SOCKADDR_POSIX_H_ */
diff --git a/src/core/iomgr/sockaddr_utils.c b/src/core/iomgr/sockaddr_utils.c
index eca14a4f39..07bf7b3a35 100644
--- a/src/core/iomgr/sockaddr_utils.c
+++ b/src/core/iomgr/sockaddr_utils.c
@@ -33,12 +33,11 @@
#include "src/core/iomgr/sockaddr_utils.h"
-#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
+#include "src/core/support/string.h"
#include <grpc/support/host_port.h>
-#include <grpc/support/string.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
diff --git a/src/core/iomgr/sockaddr_win32.h b/src/core/iomgr/sockaddr_win32.h
index 751ac3d2e7..cdea33fec0 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 <ws2tcpip.h>
+
#endif // __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_
diff --git a/src/core/iomgr/socket_utils_common_posix.c b/src/core/iomgr/socket_utils_common_posix.c
index d65b025d70..1854285b5a 100644
--- a/src/core/iomgr/socket_utils_common_posix.c
+++ b/src/core/iomgr/socket_utils_common_posix.c
@@ -31,6 +31,10 @@
*
*/
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
#include "src/core/iomgr/socket_utils_posix.h"
#include <arpa/inet.h>
@@ -46,8 +50,8 @@
#include <errno.h>
#include "src/core/iomgr/sockaddr_utils.h"
+#include "src/core/support/string.h"
#include <grpc/support/host_port.h>
-#include <grpc/support/string.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
#include <grpc/support/sync.h>
@@ -99,7 +103,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 +113,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;
@@ -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/socket_utils_posix.c b/src/core/iomgr/socket_utils_posix.c
index e8c8071037..06c5033d45 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 */
diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c
index d675c2dcec..851530ce68 100644
--- a/src/core/iomgr/tcp_client_posix.c
+++ b/src/core/iomgr/tcp_client_posix.c
@@ -31,6 +31,10 @@
*
*/
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
#include "src/core/iomgr/tcp_client.h"
#include <errno.h>
@@ -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 657f34aaf9..a9b59df885 100644
--- a/src/core/iomgr/tcp_posix.c
+++ b/src/core/iomgr/tcp_posix.c
@@ -31,6 +31,10 @@
*
*/
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
#include "src/core/iomgr/tcp_posix.h"
#include <errno.h>
@@ -40,10 +44,10 @@
#include <sys/socket.h>
#include <unistd.h>
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice.h>
-#include <grpc/support/string.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
@@ -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 8ffd7d3569..c4d836e9b5 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 <sys/types.h>
-#include <sys/socket.h>
-
#include "src/core/iomgr/endpoint.h"
/* Forward decl of grpc_tcp_server */
@@ -63,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 5762eb8a97..2d6c6a73c2 100644
--- a/src/core/iomgr/tcp_server_posix.c
+++ b/src/core/iomgr/tcp_server_posix.c
@@ -31,6 +31,10 @@
*
*/
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
#define _GNU_SOURCE
#include "src/core/iomgr/tcp_server.h"
@@ -252,7 +256,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;
@@ -265,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;
@@ -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/security/credentials.c b/src/core/security/credentials.c
index cc48623ea4..78937c1a0d 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
@@ -355,7 +355,6 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
grpc_json *token_type = NULL;
grpc_json *expires_in = NULL;
grpc_json *ptr;
- size_t new_access_token_size = 0;
json = grpc_json_parse_string(null_terminated_body);
if (json == NULL) {
gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
@@ -391,12 +390,9 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
status = GRPC_CREDENTIALS_ERROR;
goto end;
}
- new_access_token_size =
- strlen(token_type->value) + 1 + strlen(access_token->value) + 1;
- new_access_token = gpr_malloc(new_access_token_size);
- /* C89 does not have snprintf :(. */
- sprintf(new_access_token, "%s %s", token_type->value, access_token->value);
- token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
+ gpr_asprintf(&new_access_token, "%s %s", token_type->value,
+ access_token->value);
+ token_lifetime->tv_sec = expires_in->valueint;
token_lifetime->tv_nsec = 0;
if (*token_elem != NULL) grpc_mdelem_unref(*token_elem);
*token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY,
@@ -545,9 +541,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;
diff --git a/src/core/security/json_token.c b/src/core/security/json_token.c
index 010e34780a..2da30f48bf 100644
--- a/src/core/security/json_token.c
+++ b/src/core/security/json_token.c
@@ -37,9 +37,9 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include "src/core/security/base64.h"
+#include "src/core/support/string.h"
#include <openssl/bio.h>
#include <openssl/evp.h>
diff --git a/src/core/security/secure_endpoint.c b/src/core/security/secure_endpoint.c
index e73767c1aa..9f12cf5d60 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/support/slice.h>
-#include <grpc/support/string.h>
#include <grpc/support/sync.h>
#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 cce3c7fe04..58cd458415 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice_buffer.h>
-#include <grpc/support/string.h>
#include "src/core/tsi/fake_transport_security.h"
#include "src/core/tsi/ssl_transport_security.h"
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index 931fa95651..9dd4327822 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:
diff --git a/src/core/statistics/census_rpc_stats.c b/src/core/statistics/census_rpc_stats.c
index 39094b5f65..dd3c07e80b 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/sync.h>
#define NUM_INTERVALS 3
diff --git a/src/core/statistics/census_tracing.c b/src/core/statistics/census_tracing.c
index 1e61602071..3c4ba66f5f 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
-#include <grpc/support/string.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
diff --git a/src/core/support/cmdline.c b/src/core/support/cmdline.c
index ff163a1f6c..a55da9dd18 100644
--- a/src/core/support/cmdline.c
+++ b/src/core/support/cmdline.c
@@ -37,9 +37,9 @@
#include <stdio.h>
#include <string.h>
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
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 02500551fc..446c11ebec 100644
--- a/src/core/support/host_port.c
+++ b/src/core/support/host_port.c
@@ -35,8 +35,8 @@
#include <string.h>
+#include "src/core/support/string.h"
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
int gpr_join_host_port(char **out, const char *host, int port) {
if (host[0] != '[' && strchr(host, ':') != NULL) {
diff --git a/src/core/support/murmur_hash.c b/src/core/support/murmur_hash.c
index 08b1eb80d8..892e360968 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:
diff --git a/src/core/support/string.c b/src/core/support/string.c
index 7960547735..9b5cac7596 100644
--- a/src/core/support/string.c
+++ b/src/core/support/string.c
@@ -31,7 +31,7 @@
*
*/
-#include <grpc/support/string.h>
+#include "src/core/support/string.h"
#include <ctype.h>
#include <stddef.h>
@@ -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;
+}
diff --git a/include/grpc/support/string.h b/src/core/support/string.h
index 68e7452a7f..28b7029ecd 100644
--- a/include/grpc/support/string.h
+++ b/src/core/support/string.h
@@ -60,6 +60,17 @@ 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).
+ output must be at least GPR_LTOA_MIN_BUFSIZE bytes long. */
+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/time_posix.c b/src/core/support/time_posix.c
index 78d4c3b446..9e11f8a865 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
diff --git a/src/core/surface/byte_buffer.c b/src/core/surface/byte_buffer.c
index 27a6c6e33d..d1be41074d 100644
--- a/src/core/surface/byte_buffer.c
+++ b/src/core/surface/byte_buffer.c
@@ -49,6 +49,17 @@ 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 46502fb6b1..14d990df6a 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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;
gpr_uint8 got_status_code;
/* The current outstanding read message tag (only valid if have_read == 1) */
void *read_tag;
@@ -190,6 +193,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;
@@ -227,11 +232,15 @@ 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;
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,
@@ -360,16 +369,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;
@@ -384,11 +383,81 @@ 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) {
+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_internal_unref(call);
+}
+
+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;
@@ -420,7 +489,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);
@@ -431,8 +499,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);
@@ -442,20 +508,18 @@ 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;
+ grpc_call_internal_ref(call);
elem = CALL_ELEM_FROM_CALL(call, 0);
elem->filter->call_op(elem, NULL, &op);
@@ -486,6 +550,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);
@@ -535,26 +600,6 @@ grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call,
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);
@@ -617,7 +662,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);
@@ -654,8 +699,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) {
@@ -666,15 +709,25 @@ grpc_call_error grpc_call_start_write(grpc_call *call,
call->write_tag = tag;
call->have_write = 1;
- 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;
+ gpr_mu_lock(&call->read_mu);
+ if (!call->received_start) {
+ call->pending_write = grpc_byte_buffer_copy(byte_buffer);
+ call->pending_write_flags = flags;
- elem = CALL_ELEM_FROM_CALL(call, 0);
- elem->filter->call_op(elem, NULL, &op);
+ 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;
+ 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);
+ }
return GRPC_CALL_OK;
}
@@ -706,14 +759,23 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) {
call->write_tag = tag;
call->have_write = 1;
- op.type = GRPC_SEND_FINISH;
- op.dir = GRPC_CALL_DOWN;
- op.flags = 0;
- op.done_cb = done_writes_done;
- op.user_data = call;
+ gpr_mu_lock(&call->read_mu);
+ if (!call->received_start) {
+ call->pending_writes_done = 1;
- elem = CALL_ELEM_FROM_CALL(call, 0);
- elem->filter->call_op(elem, NULL, &op);
+ 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;
+ op.done_cb = done_writes_done;
+ op.user_data = call;
+
+ elem = CALL_ELEM_FROM_CALL(call, 0);
+ elem->filter->call_op(elem, NULL, &op);
+ }
return GRPC_CALL_OK;
}
@@ -760,8 +822,8 @@ grpc_call_error grpc_call_start_write_status(grpc_call *call,
/* always send status */
{
grpc_mdelem *md;
- char buffer[32];
- sprintf(buffer, "%d", status);
+ char buffer[GPR_LTOA_MIN_BUFSIZE];
+ gpr_ltoa(status, buffer);
md =
grpc_mdelem_from_strings(call->metadata_context, "grpc-status", buffer);
@@ -818,6 +880,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));
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/core/surface/channel_create.c b/src/core/surface/channel_create.c
index 41093d78ef..d3faf0c996 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 <grpc/grpc.h>
#include <stdlib.h>
@@ -48,10 +50,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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/sync.h>
#include <grpc/support/useful.h>
diff --git a/src/core/surface/client.c b/src/core/surface/client.c
index 74c79bdf9b..fe3a81f1b9 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
typedef struct { void *unused; } call_data;
diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c
index 652f23e888..2bf31c50a8 100644
--- a/src/core/surface/completion_queue.c
+++ b/src/core/surface/completion_queue.c
@@ -37,13 +37,13 @@
#include <string.h>
#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 <grpc/support/alloc.h>
#include <grpc/support/atm.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#define NUM_TAG_BUCKETS 31
@@ -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);
diff --git a/src/core/surface/event_string.c b/src/core/surface/event_string.c
index 8ae2af7472..e38ef06c9f 100644
--- a/src/core/surface/event_string.c
+++ b/src/core/surface/event_string.c
@@ -35,7 +35,7 @@
#include <stdio.h>
-#include <grpc/support/string.h>
+#include "src/core/support/string.h"
#include <grpc/byte_buffer.h>
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 a5244dbe61..056c98646b 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -36,11 +36,11 @@
#include <string.h>
#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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
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 3d5727927d..defee79766 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 <grpc/grpc.h>
#include <stdlib.h>
@@ -48,13 +50,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 <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/sync.h>
#include <grpc/support/useful.h>
#include "src/core/tsi/transport_security_interface.h"
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index cbdd3bfa30..9585e4e8ea 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/useful.h>
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 c22a223737..dee61cee50 100644
--- a/src/core/transport/chttp2/frame_data.c
+++ b/src/core/transport/chttp2/frame_data.c
@@ -35,9 +35,9 @@
#include <string.h>
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/useful.h>
#include "src/core/transport/transport.h"
@@ -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 07b0f81098..c98b90e5d1 100644
--- a/src/core/transport/chttp2/hpack_parser.c
+++ b/src/core/transport/chttp2/hpack_parser.c
@@ -38,10 +38,10 @@
#include <assert.h>
#include "src/core/transport/chttp2/bin_encoder.h"
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
-#include <grpc/support/string.h>
#include <grpc/support/useful.h>
typedef enum {
@@ -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/timeout_encoding.c b/src/core/transport/chttp2/timeout_encoding.c
index 2706c369a6..23c4554cf2 100644
--- a/src/core/transport/chttp2/timeout_encoding.c
+++ b/src/core/transport/chttp2/timeout_encoding.c
@@ -36,6 +36,8 @@
#include <stdio.h>
#include <string.h>
+#include "src/core/support/string.h"
+
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');
}
}
diff --git a/src/core/transport/chttp2/timeout_encoding.h b/src/core/transport/chttp2/timeout_encoding.h
index a4582566ad..d1e4776032 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 <grpc/support/time.h>
+#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/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index e61afb71ae..531a53b984 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -37,6 +37,7 @@
#include <stdio.h>
#include <string.h>
+#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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice_buffer.h>
-#include <grpc/support/string.h>
#include <grpc/support/useful.h>
#define DEFAULT_WINDOW 65535
@@ -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 */
@@ -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));
@@ -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/metadata.c b/src/core/transport/metadata.c
index 6881b871ec..74bbb02134 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 <stddef.h>
diff --git a/src/core/transport/stream_op.c b/src/core/transport/stream_op.c
index c77c8cde1f..555543fc4b 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/src/cpp/client/channel.cc b/src/cpp/client/channel.cc
index a8919a10d9..c8b2bb2cf6 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<char *>(call);
- void *invoke_tag = reinterpret_cast<char *>(call) + 1;
void *metadata_read_tag = reinterpret_cast<char *>(call) + 2;
void *write_tag = reinterpret_cast<char *>(call) + 3;
void *halfclose_tag = reinterpret_cast<char *>(call) + 4;
@@ -115,19 +114,11 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
// add_metadata from context
//
// invoke
- GPR_ASSERT(grpc_call_start_invoke(call, cq, invoke_tag, 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;
- }
+ 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;
- 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 e64010be64..edb2fc5ad9 100644
--- a/src/cpp/stream/stream_context.cc
+++ b/src/cpp/stream/stream_context.cc
@@ -80,17 +80,9 @@ 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(),
- 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);
- 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 8697d86e83..8def589841 100644
--- a/src/cpp/stream/stream_context.h
+++ b/src/cpp/stream/stream_context.h
@@ -76,7 +76,6 @@ class StreamContext final : public StreamContextInterface {
void *read_tag() { return reinterpret_cast<char *>(this) + 1; }
void *write_tag() { return reinterpret_cast<char *>(this) + 2; }
void *halfclose_tag() { return reinterpret_cast<char *>(this) + 3; }
- void *invoke_tag() { return reinterpret_cast<char *>(this) + 4; }
void *client_metadata_read_tag() {
return reinterpret_cast<char *>(this) + 5;
}
diff --git a/src/node/binding.gyp b/src/node/binding.gyp
index da4a943491..fe4b5da9c8 100644
--- a/src/node/binding.gyp
+++ b/src/node/binding.gyp
@@ -1,8 +1,13 @@
{
+ "variables" : {
+ 'no_install': "<!(echo $GRPC_NO_INSTALL)",
+ 'grpc_root': "<!(echo $GRPC_ROOT)",
+ 'grpc_lib_subdir': "<!(echo $GRPC_LIB_SUBDIR)"
+ },
"targets" : [
{
'include_dirs': [
- "<!(node -e \"require('nan')\")"
+ "<!(nodejs -e \"require('nan')\")"
],
'cxxflags': [
'-Wall',
@@ -11,16 +16,13 @@
'-g',
'-zdefs'
'-Werror',
- ],
+ ],
'ldflags': [
- '-g',
- '-L/usr/local/google/home/mlumish/grpc_dev/lib'
+ '-g'
],
'link_settings': {
'libraries': [
- '-lgrpc',
'-lrt',
- '-lgpr',
'-lpthread'
],
},
@@ -37,6 +39,27 @@
"server_credentials.cc",
"tag.cc",
"timeval.cc"
+ ],
+ 'conditions' : [
+ ['no_install=="yes"', {
+ 'include_dirs': [
+ "<(grpc_root)/include"
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '<(grpc_root)/<(grpc_lib_subdir)/libgrpc.a',
+ '<(grpc_root)/<(grpc_lib_subdir)/libgpr.a'
+ ]
+ }
+ }],
+ ['no_install!="yes"', {
+ 'link_settings': {
+ 'libraries': [
+ '-lgrpc',
+ '-lgpr'
+ ]
+ }
+ }]
]
}
]
diff --git a/src/node/call.cc b/src/node/call.cc
index b8ee1786a6..6434c2f0d5 100644
--- a/src/node/call.cc
+++ b/src/node/call.cc
@@ -78,8 +78,8 @@ void Call::Init(Handle<Object> 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<Call>(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<Call>(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 55a6fc65b8..1924a1bf42 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 edaa115d0f..7007852b93 100644
--- a/src/node/client.js
+++ b/src/node/client.js
@@ -45,106 +45,96 @@ 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;
+ 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
+ * 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
*/
- function readCallback(event) {
- 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);
- }
- } else {
- // Indicate that reading can be resumed by calling startReading
- reading = false;
+ this.serialize = function(value) {
+ if (value === null || value === undefined) {
+ return null;
}
+ return serialize(value);
};
+
/**
- * 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).
+ * 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
*/
- function startReading() {
- call.startRead(readCallback);
- }
- // TODO(mlumish): possibly change queue implementation due to shift slowness
- var write_queue = [];
+ this.deserialize = function(buffer) {
+ if (buffer === null) {
+ return null;
+ }
+ return deserialize(buffer);
+ };
/**
- * 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.
+ * 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 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);
+ function readCallback(event) {
+ if (finished) {
+ self.push(null);
+ return;
+ }
+ var data = event.data;
+ if (self.push(self.deserialize(data)) && data != null) {
+ self._call.startRead(readCallback);
} else {
- writing = false;
+ reading = 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 +143,7 @@ function GrpcClientStream(call, options) {
* @param {number} size Ignored
*/
GrpcClientStream.prototype._read = function(size) {
- this._enableRead();
+ this.startReading();
};
/**
@@ -164,13 +154,19 @@ 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(self.serialize(chunk), function(event) {
+ callback();
+ }, 0);
};
/**
* 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 +174,8 @@ GrpcClientStream.prototype._write = function(chunk, encoding, callback) {
*/
function makeRequest(channel,
method,
+ serialize,
+ deserialize,
metadata,
deadline) {
if (deadline === undefined) {
@@ -187,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/examples/math_server.js b/src/node/examples/math_server.js
index d649b4fd6d..e65cfe3002 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 cf75b9a77a..9306317b68 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/interop/interop_server.js b/src/node/interop/interop_server.js
index 735b7a6d18..ebf847876c 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,9 @@ 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');
+ console.log('Server attaching to port ' + argv.port);
+ server_obj.server.listen();
}
/**
diff --git a/src/node/node_grpc.cc b/src/node/node_grpc.cc
index acee0386d2..bc1dfaf899 100644
--- a/src/node/node_grpc.cc
+++ b/src/node/node_grpc.cc
@@ -148,8 +148,6 @@ void InitCompletionTypeConstants(Handle<Object> exports) {
completion_type->Set(NanNew("QUEUE_SHUTDOWN"), QUEUE_SHUTDOWN);
Handle<Value> READ(NanNew<Uint32, uint32_t>(GRPC_READ));
completion_type->Set(NanNew("READ"), READ);
- Handle<Value> INVOKE_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_INVOKE_ACCEPTED));
- completion_type->Set(NanNew("INVOKE_ACCEPTED"), INVOKE_ACCEPTED);
Handle<Value> WRITE_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_WRITE_ACCEPTED));
completion_type->Set(NanNew("WRITE_ACCEPTED"), WRITE_ACCEPTED);
Handle<Value> FINISH_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_FINISH_ACCEPTED));
diff --git a/src/node/server.cc b/src/node/server.cc
index 64826897cd..b102775d33 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<Server>(args.This());
- NanReturnValue(NanNew<Boolean>(grpc_server_add_http2_port(
+ NanReturnValue(NanNew<Number>(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<Server>(args.This());
- NanReturnValue(NanNew<Boolean>(grpc_server_add_secure_http2_port(
+ NanReturnValue(NanNew<Number>(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 e947032b29..fe50acb5a1 100644
--- a/src/node/server.js
+++ b/src/node/server.js
@@ -47,10 +47,22 @@ 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 +71,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 +114,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 +151,7 @@ function GrpcServerStream(call, options) {
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 +193,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);
};
@@ -195,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);
@@ -211,12 +249,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 +276,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;
};
@@ -256,9 +301,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_client.js b/src/node/surface_client.js
index 996e3d101f..b63ae13e8d 100644
--- a/src/node/surface_client.js
+++ b/src/node/surface_client.js
@@ -63,114 +63,70 @@ 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();
}
});
this._stream.pause();
}
-util.inherits(ClientWritableObjectStream, Writable);
-
/**
- * Class for representing a gRPC client streaming call as a Node stream on the
- * 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
+ * _read implementation for both types of streams that allow reading.
+ * @this {ClientReadableObjectStream}
+ * @param {number} size Ignored
*/
-function ClientWritableObjectStream(stream, serialize, options) {
- options = _.extend(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() {
- this._stream.end();
- });
+function _read(size) {
+ this._stream.resume();
}
+/**
+ * See docs for _read
+ */
+ClientReadableObjectStream.prototype._read = _read;
-util.inherits(ClientBidiObjectStream, Duplex);
+util.inherits(ClientWritableObjectStream, Writable);
/**
- * Class for representing a gRPC bidi streaming call as a Node stream on the
- * client side. Extends from stream.Duplex.
+ * Class for representing a gRPC client streaming call as a Node stream on the
+ * 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 {function(Buffer)} deserialize Function for deserializing binary data
- * @param {object} options Stream options
*/
-function ClientBidiObjectStream(stream, serialize, deserialize, options) {
- options = _.extend(options, {objectMode: true});
- Duplex.call(this, options);
+function ClientWritableObjectStream(stream) {
+ var options = {objectMode: true};
+ Writable.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))) {
- 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}
- * @param {number} size Ignored
- */
-function _read(size) {
- this._stream.resume();
-}
-
-/**
- * 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
*/
function _write(chunk, encoding, callback) {
- this._stream.write(this._serialize(chunk), encoding, callback);
+ this._stream.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.
@@ -196,15 +152,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 +193,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 +230,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,12 +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, metadata, deadline);
- var obj_stream = new ClientBidiObjectStream(stream,
- serialize,
- deserialize,
- {});
- return obj_stream;
+ return client.makeRequest(this.channel, method, serialize,
+ deserialize, metadata, deadline);
}
return makeBidiStreamRequest;
}
diff --git a/src/node/surface_server.js b/src/node/surface_server.js
index 0b19d96b4c..07c5339f62 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();
}
});
@@ -76,57 +74,6 @@ function ServerReadableObjectStream(stream, deserialize, options) {
this._stream.pause();
}
-util.inherits(ServerWritableObjectStream, Writable);
-
-/**
- * Class for representing a gRPC server streaming call as a Node stream on the
- * 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});
- Writable.call(this, options);
- this._stream = stream;
- this._serialize = serialize;
- this.on('finish', function() {
- 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
- * @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});
- 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))) {
- 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}
@@ -140,39 +87,46 @@ function _read(size) {
* See docs for _read
*/
ServerReadableObjectStream.prototype._read = _read;
+
+util.inherits(ServerWritableObjectStream, Writable);
+
/**
- * See docs for _read
+ * Class for representing a gRPC server streaming call as a Node stream on the
+ * server side. Extends from stream.Writable.
+ * @constructor
+ * @param {stream} stream Underlying binary Duplex stream for the call
*/
-ServerBidiObjectStream.prototype._read = _read;
+function ServerWritableObjectStream(stream) {
+ var options = {objectMode: true};
+ Writable.call(this, options);
+ this._stream = stream;
+ this.on('finish', function() {
+ this._stream.end();
+ });
+}
/**
* _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
*/
function _write(chunk, encoding, callback) {
- this._stream.write(this._serialize(chunk), encoding, callback);
+ this._stream.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
* @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 +134,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 +142,7 @@ function makeUnaryHandler(handler, serialize, deserialize) {
if (err) {
stream.emit('error', err);
} else {
- stream.write(serialize(value));
+ stream.write(value);
stream.end();
}
});
@@ -201,23 +155,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 +180,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 +190,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,23 +200,10 @@ 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) {
- /**
- * 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,
- {});
- handler(object_stream);
- };
+function makeBidiStreamHandler(handler) {
+ return handler;
}
/**
@@ -341,10 +276,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);
}
@@ -357,8 +295,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/call_test.js b/src/node/test/call_test.js
index e6dc9664f1..6e52ec89bd 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/client_server_test.js b/src/node/test/client_server_test.js
index 534a5c464f..2a25908684 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/constant_test.js b/src/node/test/constant_test.js
index f65eea3cff..0138a55226 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 40bb5f3bbd..f7ccbcf5f2 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,131 @@ 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.invoke(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);
- call.writesDone(function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.FINISH_ACCEPTED);
- assert.strictEqual(event.data, grpc.opError.OK);
- });
- },function(event) {
- assert.strictEqual(event.type,
- grpc.completionType.CLIENT_METADATA_READ);
- },function(event) {
+ 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.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) {
- assert.strictEqual(event.type,
- grpc.completionType.FINISH_ACCEPTED);
- assert.strictEqual(event.data, grpc.opError.OK);
- done();
- });
- });
+ 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();
+ });
+ });
+ call.writesDone(function(event) {
+ assert.strictEqual(event.type,
+ grpc.completionType.FINISH_ACCEPTED);
+ assert.strictEqual(event.data, grpc.opError.OK);
});
});
-
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),
+ 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.invoke(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);
+ 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) {
+ 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/interop_sanity_test.js b/src/node/test/interop_sanity_test.js
index 9959a165ad..8ea48c359f 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,18 +42,18 @@ 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) {
+ 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) {
@@ -67,8 +65,7 @@ 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) {
+ it('should pass empty_stream', function(done) {
interop_client.runTest(port, name_override, 'empty_stream', true, done);
});
});
diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js
index 5ddf75a200..0e365bf870 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 79f7b32948..457d13d2f5 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,49 @@ 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) {
- 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) {
- 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);
+ 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.invoke(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);
+ 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();
});
});
});
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index 410efbce68..b171c9c176 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);
}
/**
@@ -427,7 +424,7 @@ static zend_function_entry call_methods[] = {
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, 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)
diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c
index c63196bf90..46c825a48f 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,
diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c
index e8b4643a58..492ac06739 100644
--- a/src/php/ext/grpc/php_grpc.c
+++ b/src/php/ext/grpc/php_grpc.c
@@ -107,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 aa66dbb848..836a4b09e3 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 253052a038..795831cb65 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 3818f9531c..78c5e9f93b 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 c562a821a4..7c3ad8a07c 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);
diff --git a/src/python/__init__.py b/src/python/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/__init__.py
diff --git a/src/python/_framework/__init__.py b/src/python/_framework/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/_framework/__init__.py
diff --git a/src/python/_framework/base/__init__.py b/src/python/_framework/base/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/_framework/base/__init__.py
diff --git a/src/ruby/lib/grpc/beefcake.rb b/src/python/_framework/base/exceptions.py
index fd3ebbf4b8..b8f4752184 100644
--- a/src/ruby/lib/grpc/beefcake.rb
+++ b/src/python/_framework/base/exceptions.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Google Inc.
+# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -27,31 +27,8 @@
# (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'
+"""Exceptions defined and used by the base layer of RPC Framework."""
-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
+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 0000000000..de7137cbf7
--- /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 0000000000..6eb07ea505
--- /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 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/_framework/base/packets/__init__.py
diff --git a/src/python/_framework/base/packets/_cancellation.py b/src/python/_framework/base/packets/_cancellation.py
new file mode 100644
index 0000000000..49172d1b97
--- /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 0000000000..8fbdc82782
--- /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 0000000000..be390364b0
--- /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 0000000000..b4be5eb0ff
--- /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 0000000000..baaf5cacf9
--- /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 0000000000..772e15f08c
--- /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 0000000000..ad5ed4cada
--- /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 0000000000..5f6c0593d0
--- /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 0000000000..a2a3823d28
--- /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 0000000000..d586c2167b
--- /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 0000000000..006128774d
--- /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 0000000000..2f07054d4d
--- /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 0000000000..8bb5353176
--- /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 0000000000..17daf3acf7
--- /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 0000000000..99f9e87772
--- /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 0000000000..9b40a00505
--- /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 0000000000..1315ca650e
--- /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 0000000000..6bbd18a59a
--- /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()
diff --git a/src/python/_framework/common/__init__.py b/src/python/_framework/common/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/_framework/common/__init__.py
diff --git a/src/python/_framework/common/cardinality.py b/src/python/_framework/common/cardinality.py
new file mode 100644
index 0000000000..610425e803
--- /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'
diff --git a/src/python/_framework/face/__init__.py b/src/python/_framework/face/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/_framework/face/__init__.py
diff --git a/src/python/_framework/face/_calls.py b/src/python/_framework/face/_calls.py
new file mode 100644
index 0000000000..ab58e6378b
--- /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 0000000000..2c221321d6
--- /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 0000000000..d758c2f148
--- /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 0000000000..50b55c389f
--- /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 0000000000..96563c94ee
--- /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 0000000000..501ec6b3f8
--- /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 0000000000..48e05b2478
--- /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 0000000000..f112df70bc
--- /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 0000000000..96f5fe85d3
--- /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 0000000000..94362e2007
--- /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 0000000000..0cc7c70df3
--- /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 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/_framework/face/testing/__init__.py
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 0000000000..d9ccb3af8f
--- /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 0000000000..0b1a2f0bd2
--- /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 0000000000..7a20869abe
--- /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 0000000000..3960c4e649
--- /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 0000000000..f3aca113fe
--- /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 0000000000..8d1291c975
--- /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 0000000000..dba73a9368
--- /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 0000000000..cf8b2eeb95
--- /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 0000000000..253f6f118d
--- /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 0000000000..47fc5822de
--- /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 0000000000..771346ec2e
--- /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 0000000000..bd82877e83
--- /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 0000000000..09b5a67f5a
--- /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/_framework/foundation/__init__.py b/src/python/_framework/foundation/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/_framework/foundation/__init__.py
diff --git a/src/python/_framework/foundation/_later_test.py b/src/python/_framework/foundation/_later_test.py
new file mode 100644
index 0000000000..fbd17a4ad9
--- /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/_logging_pool_test.py b/src/python/_framework/foundation/_logging_pool_test.py
new file mode 100644
index 0000000000..f2224d80e5
--- /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 _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/_timer_future.py b/src/python/_framework/foundation/_timer_future.py
new file mode 100644
index 0000000000..86bc073d56
--- /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 0000000000..960b4d06b4
--- /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 0000000000..1f7546cb76
--- /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 0000000000..f00c503257
--- /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 0000000000..fc2cf578d0
--- /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/logging_pool.py b/src/python/_framework/foundation/logging_pool.py
new file mode 100644
index 0000000000..7c7a6eebfc
--- /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))
diff --git a/src/python/_framework/foundation/stream.py b/src/python/_framework/foundation/stream.py
new file mode 100644
index 0000000000..75c0cf145b
--- /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 0000000000..c1acedc5c6
--- /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 0000000000..3a9c043316
--- /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
diff --git a/src/python/_junkdrawer/__init__.py b/src/python/_junkdrawer/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/python/_junkdrawer/__init__.py
diff --git a/src/python/_junkdrawer/stock_pb2.py b/src/python/_junkdrawer/stock_pb2.py
new file mode 100644
index 0000000000..eef18f82d6
--- /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/src/ruby/README.md b/src/ruby/README.md
index 23aec2b20a..7f7558dc67 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 <install_dir>
+$ bundle install
+$ rake # compiles the extension, runs the unit tests, see rake -T for other options
-$ # create a workspace
-$ git5 start <your-git5-branch> net/grpc
-$
-$ # build the C library and install it in $HOME/grpc_dev
-$ <google3>/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 <this_dir>
-$
-$ # 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}")
+```
diff --git a/src/ruby/bin/interop/README.md b/src/ruby/bin/interop/README.md
index 04020868a4..84fc663620 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
diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb
index 1a08eb97df..83212823f6 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 a6dbbf3aca..cbf41eda8b 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_call.c b/src/ruby/ext/grpc/rb_call.c
index 76b80bcaa1..1b6565f729 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_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c
index c1b74e2606..47776a991a 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/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c
index 0fae9502c3..a1ab6251c8 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/grpc.gemspec b/src/ruby/grpc.gemspec
index 8d7f44f30e..450362f5a8 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/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index bd684a8d07..1cdc168bfe 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 14ef6c531f..099d57151c 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
@@ -142,7 +140,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 5ea3cc94d6..40c5ec118e 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 6d8e1899a0..6442f23e89 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/call_spec.rb b/src/ruby/spec/call_spec.rb
index b8ecd64f39..9a510df1f3 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 1bcbc66446..b2afb0581e 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
@@ -294,7 +290,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
diff --git a/src/ruby/spec/event_spec.rb b/src/ruby/spec/event_spec.rb
index 5dec07e1ed..7ef08d021b 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 898022f185..443ba3d192 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,
diff --git a/src/ruby/spec/testdata/README b/src/ruby/spec/testdata/README
index ed72661e97..cb20dcb49f 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
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 11b9438a68..b80e80ca81 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
@@ -340,8 +348,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 +707,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 +720,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++':
diff --git a/templates/vsprojects/vs2013/gpr.vcxproj.filters.template b/templates/vsprojects/vs2013/gpr.vcxproj.filters.template
new file mode 100644
index 0000000000..0ed1ed85d0
--- /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.sln.template b/templates/vsprojects/vs2013/grpc.sln.template
index fe85d03cec..18dfb1af42 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.language == 'c++']
-
-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/grpc.vcxproj.filters.template b/templates/vsprojects/vs2013/grpc.vcxproj.filters.template
new file mode 100644
index 0000000000..0327c9422d
--- /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 0000000000..48cbbd30bb
--- /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 0000000000..c25718b802
--- /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>\
+<%def name="to_filter(path)">${calc_to_filter(path)}</%def>\
+<%def name="filter_to_guid(proj, filter)">${re.sub('(........)(....)(....)(....)', r'\1-\2-\3-\4-', hashlib.md5(''.join([filter, proj])).hexdigest())}</%def>\
+<%def name="gen_project(name, libs, targets)">\
+% for project in vsprojects:
+ % if project.name == name:
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ % if project.get('src',[]):
+ <ItemGroup>
+ % for src_name in project.src:
+ <ClCompile Include="..\..\${to_windows_path(src_name)}">
+ <Filter>${to_filter(src_name)}</Filter>
+ </ClCompile>
+ % endfor
+ </ItemGroup>
+ % endif
+ % if project.get('public_headers',[]):
+ <ItemGroup>
+ % for public_header in project.public_headers:
+ <ClInclude Include="..\..\${to_windows_path(public_header)}">
+ <Filter>${to_filter(public_header)}</Filter>
+ </ClInclude>
+ % endfor
+ </ItemGroup>
+ % endif
+ % if project.get('headers',[]):
+ <ItemGroup>
+ % for header in project.headers:
+ <ClInclude Include="..\..\${to_windows_path(header)}">
+ <Filter>${to_filter(header)}</Filter>
+ </ClInclude>
+ % endfor
+ </ItemGroup>
+ % 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)
+%>
+ <ItemGroup>
+ % for filter in filters:
+ <Filter Include="${filter}">
+ <UniqueIdentifier>{${filter_to_guid(project.name, filter)}}</UniqueIdentifier>
+ </Filter>
+ % endfor
+ </ItemGroup>
+</Project>
+ % endif
+% endfor
+</%def>\ \ No newline at end of file
diff --git a/templates/vsprojects/vs2013/vcxproj_defs.include b/templates/vsprojects/vs2013/vcxproj_defs.include
index ef12c62818..e21230abb7 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>\
<%def name="get_subsystem(is_library)">${'Windows' if is_library else 'Console'}</%def>\
<%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:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
@@ -124,7 +104,7 @@ project_dict = dict([(p.name, p) for p in projects])
<ItemGroup>
% for dep in project.deps:
<ProjectReference Include="${dep}.vcxproj">
- <Project>${project_dict[dep].vs_project_guid}</Project>
+ <Project>${vsproject_dict[dep].vs_project_guid}</Project>
</ProjectReference>
% endfor
</ItemGroup>
diff --git a/test/core/echo/client.c b/test/core/echo/client.c
index 1905863e11..2ad29df53c 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,
- 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);
+ 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/echo/echo_test.c b/test/core/echo/echo_test.c
index 16d381fb65..6449b2414f 100644
--- a/test/core/echo/echo_test.c
+++ b/test/core/echo/echo_test.c
@@ -42,10 +42,10 @@
#include <sys/wait.h>
#include "src/core/iomgr/socket_utils_posix.h"
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#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 35f118dc9b..57b083779c 100644
--- a/test/core/echo/server.c
+++ b/test/core/echo/server.c
@@ -39,11 +39,11 @@
#include <string.h>
#include <time.h>
+#include "src/core/support/string.h"
#include "test/core/util/test_config.h"
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/time.h>
#include "test/core/util/port.h"
diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c
index 9fa513126f..49b131c236 100644
--- a/test/core/end2end/cq_verifier.c
+++ b/test/core/end2end/cq_verifier.c
@@ -45,10 +45,10 @@
#include <string.h>
#include "src/core/surface/event_string.h"
+#include "src/core/support/string.h"
#include <grpc/byte_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/time.h>
#include <grpc/support/useful.h>
@@ -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
@@ -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,
@@ -270,8 +269,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",
@@ -409,20 +407,15 @@ 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++;
}
}
-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 a1966c14c5..6e031d8152 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 4327b91298..6219f57500 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -115,14 +115,10 @@ 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_start_invoke(c, client_cq, tag(1), 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. */
- 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);
@@ -152,11 +148,11 @@ 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_DEADLINE_EXCEEDED,
"Deadline Exceeded", 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/no_server_test.c b/test/core/end2end/no_server_test.c
index b9660f14b3..389a6429c4 100644
--- a/test/core/end2end/no_server_test.c
+++ b/test/core/end2end/no_server_test.c
@@ -57,10 +57,8 @@ 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) ==
- 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_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_DEADLINE_EXCEEDED,
"Deadline Exceeded", NULL);
diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c
index cfbb4796aa..33aed98c38 100644
--- a/test/core/end2end/tests/cancel_after_accept.c
+++ b/test/core/end2end/tests/cancel_after_accept.c
@@ -117,9 +117,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 74670bdc91..f348488b18 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
@@ -117,9 +117,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 d4cb5e4f13..3bb86723e6 100644
--- a/test/core/end2end/tests/cancel_after_invoke.c
+++ b/test/core/end2end/tests/cancel_after_invoke.c
@@ -115,9 +115,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 == mode.initiate_cancel(c));
diff --git a/test/core/end2end/tests/cancel_before_invoke.c b/test/core/end2end/tests/cancel_before_invoke.c
index f799cba71d..ac816484fd 100644
--- a/test/core/end2end/tests/cancel_before_invoke.c
+++ b/test/core/end2end/tests/cancel_before_invoke.c
@@ -115,8 +115,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/census_simple_request.c b/test/core/end2end/tests/census_simple_request.c
index baeed5cb46..719f0fe662 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);
diff --git a/test/core/end2end/tests/disappearing_server.c b/test/core/end2end/tests/disappearing_server.c
index b27a356eaa..036fdc2501 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),
- 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_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/early_server_shutdown_finishes_inflight_calls.c b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c
index 6ed0e4e106..66e3c44f4b 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/graceful_server_shutdown.c b/test/core/end2end/tests/graceful_server_shutdown.c
index 84ad4af119..d9c9dbb8b2 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);
diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c
index fc461250d1..f187eceadb 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 1db32b1de9..20f124ee9f 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);
@@ -158,7 +156,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;
@@ -192,26 +189,24 @@ 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),
- tag(301), tag(302), 0));
- GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c2, f.client_cq, tag(400),
- 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)));
+
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 */
- live_call = (int)(gpr_intptr) ev->tag;
- live_call_obj = live_call == 300 ? c1 : c2;
+ /* We'll get tag 303 or 403, we want 300, 400 */
+ live_call = ((int)(gpr_intptr) ev->tag) - 3;
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);
@@ -233,14 +228,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;
- 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);
+ live_call = (live_call == 300) ? 400 : 300;
cq_verify(v_client);
GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(200)));
diff --git a/test/core/end2end/tests/ping_pong_streaming.c b/test/core/end2end/tests/ping_pong_streaming.c
index 03d549a7b4..6768bd8aa9 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 f58bf77dfd..1dd798dc8d 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 09923b2fc5..cfc9b61f56 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 be65bf1567..32bf5129ff 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 d99141e024..4f1de8b466 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 e2f554b322..83628449a2 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 09b3c864fd..a352783965 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 90ed227749..1e15eaa9cc 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),
- 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);
+ 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/simple_request.c b/test/core/end2end/tests/simple_request.c
index 93dfa1fb0a..23fc201d84 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);
@@ -161,9 +159,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 2de0497a7a..6a3488e7dd 100644
--- a/test/core/end2end/tests/thread_stress.c
+++ b/test/core/end2end/tests/thread_stress.c
@@ -106,25 +106,30 @@ static void drain_cq(int client, grpc_completion_queue *cq) {
/* Kick off a new request - assumes g_mu taken */
static void start_request(void) {
+ 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_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));
+
+ 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 +140,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 +170,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 +192,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 9878b4ce9a..eea459459a 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/fling/client.c b/test/core/fling/client.c
index 7e93860dc3..7eb195811b 100644
--- a/test/core/fling/client.c
+++ b/test/core/fling/client.c
@@ -55,9 +55,8 @@ static void init_ping_pong_request(void) {}
static void step_ping_pong_request(void) {
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,
- GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
- grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ GPR_ASSERT(grpc_call_invoke(call, cq, (void *)1, (void *)1,
+ 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));
@@ -66,7 +65,6 @@ static void step_ping_pong_request(void) {
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,9 +72,8 @@ static void step_ping_pong_request(void) {
static void init_ping_pong_stream(void) {
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,
- 0) == GRPC_CALL_OK);
- grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ 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));
}
diff --git a/test/core/fling/fling_stream_test.c b/test/core/fling/fling_stream_test.c
index f6fe69824b..7f52fb1bad 100644
--- a/test/core/fling/fling_stream_test.c
+++ b/test/core/fling/fling_stream_test.c
@@ -41,9 +41,9 @@
#include <sys/types.h>
#include <sys/wait.h>
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
-#include <grpc/support/string.h>
#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 4607aa5f98..b2272f20c8 100644
--- a/test/core/fling/fling_test.c
+++ b/test/core/fling/fling_test.c
@@ -43,7 +43,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
-#include <grpc/support/string.h>
+#include "src/core/support/string.h"
#include "test/core/util/port.h"
int main(int argc, char **argv) {
diff --git a/test/core/iomgr/fd_posix_test.c b/test/core/iomgr/fd_posix_test.c
index 136f34a8b0..05c91ffdd4 100644
--- a/test/core/iomgr/fd_posix_test.c
+++ b/test/core/iomgr/fd_posix_test.c
@@ -37,6 +37,7 @@
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
+#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -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);
diff --git a/test/core/iomgr/resolve_address_test.c b/test/core/iomgr/resolve_address_test.c
index 26a4bc67e6..319ee634d6 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 <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
@@ -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;
}
diff --git a/test/core/iomgr/sockaddr_utils_test.c b/test/core/iomgr/sockaddr_utils_test.c
index 14018ed66c..3e653da4c9 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);
}
diff --git a/test/core/iomgr/tcp_client_posix_test.c b/test/core/iomgr/tcp_client_posix_test.c
index 79ba777e85..f9212e7373 100644
--- a/test/core/iomgr/tcp_client_posix_test.c
+++ b/test/core/iomgr/tcp_client_posix_test.c
@@ -40,6 +40,7 @@
#include <unistd.h>
#include "src/core/iomgr/iomgr.h"
+#include "src/core/iomgr/socket_utils_posix.h"
#include <grpc/support/log.h>
#include <grpc/support/time.h>
@@ -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);
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c
index 9c60f4c233..ec21e0d42f 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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/time.h>
#include "test/core/util/test_config.h"
#include <openssl/rsa.h>
diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c
index e87a606aba..a01ec6f87f 100644
--- a/test/core/support/string_test.c
+++ b/test/core/support/string_test.c
@@ -31,7 +31,7 @@
*
*/
-#include <grpc/support/string.h>
+#include "src/core/support/string.h"
#include <stddef.h>
#include <stdlib.h>
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
index 0520a39ea2..9b9f0202d6 100644
--- a/test/core/surface/lame_client_test.c
+++ b/test/core/surface/lame_client_test.c
@@ -62,11 +62,9 @@ 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_start_invoke(call, cq, tag(1), 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_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);
diff --git a/test/core/transport/chttp2/bin_encoder_test.c b/test/core/transport/chttp2/bin_encoder_test.c
index ea24f5cbd7..048ed7edd3 100644
--- a/test/core/transport/chttp2/bin_encoder_test.c
+++ b/test/core/transport/chttp2/bin_encoder_test.c
@@ -35,9 +35,9 @@
#include <string.h>
+#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
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 cebc2634fb..eb0f688f58 100644
--- a/test/core/transport/chttp2/stream_encoder_test.c
+++ b/test/core/transport/chttp2/stream_encoder_test.c
@@ -35,10 +35,10 @@
#include <stdio.h>
+#include "src/core/support/string.h"
#include "src/core/transport/chttp2/hpack_parser.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#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/chttp2/timeout_encoding_test.c b/test/core/transport/chttp2/timeout_encoding_test.c
index 4bb84e3f0b..ffa0070e34 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));
diff --git a/test/core/transport/transport_end2end_tests.c b/test/core/transport/transport_end2end_tests.c
index 712081bc8a..8e9b4a2cc9 100644
--- a/test/core/transport/transport_end2end_tests.c
+++ b/test/core/transport/transport_end2end_tests.c
@@ -37,10 +37,10 @@
#include <stdio.h>
#include <string.h>
+#include "src/core/support/string.h"
#include "src/core/transport/transport.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/string.h>
#include <grpc/support/thd.h>
#include <grpc/support/useful.h>
@@ -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 6df86b593f..5f3b55da75 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());
diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc
index 04cfeb86cb..2a18ddb72e 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<grpc::ClientReader<StreamingOutputCallResponse>> 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'));
diff --git a/test/cpp/interop/server.cc b/test/cpp/interop/server.cc
index 5b5c35416c..8a6be57929 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;
diff --git a/third_party/openssl b/third_party/openssl
-Subproject 2c5db8dac3a06fe5b2c889838a606138ee3542e
+Subproject 4ac0329582829f5378d8078c8d314ad37db8773
diff --git a/tools/buildgen/plugins/generate_vsprojects.py b/tools/buildgen/plugins/generate_vsprojects.py
new file mode 100755
index 0000000000..982e6812e6
--- /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.
+
+"""
+
+
+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)
+ # 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++']
+
+ project_dict = dict([(p['name'], p) for p in projects])
+
+ dictionary['vsprojects'] = projects
+ dictionary['vsproject_dict'] = project_dict
diff --git a/tools/dockerfile/grpc_cxx/Dockerfile b/tools/dockerfile/grpc_cxx/Dockerfile
index ea3a1dba8f..141a20a881 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,11 @@ 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
-# Define the default command.
-CMD ["bash"]
+RUN cd /var/local/git/grpc && ls \
+ && make clean \
+ && make gens/test/cpp/util/messages.pb.cc \
+ && make interop_client \
+ && make interop_server
+
+CMD ["/var/local/git/grpc/bins/opt/interop_server", "--enable_ssl", "--port=8010"]
diff --git a/tools/dockerfile/grpc_go/Dockerfile b/tools/dockerfile/grpc_go/Dockerfile
new file mode 100644
index 0000000000..1b998152a7
--- /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/rpc
+
+# Build the interop client and server
+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/rpc/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 0000000000..0d6ad3e391
--- /dev/null
+++ b/tools/dockerfile/grpc_go/README.md
@@ -0,0 +1,4 @@
+GRPC Go Dockerfile
+==================
+
+Dockerfile for gRPC Go development, testing and deployment.
diff --git a/tools/dockerfile/grpc_java/Dockerfile b/tools/dockerfile/grpc_java/Dockerfile
index 78659dedeb..f234f514e6 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 && \
@@ -13,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/dockerfile/grpc_java_base/Dockerfile b/tools/dockerfile/grpc_java_base/Dockerfile
index 44fa52c0e8..3271d1b2c2 100644
--- a/tools/dockerfile/grpc_java_base/Dockerfile
+++ b/tools/dockerfile/grpc_java_base/Dockerfile
@@ -20,14 +20,24 @@ 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 protobuf source from GitHub and install it
+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 && \
+ make -j12 && make check && make install && make clean
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
diff --git a/tools/dockerfile/grpc_ruby/Dockerfile b/tools/dockerfile/grpc_ruby/Dockerfile
index 43ec0183ca..c677ceffff 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
diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh
index bf776126b5..d97f829435 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 <server_name1>, <server_name2> .. <server_name3>
+#
+# Updates the GCE docker instance <server_name>
+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
@@ -412,7 +472,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 +484,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"
@@ -575,6 +634,19 @@ 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/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.
#
# call-seq:
@@ -583,7 +655,7 @@ grpc_interop_gen_ruby_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
}
@@ -604,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|go
+# TODO(grpc-team): add grpc_interop_gen_xxx_cmd for python|cxx|nodejs
diff --git a/tools/gce_setup/shared_startup_funcs.sh b/tools/gce_setup/shared_startup_funcs.sh
index 9c747466a9..f1dbca9a2e 100755
--- a/tools/gce_setup/shared_startup_funcs.sh
+++ b/tools/gce_setup/shared_startup_funcs.sh
@@ -367,11 +367,12 @@ 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
- 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"
@@ -402,10 +403,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 +451,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 +463,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
diff --git a/tools/run_tests/build_node.sh b/tools/run_tests/build_node.sh
new file mode 100755
index 0000000000..600b1bde8c
--- /dev/null
+++ b/tools/run_tests/build_node.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -ex
+
+# change to grpc repo root
+cd $(dirname $0)/../..
+
+# tells npm install to look for files in that directory
+export GRPC_ROOT=`pwd`
+# tells npm install the subdirectory with library files
+export GRPC_LIB_SUBDIR=libs/opt
+# tells npm install not to use default locations
+export GRPC_NO_INSTALL=yes
+
+# build the c libraries
+make -j static_c
+
+cd src/node
+
+npm install
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh
new file mode 100755
index 0000000000..6899ac7fe3
--- /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/jobset.py b/tools/run_tests/jobset.py
index 7a6d979ba3..8f16a4ff2c 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):
diff --git a/tools/run_tests/run_python.sh b/tools/run_tests/run_python.sh
new file mode 100755
index 0000000000..ef40602eb3
--- /dev/null
+++ b/tools/run_tests/run_python.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -ex
+
+# change to grpc repo root
+cd $(dirname $0)/../..
+
+root=`pwd`
+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'
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 15c523731b..da849f04cb 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
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index ec838649e0..90571eaec6 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -263,6 +263,10 @@
},
{
"language": "c++",
+ "name": "tips_client_test"
+ },
+ {
+ "language": "c++",
"name": "status_test"
},
{
diff --git a/vsprojects/third_party/openssl/OpenSSL.mak b/vsprojects/third_party/openssl/OpenSSL.mak
index 09344e8ae1..8b1167094a 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
diff --git a/vsprojects/vs2013/.gitignore b/vsprojects/vs2013/.gitignore
new file mode 100644
index 0000000000..75fb4ed6b6
--- /dev/null
+++ b/vsprojects/vs2013/.gitignore
@@ -0,0 +1,5 @@
+Debug
+Release
+*.suo
+grpc.opensdf
+grpc.sdf
diff --git a/vsprojects/vs2013/gpr.vcxproj.filters b/vsprojects/vs2013/gpr.vcxproj.filters
new file mode 100644
index 0000000000..c1fccfff3b
--- /dev/null
+++ b/vsprojects/vs2013/gpr.vcxproj.filters
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="..\..\src\core\support\alloc.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\cancellable.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\cmdline.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\cpu_linux.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\cpu_posix.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\histogram.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\host_port.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\log.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\log_android.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\log_linux.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\log_posix.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\log_win32.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\murmur_hash.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\slice.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\slice_buffer.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\string.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\string_posix.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\string_win32.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\sync.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\sync_posix.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\sync_win32.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\thd_posix.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\thd_win32.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\time.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\time_posix.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\support\time_win32.c">
+ <Filter>src\core\support</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\include\grpc\support\alloc.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\atm.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\atm_gcc_atomic.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\atm_gcc_sync.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\atm_win32.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\cancellable_platform.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\cmdline.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\histogram.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\host_port.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\log.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\port_platform.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\slice.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\slice_buffer.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\string.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\sync.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\sync_generic.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\sync_posix.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\sync_win32.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\thd.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\thd_posix.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\thd_win32.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\time.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\time_posix.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\time_win32.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\support\useful.h">
+ <Filter>include\grpc\support</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\src\core\support\cpu.h">
+ <Filter>src\core\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\support\murmur_hash.h">
+ <Filter>src\core\support</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\support\thd_internal.h">
+ <Filter>src\core\support</Filter>
+ </ClInclude>
+ </ItemGroup>
+
+ <ItemGroup>
+ <Filter Include="include">
+ <UniqueIdentifier>{9ea89137-2bf7-b6d9-b7af-7cb4d1b74928}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="include\grpc">
+ <UniqueIdentifier>{e6957ec1-85ba-6515-03c0-e12878045b1f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="include\grpc\support">
+ <UniqueIdentifier>{31c42000-3ed7-95e1-d076-df814b72cdee}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src">
+ <UniqueIdentifier>{60eb2826-e58b-cb10-a98d-fe04727398a2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core">
+ <UniqueIdentifier>{c5e1baa7-de77-beb1-9675-942261648f79}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\support">
+ <UniqueIdentifier>{bb116f2a-ea2a-c233-82da-0c54e3cbfec1}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vs2013/grpc.vcxproj b/vsprojects/vs2013/grpc.vcxproj
index 38192548a7..28ea60339d 100644
--- a/vsprojects/vs2013/grpc.vcxproj
+++ b/vsprojects/vs2013/grpc.vcxproj
@@ -121,7 +121,9 @@
<ClInclude Include="..\..\src\core\iomgr\pollset.h" />
<ClInclude Include="..\..\src\core\iomgr\pollset_kick.h" />
<ClInclude Include="..\..\src\core\iomgr\pollset_kick_posix.h" />
+ <ClInclude Include="..\..\src\core\iomgr\pollset_kick_windows.h" />
<ClInclude Include="..\..\src\core\iomgr\pollset_posix.h" />
+ <ClInclude Include="..\..\src\core\iomgr\pollset_windows.h" />
<ClInclude Include="..\..\src\core\iomgr\resolve_address.h" />
<ClInclude Include="..\..\src\core\iomgr\sockaddr.h" />
<ClInclude Include="..\..\src\core\iomgr\sockaddr_posix.h" />
@@ -254,7 +256,9 @@
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_posix.c">
</ClCompile>
- <ClCompile Include="..\..\src\core\iomgr\resolve_address_posix.c">
+ <ClCompile Include="..\..\src\core\iomgr\pollset_windows.c">
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\resolve_address.c">
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\sockaddr_utils.c">
</ClCompile>
diff --git a/vsprojects/vs2013/grpc.vcxproj.filters b/vsprojects/vs2013/grpc.vcxproj.filters
new file mode 100644
index 0000000000..4f03c14b76
--- /dev/null
+++ b/vsprojects/vs2013/grpc.vcxproj.filters
@@ -0,0 +1,628 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="..\..\src\core\security\auth.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\base64.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\credentials.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\factories.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\google_root_certs.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\json_token.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\secure_endpoint.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\secure_transport_setup.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\security_context.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\security\server_secure_chttp2.c">
+ <Filter>src\core\security</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\tsi\fake_transport_security.c">
+ <Filter>src\core\tsi</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\tsi\ssl_transport_security.c">
+ <Filter>src\core\tsi</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\tsi\transport_security.c">
+ <Filter>src\core\tsi</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\call_op_string.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\census_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\channel_args.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\channel_stack.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\child_channel.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\client_channel.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\client_setup.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\connected_channel.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\http_client_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\http_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\http_server_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\metadata_buffer.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\noop_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\compression\algorithm.c">
+ <Filter>src\core\compression</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\compression\message_compress.c">
+ <Filter>src\core\compression</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\httpcli\format_request.c">
+ <Filter>src\core\httpcli</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\httpcli\httpcli.c">
+ <Filter>src\core\httpcli</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\httpcli\httpcli_security_context.c">
+ <Filter>src\core\httpcli</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\httpcli\parser.c">
+ <Filter>src\core\httpcli</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\alarm.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\alarm_heap.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\endpoint.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\endpoint_pair_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\fd_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\iomgr.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\pollset_kick_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\pollset_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\pollset_windows.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\resolve_address.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\sockaddr_utils.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\socket_utils_common_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\socket_utils_linux.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\socket_utils_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\tcp_client_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\tcp_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\tcp_server_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\census_init.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\census_log.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\census_rpc_stats.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\census_tracing.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\hash_table.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\window_stats.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\byte_buffer.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\byte_buffer_reader.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\call.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\channel.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\channel_create.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\client.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\completion_queue.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\event_string.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\init.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\lame_client.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\secure_channel_create.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\secure_server_create.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\server.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\server_chttp2.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\server_create.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\alpn.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\bin_encoder.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_data.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_goaway.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_ping.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_rst_stream.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_settings.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_window_update.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\hpack_parser.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\hpack_table.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\huffsyms.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\status_conversion.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\stream_encoder.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\stream_map.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\timeout_encoding.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\varint.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2_transport.c">
+ <Filter>src\core\transport</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\metadata.c">
+ <Filter>src\core\transport</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\stream_op.c">
+ <Filter>src\core\transport</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\transport.c">
+ <Filter>src\core\transport</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\third_party\cJSON\cJSON.c">
+ <Filter>third_party\cJSON</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\include\grpc\grpc_security.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\byte_buffer.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\byte_buffer_reader.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\grpc.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\status.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\src\core\security\auth.h">
+ <Filter>src\core\security</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\security\base64.h">
+ <Filter>src\core\security</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\security\credentials.h">
+ <Filter>src\core\security</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\security\google_root_certs.h">
+ <Filter>src\core\security</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\security\json_token.h">
+ <Filter>src\core\security</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\security\secure_transport_setup.h">
+ <Filter>src\core\security</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\security\security_context.h">
+ <Filter>src\core\security</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\tsi\fake_transport_security.h">
+ <Filter>src\core\tsi</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\tsi\ssl_transport_security.h">
+ <Filter>src\core\tsi</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\tsi\transport_security.h">
+ <Filter>src\core\tsi</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\tsi\transport_security_interface.h">
+ <Filter>src\core\tsi</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\census_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\channel_args.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\channel_stack.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\child_channel.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\client_channel.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\client_setup.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\connected_channel.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\http_client_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\http_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\http_server_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\metadata_buffer.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\noop_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\compression\algorithm.h">
+ <Filter>src\core\compression</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\compression\message_compress.h">
+ <Filter>src\core\compression</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\httpcli\format_request.h">
+ <Filter>src\core\httpcli</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\httpcli\httpcli.h">
+ <Filter>src\core\httpcli</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\httpcli\httpcli_security_context.h">
+ <Filter>src\core\httpcli</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\httpcli\parser.h">
+ <Filter>src\core\httpcli</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\alarm.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\alarm_heap.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\alarm_internal.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\endpoint.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\endpoint_pair.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\fd_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\iomgr.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\iomgr_internal.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\iomgr_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_kick.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_kick_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_kick_windows.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_windows.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\resolve_address.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\sockaddr.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\sockaddr_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\sockaddr_utils.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\sockaddr_win32.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\socket_utils_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\tcp_client.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\tcp_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\tcp_server.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\census_interface.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\census_log.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\census_rpc_stats.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\census_tracing.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\hash_table.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\window_stats.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\call.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\channel.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\client.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\completion_queue.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\event_string.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\lame_client.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\server.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\surface_trace.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\bin_encoder.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_data.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_goaway.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_ping.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_rst_stream.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_settings.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_window_update.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\hpack_parser.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\hpack_table.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\http2_errors.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\huffsyms.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\status_conversion.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\stream_encoder.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\stream_map.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\timeout_encoding.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\varint.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2_transport.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\metadata.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\stream_op.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\transport.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\transport_impl.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ </ItemGroup>
+
+ <ItemGroup>
+ <Filter Include="include">
+ <UniqueIdentifier>{968de0a1-346d-b75a-6f19-6a55119b8235}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="include\grpc">
+ <UniqueIdentifier>{880c644d-b84f-cfca-98bd-e145f36232ab}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src">
+ <UniqueIdentifier>{d538af37-07b2-062b-fa2a-d9f882cb2737}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core">
+ <UniqueIdentifier>{ea745680-21ea-9c5e-679b-64dc40562d08}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\channel">
+ <UniqueIdentifier>{d897b6c3-c555-234e-a589-b4f008063615}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\compression">
+ <UniqueIdentifier>{263cb913-dfe6-42a4-096b-cac231f76305}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\httpcli">
+ <UniqueIdentifier>{a9bc00ad-835f-c625-c6d9-6a1324f98b9f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\iomgr">
+ <UniqueIdentifier>{1baf3894-af37-e647-bdbc-95dc17ed0073}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\security">
+ <UniqueIdentifier>{1d850ac6-e639-4eab-5338-4ba40272fcc9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\statistics">
+ <UniqueIdentifier>{0ef49896-2313-4a3f-1ce2-716fa0e5c6ca}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\surface">
+ <UniqueIdentifier>{aeb18e82-5d25-0aad-8b02-a0a3470073ce}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\transport">
+ <UniqueIdentifier>{168fa1b1-1c18-eb55-9a4d-746bc58df2c1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\transport\chttp2">
+ <UniqueIdentifier>{b8b623c3-a168-a2b1-0d5f-b70a1f1cd8d2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\tsi">
+ <UniqueIdentifier>{0b0f9ab1-efa4-7f03-e446-6fb9b5227e84}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="third_party">
+ <UniqueIdentifier>{aaab30a4-2a15-732e-c141-3fbc0f0f5a7a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="third_party\cJSON">
+ <UniqueIdentifier>{332d0840-2c7a-bb09-8e58-585a6fb3959f}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj b/vsprojects/vs2013/grpc_unsecure.vcxproj
index 38192548a7..28ea60339d 100644
--- a/vsprojects/vs2013/grpc_unsecure.vcxproj
+++ b/vsprojects/vs2013/grpc_unsecure.vcxproj
@@ -121,7 +121,9 @@
<ClInclude Include="..\..\src\core\iomgr\pollset.h" />
<ClInclude Include="..\..\src\core\iomgr\pollset_kick.h" />
<ClInclude Include="..\..\src\core\iomgr\pollset_kick_posix.h" />
+ <ClInclude Include="..\..\src\core\iomgr\pollset_kick_windows.h" />
<ClInclude Include="..\..\src\core\iomgr\pollset_posix.h" />
+ <ClInclude Include="..\..\src\core\iomgr\pollset_windows.h" />
<ClInclude Include="..\..\src\core\iomgr\resolve_address.h" />
<ClInclude Include="..\..\src\core\iomgr\sockaddr.h" />
<ClInclude Include="..\..\src\core\iomgr\sockaddr_posix.h" />
@@ -254,7 +256,9 @@
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\pollset_posix.c">
</ClCompile>
- <ClCompile Include="..\..\src\core\iomgr\resolve_address_posix.c">
+ <ClCompile Include="..\..\src\core\iomgr\pollset_windows.c">
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\resolve_address.c">
</ClCompile>
<ClCompile Include="..\..\src\core\iomgr\sockaddr_utils.c">
</ClCompile>
diff --git a/vsprojects/vs2013/grpc_unsecure.vcxproj.filters b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters
new file mode 100644
index 0000000000..a7ba173dcc
--- /dev/null
+++ b/vsprojects/vs2013/grpc_unsecure.vcxproj.filters
@@ -0,0 +1,547 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="..\..\src\core\channel\call_op_string.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\census_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\channel_args.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\channel_stack.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\child_channel.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\client_channel.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\client_setup.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\connected_channel.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\http_client_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\http_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\http_server_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\metadata_buffer.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\channel\noop_filter.c">
+ <Filter>src\core\channel</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\compression\algorithm.c">
+ <Filter>src\core\compression</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\compression\message_compress.c">
+ <Filter>src\core\compression</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\httpcli\format_request.c">
+ <Filter>src\core\httpcli</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\httpcli\httpcli.c">
+ <Filter>src\core\httpcli</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\httpcli\httpcli_security_context.c">
+ <Filter>src\core\httpcli</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\httpcli\parser.c">
+ <Filter>src\core\httpcli</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\alarm.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\alarm_heap.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\endpoint.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\endpoint_pair_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\fd_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\iomgr.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\iomgr_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\pollset_kick_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\pollset_multipoller_with_poll_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\pollset_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\pollset_windows.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\resolve_address.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\sockaddr_utils.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\socket_utils_common_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\socket_utils_linux.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\socket_utils_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\tcp_client_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\tcp_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\tcp_server_posix.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\iomgr\time_averaged_stats.c">
+ <Filter>src\core\iomgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\census_init.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\census_log.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\census_rpc_stats.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\census_tracing.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\hash_table.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\statistics\window_stats.c">
+ <Filter>src\core\statistics</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\byte_buffer.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\byte_buffer_reader.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\call.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\channel.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\channel_create.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\client.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\completion_queue.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\event_string.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\init.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\lame_client.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\secure_channel_create.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\secure_server_create.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\server.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\server_chttp2.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\surface\server_create.c">
+ <Filter>src\core\surface</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\alpn.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\bin_encoder.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_data.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_goaway.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_ping.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_rst_stream.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_settings.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\frame_window_update.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\hpack_parser.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\hpack_table.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\huffsyms.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\status_conversion.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\stream_encoder.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\stream_map.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\timeout_encoding.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2\varint.c">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\chttp2_transport.c">
+ <Filter>src\core\transport</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\metadata.c">
+ <Filter>src\core\transport</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\stream_op.c">
+ <Filter>src\core\transport</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\core\transport\transport.c">
+ <Filter>src\core\transport</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\third_party\cJSON\cJSON.c">
+ <Filter>third_party\cJSON</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\include\grpc\byte_buffer.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\byte_buffer_reader.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\grpc.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\grpc\status.h">
+ <Filter>include\grpc</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\src\core\channel\census_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\channel_args.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\channel_stack.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\child_channel.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\client_channel.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\client_setup.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\connected_channel.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\http_client_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\http_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\http_server_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\metadata_buffer.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\channel\noop_filter.h">
+ <Filter>src\core\channel</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\compression\algorithm.h">
+ <Filter>src\core\compression</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\compression\message_compress.h">
+ <Filter>src\core\compression</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\httpcli\format_request.h">
+ <Filter>src\core\httpcli</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\httpcli\httpcli.h">
+ <Filter>src\core\httpcli</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\httpcli\httpcli_security_context.h">
+ <Filter>src\core\httpcli</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\httpcli\parser.h">
+ <Filter>src\core\httpcli</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\alarm.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\alarm_heap.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\alarm_internal.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\endpoint.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\endpoint_pair.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\fd_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\iomgr.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\iomgr_internal.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\iomgr_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_kick.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_kick_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_kick_windows.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\pollset_windows.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\resolve_address.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\sockaddr.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\sockaddr_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\sockaddr_utils.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\sockaddr_win32.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\socket_utils_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\tcp_client.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\tcp_posix.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\tcp_server.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\iomgr\time_averaged_stats.h">
+ <Filter>src\core\iomgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\census_interface.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\census_log.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\census_rpc_stats.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\census_tracing.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\hash_table.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\statistics\window_stats.h">
+ <Filter>src\core\statistics</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\call.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\channel.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\client.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\completion_queue.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\event_string.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\lame_client.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\server.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\surface\surface_trace.h">
+ <Filter>src\core\surface</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\bin_encoder.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_data.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_goaway.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_ping.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_rst_stream.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_settings.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\frame_window_update.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\hpack_parser.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\hpack_table.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\http2_errors.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\huffsyms.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\status_conversion.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\stream_encoder.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\stream_map.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\timeout_encoding.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2\varint.h">
+ <Filter>src\core\transport\chttp2</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\chttp2_transport.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\metadata.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\stream_op.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\transport.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\core\transport\transport_impl.h">
+ <Filter>src\core\transport</Filter>
+ </ClInclude>
+ </ItemGroup>
+
+ <ItemGroup>
+ <Filter Include="include">
+ <UniqueIdentifier>{10076c7e-7c8e-8005-0c81-64454af2cbc8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="include\grpc">
+ <UniqueIdentifier>{77b9717b-b8d8-dd5f-14bb-a3e96809a70a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src">
+ <UniqueIdentifier>{aaf326a1-c884-46ea-875a-cbbd9983e539}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core">
+ <UniqueIdentifier>{88491077-386b-2039-d14c-0c40136b5f7a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\channel">
+ <UniqueIdentifier>{cc102c4b-66ff-cf4c-2288-d76327e1a183}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\compression">
+ <UniqueIdentifier>{2e3aca1d-223d-10a1-b282-7f9fc68ee6f5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\httpcli">
+ <UniqueIdentifier>{1ba3a245-47e7-89b5-b0c9-aca758bd0277}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\iomgr">
+ <UniqueIdentifier>{a9df8b24-ecea-ff6d-8999-d8fa54cd70bf}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\statistics">
+ <UniqueIdentifier>{e084164c-a069-00e3-db35-4e0b1cd6f0b7}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\surface">
+ <UniqueIdentifier>{6cd0127e-c24b-d43c-38f5-198db8d4322a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\transport">
+ <UniqueIdentifier>{6687ff98-e36e-c0b1-2756-1bc79edec406}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\core\transport\chttp2">
+ <UniqueIdentifier>{5fcd6206-f774-9ae6-4b85-305d6a723843}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="third_party">
+ <UniqueIdentifier>{025c051e-8eba-125b-67f9-173f95176eb2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="third_party\cJSON">
+ <UniqueIdentifier>{7d75397e-988a-baac-897e-2ea7b43d5dd9}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project>
+