Merge github.com:grpc/grpc into ping-ping-ping-ping-ping-ping-ping-ping-ping

pull/4325/head
Craig Tiller 9 years ago
commit be9e135d6f
  1. 22
      Makefile
  2. 3
      package.json
  3. 6
      src/core/census/context.h
  4. 7
      src/core/support/slice.c
  5. 2
      src/core/transport/chttp2/frame_data.c
  6. 4
      src/core/transport/chttp2/hpack_parser.c
  7. 1
      src/csharp/Grpc.Examples/Grpc.Examples.csproj
  8. 1
      src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
  9. 52
      src/csharp/Grpc.HealthCheck/proto/health.proto
  10. 22
      src/csharp/generate_proto_csharp.sh
  11. 3
      src/node/health_check/health.js
  12. 49
      src/node/health_check/health.proto
  13. 336
      src/node/performance/benchmark_client.js
  14. 162
      src/node/performance/benchmark_server.js
  15. 180
      src/node/performance/histogram.js
  16. 119
      src/node/performance/perf_test.js
  17. 137
      src/node/performance/qps_test.js
  18. 60
      src/node/performance/worker_server.js
  19. 132
      src/node/performance/worker_service_impl.js
  20. 2
      src/node/test/async_test.js
  21. 80
      src/node/test/math/math.proto
  22. 3
      src/node/test/math/math_server.js
  23. 2
      src/node/test/math_client_test.js
  24. 3
      src/node/test/surface_test.js
  25. 3
      src/php/tests/generated_code/math.proto
  26. 1
      src/proto/grpc/health/v1alpha/health.proto
  27. 0
      src/proto/math/math.proto
  28. 286
      src/python/grpcio/grpc/_adapter/_c/types.h
  29. 186
      src/python/grpcio/grpc/_adapter/_c/types/call.c
  30. 203
      src/python/grpcio/grpc/_adapter/_c/types/call_credentials.c
  31. 187
      src/python/grpcio/grpc/_adapter/_c/types/channel.c
  32. 165
      src/python/grpcio/grpc/_adapter/_c/types/channel_credentials.c
  33. 124
      src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c
  34. 196
      src/python/grpcio/grpc/_adapter/_c/types/server.c
  35. 137
      src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c
  36. 524
      src/python/grpcio/grpc/_adapter/_c/utility.c
  37. 33
      src/python/grpcio/grpc/_adapter/_implementations.py
  38. 45
      src/python/grpcio/grpc/_adapter/_intermediary_low.py
  39. 214
      src/python/grpcio/grpc/_adapter/_low.py
  40. 94
      src/python/grpcio/grpc/_adapter/_types.py
  41. 16
      src/python/grpcio/grpc/_cython/_cygrpc/call.pyx
  42. 31
      src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx
  43. 11
      src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx
  44. 23
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd
  45. 80
      src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx
  46. 59
      src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd
  47. 1
      src/python/grpcio/grpc/_cython/_cygrpc/records.pxd
  48. 82
      src/python/grpcio/grpc/_cython/_cygrpc/records.pyx
  49. 2
      src/python/grpcio/grpc/_cython/_cygrpc/server.pyx
  50. 6
      src/python/grpcio/grpc/_cython/cygrpc.pyx
  51. 12
      src/python/grpcio/grpc/_links/invocation.py
  52. 6
      src/python/grpcio/grpc/_links/service.py
  53. 2
      src/python/grpcio/grpc/beta/_server.py
  54. 96
      src/python/grpcio/grpc/beta/implementations.py
  55. 49
      src/python/grpcio/grpc/beta/interfaces.py
  56. 23
      src/python/grpcio/setup.py
  57. 2
      src/python/grpcio/tests/interop/_secure_interop_test.py
  58. 2
      src/python/grpcio/tests/interop/client.py
  59. 12
      src/python/grpcio/tests/unit/_adapter/_intermediary_low_test.py
  60. 8
      src/python/grpcio/tests/unit/_adapter/_low_test.py
  61. 187
      src/python/grpcio/tests/unit/_cython/adapter_low_test.py
  62. 190
      src/python/grpcio/tests/unit/_cython/cygrpc_test.py
  63. 58
      src/python/grpcio/tests/unit/beta/_beta_features_test.py
  64. 4
      src/python/grpcio/tests/unit/beta/_face_interface_test.py
  65. 6
      src/python/grpcio/tests/unit/beta/test_utilities.py
  66. 80
      src/ruby/bin/math.proto
  67. 29
      src/ruby/bin/math.rb
  68. 29
      src/ruby/bin/math_services.rb
  69. 2
      src/ruby/pb/README.md
  70. 87
      src/ruby/pb/generate_proto_ruby.sh
  71. 2
      src/ruby/pb/test/proto/messages.rb
  72. 3
      test/core/bad_client/gen_build_yaml.py
  73. 32
      test/core/bad_client/tests/headers.c
  74. 43
      test/core/bad_client/tests/unknown_frame.c
  75. 17
      test/core/support/slice_test.c
  76. 53
      test/core/support/sync_test.c
  77. 14
      test/core/surface/completion_queue_test.c
  78. 6
      test/core/surface/init_test.c
  79. 15
      tools/run_tests/sources_and_headers.json
  80. 18
      tools/run_tests/tests.json
  81. 28
      vsprojects/buildtests_c.sln
  82. 187
      vsprojects/vcxproj/test/unknown_frame_bad_client_test/unknown_frame_bad_client_test.vcxproj
  83. 24
      vsprojects/vcxproj/test/unknown_frame_bad_client_test/unknown_frame_bad_client_test.vcxproj.filters

File diff suppressed because one or more lines are too long

@ -39,7 +39,8 @@
"minimist": "^1.1.0", "minimist": "^1.1.0",
"mocha": "~1.21.0", "mocha": "~1.21.0",
"mocha-jenkins-reporter": "^0.1.9", "mocha-jenkins-reporter": "^0.1.9",
"mustache": "^2.0.0" "mustache": "^2.0.0",
"poisson-process": "^0.2.1"
}, },
"engines": { "engines": {
"node": ">=0.10.13" "node": ">=0.10.13"

@ -39,11 +39,7 @@
/* census_context is the in-memory representation of information needed to /* census_context is the in-memory representation of information needed to
* maintain tracing, RPC statistics and resource usage information. */ * maintain tracing, RPC statistics and resource usage information. */
struct census_context { struct census_context {
gpr_uint64 op_id; /* Operation identifier - unique per-context */ census_tag_set *tags; /* Opaque data structure for census tags. */
gpr_uint64 trace_id; /* Globally unique trace identifier */
/* TODO(aveitch) Add census tags:
const census_tag_set *tags;
*/
}; };
#endif /* GRPC_INTERNAL_CORE_CENSUS_CONTEXT_H */ #endif /* GRPC_INTERNAL_CORE_CENSUS_CONTEXT_H */

@ -341,10 +341,3 @@ int gpr_slice_str_cmp(gpr_slice a, const char *b) {
if (d != 0) return d; if (d != 0) return d;
return memcmp(GPR_SLICE_START_PTR(a), b, b_length); return memcmp(GPR_SLICE_START_PTR(a), b, b_length);
} }
char *gpr_slice_to_cstring(gpr_slice slice) {
char *result = gpr_malloc(GPR_SLICE_LENGTH(slice) + 1);
memcpy(result, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice));
result[GPR_SLICE_LENGTH(slice)] = '\0';
return result;
}

@ -118,7 +118,7 @@ void grpc_chttp2_encode_data(gpr_uint32 id, gpr_slice_buffer *inbuf,
hdr = gpr_slice_malloc(9); hdr = gpr_slice_malloc(9);
p = GPR_SLICE_START_PTR(hdr); p = GPR_SLICE_START_PTR(hdr);
GPR_ASSERT(write_bytes < 16777316); GPR_ASSERT(write_bytes < (1<<24));
*p++ = (gpr_uint8)(write_bytes >> 16); *p++ = (gpr_uint8)(write_bytes >> 16);
*p++ = (gpr_uint8)(write_bytes >> 8); *p++ = (gpr_uint8)(write_bytes >> 8);
*p++ = (gpr_uint8)(write_bytes); *p++ = (gpr_uint8)(write_bytes);

@ -1418,6 +1418,9 @@ grpc_chttp2_parse_error grpc_chttp2_header_parser_parse(
GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
return GRPC_CHTTP2_CONNECTION_ERROR; return GRPC_CHTTP2_CONNECTION_ERROR;
} }
/* need to check for null stream: this can occur if we receive an invalid
stream id on a header */
if (stream_parsing != NULL) {
if (parser->is_boundary) { if (parser->is_boundary) {
stream_parsing stream_parsing
->got_metadata_on_parse[stream_parsing->header_frames_received] = 1; ->got_metadata_on_parse[stream_parsing->header_frames_received] = 1;
@ -1428,6 +1431,7 @@ grpc_chttp2_parse_error grpc_chttp2_header_parser_parse(
if (parser->is_eof) { if (parser->is_eof) {
stream_parsing->received_close = 1; stream_parsing->received_close = 1;
} }
}
parser->on_header = on_header_not_set; parser->on_header = on_header_not_set;
parser->on_header_user_data = NULL; parser->on_header_user_data = NULL;
parser->is_boundary = 0xde; parser->is_boundary = 0xde;

@ -66,6 +66,5 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="proto\math.proto" />
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -65,7 +65,6 @@
<ItemGroup> <ItemGroup>
<None Include="Grpc.HealthCheck.nuspec" /> <None Include="Grpc.HealthCheck.nuspec" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="proto\health.proto" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj"> <ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj">

@ -1,52 +0,0 @@
// 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(jtattermusch): switch to proto3 once C# supports that.
syntax = "proto3";
package grpc.health.v1alpha;
option csharp_namespace = "Grpc.Health.V1Alpha";
message HealthCheckRequest {
string host = 1;
string service = 2;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
}

@ -30,19 +30,19 @@
# Regenerates gRPC service stubs from proto files. # Regenerates gRPC service stubs from proto files.
set +e set +e
cd $(dirname $0) cd $(dirname $0)/../..
PROTOC=../../bins/opt/protobuf/protoc PROTOC=bins/opt/protobuf/protoc
PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin PLUGIN=protoc-gen-grpc=bins/opt/grpc_csharp_plugin
EXAMPLES_DIR=Grpc.Examples EXAMPLES_DIR=src/csharp/Grpc.Examples
TESTING_DIR=Grpc.IntegrationTesting HEALTHCHECK_DIR=src/csharp/Grpc.HealthCheck
HEALTHCHECK_DIR=Grpc.HealthCheck TESTING_DIR=src/csharp/Grpc.IntegrationTesting
$PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \ $PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \
-I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto -I src/proto/math src/proto/math/math.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \
-I ../.. ../../test/proto/*.proto ../../test/proto/benchmarks/*.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \ $PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \
-I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto -I src/proto/grpc/health/v1alpha src/proto/grpc/health/v1alpha/health.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \
-I . test/proto/{empty,messages,test}.proto test/proto/benchmarks/*.proto

@ -37,7 +37,8 @@ var grpc = require('../');
var _ = require('lodash'); var _ = require('lodash');
var health_proto = grpc.load(__dirname + '/health.proto'); var health_proto = grpc.load(__dirname +
'/../../proto/grpc/health/v1alpha/health.proto');
var HealthClient = health_proto.grpc.health.v1alpha.Health; var HealthClient = health_proto.grpc.health.v1alpha.Health;

@ -1,49 +0,0 @@
// 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.
syntax = "proto3";
package grpc.health.v1alpha;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
}

@ -0,0 +1,336 @@
/*
*
* 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.
*
*/
/**
* Benchmark client module
* @module
*/
'use strict';
var fs = require('fs');
var path = require('path');
var util = require('util');
var EventEmitter = require('events');
var _ = require('lodash');
var PoissonProcess = require('poisson-process');
var Histogram = require('./histogram');
var grpc = require('../../../');
var serviceProto = grpc.load({
root: __dirname + '/../../..',
file: 'test/proto/benchmarks/services.proto'}).grpc.testing;
/**
* Create a buffer filled with size zeroes
* @param {number} size The length of the buffer
* @return {Buffer} The new buffer
*/
function zeroBuffer(size) {
var zeros = new Buffer(size);
zeros.fill(0);
return zeros;
}
/**
* Convert a time difference, as returned by process.hrtime, to a number of
* nanoseconds.
* @param {Array.<number>} time_diff The time diff, represented as
* [seconds, nanoseconds]
* @return {number} The total number of nanoseconds
*/
function timeDiffToNanos(time_diff) {
return time_diff[0] * 1e9 + time_diff[1];
}
/**
* The BenchmarkClient class. Opens channels to servers and makes RPCs based on
* parameters from the driver, and records statistics about those RPCs.
* @param {Array.<string>} server_targets List of servers to connect to
* @param {number} channels The total number of channels to open
* @param {Object} histogram_params Options for setting up the histogram
* @param {Object=} security_params Options for TLS setup. If absent, don't use
* TLS
*/
function BenchmarkClient(server_targets, channels, histogram_params,
security_params) {
var options = {};
var creds;
if (security_params) {
var ca_path;
if (security_params.use_test_ca) {
ca_path = path.join(__dirname, '../test/data/ca.pem');
var ca_data = fs.readFileSync(ca_path);
creds = grpc.credentials.createSsl(ca_data);
} else {
creds = grpc.credentials.createSsl();
}
if (security_params.server_host_override) {
var host_override = security_params.server_host_override;
options['grpc.ssl_target_name_override'] = host_override;
options['grpc.default_authority'] = host_override;
}
} else {
creds = grpc.credentials.createInsecure();
}
this.clients = [];
for (var i = 0; i < channels; i++) {
this.clients[i] = new serviceProto.BenchmarkService(
server_targets[i % server_targets.length], creds, options);
}
this.histogram = new Histogram(histogram_params.resolution,
histogram_params.max_possible);
this.running = false;
this.pending_calls = 0;
};
util.inherits(BenchmarkClient, EventEmitter);
/**
* Start a closed-loop test. For each channel, start
* outstanding_rpcs_per_channel RPCs. Then, whenever an RPC finishes, start
* another one.
* @param {number} outstanding_rpcs_per_channel Number of RPCs to start per
* channel
* @param {string} rpc_type Which method to call. Should be 'UNARY' or
* 'STREAMING'
* @param {number} req_size The size of the payload to send with each request
* @param {number} resp_size The size of payload to request be sent in responses
*/
BenchmarkClient.prototype.startClosedLoop = function(
outstanding_rpcs_per_channel, rpc_type, req_size, resp_size) {
var self = this;
self.running = true;
self.last_wall_time = process.hrtime();
var makeCall;
var argument = {
response_size: resp_size,
payload: {
body: zeroBuffer(req_size)
}
};
if (rpc_type == 'UNARY') {
makeCall = function(client) {
if (self.running) {
self.pending_calls++;
var start_time = process.hrtime();
client.unaryCall(argument, function(error, response) {
if (error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
return;
}
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
makeCall(client);
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
}
};
} else {
makeCall = function(client) {
if (self.running) {
self.pending_calls++;
var start_time = process.hrtime();
var call = client.streamingCall();
call.write(argument);
call.on('data', function() {
});
call.on('end', function() {
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
makeCall(client);
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
call.on('error', function(error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
});
}
};
}
_.each(self.clients, function(client) {
_.times(outstanding_rpcs_per_channel, function() {
makeCall(client);
});
});
};
/**
* Start a poisson test. For each channel, this initiates a number of Poisson
* processes equal to outstanding_rpcs_per_channel, where each Poisson process
* has the load parameter offered_load.
* @param {number} outstanding_rpcs_per_channel Number of RPCs to start per
* channel
* @param {string} rpc_type Which method to call. Should be 'UNARY' or
* 'STREAMING'
* @param {number} req_size The size of the payload to send with each request
* @param {number} resp_size The size of payload to request be sent in responses
* @param {number} offered_load The load parameter for the Poisson process
*/
BenchmarkClient.prototype.startPoisson = function(
outstanding_rpcs_per_channel, rpc_type, req_size, resp_size, offered_load) {
var self = this;
self.running = true;
self.last_wall_time = process.hrtime();
var makeCall;
var argument = {
response_size: resp_size,
payload: {
body: zeroBuffer(req_size)
}
};
if (rpc_type == 'UNARY') {
makeCall = function(client, poisson) {
if (self.running) {
self.pending_calls++;
var start_time = process.hrtime();
client.unaryCall(argument, function(error, response) {
if (error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
return;
}
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
} else {
poisson.stop();
}
};
} else {
makeCall = function(client, poisson) {
if (self.running) {
self.pending_calls++;
var start_time = process.hrtime();
var call = client.streamingCall();
call.write(argument);
call.on('data', function() {
});
call.on('end', function() {
var time_diff = process.hrtime(start_time);
self.histogram.add(timeDiffToNanos(time_diff));
self.pending_calls--;
if ((!self.running) && self.pending_calls == 0) {
self.emit('finished');
}
});
call.on('error', function(error) {
self.emit('error', new Error('Client error: ' + error.message));
self.running = false;
});
} else {
poisson.stop();
}
};
}
var averageIntervalMs = (1 / offered_load) * 1000;
_.each(self.clients, function(client) {
_.times(outstanding_rpcs_per_channel, function() {
var p = PoissonProcess.create(averageIntervalMs, function() {
makeCall(client, p);
});
p.start();
});
});
};
/**
* Return curent statistics for the client. If reset is set, restart
* statistic collection.
* @param {boolean} reset Indicates that statistics should be reset
* @return {object} Client statistics
*/
BenchmarkClient.prototype.mark = function(reset) {
var wall_time_diff = process.hrtime(this.last_wall_time);
var histogram = this.histogram;
if (reset) {
this.last_wall_time = process.hrtime();
this.histogram = new Histogram(histogram.resolution,
histogram.max_possible);
}
return {
latencies: {
bucket: histogram.getContents(),
min_seen: histogram.minimum(),
max_seen: histogram.maximum(),
sum: histogram.getSum(),
sum_of_squares: histogram.sumOfSquares(),
count: histogram.getCount()
},
time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9,
// Not sure how to measure these values
time_user: 0,
time_system: 0
};
};
/**
* Stop the clients.
* @param {function} callback Called when the clients have finished shutting
* down
*/
BenchmarkClient.prototype.stop = function(callback) {
this.running = false;
this.on('finished', callback);
};
module.exports = BenchmarkClient;

@ -0,0 +1,162 @@
/*
*
* 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.
*
*/
/**
* Benchmark server module
* @module
*/
'use strict';
var fs = require('fs');
var path = require('path');
var grpc = require('../../../');
var serviceProto = grpc.load({
root: __dirname + '/../../..',
file: 'test/proto/benchmarks/services.proto'}).grpc.testing;
/**
* Create a buffer filled with size zeroes
* @param {number} size The length of the buffer
* @return {Buffer} The new buffer
*/
function zeroBuffer(size) {
var zeros = new Buffer(size);
zeros.fill(0);
return zeros;
}
/**
* Handler for the unary benchmark method. Simply responds with a payload
* containing the requested number of zero bytes.
* @param {Call} call The call object to be handled
* @param {function} callback The callback to call with the response
*/
function unaryCall(call, callback) {
var req = call.request;
var payload = {body: zeroBuffer(req.response_size)};
callback(null, {payload: payload});
}
/**
* Handler for the streaming benchmark method. Simply responds to each request
* with a payload containing the requested number of zero bytes.
* @param {Call} call The call object to be handled
*/
function streamingCall(call) {
call.on('data', function(value) {
var payload = {body: zeroBuffer(value.repsonse_size)};
call.write({payload: payload});
});
call.on('end', function() {
call.end();
});
}
/**
* BenchmarkServer class. Constructed based on parameters from the driver and
* stores statistics.
* @param {string} host The host to serve on
* @param {number} port The port to listen to
* @param {tls} Indicates whether TLS should be used
*/
function BenchmarkServer(host, port, tls) {
var server_creds;
var host_override;
if (tls) {
var key_path = path.join(__dirname, '../test/data/server1.key');
var pem_path = path.join(__dirname, '../test/data/server1.pem');
var key_data = fs.readFileSync(key_path);
var pem_data = fs.readFileSync(pem_path);
server_creds = grpc.ServerCredentials.createSsl(null,
[{private_key: key_data,
cert_chain: pem_data}]);
} else {
server_creds = grpc.ServerCredentials.createInsecure();
}
var server = new grpc.Server();
this.port = server.bind(host + ':' + port, server_creds);
server.addProtoService(serviceProto.BenchmarkService.service, {
unaryCall: unaryCall,
streamingCall: streamingCall
});
this.server = server;
}
/**
* Start the benchmark server.
*/
BenchmarkServer.prototype.start = function() {
this.server.start();
this.last_wall_time = process.hrtime();
};
/**
* Return the port number that the server is bound to.
* @return {Number} The port number
*/
BenchmarkServer.prototype.getPort = function() {
return this.port;
};
/**
* Return current statistics for the server. If reset is set, restart
* statistic collection.
* @param {boolean} reset Indicates that statistics should be reset
* @return {object} Server statistics
*/
BenchmarkServer.prototype.mark = function(reset) {
var wall_time_diff = process.hrtime(this.last_wall_time);
if (reset) {
this.last_wall_time = process.hrtime();
}
return {
time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9,
// Not sure how to measure these values
time_user: 0,
time_system: 0
};
};
/**
* Stop the server.
* @param {function} callback Called when the server has finished shutting down
*/
BenchmarkServer.prototype.stop = function(callback) {
this.server.tryShutdown(callback);
};
module.exports = BenchmarkServer;

@ -0,0 +1,180 @@
/*
*
* 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.
*
*/
/**
* Histogram module. Exports the Histogram class
* @module
*/
'use strict';
/**
* Histogram class. Collects data and exposes a histogram and other statistics.
* This data structure is taken directly from src/core/support/histogram.c, but
* pared down to the statistics needed for client stats in
* test/proto/benchmarks/stats.proto.
* @constructor
* @param {number} resolution The histogram's bucket resolution. Must be positive
* @param {number} max_possible The maximum allowed value. Must be greater than 1
*/
function Histogram(resolution, max_possible) {
this.resolution = resolution;
this.max_possible = max_possible;
this.sum = 0;
this.sum_of_squares = 0;
this.multiplier = 1 + resolution;
this.count = 0;
this.min_seen = max_possible;
this.max_seen = 0;
this.buckets = [];
for (var i = 0; i < this.bucketFor(max_possible) + 1; i++) {
this.buckets[i] = 0;
}
}
/**
* Get the bucket index for a given value.
* @param {number} value The value to check
* @return {number} The bucket index
*/
Histogram.prototype.bucketFor = function(value) {
return Math.floor(Math.log(value) / Math.log(this.multiplier));
};
/**
* Get the minimum value for a given bucket index
* @param {number} The bucket index to check
* @return {number} The minimum value for that bucket
*/
Histogram.prototype.bucketStart = function(index) {
return Math.pow(this.multiplier, index);
};
/**
* Add a value to the histogram. This updates all statistics with the new
* value. Those statistics should not be modified except with this function
* @param {number} value The value to add
*/
Histogram.prototype.add = function(value) {
// Ensure value is a number
value = +value;
this.sum += value;
this.sum_of_squares += value * value;
this.count++;
if (value < this.min_seen) {
this.min_seen = value;
}
if (value > this.max_seen) {
this.max_seen = value;
}
this.buckets[this.bucketFor(value)]++;
};
/**
* Get the mean of all added values
* @return {number} The mean
*/
Histogram.prototype.mean = function() {
return this.sum / this.count;
};
/**
* Get the variance of all added values. Used to calulate the standard deviation
* @return {number} The variance
*/
Histogram.prototype.variance = function() {
if (this.count == 0) {
return 0;
}
return (this.sum_of_squares * this.count - this.sum * this.sum) /
(this.count * this.count);
};
/**
* Get the standard deviation of all added values
* @return {number} The standard deviation
*/
Histogram.prototype.stddev = function() {
return Math.sqrt(this.variance);
};
/**
* Get the maximum among all added values
* @return {number} The maximum
*/
Histogram.prototype.maximum = function() {
return this.max_seen;
};
/**
* Get the minimum among all added values
* @return {number} The minimum
*/
Histogram.prototype.minimum = function() {
return this.min_seen;
};
/**
* Get the number of all added values
* @return {number} The count
*/
Histogram.prototype.getCount = function() {
return this.count;
};
/**
* Get the sum of all added values
* @return {number} The sum
*/
Histogram.prototype.getSum = function() {
return this.sum;
};
/**
* Get the sum of squares of all added values
* @return {number} The sum of squares
*/
Histogram.prototype.sumOfSquares = function() {
return this.sum_of_squares;
};
/**
* Get the raw histogram as a list of bucket sizes
* @return {Array.<number>} The buckets
*/
Histogram.prototype.getContents = function() {
return this.buckets;
};
module.exports = Histogram;

@ -1,119 +0,0 @@
/*
*
* 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.
*
*/
'use strict';
var grpc = require('..');
var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing;
var _ = require('lodash');
var interop_server = require('../interop/interop_server.js');
function runTest(iterations, callback) {
var testServer = interop_server.getServer(0, false);
testServer.server.start();
var client = new testProto.TestService('localhost:' + testServer.port,
grpc.credentials.createInsecure());
function runIterations(finish) {
var start = process.hrtime();
var intervals = [];
function next(i) {
if (i >= iterations) {
testServer.server.shutdown();
var totalDiff = process.hrtime(start);
finish({
total: totalDiff[0] * 1000000 + totalDiff[1] / 1000,
intervals: intervals
});
} else{
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
var startTime = process.hrtime();
client.emptyCall({}, function(err, resp) {
var timeDiff = process.hrtime(startTime);
intervals[i] = timeDiff[0] * 1000000 + timeDiff[1] / 1000;
next(i+1);
}, {}, {deadline: deadline});
}
}
next(0);
}
function warmUp(num) {
var pending = num;
function startCall() {
client.emptyCall({}, function(err, resp) {
pending--;
if (pending === 0) {
runIterations(callback);
}
});
}
for (var i = 0; i < num; i++) {
startCall();
}
}
warmUp(100);
}
function percentile(arr, pct) {
if (pct > 99) {
pct = 99;
}
if (pct < 0) {
pct = 0;
}
var index = Math.floor(arr.length * pct / 100);
return arr[index];
}
if (require.main === module) {
var count;
if (process.argv.length >= 3) {
count = process.argv[2];
} else {
count = 100;
}
runTest(count, function(results) {
var sorted_intervals = _.sortBy(results.intervals, _.identity);
console.log('count:', count);
console.log('total time:', results.total, 'us');
console.log('median:', percentile(sorted_intervals, 50), 'us');
console.log('90th percentile:', percentile(sorted_intervals, 90), 'us');
console.log('95th percentile:', percentile(sorted_intervals, 95), 'us');
console.log('99th percentile:', percentile(sorted_intervals, 99), 'us');
console.log('QPS:', (count / results.total) * 1000000);
});
}
module.exports = runTest;

@ -1,137 +0,0 @@
/*
*
* 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.
*
*/
/**
* This script runs a QPS test. It sends requests for a specified length of time
* with a specified number pending at any one time. It then outputs the measured
* QPS. Usage:
* node qps_test.js [--concurrent=count] [--time=seconds]
* concurrent defaults to 100 and time defaults to 10
*/
'use strict';
var async = require('async');
var parseArgs = require('minimist');
var grpc = require('..');
var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing;
var interop_server = require('../interop/interop_server.js');
/**
* Runs the QPS test. Sends requests constantly for the given number of seconds,
* and keeps concurrent_calls requests pending at all times. When the test ends,
* the callback is called with the number of calls that completed within the
* time limit.
* @param {number} concurrent_calls The number of calls to have pending
* simultaneously
* @param {number} seconds The number of seconds to run the test for
* @param {function(Error, number)} callback Callback for test completion
*/
function runTest(concurrent_calls, seconds, callback) {
var testServer = interop_server.getServer(0, false);
testServer.server.start();
var client = new testProto.TestService('localhost:' + testServer.port,
grpc.credentials.createInsecure());
var warmup_num = 100;
/**
* Warms up the client to avoid counting startup time in the test result
* @param {function(Error)} callback Called when warmup is complete
*/
function warmUp(callback) {
var pending = warmup_num;
function startCall() {
client.emptyCall({}, function(err, resp) {
if (err) {
callback(err);
return;
}
pending--;
if (pending === 0) {
callback(null);
}
});
}
for (var i = 0; i < warmup_num; i++) {
startCall();
}
}
/**
* Run the QPS test. Starts concurrent_calls requests, then starts a new
* request whenever one completes until time runs out.
* @param {function(Error, number)} callback Called when the test is complete.
* The second argument is the number of calls that finished within the
* time limit
*/
function run(callback) {
var running = 0;
var count = 0;
var start = process.hrtime();
function responseCallback(err, resp) {
if (process.hrtime(start)[0] < seconds) {
count += 1;
client.emptyCall({}, responseCallback);
} else {
running -= 1;
if (running <= 0) {
callback(null, count);
}
}
}
for (var i = 0; i < concurrent_calls; i++) {
running += 1;
client.emptyCall({}, responseCallback);
}
}
async.waterfall([warmUp, run], function(err, count) {
testServer.server.shutdown();
callback(err, count);
});
}
if (require.main === module) {
var argv = parseArgs(process.argv.slice(2), {
default: {'concurrent': 100,
'time': 10}
});
runTest(argv.concurrent, argv.time, function(err, count) {
if (err) {
throw err;
}
console.log('Concurrent calls:', argv.concurrent);
console.log('Time:', argv.time, 'seconds');
console.log('QPS:', (count/argv.time));
});
}

@ -31,37 +31,33 @@
* *
*/ */
#include <stdlib.h> 'use strict';
#define PY_SSIZE_T_CLEAN var worker_service_impl = require('./worker_service_impl');
#include <Python.h>
#include <grpc/grpc.h> var grpc = require('../../../');
var serviceProto = grpc.load({
#include "grpc/_adapter/_c/types.h" root: __dirname + '/../../..',
file: 'test/proto/benchmarks/services.proto'}).grpc.testing;
static PyMethodDef c_methods[] = {
{NULL} function runServer(port) {
}; var server_creds = grpc.ServerCredentials.createInsecure();
var server = new grpc.Server();
PyMODINIT_FUNC init_c(void) { server.addProtoService(serviceProto.WorkerService.service,
PyObject *module; worker_service_impl);
var address = '0.0.0.0:' + port;
module = Py_InitModule3("_c", c_methods, server.bind(address, server_creds);
"Wrappings of C structures and functions."); server.start();
return server;
if (pygrpc_module_add_types(module) < 0) { }
return;
}
if (PyModule_AddStringConstant(
module, "PRIMARY_USER_AGENT_KEY",
GRPC_ARG_PRIMARY_USER_AGENT_STRING) < 0) {
return;
}
/* GRPC maintains an internal counter of how many times it has been if (require.main === module) {
initialized and handles multiple pairs of grpc_init()/grpc_shutdown() Error.stackTraceLimit = Infinity;
invocations accordingly. */ var parseArgs = require('minimist');
grpc_init(); var argv = parseArgs(process.argv, {
atexit(&grpc_shutdown); string: ['driver_port']
});
runServer(argv.driver_port);
} }
exports.runServer = runServer;

@ -0,0 +1,132 @@
/*
*
* 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.
*
*/
'use strict';
var BenchmarkClient = require('./benchmark_client');
var BenchmarkServer = require('./benchmark_server');
exports.runClient = function runClient(call) {
var client;
call.on('data', function(request) {
var stats;
switch (request.argtype) {
case 'setup':
var setup = request.setup;
client = new BenchmarkClient(setup.server_targets,
setup.client_channels,
setup.histogram_params,
setup.security_params);
client.on('error', function(error) {
call.emit('error', error);
});
switch (setup.load_params.load) {
case 'closed_loop':
client.startClosedLoop(setup.outstanding_rpcs_per_channel,
setup.rpc_type,
setup.payload_config.simple_params.req_size,
setup.payload_config.simple_params.resp_size);
break;
case 'poisson':
client.startPoisson(setup.outstanding_rpcs_per_channel,
setup.rpc_type, setup.payload_config.req_size,
setup.payload_config.resp_size,
setup.load_params.poisson.offered_load);
break;
default:
call.emit('error', new Error('Unsupported LoadParams type' +
setup.load_params.load));
}
stats = client.mark();
call.write({
stats: stats
});
break;
case 'mark':
if (client) {
stats = client.mark(request.mark.reset);
call.write({
stats: stats
});
} else {
call.emit('error', new Error('Got Mark before ClientConfig'));
}
break;
default:
throw new Error('Nonexistent client argtype option: ' + request.argtype);
}
});
call.on('end', function() {
client.stop(function() {
call.end();
});
});
};
exports.runServer = function runServer(call) {
var server;
call.on('data', function(request) {
var stats;
switch (request.argtype) {
case 'setup':
server = new BenchmarkServer(request.setup.host, request.setup.port,
request.setup.security_params);
server.start();
stats = server.mark();
call.write({
stats: stats,
port: server.getPort()
});
break;
case 'mark':
if (server) {
stats = server.mark(request.mark.reset);
call.write({
stats: stats,
port: server.getPort(),
cores: 1
});
} else {
call.emit('error', new Error('Got Mark before ServerConfig'));
}
break;
default:
throw new Error('Nonexistent server argtype option');
}
});
call.on('end', function() {
server.stop(function() {
call.end();
});
});
};

@ -36,7 +36,7 @@
var assert = require('assert'); var assert = require('assert');
var grpc = require('..'); var grpc = require('..');
var math = grpc.load(__dirname + '/math/math.proto').math; var math = grpc.load(__dirname + '/../../proto/math/math.proto').math;
/** /**

@ -1,80 +0,0 @@
// 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.
syntax = "proto3";
package math;
message DivArgs {
int64 dividend = 1;
int64 divisor = 2;
}
message DivReply {
int64 quotient = 1;
int64 remainder = 2;
}
message FibArgs {
int64 limit = 1;
}
message Num {
int64 num = 1;
}
message FibReply {
int64 count = 1;
}
service Math {
// Div divides args.dividend by args.divisor and returns the quotient and
// remainder.
rpc Div (DivArgs) returns (DivReply) {
}
// DivMany accepts an arbitrary number of division args from the client stream
// and sends back the results in the reply stream. The stream continues until
// the client closes its end; the server does the same after sending all the
// replies. The stream ends immediately if either end aborts.
rpc DivMany (stream DivArgs) returns (stream DivReply) {
}
// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
// generates up to limit numbers; otherwise it continues until the call is
// canceled. Unlike Fib above, Fib has no final FibReply.
rpc Fib (FibArgs) returns (stream Num) {
}
// Sum sums a stream of numbers, returning the final result once the stream
// is closed.
rpc Sum (stream Num) returns (Num) {
}
}

@ -34,7 +34,8 @@
'use strict'; 'use strict';
var grpc = require('../..'); var grpc = require('../..');
var math = grpc.load(__dirname + '/math.proto').math; var math = grpc.load(__dirname + '/../../../proto/math/math.proto').math;
/** /**
* Server function for division. Provides the /Math/DivMany and /Math/Div * Server function for division. Provides the /Math/DivMany and /Math/Div

@ -36,7 +36,7 @@
var assert = require('assert'); var assert = require('assert');
var grpc = require('..'); var grpc = require('..');
var math = grpc.load(__dirname + '/math/math.proto').math; var math = grpc.load(__dirname + '/../../proto/math/math.proto').math;
/** /**
* Client to use to make requests to a running server. * Client to use to make requests to a running server.

@ -41,7 +41,8 @@ var ProtoBuf = require('protobufjs');
var grpc = require('..'); var grpc = require('..');
var math_proto = ProtoBuf.loadProtoFile(__dirname + '/math/math.proto'); var math_proto = ProtoBuf.loadProtoFile(__dirname +
'/../../proto/math/math.proto');
var mathService = math_proto.lookup('math.Math'); var mathService = math_proto.lookup('math.Math');

@ -28,6 +28,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// TODO: start using src/proto/math/math.proto and remove this file once
// PHP supports proto3.
syntax = "proto2"; syntax = "proto2";
package math; package math;

@ -30,6 +30,7 @@
syntax = "proto3"; syntax = "proto3";
package grpc.health.v1alpha; package grpc.health.v1alpha;
option csharp_namespace = "Grpc.Health.V1Alpha";
message HealthCheckRequest { message HealthCheckRequest {
string host = 1; string host = 1;

@ -1,286 +0,0 @@
/*
*
* 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__ADAPTER__C_TYPES_H_
#define GRPC__ADAPTER__C_TYPES_H_
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
/*=========================*/
/* Client-side credentials */
/*=========================*/
typedef struct ChannelCredentials {
PyObject_HEAD
grpc_channel_credentials *c_creds;
} ChannelCredentials;
void pygrpc_ChannelCredentials_dealloc(ChannelCredentials *self);
ChannelCredentials *pygrpc_ChannelCredentials_google_default(
PyTypeObject *type, PyObject *ignored);
ChannelCredentials *pygrpc_ChannelCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
ChannelCredentials *pygrpc_ChannelCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_ChannelCredentials_type;
typedef struct CallCredentials {
PyObject_HEAD
grpc_call_credentials *c_creds;
} CallCredentials;
void pygrpc_CallCredentials_dealloc(CallCredentials *self);
CallCredentials *pygrpc_CallCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
CallCredentials *pygrpc_CallCredentials_compute_engine(
PyTypeObject *type, PyObject *ignored);
CallCredentials *pygrpc_CallCredentials_jwt(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
CallCredentials *pygrpc_CallCredentials_refresh_token(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
CallCredentials *pygrpc_CallCredentials_iam(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_CallCredentials_type;
/*=========================*/
/* Server-side credentials */
/*=========================*/
typedef struct ServerCredentials {
PyObject_HEAD
grpc_server_credentials *c_creds;
} ServerCredentials;
void pygrpc_ServerCredentials_dealloc(ServerCredentials *self);
ServerCredentials *pygrpc_ServerCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_ServerCredentials_type;
/*==================*/
/* Completion queue */
/*==================*/
typedef struct CompletionQueue {
PyObject_HEAD
grpc_completion_queue *c_cq;
} CompletionQueue;
CompletionQueue *pygrpc_CompletionQueue_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_CompletionQueue_dealloc(CompletionQueue *self);
PyObject *pygrpc_CompletionQueue_next(
CompletionQueue *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_CompletionQueue_shutdown(
CompletionQueue *self, PyObject *ignored);
extern PyTypeObject pygrpc_CompletionQueue_type;
/*======*/
/* Call */
/*======*/
typedef struct Call {
PyObject_HEAD
grpc_call *c_call;
CompletionQueue *cq;
} Call;
Call *pygrpc_Call_new_empty(CompletionQueue *cq);
void pygrpc_Call_dealloc(Call *self);
PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Call_peer(Call *self);
PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args,
PyObject *kwargs);
extern PyTypeObject pygrpc_Call_type;
/*=========*/
/* Channel */
/*=========*/
typedef struct Channel {
PyObject_HEAD
grpc_channel *c_chan;
} Channel;
Channel *pygrpc_Channel_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_Channel_dealloc(Channel *self);
Call *pygrpc_Channel_create_call(
Channel *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Channel_check_connectivity_state(Channel *self, PyObject *args,
PyObject *kwargs);
PyObject *pygrpc_Channel_watch_connectivity_state(Channel *self, PyObject *args,
PyObject *kwargs);
PyObject *pygrpc_Channel_target(Channel *self);
extern PyTypeObject pygrpc_Channel_type;
/*========*/
/* Server */
/*========*/
typedef struct Server {
PyObject_HEAD
grpc_server *c_serv;
CompletionQueue *cq;
int shutdown_called;
} Server;
Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_Server_dealloc(Server *self);
PyObject *pygrpc_Server_request_call(
Server *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Server_add_http2_port(
Server *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Server_start(Server *self, PyObject *ignored);
PyObject *pygrpc_Server_shutdown(
Server *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Server_cancel_all_calls(Server *self, PyObject *unused);
extern PyTypeObject pygrpc_Server_type;
/*=========*/
/* Utility */
/*=========*/
/* Every tag that passes from Python GRPC to GRPC core is of this type. */
typedef struct pygrpc_tag {
PyObject *user_tag;
Call *call;
grpc_call_details request_call_details;
grpc_metadata_array request_metadata;
grpc_op *ops;
size_t nops;
int is_new_call;
} pygrpc_tag;
/* Construct a tag associated with a batch call. Does not take ownership of the
resources in the elements of ops. */
pygrpc_tag *pygrpc_produce_batch_tag(PyObject *user_tag, Call *call,
grpc_op *ops, size_t nops);
/* Construct a tag associated with a server request. The calling code should
use the appropriate fields of the produced tag in the invocation of
grpc_server_request_call. */
pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call);
/* Construct a tag associated with a server shutdown. */
pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag);
/* Construct a tag associated with a channel state change. */
pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag);
/* Frees all resources owned by the tag and the tag itself. */
void pygrpc_discard_tag(pygrpc_tag *tag);
/* Consumes an event and its associated tag, providing a Python tuple of the
form `(type, tag, call, call_details, results)` (where type is an integer
corresponding to a grpc_completion_type, tag is an arbitrary PyObject, call
is the call object associated with the event [if any], call_details is a
tuple of form `(method, host, deadline)` [if such details are available],
and resultd is a list of tuples of form `(type, metadata, message, status,
cancelled)` [where type corresponds to a grpc_op_type, metadata is a
sequence of 2-sequences of strings, message is a byte string, and status is
a 2-tuple of an integer corresponding to grpc_status_code and a string of
status details]).
Frees all resources associated with the event tag. */
PyObject *pygrpc_consume_event(grpc_event event);
/* Transliterate the Python tuple of form `(type, metadata, message,
status)` (where type is an integer corresponding to a grpc_op_type, metadata
is a sequence of 2-sequences of strings, message is a byte string, and
status is 2-tuple of an integer corresponding to grpc_status_code and a
string of status details) to a grpc_op suitable for use in a
grpc_call_start_batch invocation. The grpc_op is a 'directory' of resources
that must be freed after GRPC core is done with them.
Calls gpr_malloc (or the appropriate type-specific grpc_*_create function)
to populate the appropriate union-discriminated members of the op.
Returns true on success, false on failure. */
int pygrpc_produce_op(PyObject *op, grpc_op *result);
/* Discards all resources associated with the passed in op that was produced by
pygrpc_produce_op. */
void pygrpc_discard_op(grpc_op op);
/* Transliterate the grpc_ops (which have been sent through a
grpc_call_start_batch invocation and whose corresponding event has appeared
on a completion queue) to a Python tuple of form `(type, metadata, message,
status, cancelled)` (where type is an integer corresponding to a
grpc_op_type, metadata is a sequence of 2-sequences of strings, message is a
byte string, and status is 2-tuple of an integer corresponding to
grpc_status_code and a string of status details).
Calls gpr_free (or the appropriate type-specific grpc_*_destroy function) on
the appropriate union-discriminated populated members of the ops. */
PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops);
/* Transliterate from a gpr_timespec to a double (in units of seconds, either
from the epoch if interpreted absolutely or as a delta otherwise). */
double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec);
/* Transliterate from a double (in units of seconds from the epoch if
interpreted absolutely or as a delta otherwise) to a gpr_timespec. */
gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds);
/* Returns true on success, false on failure. */
int pygrpc_cast_pyseq_to_send_metadata(
PyObject *pyseq, grpc_metadata **metadata, size_t *count);
/* Returns a metadata array as a Python object on success, else NULL. */
PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata);
/* Transliterate from a list of python channel arguments (2-tuples of string
and string|integer|None) to a grpc_channel_args object. The strings placed
in the grpc_channel_args object's grpc_arg elements are views of the Python
object. The Python object must live long enough for the grpc_channel_args
to be used. Arguments set to None are silently ignored. Returns true on
success, false on failure. */
int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args);
void pygrpc_discard_channel_args(grpc_channel_args args);
/* Read the bytes from grpc_byte_buffer to a gpr_malloc'd array of bytes;
output to result and result_size. */
void pygrpc_byte_buffer_to_bytes(
grpc_byte_buffer *buffer, char **result, size_t *result_size);
/*========*/
/* Module */
/*========*/
/* Returns 0 on success, -1 on failure. */
int pygrpc_module_add_types(PyObject *module);
#endif /* GRPC__ADAPTER__C_TYPES_H_ */

@ -1,186 +0,0 @@
/*
*
* 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.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
PyMethodDef pygrpc_Call_methods[] = {
{"start_batch", (PyCFunction)pygrpc_Call_start_batch, METH_KEYWORDS, ""},
{"cancel", (PyCFunction)pygrpc_Call_cancel, METH_KEYWORDS, ""},
{"peer", (PyCFunction)pygrpc_Call_peer, METH_NOARGS, ""},
{"set_credentials", (PyCFunction)pygrpc_Call_set_credentials, METH_KEYWORDS,
""},
{NULL}
};
const char pygrpc_Call_doc[] = "See grpc._adapter._types.Call.";
PyTypeObject pygrpc_Call_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Call", /* tp_name */
sizeof(Call), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Call_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Call_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Call_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
Call *pygrpc_Call_new_empty(CompletionQueue *cq) {
Call *call = (Call *)pygrpc_Call_type.tp_alloc(&pygrpc_Call_type, 0);
call->c_call = NULL;
call->cq = cq;
Py_XINCREF(call->cq);
return call;
}
void pygrpc_Call_dealloc(Call *self) {
if (self->c_call) {
grpc_call_destroy(self->c_call);
}
Py_XDECREF(self->cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs) {
PyObject *op_list;
PyObject *user_tag;
grpc_op *ops;
size_t nops;
size_t i;
size_t j;
pygrpc_tag *tag;
grpc_call_error errcode;
static char *keywords[] = {"ops", "tag", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO:start_batch", keywords,
&op_list, &user_tag)) {
return NULL;
}
if (!PyList_Check(op_list)) {
PyErr_SetString(PyExc_TypeError, "expected a list of OpArgs");
return NULL;
}
nops = PyList_Size(op_list);
ops = gpr_malloc(sizeof(grpc_op) * nops);
for (i = 0; i < nops; ++i) {
PyObject *item = PyList_GET_ITEM(op_list, i);
if (!pygrpc_produce_op(item, &ops[i])) {
for (j = 0; j < i; ++j) {
pygrpc_discard_op(ops[j]);
}
return NULL;
}
}
tag = pygrpc_produce_batch_tag(user_tag, self, ops, nops);
errcode = grpc_call_start_batch(self->c_call, tag->ops, tag->nops, tag, NULL);
gpr_free(ops);
return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs) {
PyObject *py_code = NULL;
grpc_call_error errcode;
int code;
char *details = NULL;
static char *keywords[] = {"code", "details", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Os:start_batch", keywords,
&py_code, &details)) {
return NULL;
}
if (py_code != NULL && details != NULL) {
if (!PyInt_Check(py_code)) {
PyErr_SetString(PyExc_TypeError, "expected integer code");
return NULL;
}
code = PyInt_AsLong(py_code);
errcode = grpc_call_cancel_with_status(self->c_call, code, details, NULL);
} else if (py_code != NULL || details != NULL) {
PyErr_SetString(PyExc_ValueError,
"if `code` is specified, so must `details`");
return NULL;
} else {
errcode = grpc_call_cancel(self->c_call, NULL);
}
return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Call_peer(Call *self) {
char *peer = grpc_call_get_peer(self->c_call);
PyObject *py_peer = PyString_FromString(peer);
gpr_free(peer);
return py_peer;
}
PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args,
PyObject *kwargs) {
CallCredentials *creds;
grpc_call_error errcode;
static char *keywords[] = {"creds", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "O!:set_credentials", keywords,
&pygrpc_CallCredentials_type, &creds)) {
return NULL;
}
errcode = grpc_call_set_credentials(self->c_call, creds->c_creds);
return PyInt_FromLong(errcode);
}

@ -1,203 +0,0 @@
/*
*
* 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.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
PyMethodDef pygrpc_CallCredentials_methods[] = {
{"composite", (PyCFunction)pygrpc_CallCredentials_composite,
METH_CLASS|METH_KEYWORDS, ""},
{"compute_engine", (PyCFunction)pygrpc_CallCredentials_compute_engine,
METH_CLASS|METH_NOARGS, ""},
{"jwt", (PyCFunction)pygrpc_CallCredentials_jwt,
METH_CLASS|METH_KEYWORDS, ""},
{"refresh_token", (PyCFunction)pygrpc_CallCredentials_refresh_token,
METH_CLASS|METH_KEYWORDS, ""},
{"iam", (PyCFunction)pygrpc_CallCredentials_iam,
METH_CLASS|METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_CallCredentials_doc[] = "";
PyTypeObject pygrpc_CallCredentials_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"CallCredentials", /* tp_name */
sizeof(CallCredentials), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_CallCredentials_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_CallCredentials_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_CallCredentials_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
void pygrpc_CallCredentials_dealloc(CallCredentials *self) {
grpc_call_credentials_release(self->c_creds);
self->ob_type->tp_free((PyObject *)self);
}
CallCredentials *pygrpc_CallCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CallCredentials *self;
CallCredentials *creds1;
CallCredentials *creds2;
static char *keywords[] = {"creds1", "creds2", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!:composite", keywords,
&pygrpc_CallCredentials_type, &creds1,
&pygrpc_CallCredentials_type, &creds2)) {
return NULL;
}
self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds =
grpc_composite_call_credentials_create(
creds1->c_creds, creds2->c_creds, NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create composite credentials");
return NULL;
}
return self;
}
CallCredentials *pygrpc_CallCredentials_compute_engine(
PyTypeObject *type, PyObject *ignored) {
CallCredentials *self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_google_compute_engine_credentials_create(NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create compute engine credentials");
return NULL;
}
return self;
}
/* TODO: Rename this credentials to something like service_account_jwt_access */
CallCredentials *pygrpc_CallCredentials_jwt(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CallCredentials *self;
const char *json_key;
double lifetime;
static char *keywords[] = {"json_key", "token_lifetime", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sd:jwt", keywords,
&json_key, &lifetime)) {
return NULL;
}
self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_service_account_jwt_access_credentials_create(
json_key, pygrpc_cast_double_to_gpr_timespec(lifetime), NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create JWT credentials");
return NULL;
}
return self;
}
CallCredentials *pygrpc_CallCredentials_refresh_token(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CallCredentials *self;
const char *json_refresh_token;
static char *keywords[] = {"json_refresh_token", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:refresh_token", keywords,
&json_refresh_token)) {
return NULL;
}
self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds =
grpc_google_refresh_token_credentials_create(json_refresh_token, NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create credentials from refresh token");
return NULL;
}
return self;
}
CallCredentials *pygrpc_CallCredentials_iam(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CallCredentials *self;
const char *authorization_token;
const char *authority_selector;
static char *keywords[] = {"authorization_token", "authority_selector", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss:iam", keywords,
&authorization_token, &authority_selector)) {
return NULL;
}
self = (CallCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_google_iam_credentials_create(authorization_token,
authority_selector, NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create IAM credentials");
return NULL;
}
return self;
}

@ -1,187 +0,0 @@
/*
*
* 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.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
PyMethodDef pygrpc_Channel_methods[] = {
{"create_call", (PyCFunction)pygrpc_Channel_create_call, METH_KEYWORDS, ""},
{"check_connectivity_state", (PyCFunction)pygrpc_Channel_check_connectivity_state, METH_KEYWORDS, ""},
{"watch_connectivity_state", (PyCFunction)pygrpc_Channel_watch_connectivity_state, METH_KEYWORDS, ""},
{"target", (PyCFunction)pygrpc_Channel_target, METH_NOARGS, ""},
{NULL}
};
const char pygrpc_Channel_doc[] = "See grpc._adapter._types.Channel.";
PyTypeObject pygrpc_Channel_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Channel", /* tp_name */
sizeof(Channel), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Channel_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Channel_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Channel_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_Channel_new /* tp_new */
};
Channel *pygrpc_Channel_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
Channel *self;
const char *target;
PyObject *py_args;
ChannelCredentials *creds = NULL;
grpc_channel_args c_args;
char *keywords[] = {"target", "args", "creds", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!:Channel", keywords,
&target, &py_args, &pygrpc_ChannelCredentials_type, &creds)) {
return NULL;
}
if (!pygrpc_produce_channel_args(py_args, &c_args)) {
return NULL;
}
self = (Channel *)type->tp_alloc(type, 0);
if (creds) {
self->c_chan =
grpc_secure_channel_create(creds->c_creds, target, &c_args, NULL);
} else {
self->c_chan = grpc_insecure_channel_create(target, &c_args, NULL);
}
pygrpc_discard_channel_args(c_args);
return self;
}
void pygrpc_Channel_dealloc(Channel *self) {
grpc_channel_destroy(self->c_chan);
self->ob_type->tp_free((PyObject *)self);
}
Call *pygrpc_Channel_create_call(
Channel *self, PyObject *args, PyObject *kwargs) {
Call *call;
CompletionQueue *cq;
const char *method;
const char *host;
double deadline;
char *keywords[] = {"cq", "method", "host", "deadline", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!szd:create_call", keywords,
&pygrpc_CompletionQueue_type, &cq, &method, &host, &deadline)) {
return NULL;
}
call = pygrpc_Call_new_empty(cq);
call->c_call = grpc_channel_create_call(
self->c_chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq->c_cq, method, host,
pygrpc_cast_double_to_gpr_timespec(deadline), NULL);
return call;
}
PyObject *pygrpc_Channel_check_connectivity_state(
Channel *self, PyObject *args, PyObject *kwargs) {
PyObject *py_try_to_connect;
int try_to_connect;
char *keywords[] = {"try_to_connect", NULL};
grpc_connectivity_state state;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:connectivity_state", keywords,
&py_try_to_connect)) {
return NULL;
}
if (!PyBool_Check(py_try_to_connect)) {
Py_XDECREF(py_try_to_connect);
return NULL;
}
try_to_connect = Py_True == py_try_to_connect;
Py_DECREF(py_try_to_connect);
state = grpc_channel_check_connectivity_state(self->c_chan, try_to_connect);
return PyInt_FromLong(state);
}
PyObject *pygrpc_Channel_watch_connectivity_state(
Channel *self, PyObject *args, PyObject *kwargs) {
PyObject *tag;
double deadline;
int last_observed_state;
CompletionQueue *completion_queue;
char *keywords[] = {"last_observed_state", "deadline",
"completion_queue", "tag", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "idO!O:watch_connectivity_state", keywords,
&last_observed_state, &deadline, &pygrpc_CompletionQueue_type,
&completion_queue, &tag)) {
return NULL;
}
grpc_channel_watch_connectivity_state(
self->c_chan, (grpc_connectivity_state)last_observed_state,
pygrpc_cast_double_to_gpr_timespec(deadline), completion_queue->c_cq,
pygrpc_produce_channel_state_change_tag(tag));
Py_RETURN_NONE;
}
PyObject *pygrpc_Channel_target(Channel *self) {
char *target = grpc_channel_get_target(self->c_chan);
PyObject *py_target = PyString_FromString(target);
gpr_free(target);
return py_target;
}

@ -1,165 +0,0 @@
/*
*
* 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.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
PyMethodDef pygrpc_ChannelCredentials_methods[] = {
{"google_default", (PyCFunction)pygrpc_ChannelCredentials_google_default,
METH_CLASS|METH_NOARGS, ""},
{"ssl", (PyCFunction)pygrpc_ChannelCredentials_ssl,
METH_CLASS|METH_KEYWORDS, ""},
{"composite", (PyCFunction)pygrpc_ChannelCredentials_composite,
METH_CLASS|METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_ChannelCredentials_doc[] = "";
PyTypeObject pygrpc_ChannelCredentials_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"ChannelCredentials", /* tp_name */
sizeof(ChannelCredentials), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_ChannelCredentials_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_ChannelCredentials_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_ChannelCredentials_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
void pygrpc_ChannelCredentials_dealloc(ChannelCredentials *self) {
grpc_channel_credentials_release(self->c_creds);
self->ob_type->tp_free((PyObject *)self);
}
ChannelCredentials *pygrpc_ChannelCredentials_google_default(
PyTypeObject *type, PyObject *ignored) {
ChannelCredentials *self = (ChannelCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_google_default_credentials_create();
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create Google default credentials");
return NULL;
}
return self;
}
ChannelCredentials *pygrpc_ChannelCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ChannelCredentials *self;
const char *root_certs;
const char *private_key = NULL;
const char *cert_chain = NULL;
grpc_ssl_pem_key_cert_pair key_cert_pair;
static char *keywords[] = {"root_certs", "private_key", "cert_chain", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|zz:ssl", keywords,
&root_certs, &private_key, &cert_chain)) {
return NULL;
}
self = (ChannelCredentials *)type->tp_alloc(type, 0);
if (private_key && cert_chain) {
key_cert_pair.private_key = private_key;
key_cert_pair.cert_chain = cert_chain;
self->c_creds =
grpc_ssl_credentials_create(root_certs, &key_cert_pair, NULL);
} else {
self->c_creds = grpc_ssl_credentials_create(root_certs, NULL, NULL);
}
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create ssl credentials");
return NULL;
}
return self;
}
ChannelCredentials *pygrpc_ChannelCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ChannelCredentials *self;
ChannelCredentials *creds1;
CallCredentials *creds2;
static char *keywords[] = {"creds1", "creds2", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!:composite", keywords,
&pygrpc_ChannelCredentials_type, &creds1,
&pygrpc_CallCredentials_type, &creds2)) {
return NULL;
}
self = (ChannelCredentials *)type->tp_alloc(type, 0);
self->c_creds =
grpc_composite_channel_credentials_create(
creds1->c_creds, creds2->c_creds, NULL);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(
PyExc_RuntimeError, "couldn't create composite credentials");
return NULL;
}
return self;
}

@ -1,124 +0,0 @@
/*
*
* 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.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
PyMethodDef pygrpc_CompletionQueue_methods[] = {
{"next", (PyCFunction)pygrpc_CompletionQueue_next, METH_KEYWORDS, ""},
{"shutdown", (PyCFunction)pygrpc_CompletionQueue_shutdown, METH_NOARGS, ""},
{NULL}
};
const char pygrpc_CompletionQueue_doc[] =
"See grpc._adapter._types.CompletionQueue.";
PyTypeObject pygrpc_CompletionQueue_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"CompletionQueue", /* tp_name */
sizeof(CompletionQueue), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_CompletionQueue_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_CompletionQueue_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_CompletionQueue_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_CompletionQueue_new /* tp_new */
};
CompletionQueue *pygrpc_CompletionQueue_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CompletionQueue *self = (CompletionQueue *)type->tp_alloc(type, 0);
self->c_cq = grpc_completion_queue_create(NULL);
return self;
}
void pygrpc_CompletionQueue_dealloc(CompletionQueue *self) {
grpc_completion_queue_destroy(self->c_cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_CompletionQueue_next(
CompletionQueue *self, PyObject *args, PyObject *kwargs) {
double deadline;
grpc_event event;
PyObject *transliterated_event;
static char *keywords[] = {"deadline", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:next", keywords,
&deadline)) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS;
event = grpc_completion_queue_next(
self->c_cq, pygrpc_cast_double_to_gpr_timespec(deadline), NULL);
Py_END_ALLOW_THREADS;
transliterated_event = pygrpc_consume_event(event);
return transliterated_event;
}
PyObject *pygrpc_CompletionQueue_shutdown(
CompletionQueue *self, PyObject *ignored) {
grpc_completion_queue_shutdown(self->c_cq);
Py_RETURN_NONE;
}

@ -1,196 +0,0 @@
/*
*
* 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.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
PyMethodDef pygrpc_Server_methods[] = {
{"request_call", (PyCFunction)pygrpc_Server_request_call,
METH_KEYWORDS, ""},
{"add_http2_port", (PyCFunction)pygrpc_Server_add_http2_port,
METH_KEYWORDS, ""},
{"start", (PyCFunction)pygrpc_Server_start, METH_NOARGS, ""},
{"shutdown", (PyCFunction)pygrpc_Server_shutdown, METH_KEYWORDS, ""},
{"cancel_all_calls", (PyCFunction)pygrpc_Server_cancel_all_calls,
METH_NOARGS, ""},
{NULL}
};
const char pygrpc_Server_doc[] = "See grpc._adapter._types.Server.";
PyTypeObject pygrpc_Server_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Server", /* tp_name */
sizeof(Server), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Server_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Server_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Server_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_Server_new /* tp_new */
};
Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
Server *self;
CompletionQueue *cq;
PyObject *py_args;
grpc_channel_args c_args;
char *keywords[] = {"cq", "args", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O:Server", keywords,
&pygrpc_CompletionQueue_type, &cq, &py_args)) {
return NULL;
}
if (!pygrpc_produce_channel_args(py_args, &c_args)) {
return NULL;
}
self = (Server *)type->tp_alloc(type, 0);
self->c_serv = grpc_server_create(&c_args, NULL);
grpc_server_register_completion_queue(self->c_serv, cq->c_cq, NULL);
pygrpc_discard_channel_args(c_args);
self->cq = cq;
Py_INCREF(self->cq);
self->shutdown_called = 0;
return self;
}
void pygrpc_Server_dealloc(Server *self) {
grpc_server_destroy(self->c_serv);
Py_XDECREF(self->cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_Server_request_call(
Server *self, PyObject *args, PyObject *kwargs) {
CompletionQueue *cq;
PyObject *user_tag;
pygrpc_tag *tag;
Call *empty_call;
grpc_call_error errcode;
static char *keywords[] = {"cq", "tag", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "O!O", keywords,
&pygrpc_CompletionQueue_type, &cq, &user_tag)) {
return NULL;
}
empty_call = pygrpc_Call_new_empty(cq);
tag = pygrpc_produce_request_tag(user_tag, empty_call);
errcode = grpc_server_request_call(
self->c_serv, &tag->call->c_call, &tag->request_call_details,
&tag->request_metadata, tag->call->cq->c_cq, self->cq->c_cq, tag);
Py_DECREF(empty_call);
return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Server_add_http2_port(
Server *self, PyObject *args, PyObject *kwargs) {
const char *addr;
ServerCredentials *creds = NULL;
int port;
static char *keywords[] = {"addr", "creds", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "s|O!:add_http2_port", keywords,
&addr, &pygrpc_ServerCredentials_type, &creds)) {
return NULL;
}
if (creds) {
port = grpc_server_add_secure_http2_port(
self->c_serv, addr, creds->c_creds);
} else {
port = grpc_server_add_insecure_http2_port(self->c_serv, addr);
}
return PyInt_FromLong(port);
}
PyObject *pygrpc_Server_start(Server *self, PyObject *ignored) {
grpc_server_start(self->c_serv);
self->shutdown_called = 0;
Py_RETURN_NONE;
}
PyObject *pygrpc_Server_shutdown(
Server *self, PyObject *args, PyObject *kwargs) {
PyObject *user_tag;
pygrpc_tag *tag;
static char *keywords[] = {"tag", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &user_tag)) {
return NULL;
}
tag = pygrpc_produce_server_shutdown_tag(user_tag);
grpc_server_shutdown_and_notify(self->c_serv, self->cq->c_cq, tag);
self->shutdown_called = 1;
Py_RETURN_NONE;
}
PyObject *pygrpc_Server_cancel_all_calls(Server *self, PyObject *unused) {
if (!self->shutdown_called) {
PyErr_SetString(
PyExc_RuntimeError,
"shutdown must have been called prior to calling cancel_all_calls!");
return NULL;
}
grpc_server_cancel_all_calls(self->c_serv);
Py_RETURN_NONE;
}

@ -1,137 +0,0 @@
/*
*
* 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.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
PyMethodDef pygrpc_ServerCredentials_methods[] = {
{"ssl", (PyCFunction)pygrpc_ServerCredentials_ssl,
METH_CLASS|METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_ServerCredentials_doc[] = "";
PyTypeObject pygrpc_ServerCredentials_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"ServerCredentials", /* tp_name */
sizeof(ServerCredentials), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_ServerCredentials_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_ServerCredentials_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_ServerCredentials_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
void pygrpc_ServerCredentials_dealloc(ServerCredentials *self) {
grpc_server_credentials_release(self->c_creds);
self->ob_type->tp_free((PyObject *)self);
}
ServerCredentials *pygrpc_ServerCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ServerCredentials *self;
const char *root_certs;
PyObject *py_key_cert_pairs;
grpc_ssl_pem_key_cert_pair *key_cert_pairs;
int force_client_auth;
size_t num_key_cert_pairs;
size_t i;
static char *keywords[] = {
"root_certs", "key_cert_pairs", "force_client_auth", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zOi:ssl", keywords,
&root_certs, &py_key_cert_pairs, &force_client_auth)) {
return NULL;
}
if (!PyList_Check(py_key_cert_pairs)) {
PyErr_SetString(PyExc_TypeError, "expected a list of 2-tuples of strings");
return NULL;
}
num_key_cert_pairs = PyList_Size(py_key_cert_pairs);
key_cert_pairs =
gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs);
for (i = 0; i < num_key_cert_pairs; ++i) {
PyObject *item = PyList_GET_ITEM(py_key_cert_pairs, i);
const char *key;
const char *cert;
if (!PyArg_ParseTuple(item, "zz", &key, &cert)) {
gpr_free(key_cert_pairs);
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuples of strings");
return NULL;
}
key_cert_pairs[i].private_key = key;
key_cert_pairs[i].cert_chain = cert;
}
self = (ServerCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_ssl_server_credentials_create(
root_certs, key_cert_pairs, num_key_cert_pairs, force_client_auth, NULL);
gpr_free(key_cert_pairs);
return self;
}

@ -1,524 +0,0 @@
/*
*
* 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.
*
*/
#include <math.h>
#include <string.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/byte_buffer_reader.h>
#include <grpc/support/alloc.h>
#include <grpc/support/slice.h>
#include <grpc/support/time.h>
#include <grpc/support/string_util.h>
#include "grpc/_adapter/_c/types.h"
pygrpc_tag *pygrpc_produce_batch_tag(
PyObject *user_tag, Call *call, grpc_op *ops, size_t nops) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = call;
Py_XINCREF(tag->call);
tag->ops = gpr_malloc(sizeof(grpc_op)*nops);
memcpy(tag->ops, ops, sizeof(grpc_op)*nops);
tag->nops = nops;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 0;
return tag;
}
pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = empty_call;
Py_XINCREF(tag->call);
tag->ops = NULL;
tag->nops = 0;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 1;
return tag;
}
pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = NULL;
tag->ops = NULL;
tag->nops = 0;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 0;
return tag;
}
pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = NULL;
tag->ops = NULL;
tag->nops = 0;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 0;
return tag;
}
void pygrpc_discard_tag(pygrpc_tag *tag) {
if (!tag) {
return;
}
Py_XDECREF(tag->user_tag);
Py_XDECREF(tag->call);
gpr_free(tag->ops);
grpc_call_details_destroy(&tag->request_call_details);
grpc_metadata_array_destroy(&tag->request_metadata);
gpr_free(tag);
}
PyObject *pygrpc_consume_event(grpc_event event) {
pygrpc_tag *tag;
PyObject *result;
if (event.type == GRPC_QUEUE_TIMEOUT) {
Py_RETURN_NONE;
}
tag = event.tag;
switch (event.type) {
case GRPC_QUEUE_SHUTDOWN:
result = Py_BuildValue("iOOOOO", GRPC_QUEUE_SHUTDOWN,
Py_None, Py_None, Py_None, Py_None, Py_True);
break;
case GRPC_OP_COMPLETE:
if (tag->is_new_call) {
result = Py_BuildValue(
"iOO(ssd)[(iNOOOO)]O", GRPC_OP_COMPLETE, tag->user_tag, tag->call,
tag->request_call_details.method, tag->request_call_details.host,
pygrpc_cast_gpr_timespec_to_double(tag->request_call_details.deadline),
GRPC_OP_RECV_INITIAL_METADATA,
pygrpc_cast_metadata_array_to_pyseq(tag->request_metadata), Py_None,
Py_None, Py_None, Py_None,
event.success ? Py_True : Py_False);
} else {
result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag,
tag->call ? (PyObject*)tag->call : Py_None, Py_None,
pygrpc_consume_ops(tag->ops, tag->nops),
event.success ? Py_True : Py_False);
}
break;
default:
PyErr_SetString(PyExc_ValueError,
"unknown completion type; could not translate event");
return NULL;
}
pygrpc_discard_tag(tag);
return result;
}
int pygrpc_produce_op(PyObject *op, grpc_op *result) {
static const int OP_TUPLE_SIZE = 6;
static const int STATUS_TUPLE_SIZE = 2;
static const int TYPE_INDEX = 0;
static const int INITIAL_METADATA_INDEX = 1;
static const int TRAILING_METADATA_INDEX = 2;
static const int MESSAGE_INDEX = 3;
static const int STATUS_INDEX = 4;
static const int STATUS_CODE_INDEX = 0;
static const int STATUS_DETAILS_INDEX = 1;
static const int WRITE_FLAGS_INDEX = 5;
int type;
Py_ssize_t message_size;
char *message;
char *status_details;
gpr_slice message_slice;
grpc_op c_op;
if (!PyTuple_Check(op)) {
PyErr_SetString(PyExc_TypeError, "expected tuple op");
return 0;
}
if (PyTuple_Size(op) != OP_TUPLE_SIZE) {
char *buf;
gpr_asprintf(&buf, "expected tuple op of length %d", OP_TUPLE_SIZE);
PyErr_SetString(PyExc_ValueError, buf);
gpr_free(buf);
return 0;
}
type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX));
if (PyErr_Occurred()) {
return 0;
}
c_op.op = type;
c_op.reserved = NULL;
c_op.flags = PyInt_AsLong(PyTuple_GET_ITEM(op, WRITE_FLAGS_INDEX));
if (PyErr_Occurred()) {
return 0;
}
switch (type) {
case GRPC_OP_SEND_INITIAL_METADATA:
if (!pygrpc_cast_pyseq_to_send_metadata(
PyTuple_GetItem(op, INITIAL_METADATA_INDEX),
&c_op.data.send_initial_metadata.metadata,
&c_op.data.send_initial_metadata.count)) {
return 0;
}
break;
case GRPC_OP_SEND_MESSAGE:
PyString_AsStringAndSize(
PyTuple_GET_ITEM(op, MESSAGE_INDEX), &message, &message_size);
message_slice = gpr_slice_from_copied_buffer(message, message_size);
c_op.data.send_message = grpc_raw_byte_buffer_create(&message_slice, 1);
gpr_slice_unref(message_slice);
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
/* Don't need to fill in any other fields. */
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
if (!pygrpc_cast_pyseq_to_send_metadata(
PyTuple_GetItem(op, TRAILING_METADATA_INDEX),
&c_op.data.send_status_from_server.trailing_metadata,
&c_op.data.send_status_from_server.trailing_metadata_count)) {
return 0;
}
if (!PyTuple_Check(PyTuple_GET_ITEM(op, STATUS_INDEX))) {
char *buf;
gpr_asprintf(&buf, "expected tuple status in op of length %d",
STATUS_TUPLE_SIZE);
PyErr_SetString(PyExc_ValueError, buf);
gpr_free(buf);
return 0;
}
c_op.data.send_status_from_server.status = PyInt_AsLong(
PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_CODE_INDEX));
status_details = PyString_AsString(
PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_DETAILS_INDEX));
if (PyErr_Occurred()) {
return 0;
}
c_op.data.send_status_from_server.status_details =
gpr_malloc(strlen(status_details) + 1);
strcpy((char *)c_op.data.send_status_from_server.status_details,
status_details);
break;
case GRPC_OP_RECV_INITIAL_METADATA:
c_op.data.recv_initial_metadata = gpr_malloc(sizeof(grpc_metadata_array));
grpc_metadata_array_init(c_op.data.recv_initial_metadata);
break;
case GRPC_OP_RECV_MESSAGE:
c_op.data.recv_message = gpr_malloc(sizeof(grpc_byte_buffer *));
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
c_op.data.recv_status_on_client.trailing_metadata =
gpr_malloc(sizeof(grpc_metadata_array));
grpc_metadata_array_init(c_op.data.recv_status_on_client.trailing_metadata);
c_op.data.recv_status_on_client.status =
gpr_malloc(sizeof(grpc_status_code *));
c_op.data.recv_status_on_client.status_details =
gpr_malloc(sizeof(char *));
*c_op.data.recv_status_on_client.status_details = NULL;
c_op.data.recv_status_on_client.status_details_capacity =
gpr_malloc(sizeof(size_t));
*c_op.data.recv_status_on_client.status_details_capacity = 0;
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
c_op.data.recv_close_on_server.cancelled = gpr_malloc(sizeof(int));
break;
default:
return 0;
}
*result = c_op;
return 1;
}
void pygrpc_discard_op(grpc_op op) {
size_t i;
switch(op.op) {
case GRPC_OP_SEND_INITIAL_METADATA:
/* Whenever we produce send-metadata, we allocate new strings (to handle
arbitrary sequence input as opposed to just lists or just tuples). We
thus must free those elements. */
for (i = 0; i < op.data.send_initial_metadata.count; ++i) {
gpr_free((void *)op.data.send_initial_metadata.metadata[i].key);
gpr_free((void *)op.data.send_initial_metadata.metadata[i].value);
}
gpr_free(op.data.send_initial_metadata.metadata);
break;
case GRPC_OP_SEND_MESSAGE:
grpc_byte_buffer_destroy(op.data.send_message);
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
/* Don't need to free any fields. */
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
/* Whenever we produce send-metadata, we allocate new strings (to handle
arbitrary sequence input as opposed to just lists or just tuples). We
thus must free those elements. */
for (i = 0; i < op.data.send_status_from_server.trailing_metadata_count;
++i) {
gpr_free(
(void *)op.data.send_status_from_server.trailing_metadata[i].key);
gpr_free(
(void *)op.data.send_status_from_server.trailing_metadata[i].value);
}
gpr_free(op.data.send_status_from_server.trailing_metadata);
gpr_free((char *)op.data.send_status_from_server.status_details);
break;
case GRPC_OP_RECV_INITIAL_METADATA:
grpc_metadata_array_destroy(op.data.recv_initial_metadata);
gpr_free(op.data.recv_initial_metadata);
break;
case GRPC_OP_RECV_MESSAGE:
grpc_byte_buffer_destroy(*op.data.recv_message);
gpr_free(op.data.recv_message);
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
grpc_metadata_array_destroy(op.data.recv_status_on_client.trailing_metadata);
gpr_free(op.data.recv_status_on_client.trailing_metadata);
gpr_free(op.data.recv_status_on_client.status);
gpr_free(*op.data.recv_status_on_client.status_details);
gpr_free(op.data.recv_status_on_client.status_details);
gpr_free(op.data.recv_status_on_client.status_details_capacity);
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
gpr_free(op.data.recv_close_on_server.cancelled);
break;
}
}
PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops) {
static const int TYPE_INDEX = 0;
static const int INITIAL_METADATA_INDEX = 1;
static const int TRAILING_METADATA_INDEX = 2;
static const int MESSAGE_INDEX = 3;
static const int STATUS_INDEX = 4;
static const int CANCELLED_INDEX = 5;
static const int OPRESULT_LENGTH = 6;
PyObject *list;
size_t i;
size_t j;
char *bytes;
size_t bytes_size;
PyObject *results = PyList_New(nops);
if (!results) {
return NULL;
}
for (i = 0; i < nops; ++i) {
PyObject *result = PyTuple_Pack(OPRESULT_LENGTH, Py_None, Py_None, Py_None,
Py_None, Py_None, Py_None);
PyTuple_SetItem(result, TYPE_INDEX, PyInt_FromLong(op[i].op));
switch(op[i].op) {
case GRPC_OP_RECV_INITIAL_METADATA:
PyTuple_SetItem(result, INITIAL_METADATA_INDEX,
list=PyList_New(op[i].data.recv_initial_metadata->count));
for (j = 0; j < op[i].data.recv_initial_metadata->count; ++j) {
grpc_metadata md = op[i].data.recv_initial_metadata->metadata[j];
PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value,
(Py_ssize_t)md.value_length));
}
break;
case GRPC_OP_RECV_MESSAGE:
if (*op[i].data.recv_message) {
pygrpc_byte_buffer_to_bytes(
*op[i].data.recv_message, &bytes, &bytes_size);
PyTuple_SetItem(result, MESSAGE_INDEX,
PyString_FromStringAndSize(bytes, bytes_size));
gpr_free(bytes);
} else {
PyTuple_SetItem(result, MESSAGE_INDEX, Py_BuildValue(""));
}
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
PyTuple_SetItem(
result, TRAILING_METADATA_INDEX,
list = PyList_New(op[i].data.recv_status_on_client.trailing_metadata->count));
for (j = 0; j < op[i].data.recv_status_on_client.trailing_metadata->count; ++j) {
grpc_metadata md =
op[i].data.recv_status_on_client.trailing_metadata->metadata[j];
PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value,
(Py_ssize_t)md.value_length));
}
PyTuple_SetItem(
result, STATUS_INDEX, Py_BuildValue(
"is", *op[i].data.recv_status_on_client.status,
*op[i].data.recv_status_on_client.status_details));
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
PyTuple_SetItem(
result, CANCELLED_INDEX,
PyBool_FromLong(*op[i].data.recv_close_on_server.cancelled));
break;
default:
break;
}
pygrpc_discard_op(op[i]);
PyList_SetItem(results, i, result);
}
return results;
}
double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec) {
timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME);
return timespec.tv_sec + 1e-9*timespec.tv_nsec;
}
/* Because C89 doesn't have a way to check for infinity... */
static int pygrpc_isinf(double x) {
return x * 0 != 0;
}
gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) {
gpr_timespec result;
if (pygrpc_isinf(seconds)) {
result = seconds > 0.0 ? gpr_inf_future(GPR_CLOCK_REALTIME)
: gpr_inf_past(GPR_CLOCK_REALTIME);
} else {
result.tv_sec = (time_t)seconds;
result.tv_nsec = ((seconds - result.tv_sec) * 1e9);
result.clock_type = GPR_CLOCK_REALTIME;
}
return result;
}
int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args) {
size_t num_args = PyList_Size(py_args);
size_t i;
grpc_channel_args args;
args.num_args = num_args;
args.args = gpr_malloc(sizeof(grpc_arg) * num_args);
for (i = 0; i < args.num_args; ++i) {
char *key;
PyObject *value;
if (!PyArg_ParseTuple(PyList_GetItem(py_args, i), "zO", &key, &value)) {
gpr_free(args.args);
args.num_args = 0;
args.args = NULL;
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuple of str and str|int|None");
return 0;
}
args.args[i].key = key;
if (PyInt_Check(value)) {
args.args[i].type = GRPC_ARG_INTEGER;
args.args[i].value.integer = PyInt_AsLong(value);
} else if (PyString_Check(value)) {
args.args[i].type = GRPC_ARG_STRING;
args.args[i].value.string = PyString_AsString(value);
} else if (value == Py_None) {
--args.num_args;
--i;
continue;
} else {
gpr_free(args.args);
args.num_args = 0;
args.args = NULL;
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuple of str and str|int|None");
return 0;
}
}
*c_args = args;
return 1;
}
void pygrpc_discard_channel_args(grpc_channel_args args) {
gpr_free(args.args);
}
int pygrpc_cast_pyseq_to_send_metadata(
PyObject *pyseq, grpc_metadata **metadata, size_t *count) {
size_t i;
Py_ssize_t value_length;
char *key;
char *value;
if (!PySequence_Check(pyseq)) {
return 0;
}
*count = PySequence_Size(pyseq);
*metadata = gpr_malloc(sizeof(grpc_metadata) * *count);
for (i = 0; i < *count; ++i) {
PyObject *item = PySequence_GetItem(pyseq, i);
if (!PyArg_ParseTuple(item, "ss#", &key, &value, &value_length)) {
Py_DECREF(item);
gpr_free(*metadata);
*count = 0;
*metadata = NULL;
return 0;
} else {
(*metadata)[i].key = gpr_strdup(key);
(*metadata)[i].value = gpr_malloc(value_length);
memcpy((void *)(*metadata)[i].value, value, value_length);
Py_DECREF(item);
}
(*metadata)[i].value_length = value_length;
}
return 1;
}
PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata) {
PyObject *result = PyTuple_New(metadata.count);
size_t i;
for (i = 0; i < metadata.count; ++i) {
PyTuple_SetItem(
result, i, Py_BuildValue(
"ss#", metadata.metadata[i].key, metadata.metadata[i].value,
(Py_ssize_t)metadata.metadata[i].value_length));
if (PyErr_Occurred()) {
Py_DECREF(result);
return NULL;
}
}
return result;
}
void pygrpc_byte_buffer_to_bytes(
grpc_byte_buffer *buffer, char **result, size_t *result_size) {
grpc_byte_buffer_reader reader;
gpr_slice slice;
char *read_result = NULL;
size_t size = 0;
grpc_byte_buffer_reader_init(&reader, buffer);
while (grpc_byte_buffer_reader_next(&reader, &slice)) {
read_result = gpr_realloc(read_result, size + GPR_SLICE_LENGTH(slice));
memcpy(read_result + size, GPR_SLICE_START_PTR(slice),
GPR_SLICE_LENGTH(slice));
size = size + GPR_SLICE_LENGTH(slice);
gpr_slice_unref(slice);
}
*result_size = size;
*result = read_result;
}

@ -27,29 +27,22 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import time import collections
import unittest
from grpc._adapter import _c from grpc.beta import interfaces
from grpc._adapter import _types
class AuthMetadataContext(collections.namedtuple(
'AuthMetadataContext', [
'service_url',
'method_name'
]), interfaces.GRPCAuthMetadataContext):
pass
class CTypeSmokeTest(unittest.TestCase):
def testCompletionQueueUpDown(self): class AuthMetadataPluginCallback(interfaces.GRPCAuthMetadataContext):
completion_queue = _c.CompletionQueue()
del completion_queue
def testServerUpDown(self): def __init__(self, callback):
completion_queue = _c.CompletionQueue() self._callback = callback
serv = _c.Server(completion_queue, [])
del serv
del completion_queue
def testChannelUpDown(self): def __call__(self, metadata, error):
channel = _c.Channel('[::]:0', []) self._callback(metadata, error)
del channel
if __name__ == '__main__':
unittest.main(verbosity=2)

@ -115,16 +115,20 @@ class Call(object):
return call return call
def invoke(self, completion_queue, metadata_tag, finish_tag): def invoke(self, completion_queue, metadata_tag, finish_tag):
err0 = self._internal.start_batch([ err = self._internal.start_batch([
_types.OpArgs.send_initial_metadata(self._metadata) _types.OpArgs.send_initial_metadata(self._metadata)
], _IGNORE_ME_TAG) ], _IGNORE_ME_TAG)
err1 = self._internal.start_batch([ if err != _types.CallError.OK:
return err
err = self._internal.start_batch([
_types.OpArgs.recv_initial_metadata() _types.OpArgs.recv_initial_metadata()
], _TagAdapter(metadata_tag, Event.Kind.METADATA_ACCEPTED)) ], _TagAdapter(metadata_tag, Event.Kind.METADATA_ACCEPTED))
err2 = self._internal.start_batch([ if err != _types.CallError.OK:
return err
err = self._internal.start_batch([
_types.OpArgs.recv_status_on_client() _types.OpArgs.recv_status_on_client()
], _TagAdapter(finish_tag, Event.Kind.FINISH)) ], _TagAdapter(finish_tag, Event.Kind.FINISH))
return err0 if err0 != _types.CallError.OK else err1 if err1 != _types.CallError.OK else err2 if err2 != _types.CallError.OK else _types.CallError.OK return err
def write(self, message, tag, flags): def write(self, message, tag, flags):
return self._internal.start_batch([ return self._internal.start_batch([
@ -158,7 +162,8 @@ class Call(object):
def status(self, status, tag): def status(self, status, tag):
return self._internal.start_batch([ return self._internal.start_batch([
_types.OpArgs.send_status_from_server(self._metadata, status.code, status.details) _types.OpArgs.send_status_from_server(
self._metadata, status.code, status.details)
], _TagAdapter(tag, Event.Kind.COMPLETE_ACCEPTED)) ], _TagAdapter(tag, Event.Kind.COMPLETE_ACCEPTED))
def cancel(self): def cancel(self):
@ -168,20 +173,17 @@ class Call(object):
return self._internal.peer() return self._internal.peer()
def set_credentials(self, creds): def set_credentials(self, creds):
return self._internal.set_credentials(creds._internal) return self._internal.set_credentials(creds)
class Channel(object): class Channel(object):
"""Adapter from old _low.Channel interface to new _low.Channel.""" """Adapter from old _low.Channel interface to new _low.Channel."""
def __init__(self, hostport, client_credentials, server_host_override=None): def __init__(self, hostport, channel_credentials, server_host_override=None):
args = [] args = []
if server_host_override: if server_host_override:
args.append((_types.GrpcChannelArgumentKeys.SSL_TARGET_NAME_OVERRIDE.value, server_host_override)) args.append((_types.GrpcChannelArgumentKeys.SSL_TARGET_NAME_OVERRIDE.value, server_host_override))
creds = None self._internal = _low.Channel(hostport, args, channel_credentials)
if client_credentials:
creds = client_credentials._internal
self._internal = _low.Channel(hostport, args, creds)
class CompletionQueue(object): class CompletionQueue(object):
@ -192,7 +194,7 @@ class CompletionQueue(object):
def get(self, deadline=None): def get(self, deadline=None):
if deadline is None: if deadline is None:
ev = self._internal.next() ev = self._internal.next(float('+inf'))
else: else:
ev = self._internal.next(deadline) ev = self._internal.next(deadline)
if ev is None: if ev is None:
@ -240,7 +242,7 @@ class Server(object):
if server_credentials is None: if server_credentials is None:
return self._internal.add_http2_port(addr, None) return self._internal.add_http2_port(addr, None)
else: else:
return self._internal.add_http2_port(addr, server_credentials._internal) return self._internal.add_http2_port(addr, server_credentials)
def start(self): def start(self):
return self._internal.start() return self._internal.start()
@ -248,20 +250,9 @@ class Server(object):
def service(self, tag): def service(self, tag):
return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED)) return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED))
def cancel_all_calls(self):
self._internal.cancel_all_calls()
def stop(self): def stop(self):
return self._internal.shutdown(_TagAdapter(None, Event.Kind.STOP)) return self._internal.shutdown(_TagAdapter(None, Event.Kind.STOP))
class ClientCredentials(object):
"""Adapter from old _low.ClientCredentials interface to new _low.ChannelCredentials."""
def __init__(self, root_certificates, private_key, certificate_chain):
self._internal = _low.ChannelCredentials.ssl(root_certificates, private_key, certificate_chain)
class ServerCredentials(object):
"""Adapter from old _low.ServerCredentials interface to new _low.ServerCredentials."""
def __init__(self, root_credentials, pair_sequence, force_client_auth):
self._internal = _low.ServerCredentials.ssl(
root_credentials, list(pair_sequence), force_client_auth)

@ -27,36 +27,157 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import threading
from grpc import _grpcio_metadata from grpc import _grpcio_metadata
from grpc._adapter import _c from grpc._cython import cygrpc
from grpc._adapter import _implementations
from grpc._adapter import _types from grpc._adapter import _types
_USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__) _USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__)
ChannelCredentials = _c.ChannelCredentials ChannelCredentials = cygrpc.ChannelCredentials
CallCredentials = _c.CallCredentials CallCredentials = cygrpc.CallCredentials
ServerCredentials = _c.ServerCredentials ServerCredentials = cygrpc.ServerCredentials
channel_credentials_composite = cygrpc.channel_credentials_composite
call_credentials_composite = cygrpc.call_credentials_composite
def server_credentials_ssl(root_credentials, pair_sequence, force_client_auth):
return cygrpc.server_credentials_ssl(
root_credentials,
[cygrpc.SslPemKeyCertPair(key, pem) for key, pem in pair_sequence],
force_client_auth)
def channel_credentials_ssl(
root_certificates, private_key, certificate_chain):
pair = None
if private_key is not None or certificate_chain is not None:
pair = cygrpc.SslPemKeyCertPair(private_key, certificate_chain)
return cygrpc.channel_credentials_ssl(root_certificates, pair)
class _WrappedCygrpcCallback(object):
def __init__(self, cygrpc_callback):
self.is_called = False
self.error = None
self.is_called_lock = threading.Lock()
self.cygrpc_callback = cygrpc_callback
def _invoke_failure(self, error):
# TODO(atash) translate different Exception superclasses into different
# status codes.
self.cygrpc_callback(
cygrpc.Metadata([]), cygrpc.StatusCode.internal, error.message)
def _invoke_success(self, metadata):
try:
cygrpc_metadata = cygrpc.Metadata(
cygrpc.Metadatum(key, value)
for key, value in metadata)
except Exception as error:
self._invoke_failure(error)
return
self.cygrpc_callback(cygrpc_metadata, cygrpc.StatusCode.ok, '')
def __call__(self, metadata, error):
with self.is_called_lock:
if self.is_called:
raise RuntimeError('callback should only ever be invoked once')
if self.error:
self._invoke_failure(self.error)
return
self.is_called = True
if error is None:
self._invoke_success(metadata)
else:
self._invoke_failure(error)
def notify_failure(self, error):
with self.is_called_lock:
if not self.is_called:
self.error = error
class _WrappedPlugin(object):
def __init__(self, plugin):
self.plugin = plugin
def __call__(self, context, cygrpc_callback):
wrapped_cygrpc_callback = _WrappedCygrpcCallback(cygrpc_callback)
wrapped_context = _implementations.AuthMetadataContext(context.service_url,
context.method_name)
try:
self.plugin(
wrapped_context,
_implementations.AuthMetadataPluginCallback(wrapped_cygrpc_callback))
except Exception as error:
wrapped_cygrpc_callback.notify_failure(error)
raise
def call_credentials_metadata_plugin(plugin, name):
"""
Args:
plugin: A callable accepting a _types.AuthMetadataContext
object and a callback (itself accepting a list of metadata key/value
2-tuples and a None-able exception value). The callback must be eventually
called, but need not be called in plugin's invocation.
plugin's invocation must be non-blocking.
"""
return cygrpc.call_credentials_metadata_plugin(
cygrpc.CredentialsMetadataPlugin(_WrappedPlugin(plugin), name))
class CompletionQueue(_types.CompletionQueue): class CompletionQueue(_types.CompletionQueue):
def __init__(self): def __init__(self):
self.completion_queue = _c.CompletionQueue() self.completion_queue = cygrpc.CompletionQueue()
def next(self, deadline=float('+inf')): def next(self, deadline=float('+inf')):
raw_event = self.completion_queue.next(deadline) raw_event = self.completion_queue.poll(cygrpc.Timespec(deadline))
if raw_event is None: if raw_event.type == cygrpc.CompletionType.queue_timeout:
return None return None
event = _types.Event(*raw_event) event_type = raw_event.type
if event.call is not None: event_tag = raw_event.tag
event = event._replace(call=Call(event.call)) event_call = Call(raw_event.operation_call)
if event.call_details is not None: if raw_event.request_call_details:
event = event._replace(call_details=_types.CallDetails(*event.call_details)) event_call_details = _types.CallDetails(
if event.results is not None: raw_event.request_call_details.method,
new_results = [_types.OpResult(*r) for r in event.results] raw_event.request_call_details.host,
new_results = [r if r.status is None else r._replace(status=_types.Status(_types.StatusCode(r.status[0]), r.status[1])) for r in new_results] float(raw_event.request_call_details.deadline))
event = event._replace(results=new_results) else:
return event event_call_details = None
event_success = raw_event.success
event_results = []
if raw_event.is_new_request:
event_results.append(_types.OpResult(
_types.OpType.RECV_INITIAL_METADATA, raw_event.request_metadata,
None, None, None, None))
else:
if raw_event.batch_operations:
for operation in raw_event.batch_operations:
result_type = operation.type
result_initial_metadata = operation.received_metadata_or_none
result_trailing_metadata = operation.received_metadata_or_none
result_message = operation.received_message_or_none
if result_message is not None:
result_message = result_message.bytes()
result_cancelled = operation.received_cancelled_or_none
if operation.has_status:
result_status = _types.Status(
operation.received_status_code_or_none,
operation.received_status_details_or_none)
else:
result_status = None
event_results.append(
_types.OpResult(result_type, result_initial_metadata,
result_trailing_metadata, result_message,
result_status, result_cancelled))
return _types.Event(event_type, event_tag, event_call, event_call_details,
event_results, event_success)
def shutdown(self): def shutdown(self):
self.completion_queue.shutdown() self.completion_queue.shutdown()
@ -68,7 +189,36 @@ class Call(_types.Call):
self.call = call self.call = call
def start_batch(self, ops, tag): def start_batch(self, ops, tag):
return self.call.start_batch(ops, tag) translated_ops = []
for op in ops:
if op.type == _types.OpType.SEND_INITIAL_METADATA:
translated_op = cygrpc.operation_send_initial_metadata(
cygrpc.Metadata(
cygrpc.Metadatum(key, value)
for key, value in op.initial_metadata))
elif op.type == _types.OpType.SEND_MESSAGE:
translated_op = cygrpc.operation_send_message(op.message)
elif op.type == _types.OpType.SEND_CLOSE_FROM_CLIENT:
translated_op = cygrpc.operation_send_close_from_client()
elif op.type == _types.OpType.SEND_STATUS_FROM_SERVER:
translated_op = cygrpc.operation_send_status_from_server(
cygrpc.Metadata(
cygrpc.Metadatum(key, value)
for key, value in op.trailing_metadata),
op.status.code,
op.status.details)
elif op.type == _types.OpType.RECV_INITIAL_METADATA:
translated_op = cygrpc.operation_receive_initial_metadata()
elif op.type == _types.OpType.RECV_MESSAGE:
translated_op = cygrpc.operation_receive_message()
elif op.type == _types.OpType.RECV_STATUS_ON_CLIENT:
translated_op = cygrpc.operation_receive_status_on_client()
elif op.type == _types.OpType.RECV_CLOSE_ON_SERVER:
translated_op = cygrpc.operation_receive_close_on_server()
else:
raise ValueError('unexpected operation type {}'.format(op.type))
translated_ops.append(translated_op)
return self.call.start_batch(cygrpc.Operations(translated_ops), tag)
def cancel(self, code=None, details=None): def cancel(self, code=None, details=None):
if code is None and details is None: if code is None and details is None:
@ -86,14 +236,20 @@ class Call(_types.Call):
class Channel(_types.Channel): class Channel(_types.Channel):
def __init__(self, target, args, creds=None): def __init__(self, target, args, creds=None):
args = list(args) + [(_c.PRIMARY_USER_AGENT_KEY, _USER_AGENT)] args = list(args) + [
(cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT)]
args = cygrpc.ChannelArgs(
cygrpc.ChannelArg(key, value) for key, value in args)
if creds is None: if creds is None:
self.channel = _c.Channel(target, args) self.channel = cygrpc.Channel(target, args)
else: else:
self.channel = _c.Channel(target, args, creds) self.channel = cygrpc.Channel(target, args, creds)
def create_call(self, completion_queue, method, host, deadline=None): def create_call(self, completion_queue, method, host, deadline=None):
return Call(self.channel.create_call(completion_queue.completion_queue, method, host, deadline)) internal_call = self.channel.create_call(
None, 0, completion_queue.completion_queue, method, host,
cygrpc.Timespec(deadline))
return Call(internal_call)
def check_connectivity_state(self, try_to_connect): def check_connectivity_state(self, try_to_connect):
return self.channel.check_connectivity_state(try_to_connect) return self.channel.check_connectivity_state(try_to_connect)
@ -101,7 +257,8 @@ class Channel(_types.Channel):
def watch_connectivity_state(self, last_observed_state, deadline, def watch_connectivity_state(self, last_observed_state, deadline,
completion_queue, tag): completion_queue, tag):
self.channel.watch_connectivity_state( self.channel.watch_connectivity_state(
last_observed_state, deadline, completion_queue.completion_queue, tag) last_observed_state, cygrpc.Timespec(deadline),
completion_queue.completion_queue, tag)
def target(self): def target(self):
return self.channel.target() return self.channel.target()
@ -112,7 +269,11 @@ _NO_TAG = object()
class Server(_types.Server): class Server(_types.Server):
def __init__(self, completion_queue, args): def __init__(self, completion_queue, args):
self.server = _c.Server(completion_queue.completion_queue, args) args = cygrpc.ChannelArgs(
cygrpc.ChannelArg(key, value) for key, value in args)
self.server = cygrpc.Server(args)
self.server.register_completion_queue(completion_queue.completion_queue)
self.server_queue = completion_queue
def add_http2_port(self, addr, creds=None): def add_http2_port(self, addr, creds=None):
if creds is None: if creds is None:
@ -124,10 +285,11 @@ class Server(_types.Server):
return self.server.start() return self.server.start()
def shutdown(self, tag=None): def shutdown(self, tag=None):
return self.server.shutdown(tag) return self.server.shutdown(self.server_queue.completion_queue, tag)
def request_call(self, completion_queue, tag): def request_call(self, completion_queue, tag):
return self.server.request_call(completion_queue.completion_queue, tag) return self.server.request_call(completion_queue.completion_queue,
self.server_queue.completion_queue, tag)
def cancel_all_calls(self): def cancel_all_calls(self):
return self.server.cancel_all_calls() return self.server.cancel_all_calls()

@ -31,6 +31,8 @@ import abc
import collections import collections
import enum import enum
from grpc._cython import cygrpc
class GrpcChannelArgumentKeys(enum.Enum): class GrpcChannelArgumentKeys(enum.Enum):
"""Mirrors keys used in grpc_channel_args for GRPC-specific arguments.""" """Mirrors keys used in grpc_channel_args for GRPC-specific arguments."""
@ -40,77 +42,77 @@ class GrpcChannelArgumentKeys(enum.Enum):
@enum.unique @enum.unique
class CallError(enum.IntEnum): class CallError(enum.IntEnum):
"""Mirrors grpc_call_error in the C core.""" """Mirrors grpc_call_error in the C core."""
OK = 0 OK = cygrpc.CallError.ok
ERROR = 1 ERROR = cygrpc.CallError.error
ERROR_NOT_ON_SERVER = 2 ERROR_NOT_ON_SERVER = cygrpc.CallError.not_on_server
ERROR_NOT_ON_CLIENT = 3 ERROR_NOT_ON_CLIENT = cygrpc.CallError.not_on_client
ERROR_ALREADY_ACCEPTED = 4 ERROR_ALREADY_ACCEPTED = cygrpc.CallError.already_accepted
ERROR_ALREADY_INVOKED = 5 ERROR_ALREADY_INVOKED = cygrpc.CallError.already_invoked
ERROR_NOT_INVOKED = 6 ERROR_NOT_INVOKED = cygrpc.CallError.not_invoked
ERROR_ALREADY_FINISHED = 7 ERROR_ALREADY_FINISHED = cygrpc.CallError.already_finished
ERROR_TOO_MANY_OPERATIONS = 8 ERROR_TOO_MANY_OPERATIONS = cygrpc.CallError.too_many_operations
ERROR_INVALID_FLAGS = 9 ERROR_INVALID_FLAGS = cygrpc.CallError.invalid_flags
ERROR_INVALID_METADATA = 10 ERROR_INVALID_METADATA = cygrpc.CallError.invalid_metadata
@enum.unique @enum.unique
class StatusCode(enum.IntEnum): class StatusCode(enum.IntEnum):
"""Mirrors grpc_status_code in the C core.""" """Mirrors grpc_status_code in the C core."""
OK = 0 OK = cygrpc.StatusCode.ok
CANCELLED = 1 CANCELLED = cygrpc.StatusCode.cancelled
UNKNOWN = 2 UNKNOWN = cygrpc.StatusCode.unknown
INVALID_ARGUMENT = 3 INVALID_ARGUMENT = cygrpc.StatusCode.invalid_argument
DEADLINE_EXCEEDED = 4 DEADLINE_EXCEEDED = cygrpc.StatusCode.deadline_exceeded
NOT_FOUND = 5 NOT_FOUND = cygrpc.StatusCode.not_found
ALREADY_EXISTS = 6 ALREADY_EXISTS = cygrpc.StatusCode.already_exists
PERMISSION_DENIED = 7 PERMISSION_DENIED = cygrpc.StatusCode.permission_denied
RESOURCE_EXHAUSTED = 8 RESOURCE_EXHAUSTED = cygrpc.StatusCode.resource_exhausted
FAILED_PRECONDITION = 9 FAILED_PRECONDITION = cygrpc.StatusCode.failed_precondition
ABORTED = 10 ABORTED = cygrpc.StatusCode.aborted
OUT_OF_RANGE = 11 OUT_OF_RANGE = cygrpc.StatusCode.out_of_range
UNIMPLEMENTED = 12 UNIMPLEMENTED = cygrpc.StatusCode.unimplemented
INTERNAL = 13 INTERNAL = cygrpc.StatusCode.internal
UNAVAILABLE = 14 UNAVAILABLE = cygrpc.StatusCode.unavailable
DATA_LOSS = 15 DATA_LOSS = cygrpc.StatusCode.data_loss
UNAUTHENTICATED = 16 UNAUTHENTICATED = cygrpc.StatusCode.unauthenticated
@enum.unique @enum.unique
class OpWriteFlags(enum.IntEnum): class OpWriteFlags(enum.IntEnum):
"""Mirrors defined write-flag constants in the C core.""" """Mirrors defined write-flag constants in the C core."""
WRITE_BUFFER_HINT = 1 WRITE_BUFFER_HINT = cygrpc.WriteFlag.buffer_hint
WRITE_NO_COMPRESS = 2 WRITE_NO_COMPRESS = cygrpc.WriteFlag.no_compress
@enum.unique @enum.unique
class OpType(enum.IntEnum): class OpType(enum.IntEnum):
"""Mirrors grpc_op_type in the C core.""" """Mirrors grpc_op_type in the C core."""
SEND_INITIAL_METADATA = 0 SEND_INITIAL_METADATA = cygrpc.OperationType.send_initial_metadata
SEND_MESSAGE = 1 SEND_MESSAGE = cygrpc.OperationType.send_message
SEND_CLOSE_FROM_CLIENT = 2 SEND_CLOSE_FROM_CLIENT = cygrpc.OperationType.send_close_from_client
SEND_STATUS_FROM_SERVER = 3 SEND_STATUS_FROM_SERVER = cygrpc.OperationType.send_status_from_server
RECV_INITIAL_METADATA = 4 RECV_INITIAL_METADATA = cygrpc.OperationType.receive_initial_metadata
RECV_MESSAGE = 5 RECV_MESSAGE = cygrpc.OperationType.receive_message
RECV_STATUS_ON_CLIENT = 6 RECV_STATUS_ON_CLIENT = cygrpc.OperationType.receive_status_on_client
RECV_CLOSE_ON_SERVER = 7 RECV_CLOSE_ON_SERVER = cygrpc.OperationType.receive_close_on_server
@enum.unique @enum.unique
class EventType(enum.IntEnum): class EventType(enum.IntEnum):
"""Mirrors grpc_completion_type in the C core.""" """Mirrors grpc_completion_type in the C core."""
QUEUE_SHUTDOWN = 0 QUEUE_SHUTDOWN = cygrpc.CompletionType.queue_shutdown
QUEUE_TIMEOUT = 1 # if seen on the Python side, something went horridly wrong QUEUE_TIMEOUT = cygrpc.CompletionType.queue_timeout
OP_COMPLETE = 2 OP_COMPLETE = cygrpc.CompletionType.operation_complete
@enum.unique @enum.unique
class ConnectivityState(enum.IntEnum): class ConnectivityState(enum.IntEnum):
"""Mirrors grpc_connectivity_state in the C core.""" """Mirrors grpc_connectivity_state in the C core."""
IDLE = 0 IDLE = cygrpc.ConnectivityState.idle
CONNECTING = 1 CONNECTING = cygrpc.ConnectivityState.connecting
READY = 2 READY = cygrpc.ConnectivityState.ready
TRANSIENT_FAILURE = 3 TRANSIENT_FAILURE = cygrpc.ConnectivityState.transient_failure
FATAL_FAILURE = 4 FATAL_FAILURE = cygrpc.ConnectivityState.fatal_failure
class Status(collections.namedtuple( class Status(collections.namedtuple(

@ -53,24 +53,24 @@ cdef class Call:
self.c_call, cy_operations.c_ops, cy_operations.c_nops, self.c_call, cy_operations.c_ops, cy_operations.c_nops,
<cpython.PyObject *>operation_tag, NULL) <cpython.PyObject *>operation_tag, NULL)
def cancel(self, def cancel(
grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE, self, grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE,
details=None): details=None):
if not self.is_valid: if not self.is_valid:
raise ValueError("invalid call object cannot be used from Python") raise ValueError("invalid call object cannot be used from Python")
if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE): if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE):
raise ValueError("if error_code is specified, so must details " raise ValueError("if error_code is specified, so must details "
"(and vice-versa)") "(and vice-versa)")
if error_code != grpc.GRPC_STATUS__DO_NOT_USE:
if isinstance(details, bytes): if isinstance(details, bytes):
pass pass
elif isinstance(details, basestring): elif isinstance(details, basestring):
details = details.encode() details = details.encode()
else: else:
raise TypeError("expected details to be str or bytes") raise TypeError("expected details to be str or bytes")
if error_code != grpc.GRPC_STATUS__DO_NOT_USE:
self.references.append(details) self.references.append(details)
return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details, return grpc.grpc_call_cancel_with_status(
NULL) self.c_call, error_code, details, NULL)
else: else:
return grpc.grpc_call_cancel(self.c_call, NULL) return grpc.grpc_call_cancel(self.c_call, NULL)
@ -79,6 +79,12 @@ cdef class Call:
return grpc.grpc_call_set_credentials( return grpc.grpc_call_set_credentials(
self.c_call, call_credentials.c_credentials) self.c_call, call_credentials.c_credentials)
def peer(self):
cdef char *peer = grpc.grpc_call_get_peer(self.c_call)
result = <bytes>peer
grpc.gpr_free(peer)
return result
def __dealloc__(self): def __dealloc__(self):
if self.c_call != NULL: if self.c_call != NULL:
grpc.grpc_call_destroy(self.c_call) grpc.grpc_call_destroy(self.c_call)

@ -27,6 +27,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport call from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport completion_queue from grpc._cython._cygrpc cimport completion_queue
from grpc._cython._cygrpc cimport credentials from grpc._cython._cygrpc cimport credentials
@ -70,12 +72,16 @@ cdef class Channel:
method = method.encode() method = method.encode()
else: else:
raise TypeError("expected method to be str or bytes") raise TypeError("expected method to be str or bytes")
if isinstance(host, bytes): cdef char *host_c_string = NULL
if host is None:
pass pass
elif isinstance(host, bytes):
host_c_string = host
elif isinstance(host, basestring): elif isinstance(host, basestring):
host = host.encode() host = host.encode()
host_c_string = host
else: else:
raise TypeError("expected host to be str or bytes") raise TypeError("expected host to be str, bytes, or None")
cdef call.Call operation_call = call.Call() cdef call.Call operation_call = call.Call()
operation_call.references = [self, method, host, queue] operation_call.references = [self, method, host, queue]
cdef grpc.grpc_call *parent_call = NULL cdef grpc.grpc_call *parent_call = NULL
@ -83,10 +89,29 @@ cdef class Channel:
parent_call = parent.c_call parent_call = parent.c_call
operation_call.c_call = grpc.grpc_channel_create_call( operation_call.c_call = grpc.grpc_channel_create_call(
self.c_channel, parent_call, flags, self.c_channel, parent_call, flags,
queue.c_completion_queue, method, host, deadline.c_time, queue.c_completion_queue, method, host_c_string, deadline.c_time,
NULL) NULL)
return operation_call return operation_call
def check_connectivity_state(self, bint try_to_connect):
return grpc.grpc_channel_check_connectivity_state(self.c_channel,
try_to_connect)
def watch_connectivity_state(
self, last_observed_state, records.Timespec deadline not None,
completion_queue.CompletionQueue queue not None, tag):
cdef records.OperationTag operation_tag = records.OperationTag(tag)
cpython.Py_INCREF(operation_tag)
grpc.grpc_channel_watch_connectivity_state(
self.c_channel, last_observed_state, deadline.c_time,
queue.c_completion_queue, <cpython.PyObject *>operation_tag)
def target(self):
cdef char * target = grpc.grpc_channel_get_target(self.c_channel)
result = <bytes>target
grpc.gpr_free(target)
return result
def __dealloc__(self): def __dealloc__(self):
if self.c_channel != NULL: if self.c_channel != NULL:
grpc.grpc_channel_destroy(self.c_channel) grpc.grpc_channel_destroy(self.c_channel)

@ -62,6 +62,8 @@ cdef class CompletionQueue:
cdef grpc.grpc_event event cdef grpc.grpc_event event
# Poll within a critical section # Poll within a critical section
# TODO consider making queue polling contention a hard error to enable
# easier bug discovery
with self.poll_condition: with self.poll_condition:
while self.is_polling: while self.is_polling:
self.poll_condition.wait(float(deadline) - time.time()) self.poll_condition.wait(float(deadline) - time.time())
@ -74,10 +76,12 @@ cdef class CompletionQueue:
self.poll_condition.notify() self.poll_condition.notify()
if event.type == grpc.GRPC_QUEUE_TIMEOUT: if event.type == grpc.GRPC_QUEUE_TIMEOUT:
return records.Event(event.type, False, None, None, None, None, None) return records.Event(
event.type, False, None, None, None, None, False, None)
elif event.type == grpc.GRPC_QUEUE_SHUTDOWN: elif event.type == grpc.GRPC_QUEUE_SHUTDOWN:
self.is_shutdown = True self.is_shutdown = True
return records.Event(event.type, True, None, None, None, None, None) return records.Event(
event.type, True, None, None, None, None, False, None)
else: else:
if event.tag != NULL: if event.tag != NULL:
tag = <records.OperationTag>event.tag tag = <records.OperationTag>event.tag
@ -97,7 +101,8 @@ cdef class CompletionQueue:
operation_call.references.extend(tag.references) operation_call.references.extend(tag.references)
return records.Event( return records.Event(
event.type, event.success, user_tag, operation_call, event.type, event.success, user_tag, operation_call,
request_call_details, request_metadata, batch_operations) request_call_details, request_metadata, tag.is_new_request,
batch_operations)
def shutdown(self): def shutdown(self):
grpc.grpc_completion_queue_shutdown(self.c_completion_queue) grpc.grpc_completion_queue_shutdown(self.c_completion_queue)

@ -27,7 +27,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport grpc from grpc._cython._cygrpc cimport grpc
from grpc._cython._cygrpc cimport records
cdef class ChannelCredentials: cdef class ChannelCredentials:
@ -49,3 +52,23 @@ cdef class ServerCredentials:
cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs
cdef size_t c_ssl_pem_key_cert_pairs_count cdef size_t c_ssl_pem_key_cert_pairs_count
cdef list references cdef list references
cdef class CredentialsMetadataPlugin:
cdef object plugin_callback
cdef str plugin_name
cdef grpc.grpc_metadata_credentials_plugin make_c_plugin(self)
cdef class AuthMetadataContext:
cdef grpc.grpc_auth_metadata_context context
cdef void plugin_get_metadata(
void *state, grpc.grpc_auth_metadata_context context,
grpc.grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil
cdef void plugin_destroy_c_plugin_state(void *state)

@ -27,6 +27,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport grpc from grpc._cython._cygrpc cimport grpc
from grpc._cython._cygrpc cimport records from grpc._cython._cygrpc cimport records
@ -71,12 +73,73 @@ cdef class ServerCredentials:
def __cinit__(self): def __cinit__(self):
self.c_credentials = NULL self.c_credentials = NULL
self.references = []
def __dealloc__(self): def __dealloc__(self):
if self.c_credentials != NULL: if self.c_credentials != NULL:
grpc.grpc_server_credentials_release(self.c_credentials) grpc.grpc_server_credentials_release(self.c_credentials)
cdef class CredentialsMetadataPlugin:
def __cinit__(self, object plugin_callback, str name):
"""
Args:
plugin_callback (callable): Callback accepting a service URL (str/bytes)
and callback object (accepting a records.Metadata,
grpc.grpc_status_code, and a str/bytes error message). This argument
when called should be non-blocking and eventually call the callback
object with the appropriate status code/details and metadata (if
successful).
name (str): Plugin name.
"""
if not callable(plugin_callback):
raise ValueError('expected callable plugin_callback')
self.plugin_callback = plugin_callback
self.plugin_name = name
@staticmethod
cdef grpc.grpc_metadata_credentials_plugin make_c_plugin(self):
cdef grpc.grpc_metadata_credentials_plugin result
result.get_metadata = plugin_get_metadata
result.destroy = plugin_destroy_c_plugin_state
result.state = <void *>self
result.type = self.plugin_name
cpython.Py_INCREF(self)
return result
cdef class AuthMetadataContext:
def __cinit__(self):
self.context.service_url = NULL
self.context.method_name = NULL
@property
def service_url(self):
return self.context.service_url
@property
def method_name(self):
return self.context.method_name
cdef void plugin_get_metadata(
void *state, grpc.grpc_auth_metadata_context context,
grpc.grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil:
def python_callback(
records.Metadata metadata, grpc.grpc_status_code status,
const char *error_details):
cb(user_data, metadata.c_metadata_array.metadata,
metadata.c_metadata_array.count, status, error_details)
cdef CredentialsMetadataPlugin self = <CredentialsMetadataPlugin>state
cdef AuthMetadataContext cy_context = AuthMetadataContext()
cy_context.context = context
self.plugin_callback(cy_context, python_callback)
cdef void plugin_destroy_c_plugin_state(void *state):
cpython.Py_DECREF(<CredentialsMetadataPlugin>state)
def channel_credentials_google_default(): def channel_credentials_google_default():
cdef ChannelCredentials credentials = ChannelCredentials(); cdef ChannelCredentials credentials = ChannelCredentials();
credentials.c_credentials = grpc.grpc_google_default_credentials_create() credentials.c_credentials = grpc.grpc_google_default_credentials_create()
@ -104,6 +167,7 @@ def channel_credentials_ssl(pem_root_certificates,
else: else:
credentials.c_credentials = grpc.grpc_ssl_credentials_create( credentials.c_credentials = grpc.grpc_ssl_credentials_create(
c_pem_root_certificates, NULL, NULL) c_pem_root_certificates, NULL, NULL)
return credentials
def channel_credentials_composite( def channel_credentials_composite(
ChannelCredentials credentials_1 not None, ChannelCredentials credentials_1 not None,
@ -135,7 +199,6 @@ def call_credentials_google_compute_engine():
grpc.grpc_google_compute_engine_credentials_create(NULL)) grpc.grpc_google_compute_engine_credentials_create(NULL))
return credentials return credentials
#TODO rename to something like client_credentials_service_account_jwt_access.
def call_credentials_service_account_jwt_access( def call_credentials_service_account_jwt_access(
json_key, records.Timespec token_lifetime not None): json_key, records.Timespec token_lifetime not None):
if isinstance(json_key, bytes): if isinstance(json_key, bytes):
@ -184,14 +247,25 @@ def call_credentials_google_iam(authorization_token, authority_selector):
credentials.references.append(authority_selector) credentials.references.append(authority_selector)
return credentials return credentials
def call_credentials_metadata_plugin(CredentialsMetadataPlugin plugin):
cdef CallCredentials credentials = CallCredentials()
credentials.c_credentials = (
grpc.grpc_metadata_credentials_create_from_plugin(plugin.make_c_plugin(),
NULL))
# TODO(atash): the following held reference is *probably* never necessary
credentials.references.append(plugin)
return credentials
def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs, def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
bint force_client_auth): bint force_client_auth):
cdef char *c_pem_root_certs = NULL
if pem_root_certs is None: if pem_root_certs is None:
pass pass
elif isinstance(pem_root_certs, bytes): elif isinstance(pem_root_certs, bytes):
pass c_pem_root_certs = pem_root_certs
elif isinstance(pem_root_certs, basestring): elif isinstance(pem_root_certs, basestring):
pem_root_certs = pem_root_certs.encode() pem_root_certs = pem_root_certs.encode()
c_pem_root_certs = pem_root_certs
else: else:
raise TypeError("expected pem_root_certs to be str or bytes") raise TypeError("expected pem_root_certs to be str or bytes")
pem_key_cert_pairs = list(pem_key_cert_pairs) pem_key_cert_pairs = list(pem_key_cert_pairs)
@ -212,7 +286,7 @@ def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs,
credentials.c_ssl_pem_key_cert_pairs[i] = ( credentials.c_ssl_pem_key_cert_pairs[i] = (
(<records.SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair) (<records.SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
credentials.c_credentials = grpc.grpc_ssl_server_credentials_create( credentials.c_credentials = grpc.grpc_ssl_server_credentials_create(
pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
credentials.c_ssl_pem_key_cert_pairs_count, force_client_auth, NULL) credentials.c_ssl_pem_key_cert_pairs_count, force_client_auth, NULL)
return credentials return credentials

@ -132,6 +132,20 @@ cdef extern from "grpc/byte_buffer.h":
cdef extern from "grpc/grpc.h": cdef extern from "grpc/grpc.h":
const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
const char *GRPC_ARG_ENABLE_CENSUS
const char *GRPC_ARG_MAX_CONCURRENT_STREAMS
const char *GRPC_ARG_MAX_MESSAGE_LENGTH
const char *GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
const char *GRPC_ARG_DEFAULT_AUTHORITY
const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
const char *GRPC_ARG_SECONDARY_USER_AGENT_STRING
const char *GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
const int GRPC_WRITE_BUFFER_HINT
const int GRPC_WRITE_NO_COMPRESS
const int GRPC_WRITE_USED_MASK
ctypedef struct grpc_completion_queue: ctypedef struct grpc_completion_queue:
# We don't care about the internals (and in fact don't know them) # We don't care about the internals (and in fact don't know them)
pass pass
@ -149,9 +163,9 @@ cdef extern from "grpc/grpc.h":
pass pass
ctypedef enum grpc_arg_type: ctypedef enum grpc_arg_type:
grpc_arg_string "GRPC_ARG_STRING" GRPC_ARG_STRING
grpc_arg_integer "GRPC_ARG_INTEGER" GRPC_ARG_INTEGER
grpc_arg_pointer "GRPC_ARG_POINTER" GRPC_ARG_POINTER
ctypedef struct grpc_arg_value_pointer: ctypedef struct grpc_arg_value_pointer:
void *address "p" void *address "p"
@ -185,6 +199,13 @@ cdef extern from "grpc/grpc.h":
GRPC_CALL_ERROR_INVALID_FLAGS GRPC_CALL_ERROR_INVALID_FLAGS
GRPC_CALL_ERROR_INVALID_METADATA GRPC_CALL_ERROR_INVALID_METADATA
ctypedef enum grpc_connectivity_state:
GRPC_CHANNEL_IDLE
GRPC_CHANNEL_CONNECTING
GRPC_CHANNEL_READY
GRPC_CHANNEL_TRANSIENT_FAILURE
GRPC_CHANNEL_FATAL_FAILURE
ctypedef struct grpc_metadata: ctypedef struct grpc_metadata:
const char *key const char *key
const char *value const char *value
@ -279,9 +300,9 @@ cdef extern from "grpc/grpc.h":
grpc_status_code status, grpc_status_code status,
const char *description, const char *description,
void *reserved) void *reserved)
char *grpc_call_get_peer(grpc_call *call)
void grpc_call_destroy(grpc_call *call) void grpc_call_destroy(grpc_call *call)
grpc_channel *grpc_insecure_channel_create(const char *target, grpc_channel *grpc_insecure_channel_create(const char *target,
const grpc_channel_args *args, const grpc_channel_args *args,
void *reserved) void *reserved)
@ -291,6 +312,12 @@ cdef extern from "grpc/grpc.h":
grpc_completion_queue *completion_queue, grpc_completion_queue *completion_queue,
const char *method, const char *host, const char *method, const char *host,
gpr_timespec deadline, void *reserved) gpr_timespec deadline, void *reserved)
grpc_connectivity_state grpc_channel_check_connectivity_state(
grpc_channel *channel, int try_to_connect)
void grpc_channel_watch_connectivity_state(
grpc_channel *channel, grpc_connectivity_state last_observed_state,
gpr_timespec deadline, grpc_completion_queue *cq, void *tag)
char *grpc_channel_get_target(grpc_channel *channel)
void grpc_channel_destroy(grpc_channel *channel) void grpc_channel_destroy(grpc_channel *channel)
grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved)
@ -367,3 +394,27 @@ cdef extern from "grpc/grpc_security.h":
grpc_call_error grpc_call_set_credentials(grpc_call *call, grpc_call_error grpc_call_set_credentials(grpc_call *call,
grpc_call_credentials *creds) grpc_call_credentials *creds)
ctypedef struct grpc_auth_context:
# We don't care about the internals (and in fact don't know them)
pass
ctypedef struct grpc_auth_metadata_context:
const char *service_url
const char *method_name
const grpc_auth_context *channel_auth_context
ctypedef void (*grpc_credentials_plugin_metadata_cb)(
void *user_data, const grpc_metadata *creds_md, size_t num_creds_md,
grpc_status_code status, const char *error_details)
ctypedef struct grpc_metadata_credentials_plugin:
void (*get_metadata)(
void *state, grpc_auth_metadata_context context,
grpc_credentials_plugin_metadata_cb cb, void *user_data)
void (*destroy)(void *state)
void *state
const char *type
grpc_call_credentials *grpc_metadata_credentials_create_from_plugin(
grpc_metadata_credentials_plugin plugin, void *reserved)

@ -66,6 +66,7 @@ cdef class Event:
cdef readonly call.Call operation_call cdef readonly call.Call operation_call
# For Server.request_call # For Server.request_call
cdef readonly bint is_new_request
cdef readonly CallDetails request_call_details cdef readonly CallDetails request_call_details
cdef readonly Metadata request_metadata cdef readonly Metadata request_metadata

@ -32,6 +32,30 @@ from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport server from grpc._cython._cygrpc cimport server
class ConnectivityState:
idle = grpc.GRPC_CHANNEL_IDLE
connecting = grpc.GRPC_CHANNEL_CONNECTING
ready = grpc.GRPC_CHANNEL_READY
transient_failure = grpc.GRPC_CHANNEL_TRANSIENT_FAILURE
fatal_failure = grpc.GRPC_CHANNEL_FATAL_FAILURE
class ChannelArgKey:
enable_census = grpc.GRPC_ARG_ENABLE_CENSUS
max_concurrent_streams = grpc.GRPC_ARG_MAX_CONCURRENT_STREAMS
max_message_length = grpc.GRPC_ARG_MAX_MESSAGE_LENGTH
http2_initial_sequence_number = grpc.GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
default_authority = grpc.GRPC_ARG_DEFAULT_AUTHORITY
primary_user_agent_string = grpc.GRPC_ARG_PRIMARY_USER_AGENT_STRING
secondary_user_agent_string = grpc.GRPC_ARG_SECONDARY_USER_AGENT_STRING
ssl_target_name_override = grpc.GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
class WriteFlag:
buffer_hint = grpc.GRPC_WRITE_BUFFER_HINT
no_compress = grpc.GRPC_WRITE_NO_COMPRESS
class StatusCode: class StatusCode:
ok = grpc.GRPC_STATUS_OK ok = grpc.GRPC_STATUS_OK
cancelled = grpc.GRPC_STATUS_CANCELLED cancelled = grpc.GRPC_STATUS_CANCELLED
@ -88,7 +112,10 @@ cdef class Timespec:
def __cinit__(self, time): def __cinit__(self, time):
if time is None: if time is None:
self.c_time = grpc.gpr_now(grpc.GPR_CLOCK_REALTIME) self.c_time = grpc.gpr_now(grpc.GPR_CLOCK_REALTIME)
elif isinstance(time, float): return
if isinstance(time, int):
time = float(time)
if isinstance(time, float):
if time == float("+inf"): if time == float("+inf"):
self.c_time = grpc.gpr_inf_future(grpc.GPR_CLOCK_REALTIME) self.c_time = grpc.gpr_inf_future(grpc.GPR_CLOCK_REALTIME)
elif time == float("-inf"): elif time == float("-inf"):
@ -97,8 +124,11 @@ cdef class Timespec:
self.c_time.seconds = time self.c_time.seconds = time
self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9 self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9
self.c_time.clock_type = grpc.GPR_CLOCK_REALTIME self.c_time.clock_type = grpc.GPR_CLOCK_REALTIME
elif isinstance(time, Timespec):
self.c_time = (<Timespec>time).c_time
else: else:
raise TypeError("expected time to be float") raise TypeError("expected time to be float, int, or Timespec, not {}"
.format(type(time)))
@property @property
def seconds(self): def seconds(self):
@ -166,6 +196,7 @@ cdef class Event:
object tag, call.Call operation_call, object tag, call.Call operation_call,
CallDetails request_call_details, CallDetails request_call_details,
Metadata request_metadata, Metadata request_metadata,
bint is_new_request,
Operations batch_operations): Operations batch_operations):
self.type = type self.type = type
self.success = success self.success = success
@ -174,6 +205,7 @@ cdef class Event:
self.request_call_details = request_call_details self.request_call_details = request_call_details
self.request_metadata = request_metadata self.request_metadata = request_metadata
self.batch_operations = batch_operations self.batch_operations = batch_operations
self.is_new_request = is_new_request
cdef class ByteBuffer: cdef class ByteBuffer:
@ -186,8 +218,14 @@ cdef class ByteBuffer:
pass pass
elif isinstance(data, basestring): elif isinstance(data, basestring):
data = data.encode() data = data.encode()
elif isinstance(data, ByteBuffer):
data = (<ByteBuffer>data).bytes()
if data is None:
self.c_byte_buffer = NULL
return
else: else:
raise TypeError("expected value to be of type str or bytes") raise TypeError("expected value to be of type str, bytes, or "
"ByteBuffer, not {}".format(type(data)))
cdef char *c_data = data cdef char *c_data = data
data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data)) data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data))
@ -409,12 +447,22 @@ cdef class Operation:
def type(self): def type(self):
return self.c_op.type return self.c_op.type
@property
def has_status(self):
return self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
@property @property
def received_message(self): def received_message(self):
if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE: if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE:
raise TypeError("self must be an operation receiving a message") raise TypeError("self must be an operation receiving a message")
return self._received_message return self._received_message
@property
def received_message_or_none(self):
if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE:
return None
return self._received_message
@property @property
def received_metadata(self): def received_metadata(self):
if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and
@ -422,12 +470,25 @@ cdef class Operation:
raise TypeError("self must be an operation receiving metadata") raise TypeError("self must be an operation receiving metadata")
return self._received_metadata return self._received_metadata
@property
def received_metadata_or_none(self):
if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and
self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT):
return None
return self._received_metadata
@property @property
def received_status_code(self): def received_status_code(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
raise TypeError("self must be an operation receiving a status code") raise TypeError("self must be an operation receiving a status code")
return self._received_status_code return self._received_status_code
@property
def received_status_code_or_none(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
return None
return self._received_status_code
@property @property
def received_status_details(self): def received_status_details(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
@ -437,6 +498,15 @@ cdef class Operation:
else: else:
return None return None
@property
def received_status_details_or_none(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
return None
if self._received_status_details:
return self._received_status_details
else:
return None
@property @property
def received_cancelled(self): def received_cancelled(self):
if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER: if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER:
@ -444,6 +514,12 @@ cdef class Operation:
"information") "information")
return False if self._received_cancelled == 0 else True return False if self._received_cancelled == 0 else True
@property
def received_cancelled_or_none(self):
if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER:
return None
return False if self._received_cancelled == 0 else True
def __dealloc__(self): def __dealloc__(self):
# We *almost* don't need to do anything; most of the objects are handled by # We *almost* don't need to do anything; most of the objects are handled by
# Python. The remaining one(s) are primitive fields filled in by GRPC core. # Python. The remaining one(s) are primitive fields filled in by GRPC core.

@ -132,7 +132,7 @@ cdef class Server:
def cancel_all_calls(self): def cancel_all_calls(self):
if not self.is_shutting_down: if not self.is_shutting_down:
raise ValueError("the server must be shutting down to cancel all calls") raise RuntimeError("the server must be shutting down to cancel all calls")
elif self.is_shutdown: elif self.is_shutdown:
return return
else: else:

@ -44,6 +44,9 @@ from grpc._cython._cygrpc import completion_queue
from grpc._cython._cygrpc import records from grpc._cython._cygrpc import records
from grpc._cython._cygrpc import server from grpc._cython._cygrpc import server
ConnectivityState = records.ConnectivityState
ChannelArgKey = records.ChannelArgKey
WriteFlag = records.WriteFlag
StatusCode = records.StatusCode StatusCode = records.StatusCode
CallError = records.CallError CallError = records.CallError
CompletionType = records.CompletionType CompletionType = records.CompletionType
@ -73,6 +76,8 @@ Operations = records.Operations
CallCredentials = credentials.CallCredentials CallCredentials = credentials.CallCredentials
ChannelCredentials = credentials.ChannelCredentials ChannelCredentials = credentials.ChannelCredentials
ServerCredentials = credentials.ServerCredentials ServerCredentials = credentials.ServerCredentials
CredentialsMetadataPlugin = credentials.CredentialsMetadataPlugin
AuthMetadataContext = credentials.AuthMetadataContext
channel_credentials_google_default = ( channel_credentials_google_default = (
credentials.channel_credentials_google_default) credentials.channel_credentials_google_default)
@ -88,6 +93,7 @@ call_credentials_jwt_access = (
call_credentials_refresh_token = ( call_credentials_refresh_token = (
credentials.call_credentials_google_refresh_token) credentials.call_credentials_google_refresh_token)
call_credentials_google_iam = credentials.call_credentials_google_iam call_credentials_google_iam = credentials.call_credentials_google_iam
call_credentials_metadata_plugin = credentials.call_credentials_metadata_plugin
server_credentials_ssl = credentials.server_credentials_ssl server_credentials_ssl = credentials.server_credentials_ssl
CompletionQueue = completion_queue.CompletionQueue CompletionQueue = completion_queue.CompletionQueue

@ -182,15 +182,15 @@ class _Kernel(object):
def _on_finish_event(self, operation_id, event, rpc_state): def _on_finish_event(self, operation_id, event, rpc_state):
_no_longer_due(_FINISH, rpc_state, operation_id, self._rpc_states) _no_longer_due(_FINISH, rpc_state, operation_id, self._rpc_states)
if event.status.code is _intermediary_low.Code.OK: if event.status.code == _intermediary_low.Code.OK:
termination = links.Ticket.Termination.COMPLETION termination = links.Ticket.Termination.COMPLETION
elif event.status.code is _intermediary_low.Code.CANCELLED: elif event.status.code == _intermediary_low.Code.CANCELLED:
termination = links.Ticket.Termination.CANCELLATION termination = links.Ticket.Termination.CANCELLATION
elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED: elif event.status.code == _intermediary_low.Code.DEADLINE_EXCEEDED:
termination = links.Ticket.Termination.EXPIRATION termination = links.Ticket.Termination.EXPIRATION
elif event.status.code is _intermediary_low.Code.UNIMPLEMENTED: elif event.status.code == _intermediary_low.Code.UNIMPLEMENTED:
termination = links.Ticket.Termination.REMOTE_FAILURE termination = links.Ticket.Termination.REMOTE_FAILURE
elif event.status.code is _intermediary_low.Code.UNKNOWN: elif event.status.code == _intermediary_low.Code.UNKNOWN:
termination = links.Ticket.Termination.LOCAL_FAILURE termination = links.Ticket.Termination.LOCAL_FAILURE
else: else:
termination = links.Ticket.Termination.TRANSMISSION_FAILURE termination = links.Ticket.Termination.TRANSMISSION_FAILURE
@ -262,7 +262,7 @@ class _Kernel(object):
self._channel, self._completion_queue, '/%s/%s' % (group, method), self._channel, self._completion_queue, '/%s/%s' % (group, method),
self._host, time.time() + timeout) self._host, time.time() + timeout)
if options is not None and options.credentials is not None: if options is not None and options.credentials is not None:
call.set_credentials(options.credentials._intermediary_low_credentials) call.set_credentials(options.credentials._low_credentials)
if transformed_initial_metadata is not None: if transformed_initial_metadata is not None:
for metadata_key, metadata_value in transformed_initial_metadata: for metadata_key, metadata_value in transformed_initial_metadata:
call.add_metadata(metadata_key, metadata_value) call.add_metadata(metadata_key, metadata_value)

@ -254,12 +254,12 @@ class _Kernel(object):
rpc_state = self._rpc_states[call] rpc_state = self._rpc_states[call]
_no_longer_due(_FINISH, rpc_state, call, self._rpc_states) _no_longer_due(_FINISH, rpc_state, call, self._rpc_states)
code = event.status.code code = event.status.code
if code is _intermediary_low.Code.OK: if code == _intermediary_low.Code.OK:
return return
if code is _intermediary_low.Code.CANCELLED: if code == _intermediary_low.Code.CANCELLED:
termination = links.Ticket.Termination.CANCELLATION termination = links.Ticket.Termination.CANCELLATION
elif code is _intermediary_low.Code.DEADLINE_EXCEEDED: elif code == _intermediary_low.Code.DEADLINE_EXCEEDED:
termination = links.Ticket.Termination.EXPIRATION termination = links.Ticket.Termination.EXPIRATION
else: else:
termination = links.Ticket.Termination.TRANSMISSION_FAILURE termination = links.Ticket.Termination.TRANSMISSION_FAILURE

@ -170,7 +170,7 @@ class _Server(interfaces.Server):
with self._lock: with self._lock:
if self._end_link is None: if self._end_link is None:
return self._grpc_link.add_port( return self._grpc_link.add_port(
address, server_credentials._intermediary_low_credentials) # pylint: disable=protected-access address, server_credentials._low_credentials) # pylint: disable=protected-access
else: else:
raise ValueError('Can\'t add port to serving server!') raise ValueError('Can\'t add port to serving server!')

@ -36,6 +36,7 @@ import threading # pylint: disable=unused-import
# cardinality and face are referenced from specification in this module. # cardinality and face are referenced from specification in this module.
from grpc._adapter import _intermediary_low from grpc._adapter import _intermediary_low
from grpc._adapter import _low
from grpc._adapter import _types from grpc._adapter import _types
from grpc.beta import _connectivity_channel from grpc.beta import _connectivity_channel
from grpc.beta import _server from grpc.beta import _server
@ -48,7 +49,7 @@ _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = (
'Exception calling channel subscription callback!') 'Exception calling channel subscription callback!')
class ClientCredentials(object): class ChannelCredentials(object):
"""A value encapsulating the data required to create a secure Channel. """A value encapsulating the data required to create a secure Channel.
This class and its instances have no supported interface - it exists to define This class and its instances have no supported interface - it exists to define
@ -56,13 +57,12 @@ class ClientCredentials(object):
functions. functions.
""" """
def __init__(self, low_credentials, intermediary_low_credentials): def __init__(self, low_credentials):
self._low_credentials = low_credentials self._low_credentials = low_credentials
self._intermediary_low_credentials = intermediary_low_credentials
def ssl_client_credentials(root_certificates, private_key, certificate_chain): def ssl_channel_credentials(root_certificates, private_key, certificate_chain):
"""Creates a ClientCredentials for use with an SSL-enabled Channel. """Creates a ChannelCredentials for use with an SSL-enabled Channel.
Args: Args:
root_certificates: The PEM-encoded root certificates or None to ask for root_certificates: The PEM-encoded root certificates or None to ask for
@ -73,12 +73,73 @@ def ssl_client_credentials(root_certificates, private_key, certificate_chain):
certificate chain should be used. certificate chain should be used.
Returns: Returns:
A ClientCredentials for use with an SSL-enabled Channel. A ChannelCredentials for use with an SSL-enabled Channel.
""" """
intermediary_low_credentials = _intermediary_low.ClientCredentials( return ChannelCredentials(_low.channel_credentials_ssl(
root_certificates, private_key, certificate_chain) root_certificates, private_key, certificate_chain))
return ClientCredentials(
intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access
class CallCredentials(object):
"""A value encapsulating data asserting an identity over an *established*
channel. May be composed with ChannelCredentials to always assert identity for
every call over that channel.
This class and its instances have no supported interface - it exists to define
the type of its instances and its instances exist to be passed to other
functions.
"""
def __init__(self, low_credentials):
self._low_credentials = low_credentials
def metadata_call_credentials(metadata_plugin, name=None):
"""Construct CallCredentials from an interfaces.GRPCAuthMetadataPlugin.
Args:
metadata_plugin: An interfaces.GRPCAuthMetadataPlugin to use in constructing
the CallCredentials object.
Returns:
A CallCredentials object for use in a GRPCCallOptions object.
"""
if name is None:
name = metadata_plugin.__name__
return CallCredentials(
_low.call_credentials_metadata_plugin(metadata_plugin, name))
def composite_call_credentials(call_credentials, additional_call_credentials):
"""Compose two CallCredentials to make a new one.
Args:
call_credentials: A CallCredentials object.
additional_call_credentials: Another CallCredentials object to compose on
top of call_credentials.
Returns:
A CallCredentials object for use in a GRPCCallOptions object.
"""
return CallCredentials(
_low.call_credentials_composite(
call_credentials._low_credentials,
additional_call_credentials._low_credentials))
def composite_channel_credentials(channel_credentials,
additional_call_credentials):
"""Compose ChannelCredentials on top of client credentials to make a new one.
Args:
channel_credentials: A ChannelCredentials object.
additional_call_credentials: A CallCredentials object to compose on
top of channel_credentials.
Returns:
A ChannelCredentials object for use in a GRPCCallOptions object.
"""
return ChannelCredentials(
_low.channel_credentials_composite(
channel_credentials._low_credentials,
additional_call_credentials._low_credentials))
class Channel(object): class Channel(object):
@ -135,19 +196,19 @@ def insecure_channel(host, port):
return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access
def secure_channel(host, port, client_credentials): def secure_channel(host, port, channel_credentials):
"""Creates a secure Channel to a remote host. """Creates a secure Channel to a remote host.
Args: Args:
host: The name of the remote host to which to connect. host: The name of the remote host to which to connect.
port: The port of the remote host to which to connect. port: The port of the remote host to which to connect.
client_credentials: A ClientCredentials. channel_credentials: A ChannelCredentials.
Returns: Returns:
A secure Channel to the remote host through which RPCs may be conducted. A secure Channel to the remote host through which RPCs may be conducted.
""" """
intermediary_low_channel = _intermediary_low.Channel( intermediary_low_channel = _intermediary_low.Channel(
'%s:%d' % (host, port), client_credentials._intermediary_low_credentials) '%s:%d' % (host, port), channel_credentials._low_credentials)
return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access
@ -251,9 +312,8 @@ class ServerCredentials(object):
functions. functions.
""" """
def __init__(self, low_credentials, intermediary_low_credentials): def __init__(self, low_credentials):
self._low_credentials = low_credentials self._low_credentials = low_credentials
self._intermediary_low_credentials = intermediary_low_credentials
def ssl_server_credentials( def ssl_server_credentials(
@ -282,11 +342,9 @@ def ssl_server_credentials(
raise ValueError( raise ValueError(
'Illegal to require client auth without providing root certificates!') 'Illegal to require client auth without providing root certificates!')
else: else:
intermediary_low_credentials = _intermediary_low.ServerCredentials( return ServerCredentials(_low.server_credentials_ssl(
root_certificates, private_key_certificate_chain_pairs, root_certificates, private_key_certificate_chain_pairs,
require_client_auth) require_client_auth))
return ServerCredentials(
intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access
class ServerOptions(object): class ServerOptions(object):

@ -100,14 +100,55 @@ def grpc_call_options(disable_compression=False, credentials=None):
disable_compression: A boolean indicating whether or not compression should disable_compression: A boolean indicating whether or not compression should
be disabled for the request object of the RPC. Only valid for be disabled for the request object of the RPC. Only valid for
request-unary RPCs. request-unary RPCs.
credentials: Reserved for gRPC per-call credentials. The type for this does credentials: A CallCredentials object to use for the invoked RPC.
not exist yet at the Python level.
""" """
if credentials is not None:
raise ValueError('`credentials` is a reserved argument')
return GRPCCallOptions(disable_compression, None, credentials) return GRPCCallOptions(disable_compression, None, credentials)
class GRPCAuthMetadataContext(object):
"""Provides information to call credentials metadata plugins.
Attributes:
service_url: A string URL of the service being called into.
method_name: A string of the fully qualified method name being called.
"""
__metaclass__ = abc.ABCMeta
class GRPCAuthMetadataPluginCallback(object):
"""Callback object received by a metadata plugin."""
__metaclass__ = abc.ABCMeta
def __call__(self, metadata, error):
"""Inform the gRPC runtime of the metadata to construct a CallCredentials.
Args:
metadata: An iterable of 2-sequences (e.g. tuples) of metadata key/value
pairs.
error: An Exception to indicate error or None to indicate success.
"""
raise NotImplementedError()
class GRPCAuthMetadataPlugin(object):
"""
"""
__metaclass__ = abc.ABCMeta
def __call__(self, context, callback):
"""Invoke the plugin.
Must not block. Need only be called by the gRPC runtime.
Args:
context: A GRPCAuthMetadataContext providing information on what the
plugin is being used for.
callback: A GRPCAuthMetadataPluginCallback to be invoked either
synchronously or asynchronously.
"""
raise NotImplementedError()
class GRPCServicerContext(object): class GRPCServicerContext(object):
"""Exposes gRPC-specific options and behaviors to code servicing RPCs.""" """Exposes gRPC-specific options and behaviors to code servicing RPCs."""
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta

@ -57,19 +57,6 @@ ENABLE_CYTHON_TRACING = os.environ.get(
# the installation. # the installation.
INSTALL_TESTS = os.environ.get('GRPC_PYTHON_INSTALL_TESTS', False) INSTALL_TESTS = os.environ.get('GRPC_PYTHON_INSTALL_TESTS', False)
C_EXTENSION_SOURCES = (
'grpc/_adapter/_c/module.c',
'grpc/_adapter/_c/types.c',
'grpc/_adapter/_c/utility.c',
'grpc/_adapter/_c/types/call_credentials.c',
'grpc/_adapter/_c/types/channel_credentials.c',
'grpc/_adapter/_c/types/server_credentials.c',
'grpc/_adapter/_c/types/completion_queue.c',
'grpc/_adapter/_c/types/call.c',
'grpc/_adapter/_c/types/channel.c',
'grpc/_adapter/_c/types/server.c',
)
CYTHON_EXTENSION_PACKAGE_NAMES = () CYTHON_EXTENSION_PACKAGE_NAMES = ()
CYTHON_EXTENSION_MODULE_NAMES = ( CYTHON_EXTENSION_MODULE_NAMES = (
@ -94,14 +81,6 @@ if not "darwin" in sys.platform:
EXTENSION_LIBRARIES += ('rt',) EXTENSION_LIBRARIES += ('rt',)
C_EXTENSION_MODULE = _core.Extension(
'grpc._adapter._c', sources=list(C_EXTENSION_SOURCES),
include_dirs=list(EXTENSION_INCLUDE_DIRECTORIES),
libraries=list(EXTENSION_LIBRARIES)
)
EXTENSION_MODULES = [C_EXTENSION_MODULE]
def cython_extensions(package_names, module_names, include_dirs, libraries, def cython_extensions(package_names, module_names, include_dirs, libraries,
build_with_cython=False): build_with_cython=False):
file_extension = 'pyx' if build_with_cython else 'c' file_extension = 'pyx' if build_with_cython else 'c'
@ -185,7 +164,7 @@ else:
setuptools.setup( setuptools.setup(
name='grpcio', name='grpcio',
version='0.11.0b2', version='0.11.0b2',
ext_modules=EXTENSION_MODULES + CYTHON_EXTENSION_MODULES, ext_modules=CYTHON_EXTENSION_MODULES,
packages=list(PACKAGES), packages=list(PACKAGES),
package_dir=PACKAGE_DIRECTORIES, package_dir=PACKAGE_DIRECTORIES,
install_requires=INSTALL_REQUIRES, install_requires=INSTALL_REQUIRES,

@ -55,7 +55,7 @@ class SecureInteropTest(
self.server.start() self.server.start()
self.stub = test_pb2.beta_create_TestService_stub( self.stub = test_pb2.beta_create_TestService_stub(
test_utilities.not_really_secure_channel( test_utilities.not_really_secure_channel(
'[::]', port, implementations.ssl_client_credentials( '[::]', port, implementations.ssl_channel_credentials(
resources.test_root_certificates(), None, None), resources.test_root_certificates(), None, None),
_SERVER_HOST_OVERRIDE)) _SERVER_HOST_OVERRIDE))

@ -94,7 +94,7 @@ def _stub(args):
channel = test_utilities.not_really_secure_channel( channel = test_utilities.not_really_secure_channel(
args.server_host, args.server_port, args.server_host, args.server_port,
implementations.ssl_client_credentials(root_certificates, None, None), implementations.ssl_channel_credentials(root_certificates, None, None),
args.server_host_override) args.server_host_override)
stub = test_pb2.beta_create_TestService_stub( stub = test_pb2.beta_create_TestService_stub(
channel, metadata_transformer=metadata_transformer) channel, metadata_transformer=metadata_transformer)

@ -115,6 +115,7 @@ class EchoTest(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.server.stop() self.server.stop()
self.server.cancel_all_calls()
self.server_completion_queue.stop() self.server_completion_queue.stop()
self.client_completion_queue.stop() self.client_completion_queue.stop()
self.server_completion_queue_thread.join() self.server_completion_queue_thread.join()
@ -286,11 +287,6 @@ class EchoTest(unittest.TestCase):
set((server_trailing_metadata_key, set((server_trailing_metadata_key,
server_trailing_binary_metadata_key,))) server_trailing_binary_metadata_key,)))
server_timeout_none_event = self.server_completion_queue.get(0)
self.assertIsNone(server_timeout_none_event)
client_timeout_none_event = self.client_completion_queue.get(0)
self.assertIsNone(client_timeout_none_event)
self.assertSequenceEqual(test_data, server_data) self.assertSequenceEqual(test_data, server_data)
self.assertSequenceEqual(test_data, client_data) self.assertSequenceEqual(test_data, client_data)
@ -335,6 +331,7 @@ class CancellationTest(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.server.stop() self.server.stop()
self.server.cancel_all_calls()
self.server_completion_queue.stop() self.server_completion_queue.stop()
self.client_completion_queue.stop() self.client_completion_queue.stop()
self.server_completion_queue_thread.join() self.server_completion_queue_thread.join()
@ -413,11 +410,6 @@ class CancellationTest(unittest.TestCase):
self.assertEqual(_low.Status(_low.Code.CANCELLED, 'Cancelled'), self.assertEqual(_low.Status(_low.Code.CANCELLED, 'Cancelled'),
finish_event.status) finish_event.status)
server_timeout_none_event = self.server_completion_queue.get(0)
self.assertIsNone(server_timeout_none_event)
client_timeout_none_event = self.client_completion_queue.get(0)
self.assertIsNone(client_timeout_none_event)
self.assertSequenceEqual(test_data, server_data) self.assertSequenceEqual(test_data, server_data)
self.assertSequenceEqual(test_data, client_data) self.assertSequenceEqual(test_data, client_data)

@ -80,11 +80,11 @@ class InsecureServerInsecureClient(unittest.TestCase):
del self.client_channel del self.client_channel
self.client_completion_queue.shutdown() self.client_completion_queue.shutdown()
while (self.client_completion_queue.next().type != while (self.client_completion_queue.next(float('+inf')).type !=
_types.EventType.QUEUE_SHUTDOWN): _types.EventType.QUEUE_SHUTDOWN):
pass pass
self.server_completion_queue.shutdown() self.server_completion_queue.shutdown()
while (self.server_completion_queue.next().type != while (self.server_completion_queue.next(float('+inf')).type !=
_types.EventType.QUEUE_SHUTDOWN): _types.EventType.QUEUE_SHUTDOWN):
pass pass
@ -294,8 +294,12 @@ class HangingServerShutdown(unittest.TestCase):
# Now try to shutdown the server and expect that we see server shutdown # Now try to shutdown the server and expect that we see server shutdown
# almost immediately after calling cancel_all_calls. # almost immediately after calling cancel_all_calls.
# First attempt to cancel all calls before shutting down, and expect
# our state machine to catch the erroneous API use.
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
self.server.cancel_all_calls() self.server.cancel_all_calls()
shutdown_tag = object() shutdown_tag = object()
self.server.shutdown(shutdown_tag) self.server.shutdown(shutdown_tag)
pre_cancel_timestamp = time.time() pre_cancel_timestamp = time.time()

@ -1,187 +0,0 @@
# 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.
# Fork of grpc._adapter._low_test; the grpc._cython.types adapter in
# grpc._cython.low should transparently support the semantics expected of
# grpc._adapter._low.
import time
import unittest
from grpc._adapter import _types
from grpc._cython import adapter_low as _low
class InsecureServerInsecureClient(unittest.TestCase):
def setUp(self):
self.server_completion_queue = _low.CompletionQueue()
self.server = _low.Server(self.server_completion_queue, [])
self.port = self.server.add_http2_port('[::]:0')
self.client_completion_queue = _low.CompletionQueue()
self.client_channel = _low.Channel('localhost:%d'%self.port, [])
self.server.start()
def tearDown(self):
self.server.shutdown()
del self.client_channel
self.client_completion_queue.shutdown()
while (self.client_completion_queue.next().type !=
_types.EventType.QUEUE_SHUTDOWN):
pass
self.server_completion_queue.shutdown()
while (self.server_completion_queue.next().type !=
_types.EventType.QUEUE_SHUTDOWN):
pass
del self.client_completion_queue
del self.server_completion_queue
del self.server
@unittest.skip('TODO(atash): implement grpc._cython.adapter_low')
def testEcho(self):
DEADLINE = time.time()+5
DEADLINE_TOLERANCE = 0.25
CLIENT_METADATA_ASCII_KEY = 'key'
CLIENT_METADATA_ASCII_VALUE = 'val'
CLIENT_METADATA_BIN_KEY = 'key-bin'
CLIENT_METADATA_BIN_VALUE = b'\0'*1000
SERVER_INITIAL_METADATA_KEY = 'init_me_me_me'
SERVER_INITIAL_METADATA_VALUE = 'whodawha?'
SERVER_TRAILING_METADATA_KEY = 'california_is_in_a_drought'
SERVER_TRAILING_METADATA_VALUE = 'zomg it is'
SERVER_STATUS_CODE = _types.StatusCode.OK
SERVER_STATUS_DETAILS = 'our work is never over'
REQUEST = 'in death a member of project mayhem has a name'
RESPONSE = 'his name is robert paulson'
METHOD = 'twinkies'
HOST = 'hostess'
server_request_tag = object()
request_call_result = self.server.request_call(self.server_completion_queue,
server_request_tag)
self.assertEqual(_types.CallError.OK, request_call_result)
client_call_tag = object()
client_call = self.client_channel.create_call(self.client_completion_queue,
METHOD, HOST, DEADLINE)
client_initial_metadata = [
(CLIENT_METADATA_ASCII_KEY, CLIENT_METADATA_ASCII_VALUE),
(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]
client_start_batch_result = client_call.start_batch([
_types.OpArgs.send_initial_metadata(client_initial_metadata),
_types.OpArgs.send_message(REQUEST),
_types.OpArgs.send_close_from_client(),
_types.OpArgs.recv_initial_metadata(),
_types.OpArgs.recv_message(),
_types.OpArgs.recv_status_on_client()
], client_call_tag)
self.assertEqual(_types.CallError.OK, client_start_batch_result)
request_event = self.server_completion_queue.next(DEADLINE)
self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type)
self.assertIsInstance(request_event.call, _low.Call)
self.assertIs(server_request_tag, request_event.tag)
self.assertEqual(1, len(request_event.results))
self.assertEqual(dict(client_initial_metadata),
dict(request_event.results[0].initial_metadata))
self.assertEqual(METHOD, request_event.call_details.method)
self.assertEqual(HOST, request_event.call_details.host)
self.assertLess(abs(DEADLINE - request_event.call_details.deadline),
DEADLINE_TOLERANCE)
server_call_tag = object()
server_call = request_event.call
server_initial_metadata = [
(SERVER_INITIAL_METADATA_KEY, SERVER_INITIAL_METADATA_VALUE)]
server_trailing_metadata = [
(SERVER_TRAILING_METADATA_KEY, SERVER_TRAILING_METADATA_VALUE)]
server_start_batch_result = server_call.start_batch([
_types.OpArgs.send_initial_metadata(server_initial_metadata),
_types.OpArgs.recv_message(),
_types.OpArgs.send_message(RESPONSE),
_types.OpArgs.recv_close_on_server(),
_types.OpArgs.send_status_from_server(
server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS)
], server_call_tag)
self.assertEqual(_types.CallError.OK, server_start_batch_result)
client_event = self.client_completion_queue.next(DEADLINE)
server_event = self.server_completion_queue.next(DEADLINE)
self.assertEqual(6, len(client_event.results))
found_client_op_types = set()
for client_result in client_event.results:
# we expect each op type to be unique
self.assertNotIn(client_result.type, found_client_op_types)
found_client_op_types.add(client_result.type)
if client_result.type == _types.OpType.RECV_INITIAL_METADATA:
self.assertEqual(dict(server_initial_metadata),
dict(client_result.initial_metadata))
elif client_result.type == _types.OpType.RECV_MESSAGE:
self.assertEqual(RESPONSE, client_result.message)
elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT:
self.assertEqual(dict(server_trailing_metadata),
dict(client_result.trailing_metadata))
self.assertEqual(SERVER_STATUS_DETAILS, client_result.status.details)
self.assertEqual(SERVER_STATUS_CODE, client_result.status.code)
self.assertEqual(set([
_types.OpType.SEND_INITIAL_METADATA,
_types.OpType.SEND_MESSAGE,
_types.OpType.SEND_CLOSE_FROM_CLIENT,
_types.OpType.RECV_INITIAL_METADATA,
_types.OpType.RECV_MESSAGE,
_types.OpType.RECV_STATUS_ON_CLIENT
]), found_client_op_types)
self.assertEqual(5, len(server_event.results))
found_server_op_types = set()
for server_result in server_event.results:
self.assertNotIn(client_result.type, found_server_op_types)
found_server_op_types.add(server_result.type)
if server_result.type == _types.OpType.RECV_MESSAGE:
self.assertEqual(REQUEST, server_result.message)
elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER:
self.assertFalse(server_result.cancelled)
self.assertEqual(set([
_types.OpType.SEND_INITIAL_METADATA,
_types.OpType.RECV_MESSAGE,
_types.OpType.SEND_MESSAGE,
_types.OpType.RECV_CLOSE_ON_SERVER,
_types.OpType.SEND_STATUS_FROM_SERVER
]), found_server_op_types)
del client_call
del server_call
if __name__ == '__main__':
unittest.main(verbosity=2)

@ -28,11 +28,24 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import time import time
import threading
import unittest import unittest
from grpc._cython import cygrpc from grpc._cython import cygrpc
from tests.unit._cython import test_utilities from tests.unit._cython import test_utilities
from tests.unit import test_common from tests.unit import test_common
from tests.unit import resources
_SSL_HOST_OVERRIDE = 'foo.test.google.fr'
_CALL_CREDENTIALS_METADATA_KEY = 'call-creds-key'
_CALL_CREDENTIALS_METADATA_VALUE = 'call-creds-value'
def _metadata_plugin_callback(context, callback):
callback(cygrpc.Metadata(
[cygrpc.Metadatum(_CALL_CREDENTIALS_METADATA_KEY,
_CALL_CREDENTIALS_METADATA_VALUE)]),
cygrpc.StatusCode.ok, '')
class TypeSmokeTest(unittest.TestCase): class TypeSmokeTest(unittest.TestCase):
@ -89,7 +102,17 @@ class TypeSmokeTest(unittest.TestCase):
channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([]))
del channel del channel
@unittest.skip('TODO(atash): undo skip after #2229 is merged') def testCredentialsMetadataPluginUpDown(self):
plugin = cygrpc.CredentialsMetadataPlugin(
lambda ignored_a, ignored_b: None, '')
del plugin
def testCallCredentialsFromPluginUpDown(self):
plugin = cygrpc.CredentialsMetadataPlugin(_metadata_plugin_callback, '')
call_credentials = cygrpc.call_credentials_metadata_plugin(plugin)
del plugin
del call_credentials
def testServerStartNoExplicitShutdown(self): def testServerStartNoExplicitShutdown(self):
server = cygrpc.Server() server = cygrpc.Server()
completion_queue = cygrpc.CompletionQueue() completion_queue = cygrpc.CompletionQueue()
@ -99,7 +122,6 @@ class TypeSmokeTest(unittest.TestCase):
server.start() server.start()
del server del server
@unittest.skip('TODO(atash): undo skip after #2229 is merged')
def testServerStartShutdown(self): def testServerStartShutdown(self):
completion_queue = cygrpc.CompletionQueue() completion_queue = cygrpc.CompletionQueue()
server = cygrpc.Server() server = cygrpc.Server()
@ -262,5 +284,169 @@ class InsecureServerInsecureClient(unittest.TestCase):
del server_call del server_call
class SecureServerSecureClient(unittest.TestCase):
def setUp(self):
server_credentials = cygrpc.server_credentials_ssl(
None, [cygrpc.SslPemKeyCertPair(resources.private_key(),
resources.certificate_chain())], False)
channel_credentials = cygrpc.channel_credentials_ssl(
resources.test_root_certificates(), None)
self.server_completion_queue = cygrpc.CompletionQueue()
self.server = cygrpc.Server()
self.server.register_completion_queue(self.server_completion_queue)
self.port = self.server.add_http2_port('[::]:0', server_credentials)
self.server.start()
self.client_completion_queue = cygrpc.CompletionQueue()
client_channel_arguments = cygrpc.ChannelArgs([
cygrpc.ChannelArg(cygrpc.ChannelArgKey.ssl_target_name_override,
_SSL_HOST_OVERRIDE)])
self.client_channel = cygrpc.Channel(
'localhost:{}'.format(self.port), client_channel_arguments,
channel_credentials)
def tearDown(self):
del self.server
del self.client_completion_queue
del self.server_completion_queue
def testEcho(self):
DEADLINE = time.time()+5
DEADLINE_TOLERANCE = 0.25
CLIENT_METADATA_ASCII_KEY = b'key'
CLIENT_METADATA_ASCII_VALUE = b'val'
CLIENT_METADATA_BIN_KEY = b'key-bin'
CLIENT_METADATA_BIN_VALUE = b'\0'*1000
SERVER_INITIAL_METADATA_KEY = b'init_me_me_me'
SERVER_INITIAL_METADATA_VALUE = b'whodawha?'
SERVER_TRAILING_METADATA_KEY = b'california_is_in_a_drought'
SERVER_TRAILING_METADATA_VALUE = b'zomg it is'
SERVER_STATUS_CODE = cygrpc.StatusCode.ok
SERVER_STATUS_DETAILS = b'our work is never over'
REQUEST = b'in death a member of project mayhem has a name'
RESPONSE = b'his name is robert paulson'
METHOD = b'/twinkies'
HOST = None # Default host
cygrpc_deadline = cygrpc.Timespec(DEADLINE)
server_request_tag = object()
request_call_result = self.server.request_call(
self.server_completion_queue, self.server_completion_queue,
server_request_tag)
self.assertEqual(cygrpc.CallError.ok, request_call_result)
plugin = cygrpc.CredentialsMetadataPlugin(_metadata_plugin_callback, '')
call_credentials = cygrpc.call_credentials_metadata_plugin(plugin)
client_call_tag = object()
client_call = self.client_channel.create_call(
None, 0, self.client_completion_queue, METHOD, HOST, cygrpc_deadline)
client_call.set_credentials(call_credentials)
client_initial_metadata = cygrpc.Metadata([
cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY,
CLIENT_METADATA_ASCII_VALUE),
cygrpc.Metadatum(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)])
client_start_batch_result = client_call.start_batch(cygrpc.Operations([
cygrpc.operation_send_initial_metadata(client_initial_metadata),
cygrpc.operation_send_message(REQUEST),
cygrpc.operation_send_close_from_client(),
cygrpc.operation_receive_initial_metadata(),
cygrpc.operation_receive_message(),
cygrpc.operation_receive_status_on_client()
]), client_call_tag)
self.assertEqual(cygrpc.CallError.ok, client_start_batch_result)
client_event_future = test_utilities.CompletionQueuePollFuture(
self.client_completion_queue, cygrpc_deadline)
request_event = self.server_completion_queue.poll(cygrpc_deadline)
self.assertEqual(cygrpc.CompletionType.operation_complete,
request_event.type)
self.assertIsInstance(request_event.operation_call, cygrpc.Call)
self.assertIs(server_request_tag, request_event.tag)
self.assertEqual(0, len(request_event.batch_operations))
client_metadata_with_credentials = list(client_initial_metadata) + [
(_CALL_CREDENTIALS_METADATA_KEY, _CALL_CREDENTIALS_METADATA_VALUE)]
self.assertTrue(
test_common.metadata_transmitted(client_metadata_with_credentials,
request_event.request_metadata))
self.assertEqual(METHOD, request_event.request_call_details.method)
self.assertEqual(_SSL_HOST_OVERRIDE,
request_event.request_call_details.host)
self.assertLess(
abs(DEADLINE - float(request_event.request_call_details.deadline)),
DEADLINE_TOLERANCE)
server_call_tag = object()
server_call = request_event.operation_call
server_initial_metadata = cygrpc.Metadata([
cygrpc.Metadatum(SERVER_INITIAL_METADATA_KEY,
SERVER_INITIAL_METADATA_VALUE)])
server_trailing_metadata = cygrpc.Metadata([
cygrpc.Metadatum(SERVER_TRAILING_METADATA_KEY,
SERVER_TRAILING_METADATA_VALUE)])
server_start_batch_result = server_call.start_batch([
cygrpc.operation_send_initial_metadata(server_initial_metadata),
cygrpc.operation_receive_message(),
cygrpc.operation_send_message(RESPONSE),
cygrpc.operation_receive_close_on_server(),
cygrpc.operation_send_status_from_server(
server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS)
], server_call_tag)
self.assertEqual(cygrpc.CallError.ok, server_start_batch_result)
client_event = client_event_future.result()
server_event = self.server_completion_queue.poll(cygrpc_deadline)
self.assertEqual(6, len(client_event.batch_operations))
found_client_op_types = set()
for client_result in client_event.batch_operations:
# we expect each op type to be unique
self.assertNotIn(client_result.type, found_client_op_types)
found_client_op_types.add(client_result.type)
if client_result.type == cygrpc.OperationType.receive_initial_metadata:
self.assertTrue(
test_common.metadata_transmitted(server_initial_metadata,
client_result.received_metadata))
elif client_result.type == cygrpc.OperationType.receive_message:
self.assertEqual(RESPONSE, client_result.received_message.bytes())
elif client_result.type == cygrpc.OperationType.receive_status_on_client:
self.assertTrue(
test_common.metadata_transmitted(server_trailing_metadata,
client_result.received_metadata))
self.assertEqual(SERVER_STATUS_DETAILS,
client_result.received_status_details)
self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code)
self.assertEqual(set([
cygrpc.OperationType.send_initial_metadata,
cygrpc.OperationType.send_message,
cygrpc.OperationType.send_close_from_client,
cygrpc.OperationType.receive_initial_metadata,
cygrpc.OperationType.receive_message,
cygrpc.OperationType.receive_status_on_client
]), found_client_op_types)
self.assertEqual(5, len(server_event.batch_operations))
found_server_op_types = set()
for server_result in server_event.batch_operations:
self.assertNotIn(client_result.type, found_server_op_types)
found_server_op_types.add(server_result.type)
if server_result.type == cygrpc.OperationType.receive_message:
self.assertEqual(REQUEST, server_result.received_message.bytes())
elif server_result.type == cygrpc.OperationType.receive_close_on_server:
self.assertFalse(server_result.received_cancelled)
self.assertEqual(set([
cygrpc.OperationType.send_initial_metadata,
cygrpc.OperationType.receive_message,
cygrpc.OperationType.send_message,
cygrpc.OperationType.receive_close_on_server,
cygrpc.OperationType.send_status_from_server
]), found_server_op_types)
del client_call
del server_call
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(verbosity=2) unittest.main(verbosity=2)

@ -42,6 +42,9 @@ from tests.unit.framework.common import test_constants
_SERVER_HOST_OVERRIDE = 'foo.test.google.fr' _SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
_PER_RPC_CREDENTIALS_METADATA_KEY = 'my-call-credentials-metadata-key'
_PER_RPC_CREDENTIALS_METADATA_VALUE = 'my-call-credentials-metadata-value'
_GROUP = 'group' _GROUP = 'group'
_UNARY_UNARY = 'unary-unary' _UNARY_UNARY = 'unary-unary'
_UNARY_STREAM = 'unary-stream' _UNARY_STREAM = 'unary-stream'
@ -63,6 +66,7 @@ class _Servicer(object):
with self._condition: with self._condition:
self._request = request self._request = request
self._peer = context.protocol_context().peer() self._peer = context.protocol_context().peer()
self._invocation_metadata = context.invocation_metadata()
context.protocol_context().disable_next_response_compression() context.protocol_context().disable_next_response_compression()
self._serviced = True self._serviced = True
self._condition.notify_all() self._condition.notify_all()
@ -72,6 +76,7 @@ class _Servicer(object):
with self._condition: with self._condition:
self._request = request self._request = request
self._peer = context.protocol_context().peer() self._peer = context.protocol_context().peer()
self._invocation_metadata = context.invocation_metadata()
context.protocol_context().disable_next_response_compression() context.protocol_context().disable_next_response_compression()
self._serviced = True self._serviced = True
self._condition.notify_all() self._condition.notify_all()
@ -83,6 +88,7 @@ class _Servicer(object):
self._request = request self._request = request
with self._condition: with self._condition:
self._peer = context.protocol_context().peer() self._peer = context.protocol_context().peer()
self._invocation_metadata = context.invocation_metadata()
context.protocol_context().disable_next_response_compression() context.protocol_context().disable_next_response_compression()
self._serviced = True self._serviced = True
self._condition.notify_all() self._condition.notify_all()
@ -95,6 +101,7 @@ class _Servicer(object):
context.protocol_context().disable_next_response_compression() context.protocol_context().disable_next_response_compression()
yield _RESPONSE yield _RESPONSE
with self._condition: with self._condition:
self._invocation_metadata = context.invocation_metadata()
self._serviced = True self._serviced = True
self._condition.notify_all() self._condition.notify_all()
@ -137,6 +144,11 @@ class _BlockingIterator(object):
self._condition.notify_all() self._condition.notify_all()
def _metadata_plugin(context, callback):
callback([(_PER_RPC_CREDENTIALS_METADATA_KEY,
_PER_RPC_CREDENTIALS_METADATA_VALUE)], None)
class BetaFeaturesTest(unittest.TestCase): class BetaFeaturesTest(unittest.TestCase):
def setUp(self): def setUp(self):
@ -167,10 +179,12 @@ class BetaFeaturesTest(unittest.TestCase):
[(resources.private_key(), resources.certificate_chain(),),]) [(resources.private_key(), resources.certificate_chain(),),])
port = self._server.add_secure_port('[::]:0', server_credentials) port = self._server.add_secure_port('[::]:0', server_credentials)
self._server.start() self._server.start()
self._client_credentials = implementations.ssl_client_credentials( self._channel_credentials = implementations.ssl_channel_credentials(
resources.test_root_certificates(), None, None) resources.test_root_certificates(), None, None)
self._call_credentials = implementations.metadata_call_credentials(
_metadata_plugin)
channel = test_utilities.not_really_secure_channel( channel = test_utilities.not_really_secure_channel(
'localhost', port, self._client_credentials, _SERVER_HOST_OVERRIDE) 'localhost', port, self._channel_credentials, _SERVER_HOST_OVERRIDE)
stub_options = implementations.stub_options( stub_options = implementations.stub_options(
thread_pool_size=test_constants.POOL_SIZE) thread_pool_size=test_constants.POOL_SIZE)
self._dynamic_stub = implementations.dynamic_stub( self._dynamic_stub = implementations.dynamic_stub(
@ -181,21 +195,36 @@ class BetaFeaturesTest(unittest.TestCase):
self._server.stop(test_constants.SHORT_TIMEOUT).wait() self._server.stop(test_constants.SHORT_TIMEOUT).wait()
def test_unary_unary(self): def test_unary_unary(self):
call_options = interfaces.grpc_call_options(disable_compression=True) call_options = interfaces.grpc_call_options(
disable_compression=True, credentials=self._call_credentials)
response = getattr(self._dynamic_stub, _UNARY_UNARY)( response = getattr(self._dynamic_stub, _UNARY_UNARY)(
_REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options) _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options)
self.assertEqual(_RESPONSE, response) self.assertEqual(_RESPONSE, response)
self.assertIsNotNone(self._servicer.peer()) self.assertIsNotNone(self._servicer.peer())
invocation_metadata = [(metadatum.key, metadatum.value) for metadatum in
self._servicer._invocation_metadata]
self.assertIn(
(_PER_RPC_CREDENTIALS_METADATA_KEY,
_PER_RPC_CREDENTIALS_METADATA_VALUE),
invocation_metadata)
def test_unary_stream(self): def test_unary_stream(self):
call_options = interfaces.grpc_call_options(disable_compression=True) call_options = interfaces.grpc_call_options(
disable_compression=True, credentials=self._call_credentials)
response_iterator = getattr(self._dynamic_stub, _UNARY_STREAM)( response_iterator = getattr(self._dynamic_stub, _UNARY_STREAM)(
_REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options) _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options)
self._servicer.block_until_serviced() self._servicer.block_until_serviced()
self.assertIsNotNone(self._servicer.peer()) self.assertIsNotNone(self._servicer.peer())
invocation_metadata = [(metadatum.key, metadatum.value) for metadatum in
self._servicer._invocation_metadata]
self.assertIn(
(_PER_RPC_CREDENTIALS_METADATA_KEY,
_PER_RPC_CREDENTIALS_METADATA_VALUE),
invocation_metadata)
def test_stream_unary(self): def test_stream_unary(self):
call_options = interfaces.grpc_call_options() call_options = interfaces.grpc_call_options(
credentials=self._call_credentials)
request_iterator = _BlockingIterator(iter((_REQUEST,))) request_iterator = _BlockingIterator(iter((_REQUEST,)))
response_future = getattr(self._dynamic_stub, _STREAM_UNARY).future( response_future = getattr(self._dynamic_stub, _STREAM_UNARY).future(
request_iterator, test_constants.LONG_TIMEOUT, request_iterator, test_constants.LONG_TIMEOUT,
@ -207,9 +236,16 @@ class BetaFeaturesTest(unittest.TestCase):
self._servicer.block_until_serviced() self._servicer.block_until_serviced()
self.assertIsNotNone(self._servicer.peer()) self.assertIsNotNone(self._servicer.peer())
self.assertEqual(_RESPONSE, response_future.result()) self.assertEqual(_RESPONSE, response_future.result())
invocation_metadata = [(metadatum.key, metadatum.value) for metadatum in
self._servicer._invocation_metadata]
self.assertIn(
(_PER_RPC_CREDENTIALS_METADATA_KEY,
_PER_RPC_CREDENTIALS_METADATA_VALUE),
invocation_metadata)
def test_stream_stream(self): def test_stream_stream(self):
call_options = interfaces.grpc_call_options() call_options = interfaces.grpc_call_options(
credentials=self._call_credentials)
request_iterator = _BlockingIterator(iter((_REQUEST,))) request_iterator = _BlockingIterator(iter((_REQUEST,)))
response_iterator = getattr(self._dynamic_stub, _STREAM_STREAM)( response_iterator = getattr(self._dynamic_stub, _STREAM_STREAM)(
request_iterator, test_constants.SHORT_TIMEOUT, request_iterator, test_constants.SHORT_TIMEOUT,
@ -222,6 +258,12 @@ class BetaFeaturesTest(unittest.TestCase):
self._servicer.block_until_serviced() self._servicer.block_until_serviced()
self.assertIsNotNone(self._servicer.peer()) self.assertIsNotNone(self._servicer.peer())
self.assertEqual(_RESPONSE, response) self.assertEqual(_RESPONSE, response)
invocation_metadata = [(metadatum.key, metadatum.value) for metadatum in
self._servicer._invocation_metadata]
self.assertIn(
(_PER_RPC_CREDENTIALS_METADATA_KEY,
_PER_RPC_CREDENTIALS_METADATA_VALUE),
invocation_metadata)
class ContextManagementAndLifecycleTest(unittest.TestCase): class ContextManagementAndLifecycleTest(unittest.TestCase):
@ -250,7 +292,7 @@ class ContextManagementAndLifecycleTest(unittest.TestCase):
thread_pool_size=test_constants.POOL_SIZE) thread_pool_size=test_constants.POOL_SIZE)
self._server_credentials = implementations.ssl_server_credentials( self._server_credentials = implementations.ssl_server_credentials(
[(resources.private_key(), resources.certificate_chain(),),]) [(resources.private_key(), resources.certificate_chain(),),])
self._client_credentials = implementations.ssl_client_credentials( self._channel_credentials = implementations.ssl_channel_credentials(
resources.test_root_certificates(), None, None) resources.test_root_certificates(), None, None)
self._stub_options = implementations.stub_options( self._stub_options = implementations.stub_options(
thread_pool_size=test_constants.POOL_SIZE) thread_pool_size=test_constants.POOL_SIZE)
@ -262,7 +304,7 @@ class ContextManagementAndLifecycleTest(unittest.TestCase):
server.start() server.start()
channel = test_utilities.not_really_secure_channel( channel = test_utilities.not_really_secure_channel(
'localhost', port, self._client_credentials, _SERVER_HOST_OVERRIDE) 'localhost', port, self._channel_credentials, _SERVER_HOST_OVERRIDE)
dynamic_stub = implementations.dynamic_stub( dynamic_stub = implementations.dynamic_stub(
channel, _GROUP, self._cardinalities, options=self._stub_options) channel, _GROUP, self._cardinalities, options=self._stub_options)
for _ in range(100): for _ in range(100):

@ -91,10 +91,10 @@ class _Implementation(test_interfaces.Implementation):
[(resources.private_key(), resources.certificate_chain(),),]) [(resources.private_key(), resources.certificate_chain(),),])
port = server.add_secure_port('[::]:0', server_credentials) port = server.add_secure_port('[::]:0', server_credentials)
server.start() server.start()
client_credentials = implementations.ssl_client_credentials( channel_credentials = implementations.ssl_channel_credentials(
resources.test_root_certificates(), None, None) resources.test_root_certificates(), None, None)
channel = test_utilities.not_really_secure_channel( channel = test_utilities.not_really_secure_channel(
'localhost', port, client_credentials, _SERVER_HOST_OVERRIDE) 'localhost', port, channel_credentials, _SERVER_HOST_OVERRIDE)
stub_options = implementations.stub_options( stub_options = implementations.stub_options(
request_serializers=serialization_behaviors.request_serializers, request_serializers=serialization_behaviors.request_serializers,
response_deserializers=serialization_behaviors.response_deserializers, response_deserializers=serialization_behaviors.response_deserializers,

@ -34,13 +34,13 @@ from grpc.beta import implementations
def not_really_secure_channel( def not_really_secure_channel(
host, port, client_credentials, server_host_override): host, port, channel_credentials, server_host_override):
"""Creates an insecure Channel to a remote host. """Creates an insecure Channel to a remote host.
Args: Args:
host: The name of the remote host to which to connect. host: The name of the remote host to which to connect.
port: The port of the remote host to which to connect. port: The port of the remote host to which to connect.
client_credentials: The implementations.ClientCredentials with which to channel_credentials: The implementations.ChannelCredentials with which to
connect. connect.
server_host_override: The target name used for SSL host name checking. server_host_override: The target name used for SSL host name checking.
@ -50,7 +50,7 @@ def not_really_secure_channel(
""" """
hostport = '%s:%d' % (host, port) hostport = '%s:%d' % (host, port)
intermediary_low_channel = _intermediary_low.Channel( intermediary_low_channel = _intermediary_low.Channel(
hostport, client_credentials._intermediary_low_credentials, hostport, channel_credentials._low_credentials,
server_host_override=server_host_override) server_host_override=server_host_override)
return implementations.Channel( return implementations.Channel(
intermediary_low_channel._internal, intermediary_low_channel) intermediary_low_channel._internal, intermediary_low_channel)

@ -1,80 +0,0 @@
// 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.
syntax = "proto3";
package math;
message DivArgs {
int64 dividend = 1;
int64 divisor = 2;
}
message DivReply {
int64 quotient = 1;
int64 remainder = 2;
}
message FibArgs {
int64 limit = 1;
}
message Num {
int64 num = 1;
}
message FibReply {
int64 count = 1;
}
service Math {
// Div divides args.dividend by args.divisor and returns the quotient and
// remainder.
rpc Div (DivArgs) returns (DivReply) {
}
// DivMany accepts an arbitrary number of division args from the client stream
// and sends back the results in the reply stream. The stream continues until
// the client closes its end; the server does the same after sending all the
// replies. The stream ends immediately if either end aborts.
rpc DivMany (stream DivArgs) returns (stream DivReply) {
}
// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
// generates up to limit numbers; otherwise it continues until the call is
// canceled. Unlike Fib above, Fib has no final FibReply.
rpc Fib (FibArgs) returns (stream Num) {
}
// Sum sums a stream of numbers, returning the final result once the stream
// is closed.
rpc Sum (stream Num) returns (Num) {
}
}

@ -1,32 +1,3 @@
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT! # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: math.proto # source: math.proto

@ -1,32 +1,3 @@
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT! # Generated by the protocol buffer compiler. DO NOT EDIT!
# Source: math.proto for package 'math' # Source: math.proto for package 'math'

@ -20,7 +20,7 @@ re-generate the surface.
```bash ```bash
$ # (from this directory) $ # (from this directory)
$ protoc -I . grpc/health/v1alpha/health.proto \ $ protoc -I ../../proto ../../proto/grpc/health/v1alpha/health.proto \
--grpc_out=. \ --grpc_out=. \
--ruby_out=. \ --ruby_out=. \
--plugin=protoc-gen-grpc=`which grpc_ruby_plugin` --plugin=protoc-gen-grpc=`which grpc_ruby_plugin`

@ -1,3 +1,4 @@
#!/bin/sh
# Copyright 2015, Google Inc. # Copyright 2015, Google Inc.
# All rights reserved. # All rights reserved.
# #
@ -27,76 +28,24 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Regenerates gRPC service stubs from proto files.
set +e
cd $(dirname $0)/../../..
# Adapter from grpc._cython.types to the surface expected by PROTOC=bins/opt/protobuf/protoc
# grpc._adapter._intermediary_low. PLUGIN=protoc-gen-grpc=bins/opt/grpc_ruby_plugin
#
# TODO(atash): Once this is plugged into grpc._adapter._intermediary_low, remove
# both grpc._adapter._intermediary_low and this file. The fore and rear links in
# grpc._adapter should be able to use grpc._cython.types directly.
from grpc._adapter import _types as type_interfaces
from grpc._cython import cygrpc
class ClientCredentials(object):
def __init__(self):
raise NotImplementedError()
@staticmethod
def google_default():
raise NotImplementedError()
@staticmethod
def ssl():
raise NotImplementedError()
@staticmethod
def composite():
raise NotImplementedError()
@staticmethod
def compute_engine():
raise NotImplementedError()
@staticmethod
def jwt():
raise NotImplementedError()
@staticmethod
def refresh_token():
raise NotImplementedError()
@staticmethod
def iam():
raise NotImplementedError()
class ServerCredentials(object):
def __init__(self):
raise NotImplementedError()
@staticmethod
def ssl():
raise NotImplementedError()
class CompletionQueue(type_interfaces.CompletionQueue):
def __init__(self):
raise NotImplementedError()
class Call(type_interfaces.Call):
def __init__(self):
raise NotImplementedError()
class Channel(type_interfaces.Channel):
def __init__(self):
raise NotImplementedError()
$PROTOC -I src/proto src/proto/grpc/health/v1alpha/health.proto \
--grpc_out=src/ruby/pb \
--ruby_out=src/ruby/pb \
--plugin=$PLUGIN
class Server(type_interfaces.Server): $PROTOC -I . test/proto/{messages,test,empty}.proto \
def __init__(self): --grpc_out=src/ruby/pb \
raise NotImplementedError() --ruby_out=src/ruby/pb \
--plugin=$PLUGIN
$PROTOC -I src/proto/math src/proto/math/math.proto \
--grpc_out=src/ruby/bin \
--ruby_out=src/ruby/bin \
--plugin=$PLUGIN

@ -6,7 +6,7 @@ require 'google/protobuf'
Google::Protobuf::DescriptorPool.generated_pool.build do Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "grpc.testing.Payload" do add_message "grpc.testing.Payload" do
optional :type, :enum, 1, "grpc.testing.PayloadType" optional :type, :enum, 1, "grpc.testing.PayloadType"
optional :body, :string, 2 optional :body, :bytes, 2
end end
add_message "grpc.testing.EchoStatus" do add_message "grpc.testing.EchoStatus" do
optional :code, :int32, 1 optional :code, :int32, 1

@ -41,8 +41,9 @@ default_test_options = TestOptions(False)
# maps test names to options # maps test names to options
BAD_CLIENT_TESTS = { BAD_CLIENT_TESTS = {
'connection_prefix': default_test_options, 'connection_prefix': default_test_options,
'initial_settings_frame': default_test_options,
'headers': default_test_options, 'headers': default_test_options,
'initial_settings_frame': default_test_options,
'unknown_frame': default_test_options,
} }
def main(): def main():

@ -94,6 +94,22 @@ int main(int argc, char **argv) {
"\x00\x00\x04\x01\x24\x00\x00\x00\x01" "\x00\x00\x04\x01\x24\x00\x00\x00\x01"
"\x00\x00\x00\x00", "\x00\x00\x00\x00",
0); 0);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x05\x01\x24\x00\x00\x00\x01"
"\x00",
GRPC_BAD_CLIENT_DISCONNECT);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x05\x01\x24\x00\x00\x00\x01"
"\x00\x00",
GRPC_BAD_CLIENT_DISCONNECT);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x05\x01\x24\x00\x00\x00\x01"
"\x00\x00\x00",
GRPC_BAD_CLIENT_DISCONNECT);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x05\x01\x24\x00\x00\x00\x01"
"\x00\x00\x00\x00",
GRPC_BAD_CLIENT_DISCONNECT);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x05\x01\x24\x00\x00\x00\x01" "\x00\x00\x05\x01\x24\x00\x00\x00\x01"
"\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00",
@ -106,24 +122,24 @@ int main(int argc, char **argv) {
0); 0);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x04\x01\x04\x00\x00\x00\x01" "\x00\x00\x04\x01\x04\x00\x00\x00\x01"
"\x7f\x7f\x01a", "\x7f\x7f\x01""a",
0); 0);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x04\x01\x04\x00\x00\x00\x01" "\x00\x00\x04\x01\x04\x00\x00\x00\x01"
"\x0f\x7f\x01a", "\x0f\x7f\x01""a",
0); 0);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x04\x01\x04\x00\x00\x00\x01" "\x00\x00\x04\x01\x04\x00\x00\x00\x01"
"\x1f\x7f\x01a", "\x1f\x7f\x01""a",
0); 0);
/* test nvr, not indexed in static table */ /* test nvr, not indexed in static table */
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x03\x01\x04\x00\x00\x00\x01" "\x00\x00\x03\x01\x04\x00\x00\x00\x01"
"\x01\x01a", "\x01\x01""a",
GRPC_BAD_CLIENT_DISCONNECT); GRPC_BAD_CLIENT_DISCONNECT);
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x03\x01\x04\x00\x00\x00\x01" "\x00\x00\x03\x01\x04\x00\x00\x00\x01"
"\x11\x01a", "\x11\x01""a",
GRPC_BAD_CLIENT_DISCONNECT); GRPC_BAD_CLIENT_DISCONNECT);
/* illegal op code */ /* illegal op code */
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
@ -195,5 +211,11 @@ int main(int argc, char **argv) {
"\x00\x00\x00\x09\x04\x00\x00\x00\x01", "\x00\x00\x00\x09\x04\x00\x00\x00\x01",
0); 0);
/* an invalid header found with fuzzing */
GRPC_RUN_BAD_CLIENT_TEST(verifier,
PFX_STR
"\x00\x00\x00\x01\x39\x67\xed\x1d\x64",
GRPC_BAD_CLIENT_DISCONNECT);
return 0; return 0;
} }

@ -31,31 +31,28 @@
* *
*/ */
#include "grpc/_adapter/_c/types.h" #include "test/core/bad_client/bad_client.h"
#include "src/core/surface/server.h"
#define PY_SSIZE_T_CLEAN #define PFX_STR \
#include <Python.h> "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" \
#include <grpc/grpc.h> "\x00\x00\x00\x04\x00\x00\x00\x00\x00"
int pygrpc_module_add_types(PyObject *module) { static void verifier(grpc_server *server, grpc_completion_queue *cq) {
int i; while (grpc_server_has_open_connections(server)) {
PyTypeObject *types[] = { GPR_ASSERT(grpc_completion_queue_next(
&pygrpc_CallCredentials_type, cq, GRPC_TIMEOUT_MILLIS_TO_DEADLINE(20), NULL)
&pygrpc_ChannelCredentials_type, .type == GRPC_QUEUE_TIMEOUT);
&pygrpc_ServerCredentials_type,
&pygrpc_CompletionQueue_type,
&pygrpc_Call_type,
&pygrpc_Channel_type,
&pygrpc_Server_type
};
for (i = 0; i < sizeof(types)/sizeof(PyTypeObject *); ++i) {
if (PyType_Ready(types[i]) < 0) {
return -1;
}
}
for (i = 0; i < sizeof(types)/sizeof(PyTypeObject *); ++i) {
Py_INCREF(types[i]);
PyModule_AddObject(module, types[i]->tp_name, (PyObject *)types[i]);
} }
}
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
/* test adding prioritization data */
GRPC_RUN_BAD_CLIENT_TEST(verifier, PFX_STR
"\x00\x00\x00\x88\x00\x00\x00\x00\x01",
GRPC_BAD_CLIENT_DISCONNECT);
return 0; return 0;
} }

@ -94,12 +94,27 @@ static void do_nothing_with_len_1(void *ignored, size_t len) {
static void test_slice_new_with_len_returns_something_sensible(void) { static void test_slice_new_with_len_returns_something_sensible(void) {
gpr_uint8 x; gpr_uint8 x;
int num_refs = 5; /* To test adding/removing an arbitrary number of refs */
int i;
gpr_slice slice = gpr_slice_new_with_len(&x, 1, do_nothing_with_len_1); gpr_slice slice = gpr_slice_new_with_len(&x, 1, do_nothing_with_len_1);
GPR_ASSERT(slice.refcount); GPR_ASSERT(slice.refcount); /* ref count is initialized to 1 at this point */
GPR_ASSERT(slice.data.refcounted.bytes == &x); GPR_ASSERT(slice.data.refcounted.bytes == &x);
GPR_ASSERT(slice.data.refcounted.length == 1); GPR_ASSERT(slice.data.refcounted.length == 1);
GPR_ASSERT(do_nothing_with_len_1_calls == 0); GPR_ASSERT(do_nothing_with_len_1_calls == 0);
/* Add an arbitrary number of refs to the slice and remoe the refs. This is to
make sure that that the destroy callback (i.e do_nothing_with_len_1()) is
not called until the last unref operation */
for (i = 0; i < num_refs; i++) {
gpr_slice_ref(slice);
}
for (i = 0; i < num_refs; i++) {
gpr_slice_unref(slice);
}
GPR_ASSERT(do_nothing_with_len_1_calls == 0); /* Shouldn't be called yet */
/* last unref */
gpr_slice_unref(slice); gpr_slice_unref(slice);
GPR_ASSERT(do_nothing_with_len_1_calls == 1); GPR_ASSERT(do_nothing_with_len_1_calls == 1);
} }

@ -153,6 +153,7 @@ struct test {
gpr_int64 counter; gpr_int64 counter;
int thread_count; /* used to allocate thread ids */ int thread_count; /* used to allocate thread ids */
int done; /* threads not yet completed */ int done; /* threads not yet completed */
int incr_step; /* how much to increment/decrement refcount each time */
gpr_mu mu; /* protects iterations, counter, thread_count, done */ gpr_mu mu; /* protects iterations, counter, thread_count, done */
@ -170,13 +171,14 @@ struct test {
}; };
/* Return pointer to a new struct test. */ /* Return pointer to a new struct test. */
static struct test *test_new(int threads, gpr_int64 iterations) { static struct test *test_new(int threads, gpr_int64 iterations, int incr_step) {
struct test *m = gpr_malloc(sizeof(*m)); struct test *m = gpr_malloc(sizeof(*m));
m->threads = threads; m->threads = threads;
m->iterations = iterations; m->iterations = iterations;
m->counter = 0; m->counter = 0;
m->thread_count = 0; m->thread_count = 0;
m->done = threads; m->done = threads;
m->incr_step = incr_step;
gpr_mu_init(&m->mu); gpr_mu_init(&m->mu);
gpr_cv_init(&m->cv); gpr_cv_init(&m->cv);
gpr_cv_init(&m->done_cv); gpr_cv_init(&m->done_cv);
@ -238,9 +240,12 @@ static void mark_thread_done(struct test *m) {
/* Test several threads running (*body)(struct test *m) for increasing settings /* Test several threads running (*body)(struct test *m) for increasing settings
of m->iterations, until about timeout_s to 2*timeout_s seconds have elapsed. of m->iterations, until about timeout_s to 2*timeout_s seconds have elapsed.
If extra!=NULL, run (*extra)(m) in an additional thread. */ If extra!=NULL, run (*extra)(m) in an additional thread.
incr_step controls by how much m->refcount should be incremented/decremented
(if at all) each time in the tests.
*/
static void test(const char *name, void (*body)(void *m), static void test(const char *name, void (*body)(void *m),
void (*extra)(void *m), int timeout_s) { void (*extra)(void *m), int timeout_s, int incr_step) {
gpr_int64 iterations = 1024; gpr_int64 iterations = 1024;
struct test *m; struct test *m;
gpr_timespec start = gpr_now(GPR_CLOCK_REALTIME); gpr_timespec start = gpr_now(GPR_CLOCK_REALTIME);
@ -251,7 +256,7 @@ static void test(const char *name, void (*body)(void *m),
while (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0) { while (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0) {
iterations <<= 1; iterations <<= 1;
fprintf(stderr, " %ld", (long)iterations); fprintf(stderr, " %ld", (long)iterations);
m = test_new(10, iterations); m = test_new(10, iterations, incr_step);
if (extra != NULL) { if (extra != NULL) {
gpr_thd_id id; gpr_thd_id id;
GPR_ASSERT(gpr_thd_new(&id, extra, m, NULL)); GPR_ASSERT(gpr_thd_new(&id, extra, m, NULL));
@ -259,7 +264,7 @@ static void test(const char *name, void (*body)(void *m),
} }
test_create_threads(m, body); test_create_threads(m, body);
test_wait(m); test_wait(m);
if (m->counter != m->threads * m->iterations) { if (m->counter != m->threads * m->iterations * m->incr_step) {
fprintf(stderr, "counter %ld threads %d iterations %ld\n", fprintf(stderr, "counter %ld threads %d iterations %ld\n",
(long)m->counter, m->threads, (long)m->iterations); (long)m->counter, m->threads, (long)m->iterations);
GPR_ASSERT(0); GPR_ASSERT(0);
@ -406,14 +411,18 @@ static void statsinc(void *v /*=m*/) {
mark_thread_done(m); mark_thread_done(m);
} }
/* Increment m->refcount m->iterations times, decrement m->thread_refcount /* Increment m->refcount by m->incr_step for m->iterations times. Decrement
once, and if it reaches zero, set m->event to (void*)1; then mark thread as m->thread_refcount once, and if it reaches zero, set m->event to (void*)1;
done. */ then mark thread as done. */
static void refinc(void *v /*=m*/) { static void refinc(void *v /*=m*/) {
struct test *m = v; struct test *m = v;
gpr_int64 i; gpr_int64 i;
for (i = 0; i != m->iterations; i++) { for (i = 0; i != m->iterations; i++) {
if (m->incr_step == 1) {
gpr_ref(&m->refcount); gpr_ref(&m->refcount);
} else {
gpr_refn(&m->refcount, m->incr_step);
}
} }
if (gpr_unref(&m->thread_refcount)) { if (gpr_unref(&m->thread_refcount)) {
gpr_event_set(&m->event, (void *)1); gpr_event_set(&m->event, (void *)1);
@ -421,12 +430,13 @@ static void refinc(void *v /*=m*/) {
mark_thread_done(m); mark_thread_done(m);
} }
/* Wait until m->event is set to (void *)1, then decrement m->refcount
m->stats_counter m->iterations times, and ensure that the last decrement /* Wait until m->event is set to (void *)1, then decrement m->refcount by 1
caused the counter to reach zero, then mark thread as done. */ (m->threads * m->iterations * m->incr_step) times, and ensure that the last
decrement caused the counter to reach zero, then mark thread as done. */
static void refcheck(void *v /*=m*/) { static void refcheck(void *v /*=m*/) {
struct test *m = v; struct test *m = v;
gpr_int64 n = m->iterations * m->threads; gpr_int64 n = m->iterations * m->threads * m->incr_step;
gpr_int64 i; gpr_int64 i;
GPR_ASSERT(gpr_event_wait(&m->event, gpr_inf_future(GPR_CLOCK_REALTIME)) == GPR_ASSERT(gpr_event_wait(&m->event, gpr_inf_future(GPR_CLOCK_REALTIME)) ==
(void *)1); (void *)1);
@ -444,13 +454,16 @@ static void refcheck(void *v /*=m*/) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
grpc_test_init(argc, argv); grpc_test_init(argc, argv);
test("mutex", &inc, NULL, 1); test("mutex", &inc, NULL, 1, 1);
test("mutex try", &inctry, NULL, 1); test("mutex try", &inctry, NULL, 1, 1);
test("cv", &inc_by_turns, NULL, 1); test("cv", &inc_by_turns, NULL, 1, 1);
test("timedcv", &inc_with_1ms_delay, NULL, 1); test("timedcv", &inc_with_1ms_delay, NULL, 1, 1);
test("queue", &many_producers, &consumer, 10); test("queue", &many_producers, &consumer, 10, 1);
test("stats_counter", &statsinc, NULL, 1); test("stats_counter", &statsinc, NULL, 1, 1);
test("refcount", &refinc, &refcheck, 1); test("refcount by 1", &refinc, &refcheck, 1, 1);
test("timedevent", &inc_with_1ms_delay_event, NULL, 1); test("refcount by 3", &refinc, &refcheck, 1, 3); /* incr_step of 3 is an
arbitrary choice. Any
number > 1 is okay here */
test("timedevent", &inc_with_1ms_delay_event, NULL, 1, 1);
return 0; return 0;
} }

@ -175,6 +175,19 @@ static void test_pluck(void) {
grpc_exec_ctx_finish(&exec_ctx); grpc_exec_ctx_finish(&exec_ctx);
} }
static void test_pluck_after_shutdown(void) {
grpc_event ev;
grpc_completion_queue *cc;
LOG_TEST("test_pluck_after_shutdown");
cc = grpc_completion_queue_create(NULL);
grpc_completion_queue_shutdown(cc);
ev = grpc_completion_queue_pluck(cc, NULL, gpr_inf_future(GPR_CLOCK_REALTIME),
NULL);
GPR_ASSERT(ev.type == GRPC_QUEUE_SHUTDOWN);
grpc_completion_queue_destroy(cc);
}
#define TEST_THREAD_EVENTS 10000 #define TEST_THREAD_EVENTS 10000
typedef struct test_thread_options { typedef struct test_thread_options {
@ -343,6 +356,7 @@ int main(int argc, char **argv) {
test_shutdown_then_next_with_timeout(); test_shutdown_then_next_with_timeout();
test_cq_end_op(); test_cq_end_op();
test_pluck(); test_pluck();
test_pluck_after_shutdown();
test_threading(1, 1); test_threading(1, 1);
test_threading(1, 10); test_threading(1, 10);
test_threading(10, 1); test_threading(10, 1);

@ -47,7 +47,7 @@ static void test(int rounds) {
} }
} }
static void test_mixed() { static void test_mixed(void) {
grpc_init(); grpc_init();
grpc_init(); grpc_init();
grpc_shutdown(); grpc_shutdown();
@ -56,8 +56,8 @@ static void test_mixed() {
grpc_shutdown(); grpc_shutdown();
} }
static void plugin_init() { g_flag = 1; } static void plugin_init(void) { g_flag = 1; }
static void plugin_destroy() { g_flag = 2; } static void plugin_destroy(void) { g_flag = 2; }
static void test_plugin() { static void test_plugin() {
grpc_register_plugin(plugin_init, plugin_destroy); grpc_register_plugin(plugin_init, plugin_destroy);

@ -14313,6 +14313,21 @@
"test/core/bad_client/tests/initial_settings_frame.c" "test/core/bad_client/tests/initial_settings_frame.c"
] ]
}, },
{
"deps": [
"bad_client_test",
"gpr",
"gpr_test_util",
"grpc_test_util_unsecure",
"grpc_unsecure"
],
"headers": [],
"language": "c",
"name": "unknown_frame_bad_client_test",
"src": [
"test/core/bad_client/tests/unknown_frame.c"
]
},
{ {
"deps": [], "deps": [],
"headers": [ "headers": [

@ -15554,5 +15554,23 @@
"posix", "posix",
"windows" "windows"
] ]
},
{
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"exclude_configs": [],
"flaky": false,
"language": "c",
"name": "unknown_frame_bad_client_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
]
} }
] ]

@ -10047,6 +10047,18 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "initial_settings_frame_bad_
{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unknown_frame_bad_client_test", "vcxproj\test\unknown_frame_bad_client_test\unknown_frame_bad_client_test.vcxproj", "{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}"
ProjectSection(myProperties) = preProject
lib = "False"
EndProjectSection
ProjectSection(ProjectDependencies) = postProject
{BA67B418-B699-E41A-9CC4-0279C49481A5} = {BA67B418-B699-E41A-9CC4-0279C49481A5}
{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF} = {0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}
{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5} = {46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}
{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32 Debug|Win32 = Debug|Win32
@ -22411,6 +22423,22 @@ Global
{6756895E-05BF-8CC7-58F2-868DF0C0300C}.Release-DLL|Win32.Build.0 = Release|Win32 {6756895E-05BF-8CC7-58F2-868DF0C0300C}.Release-DLL|Win32.Build.0 = Release|Win32
{6756895E-05BF-8CC7-58F2-868DF0C0300C}.Release-DLL|x64.ActiveCfg = Release|x64 {6756895E-05BF-8CC7-58F2-868DF0C0300C}.Release-DLL|x64.ActiveCfg = Release|x64
{6756895E-05BF-8CC7-58F2-868DF0C0300C}.Release-DLL|x64.Build.0 = Release|x64 {6756895E-05BF-8CC7-58F2-868DF0C0300C}.Release-DLL|x64.Build.0 = Release|x64
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Debug|Win32.ActiveCfg = Debug|Win32
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Debug|x64.ActiveCfg = Debug|x64
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Release|Win32.ActiveCfg = Release|Win32
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Release|x64.ActiveCfg = Release|x64
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Debug|Win32.Build.0 = Debug|Win32
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Debug|x64.Build.0 = Debug|x64
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Release|Win32.Build.0 = Release|Win32
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Release|x64.Build.0 = Release|x64
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Debug-DLL|Win32.Build.0 = Debug|Win32
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Debug-DLL|x64.ActiveCfg = Debug|x64
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Debug-DLL|x64.Build.0 = Debug|x64
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Release-DLL|Win32.ActiveCfg = Release|Win32
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Release-DLL|Win32.Build.0 = Release|Win32
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Release-DLL|x64.ActiveCfg = Release|x64
{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}.Release-DLL|x64.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9E0A2239-20D5-DB2D-CA0D-69F24E3416E7}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
<PlatformToolset>v100</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
<PlatformToolset>v110</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\..\vsprojects\global.props" />
<Import Project="..\..\..\..\vsprojects\openssl.props" />
<Import Project="..\..\..\..\vsprojects\winsock.props" />
<Import Project="..\..\..\..\vsprojects\zlib.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<TargetName>unknown_frame_bad_client_test</TargetName>
<Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
<Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
<Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<TargetName>unknown_frame_bad_client_test</TargetName>
<Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
<Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
<Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\..\..\test\core\bad_client\tests\unknown_frame.c">
</ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\vsprojects\vcxproj\test\bad_client_test\bad_client_test.vcxproj">
<Project>{BA67B418-B699-E41A-9CC4-0279C49481A5}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\vsprojects\vcxproj\.\grpc_test_util_unsecure\grpc_test_util_unsecure.vcxproj">
<Project>{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\vsprojects\vcxproj\.\grpc_unsecure\grpc_unsecure.vcxproj">
<Project>{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
<Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
<Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
<Import Project="..\..\..\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
<Import Project="..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
<Import Project="..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
<Error Condition="!Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
<Error Condition="!Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
<Error Condition="!Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
<Error Condition="!Exists('..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
</Target>
</Project>

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="..\..\..\..\test\core\bad_client\tests\unknown_frame.c">
<Filter>test\core\bad_client\tests</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="test">
<UniqueIdentifier>{77ca54f4-deb2-3a34-5548-0a2bc7cbacc1}</UniqueIdentifier>
</Filter>
<Filter Include="test\core">
<UniqueIdentifier>{2e30ac00-932d-ab0d-fabf-e0ea035aae46}</UniqueIdentifier>
</Filter>
<Filter Include="test\core\bad_client">
<UniqueIdentifier>{527abde4-b314-c50f-b007-dd3fdb2445ba}</UniqueIdentifier>
</Filter>
<Filter Include="test\core\bad_client\tests">
<UniqueIdentifier>{691de782-c331-3836-ac26-43873334f0c0}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
Loading…
Cancel
Save