Merge branch 'master' of github.com:grpc/grpc into flexible_default_creds

pull/2323/head
Julien Boeuf 10 years ago
commit 0b1b1a5999
  1. 4
      .gitignore
  2. 3
      .travis.yml
  3. 12
      BUILD
  4. 1035
      Makefile
  5. 51
      build.json
  6. 49
      doc/interop-test-descriptions.md
  7. 3
      gRPC.podspec
  8. 62
      include/grpc++/auth_context.h
  9. 10
      include/grpc++/client_context.h
  10. 6
      include/grpc++/credentials.h
  11. 4
      include/grpc++/server.h
  12. 33
      include/grpc++/server_builder.h
  13. 9
      include/grpc++/server_context.h
  14. 4
      include/grpc/census.h
  15. 17
      include/grpc/grpc_security.h
  16. 28
      src/core/census/grpc_context.c
  17. 19
      src/core/census/grpc_context.h
  18. 2
      src/core/census/initialize.c
  19. 2
      src/core/channel/census_filter.c
  20. 2
      src/core/channel/client_channel.c
  21. 18
      src/core/channel/http_client_filter.c
  22. 30
      src/core/channel/http_server_filter.c
  23. 4
      src/core/httpcli/httpcli.h
  24. 5
      src/core/iomgr/iomgr.c
  25. 5
      src/core/iomgr/socket_windows.c
  26. 8
      src/core/iomgr/tcp_posix.c
  27. 2
      src/core/json/json.h
  28. 38
      src/core/security/client_auth_filter.c
  29. 50
      src/core/security/credentials.c
  30. 8
      src/core/security/credentials.h
  31. 830
      src/core/security/jwt_verifier.c
  32. 136
      src/core/security/jwt_verifier.h
  33. 8
      src/core/security/secure_endpoint.c
  34. 14
      src/core/security/security_context.c
  35. 8
      src/core/security/security_context.h
  36. 7
      src/core/support/slice.c
  37. 57
      src/core/support/string.c
  38. 15
      src/core/support/string.h
  39. 22
      src/core/surface/call.c
  40. 4
      src/core/surface/call_log_batch.c
  41. 34
      src/core/surface/channel.c
  42. 238
      src/core/surface/server.c
  43. 19
      src/core/transport/chttp2/hpack_parser.c
  44. 6
      src/core/transport/chttp2/hpack_table.c
  45. 2
      src/core/transport/chttp2/incoming_metadata.c
  46. 8
      src/core/transport/chttp2/internal.h
  47. 4
      src/core/transport/chttp2/parsing.c
  48. 26
      src/core/transport/chttp2/stream_encoder.c
  49. 9
      src/core/transport/chttp2/stream_lists.c
  50. 24
      src/core/transport/chttp2_transport.c
  51. 107
      src/core/transport/metadata.c
  52. 26
      src/core/transport/metadata.h
  53. 6
      src/core/transport/stream_op.c
  54. 2
      src/core/transport/transport.c
  55. 4
      src/core/transport/transport.h
  56. 10
      src/core/transport/transport_op_string.c
  57. 4
      src/cpp/client/channel.cc
  58. 8
      src/cpp/client/client_context.cc
  59. 7
      src/cpp/client/secure_credentials.cc
  60. 42
      src/cpp/common/create_auth_context.h
  61. 45
      src/cpp/common/insecure_create_auth_context.cc
  62. 80
      src/cpp/common/secure_auth_context.cc
  63. 62
      src/cpp/common/secure_auth_context.h
  64. 50
      src/cpp/common/secure_create_auth_context.cc
  65. 13
      src/cpp/server/server.cc
  66. 18
      src/cpp/server/server_builder.cc
  67. 7
      src/cpp/server/server_context.cc
  68. 57
      src/node/binding.gyp
  69. 2
      src/node/package.json
  70. 2
      src/objective-c/GRPCClient/private/GRPCChannel.m
  71. 1
      src/objective-c/tests/Podfile
  72. 1
      src/python/requirements.txt
  73. 1
      src/python/src/.gitignore
  74. 5
      src/python/src/grpc/_adapter/.gitignore
  75. 7
      src/python/src/grpc/_cython/.gitignore
  76. 52
      src/python/src/grpc/_cython/README.rst
  77. 28
      src/python/src/grpc/_cython/__init__.py
  78. 28
      src/python/src/grpc/_cython/_cygrpc/__init__.py
  79. 37
      src/python/src/grpc/_cython/_cygrpc/call.pxd
  80. 82
      src/python/src/grpc/_cython/_cygrpc/call.pyx
  81. 36
      src/python/src/grpc/_cython/_cygrpc/channel.pxd
  82. 84
      src/python/src/grpc/_cython/_cygrpc/channel.pyx
  83. 39
      src/python/src/grpc/_cython/_cygrpc/completion_queue.pxd
  84. 117
      src/python/src/grpc/_cython/_cygrpc/completion_queue.pyx
  85. 45
      src/python/src/grpc/_cython/_cygrpc/credentials.pxd
  86. 217
      src/python/src/grpc/_cython/_cygrpc/credentials.pyx
  87. 344
      src/python/src/grpc/_cython/_cygrpc/grpc.pxd
  88. 129
      src/python/src/grpc/_cython/_cygrpc/records.pxd
  89. 575
      src/python/src/grpc/_cython/_cygrpc/records.pyx
  90. 45
      src/python/src/grpc/_cython/_cygrpc/server.pxd
  91. 158
      src/python/src/grpc/_cython/_cygrpc/server.pyx
  92. 114
      src/python/src/grpc/_cython/adapter_low.py
  93. 187
      src/python/src/grpc/_cython/adapter_low_test.py
  94. 111
      src/python/src/grpc/_cython/cygrpc.pyx
  95. 276
      src/python/src/grpc/_cython/cygrpc_test.py
  96. 46
      src/python/src/grpc/_cython/test_utilities.py
  97. 70
      src/python/src/setup.py
  98. 75
      src/ruby/ext/grpc/extconf.rb
  99. 6
      src/ruby/ext/grpc/rb_server.c
  100. 2
      src/ruby/lib/grpc/version.rb
  101. Some files were not shown because too many files have changed in this diff Show More

4
.gitignore vendored

@ -4,8 +4,8 @@ gens
libs
objs
# Python virtual environment (pre-3.4 only)
python2.7_virtual_environment
# Python virtual environments
python*_virtual_environment
# gcov coverage data
coverage

@ -6,7 +6,8 @@ before_install:
- echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
- echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
- sudo apt-get update -qq
- sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv clang-3.5
- sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv python-dev python3-dev clang-3.5
- sudo pip install --upgrade virtualenv
- sudo pip install cpp-coveralls mako simplejson
- sudo apt-get install -qq mono-devel nunit
- wget www.nuget.org/NuGet.exe -O nuget.exe

12
BUILD

@ -138,6 +138,7 @@ cc_library(
"src/core/security/base64.h",
"src/core/security/credentials.h",
"src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h",
@ -254,6 +255,7 @@ cc_library(
"src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c",
@ -627,11 +629,15 @@ cc_library(
name = "grpc++",
srcs = [
"src/cpp/client/secure_credentials.h",
"src/cpp/common/secure_auth_context.h",
"src/cpp/server/secure_server_credentials.h",
"src/cpp/client/channel.h",
"src/cpp/common/create_auth_context.h",
"src/cpp/server/thread_pool.h",
"src/cpp/client/secure_channel_arguments.cc",
"src/cpp/client/secure_credentials.cc",
"src/cpp/common/secure_auth_context.cc",
"src/cpp/common/secure_create_auth_context.cc",
"src/cpp/server/secure_server_credentials.cc",
"src/cpp/client/channel.cc",
"src/cpp/client/channel_arguments.cc",
@ -661,6 +667,7 @@ cc_library(
hdrs = [
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",
@ -713,7 +720,9 @@ cc_library(
name = "grpc++_unsecure",
srcs = [
"src/cpp/client/channel.h",
"src/cpp/common/create_auth_context.h",
"src/cpp/server/thread_pool.h",
"src/cpp/common/insecure_create_auth_context.cc",
"src/cpp/client/channel.cc",
"src/cpp/client/channel_arguments.cc",
"src/cpp/client/client_context.cc",
@ -742,6 +751,7 @@ cc_library(
hdrs = [
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",
@ -946,6 +956,7 @@ objc_library(
"src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c",
@ -1083,6 +1094,7 @@ objc_library(
"src/core/security/base64.h",
"src/core/security/credentials.h",
"src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h",

1035
Makefile

File diff suppressed because one or more lines are too long

@ -30,6 +30,7 @@
"public_headers": [
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",
@ -68,6 +69,7 @@
],
"headers": [
"src/cpp/client/channel.h",
"src/cpp/common/create_auth_context.h",
"src/cpp/server/thread_pool.h"
],
"src": [
@ -451,6 +453,7 @@
"src/core/security/base64.h",
"src/core/security/credentials.h",
"src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h",
@ -473,6 +476,7 @@
"src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c",
@ -557,11 +561,14 @@
"language": "c++",
"headers": [
"src/cpp/client/secure_credentials.h",
"src/cpp/common/secure_auth_context.h",
"src/cpp/server/secure_server_credentials.h"
],
"src": [
"src/cpp/client/secure_channel_arguments.cc",
"src/cpp/client/secure_credentials.cc",
"src/cpp/common/secure_auth_context.cc",
"src/cpp/common/secure_create_auth_context.cc",
"src/cpp/server/secure_server_credentials.cc"
],
"deps": [
@ -614,6 +621,9 @@
"name": "grpc++_unsecure",
"build": "all",
"language": "c++",
"src": [
"src/cpp/common/insecure_create_auth_context.cc"
],
"deps": [
"gpr",
"grpc_unsecure"
@ -1356,6 +1366,20 @@
"gpr"
]
},
{
"name": "grpc_jwt_verifier_test",
"build": "test",
"language": "c",
"src": [
"test/core/security/jwt_verifier_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "grpc_print_google_default_creds_token",
"build": "tool",
@ -1398,6 +1422,20 @@
"gpr"
]
},
{
"name": "grpc_verify_jwt",
"build": "tool",
"language": "c",
"src": [
"test/core/security/verify_jwt.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "hpack_parser_test",
"build": "test",
@ -2313,6 +2351,19 @@
"grpc++_test_config"
]
},
{
"name": "secure_auth_context_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/common/secure_auth_context_test.cc"
],
"deps": [
"grpc++",
"grpc",
"gpr"
]
},
{
"name": "server_crash_test",
"build": "test",

@ -392,6 +392,50 @@ Asserts:
* clients are free to assert that the response payload body contents are zero
and comparing the entire response message against a golden response
### oauth2_auth_token
Similar to the other auth tests, this test is only for cloud-to-prod path.
This test verifies unary calls succeed in sending messages using an OAuth2 token that is obtained OOB. For the purpose of the test, the OAuth2 token is actually obtained from the service account credentials via the language-specific authorization library.
The difference between this test and the other auth tests is that rather than configuring the test client with ServiceAccountCredentials directly, the test first uses the authorization library to obtain an authorization token.
The test
- uses the flag`--service_account_key_file` with the path to a json key file
downloaded from https://console.developers.google.com. Alternately, if using a usable auth implementation, it may specify the file location in the environment variable GOOGLE_APPLICATION_CREDENTIALS
- uses the flag `--oauth_scope` for the oauth scope. For testing against grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should be passed as the `--oauth_scope`.
Server features:
* [UnaryCall][]
* [Compressable Payload][]
* [Echo Authenticated Username][]
* [Echo OAuth Scope][]
Procedure:
1. Client use the auth library to obtain an authorization token
2. Client calls UnaryCall, attaching the authorization token obtained in step1, with the following message
```
{
response_type: COMPRESSABLE
response_size: 314159
payload:{
body: 271828 bytes of zeros
}
fill_username: true
fill_oauth_scope: true
}
```
Asserts:
* call was successful
* received SimpleResponse.username is in the json key file used by the auth library to obtain the authorization token
* received SimpleResponse.oauth_scope is in `--oauth_scope`
* response payload body is 314159 bytes in size
* clients are free to assert that the response payload body contents are zero
and comparing the entire response message against a golden response
### Metadata (TODO: fix name)
Status: Not yet implementable
@ -560,11 +604,6 @@ Propagation of status code and message (yangg)
Multiple thousand simultaneous calls on same Channel (ctiller)
OAuth2 tokens + Service Credentials from GCE metadata server (GCE->prod only)
(abhishek)
OAuth2 tokens + JWT signing key (GCE->prod only) (abhishek)
Metadata: client headers, server headers + trailers, binary+ascii
#### Normal priority:

@ -140,6 +140,7 @@ Pod::Spec.new do |s|
'src/core/security/base64.h',
'src/core/security/credentials.h',
'src/core/security/json_token.h',
'src/core/security/jwt_verifier.h',
'src/core/security/secure_endpoint.h',
'src/core/security/secure_transport_setup.h',
'src/core/security/security_connector.h',
@ -263,6 +264,7 @@ Pod::Spec.new do |s|
'src/core/security/credentials_win32.c',
'src/core/security/google_default_credentials.c',
'src/core/security/json_token.c',
'src/core/security/jwt_verifier.c',
'src/core/security/secure_endpoint.c',
'src/core/security/secure_transport_setup.c',
'src/core/security/security_connector.c',
@ -398,6 +400,7 @@ Pod::Spec.new do |s|
'src/core/security/base64.h',
'src/core/security/credentials.h',
'src/core/security/json_token.h',
'src/core/security/jwt_verifier.h',
'src/core/security/secure_endpoint.h',
'src/core/security/secure_transport_setup.h',
'src/core/security/security_connector.h',

@ -0,0 +1,62 @@
/*
*
* 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 GRPCXX_AUTH_CONTEXT_H
#define GRPCXX_AUTH_CONTEXT_H
#include <vector>
#include <grpc++/config.h>
namespace grpc {
class AuthContext {
public:
typedef std::pair<grpc::string, grpc::string> Property;
virtual ~AuthContext() {}
// A peer identity, in general is one or more properties (in which case they
// have the same name).
virtual std::vector<grpc::string> GetPeerIdentity() const = 0;
virtual grpc::string GetPeerIdentityPropertyName() const = 0;
// Returns all the property values with the given name.
virtual std::vector<grpc::string> FindPropertyValues(
const grpc::string& name) const = 0;
};
} // namespace grpc
#endif // GRPCXX_AUTH_CONTEXT_H

@ -40,12 +40,14 @@
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpc++/auth_context.h>
#include <grpc++/config.h>
#include <grpc++/status.h>
#include <grpc++/time.h>
struct grpc_call;
struct grpc_completion_queue;
struct census_context;
namespace grpc {
@ -107,6 +109,12 @@ class ClientContext {
creds_ = creds;
}
std::shared_ptr<const AuthContext> auth_context() const;
// Get and set census context
void set_census_context(census_context* ccp) { census_context_ = ccp; }
census_context* get_census_context() const { return census_context_; }
void TryCancel();
private:
@ -154,6 +162,8 @@ class ClientContext {
gpr_timespec deadline_;
grpc::string authority_;
std::shared_ptr<Credentials> creds_;
mutable std::shared_ptr<const AuthContext> auth_context_;
census_context* census_context_;
std::multimap<grpc::string, grpc::string> send_initial_metadata_;
std::multimap<grpc::string, grpc::string> recv_initial_metadata_;
std::multimap<grpc::string, grpc::string> trailing_metadata_;

@ -120,6 +120,12 @@ std::shared_ptr<Credentials> JWTCredentials(const grpc::string& json_key,
std::shared_ptr<Credentials> RefreshTokenCredentials(
const grpc::string& json_refresh_token);
// Builds access token credentials.
// access_token is an oauth2 access token that was fetched using an out of band
// mechanism.
std::shared_ptr<Credentials> AccessTokenCredentials(
const grpc::string& access_token);
// Builds IAM credentials.
std::shared_ptr<Credentials> IAMCredentials(
const grpc::string& authorization_token,

@ -84,8 +84,8 @@ class Server GRPC_FINAL : public GrpcLibrary, private CallHook {
int max_message_size);
// Register a service. This call does not take ownership of the service.
// The service must exist for the lifetime of the Server instance.
bool RegisterService(RpcService* service);
bool RegisterAsyncService(AsynchronousService* service);
bool RegisterService(const grpc::string *host, RpcService* service);
bool RegisterAsyncService(const grpc::string *host, AsynchronousService* service);
void RegisterAsyncGenericService(AsyncGenericService* service);
// Add a listening port. Can be called multiple times.
int AddListeningPort(const grpc::string& addr, ServerCredentials* creds);

@ -58,17 +58,35 @@ class ServerBuilder {
// Register a service. This call does not take ownership of the service.
// The service must exist for the lifetime of the Server instance returned by
// BuildAndStart().
// Matches requests with any :authority
void RegisterService(SynchronousService* service);
// Register an asynchronous service. New calls will be delevered to cq.
// Register an asynchronous service.
// This call does not take ownership of the service or completion queue.
// The service and completion queuemust exist for the lifetime of the Server
// instance returned by BuildAndStart().
// Matches requests with any :authority
void RegisterAsyncService(AsynchronousService* service);
// Register a generic service.
// Matches requests with any :authority
void RegisterAsyncGenericService(AsyncGenericService* service);
// Register a service. This call does not take ownership of the service.
// The service must exist for the lifetime of the Server instance returned by
// BuildAndStart().
// Only matches requests with :authority \a host
void RegisterService(const grpc::string& host,
SynchronousService* service);
// Register an asynchronous service.
// This call does not take ownership of the service or completion queue.
// The service and completion queuemust exist for the lifetime of the Server
// instance returned by BuildAndStart().
// Only matches requests with :authority \a host
void RegisterAsyncService(const grpc::string& host,
AsynchronousService* service);
// Set max message size in bytes.
void SetMaxMessageSize(int max_message_size) {
max_message_size_ = max_message_size;
@ -98,9 +116,18 @@ class ServerBuilder {
int* selected_port;
};
typedef std::unique_ptr<grpc::string> HostString;
template <class T> struct NamedService {
explicit NamedService(T* s) : service(s) {}
NamedService(const grpc::string& h, T *s)
: host(new grpc::string(h)), service(s) {}
HostString host;
T* service;
};
int max_message_size_;
std::vector<RpcService*> services_;
std::vector<AsynchronousService*> async_services_;
std::vector<std::unique_ptr<NamedService<RpcService>>> services_;
std::vector<std::unique_ptr<NamedService<AsynchronousService>>> async_services_;
std::vector<Port> ports_;
std::vector<ServerCompletionQueue*> cqs_;
std::shared_ptr<ServerCredentials> creds_;

@ -35,8 +35,10 @@
#define GRPCXX_SERVER_CONTEXT_H
#include <map>
#include <memory>
#include <grpc/support/time.h>
#include <grpc++/auth_context.h>
#include <grpc++/config.h>
#include <grpc++/time.h>
@ -97,6 +99,10 @@ class ServerContext {
return client_metadata_;
}
std::shared_ptr<const AuthContext> auth_context() const {
return auth_context_;
}
private:
friend class ::grpc::Server;
template <class W, class R>
@ -133,12 +139,15 @@ class ServerContext {
ServerContext(gpr_timespec deadline, grpc_metadata* metadata,
size_t metadata_count);
void set_call(grpc_call* call);
CompletionOp* completion_op_;
gpr_timespec deadline_;
grpc_call* call_;
CompletionQueue* cq_;
bool sent_initial_metadata_;
std::shared_ptr<const AuthContext> auth_context_;
std::multimap<grpc::string, grpc::string> client_metadata_;
std::multimap<grpc::string, grpc::string> initial_metadata_;
std::multimap<grpc::string, grpc::string> trailing_metadata_;

@ -61,6 +61,10 @@ enum census_functions {
int census_initialize(int functions);
void census_shutdown();
/* If any census feature has been initialized, this funtion will return a
* non-zero value. */
int census_available();
/* Internally, Census relies on a context, which should be propagated across
* RPC's. From the RPC subsystems viewpoint, this is an opaque data structure.
* A context must be used as the first argument to all other census

@ -131,13 +131,18 @@ grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
grpc_credentials *grpc_refresh_token_credentials_create(
const char *json_refresh_token);
/* Creates a fake transport security credentials object for testing. */
grpc_credentials *grpc_fake_transport_security_credentials_create(void);
/* Creates an Oauth2 Access Token credentials with an access token that was
aquired by an out of band mechanism. */
grpc_credentials *grpc_access_token_credentials_create(
const char *access_token);
/* Creates an IAM credentials object. */
grpc_credentials *grpc_iam_credentials_create(const char *authorization_token,
const char *authority_selector);
/* Creates a fake transport security credentials object for testing. */
grpc_credentials *grpc_fake_transport_security_credentials_create(void);
/* --- Secure channel creation. --- */
/* The caller of the secure_channel_create functions may override the target
@ -248,8 +253,12 @@ const char *grpc_auth_context_peer_identity_property_name(
/* Returns 1 if the peer is authenticated, 0 otherwise. */
int grpc_auth_context_peer_is_authenticated(const grpc_auth_context *ctx);
/* Gets the auth context from the call. */
const grpc_auth_context *grpc_call_auth_context(grpc_call *call);
/* Gets the auth context from the call. Caller needs to call
grpc_auth_context_release on the returned context. */
grpc_auth_context *grpc_call_auth_context(grpc_call *call);
/* Releases the auth context returned from grpc_call_auth_context. */
void grpc_auth_context_release(grpc_auth_context *context);
#ifdef __cplusplus
}

@ -34,12 +34,28 @@
#include <grpc/census.h>
#include "src/core/census/grpc_context.h"
void *grpc_census_context_create() {
census_context *context;
census_context_deserialize(NULL, &context);
return (void *)context;
static void grpc_census_context_destroy(void *context) {
census_context_destroy((census_context *)context);
}
void grpc_census_context_destroy(void *context) {
census_context_destroy((census_context *)context);
void grpc_census_call_set_context(grpc_call *call, census_context *context) {
if (!census_available()) {
return;
}
if (context == NULL) {
if (grpc_call_is_client(call)) {
census_context *context_ptr;
census_context_deserialize(NULL, &context_ptr);
grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context_ptr,
grpc_census_context_destroy);
} else {
/* TODO(aveitch): server side context code to be implemented. */
}
} else {
grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context, NULL);
}
}
census_context *grpc_census_call_get_context(grpc_call *call) {
return (census_context *)grpc_call_context_get(call, GRPC_CONTEXT_TRACING);
}

@ -36,7 +36,22 @@
#ifndef CENSUS_GRPC_CONTEXT_H
#define CENSUS_GRPC_CONTEXT_H
void *grpc_census_context_create();
void grpc_census_context_destroy(void *context);
#include <grpc/census.h>
#include "src/core/surface/call.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Set census context for the call; Must be called before first call to
grpc_call_start_batch(). */
void grpc_census_call_set_context(grpc_call *call, census_context *context);
/* Retrieve the calls current census context. */
census_context *grpc_census_call_get_context(grpc_call *call);
#ifdef __cplusplus
}
#endif
#endif /* CENSUS_GRPC_CONTEXT_H */

@ -48,3 +48,5 @@ int census_initialize(int functions) {
}
void census_shutdown() { census_fns_enabled = CENSUS_NONE; }
int census_available() { return (census_fns_enabled != CENSUS_NONE); }

@ -197,7 +197,7 @@ static void destroy_channel_elem(grpc_channel_element* elem) {
channel_data* chand = elem->channel_data;
GPR_ASSERT(chand != NULL);
if (chand->path_str != NULL) {
grpc_mdstr_unref(chand->path_str);
GRPC_MDSTR_UNREF(chand->path_str);
}
}

@ -427,7 +427,7 @@ static void cc_on_config_changed(void *arg, int iomgr_success) {
GRPC_RESOLVER_REF(resolver, "channel-next");
gpr_mu_unlock(&chand->mu_config);
GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver");
grpc_resolver_next(chand->resolver, &chand->incoming_configuration,
grpc_resolver_next(resolver, &chand->incoming_configuration,
&chand->on_config_changed);
GRPC_RESOLVER_UNREF(resolver, "channel-next");
} else {

@ -108,13 +108,13 @@ static void hc_mutate_op(grpc_call_element *elem,
/* Send : prefixed headers, which have to be before any application
layer headers. */
grpc_metadata_batch_add_head(&op->data.metadata, &calld->method,
grpc_mdelem_ref(channeld->method));
GRPC_MDELEM_REF(channeld->method));
grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme,
grpc_mdelem_ref(channeld->scheme));
GRPC_MDELEM_REF(channeld->scheme));
grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers,
grpc_mdelem_ref(channeld->te_trailers));
GRPC_MDELEM_REF(channeld->te_trailers));
grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type,
grpc_mdelem_ref(channeld->content_type));
GRPC_MDELEM_REF(channeld->content_type));
break;
}
}
@ -196,11 +196,11 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
/* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->te_trailers);
grpc_mdelem_unref(channeld->method);
grpc_mdelem_unref(channeld->scheme);
grpc_mdelem_unref(channeld->content_type);
grpc_mdelem_unref(channeld->status);
GRPC_MDELEM_UNREF(channeld->te_trailers);
GRPC_MDELEM_UNREF(channeld->method);
GRPC_MDELEM_UNREF(channeld->scheme);
GRPC_MDELEM_UNREF(channeld->content_type);
GRPC_MDELEM_UNREF(channeld->status);
}
const grpc_channel_filter grpc_http_client_filter = {

@ -129,9 +129,9 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
/* translate host to :authority since :authority may be
omitted */
grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
channeld->mdctx, grpc_mdstr_ref(channeld->authority_key),
grpc_mdstr_ref(md->value));
grpc_mdelem_unref(md);
channeld->mdctx, GRPC_MDSTR_REF(channeld->authority_key),
GRPC_MDSTR_REF(md->value));
GRPC_MDELEM_UNREF(md);
return authority;
} else {
return md;
@ -193,7 +193,7 @@ static void hs_mutate_op(grpc_call_element *elem,
if (op->type != GRPC_OP_METADATA) continue;
calld->sent_status = 1;
grpc_metadata_batch_add_head(&op->data.metadata, &calld->status,
grpc_mdelem_ref(channeld->status_ok));
GRPC_MDELEM_REF(channeld->status_ok));
break;
}
}
@ -264,17 +264,17 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
/* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->te_trailers);
grpc_mdelem_unref(channeld->status_ok);
grpc_mdelem_unref(channeld->status_not_found);
grpc_mdelem_unref(channeld->method_post);
grpc_mdelem_unref(channeld->http_scheme);
grpc_mdelem_unref(channeld->https_scheme);
grpc_mdelem_unref(channeld->grpc_scheme);
grpc_mdelem_unref(channeld->content_type);
grpc_mdstr_unref(channeld->path_key);
grpc_mdstr_unref(channeld->authority_key);
grpc_mdstr_unref(channeld->host_key);
GRPC_MDELEM_UNREF(channeld->te_trailers);
GRPC_MDELEM_UNREF(channeld->status_ok);
GRPC_MDELEM_UNREF(channeld->status_not_found);
GRPC_MDELEM_UNREF(channeld->method_post);
GRPC_MDELEM_UNREF(channeld->http_scheme);
GRPC_MDELEM_UNREF(channeld->https_scheme);
GRPC_MDELEM_UNREF(channeld->grpc_scheme);
GRPC_MDELEM_UNREF(channeld->content_type);
GRPC_MDSTR_UNREF(channeld->path_key);
GRPC_MDSTR_UNREF(channeld->authority_key);
GRPC_MDSTR_UNREF(channeld->host_key);
}
const grpc_channel_filter grpc_http_server_filter = {

@ -85,7 +85,7 @@ typedef struct grpc_httpcli_response {
char *body;
} grpc_httpcli_response;
/* Callback for grpc_httpcli_get */
/* Callback for grpc_httpcli_get and grpc_httpcli_post. */
typedef void (*grpc_httpcli_response_cb)(void *user_data,
const grpc_httpcli_response *response);
@ -100,8 +100,6 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
'request' contains request parameters - these are caller owned and can be
destroyed once the call returns
'deadline' contains a deadline for the request (or gpr_inf_future)
'em' points to a caller owned event manager that must be alive for the
lifetime of the request
'on_response' is a callback to report results to (and 'user_data' is a user
supplied pointer to pass to said call) */
void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset,

@ -111,10 +111,13 @@ void grpc_iomgr_shutdown(void) {
grpc_iomgr_closure *closure;
gpr_timespec shutdown_deadline =
gpr_time_add(gpr_now(), gpr_time_from_seconds(10));
gpr_timespec last_warning_time = gpr_now();
gpr_mu_lock(&g_mu);
g_shutdown = 1;
while (g_cbs_head != NULL || g_root_object.next != &g_root_object) {
if (gpr_time_cmp(gpr_time_sub(gpr_now(), last_warning_time),
gpr_time_from_seconds(1)) >= 0) {
if (g_cbs_head != NULL && g_root_object.next != &g_root_object) {
gpr_log(GPR_DEBUG,
"Waiting for %d iomgr objects to be destroyed and executing "
@ -126,6 +129,8 @@ void grpc_iomgr_shutdown(void) {
gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed",
count_objects());
}
last_warning_time = gpr_now();
}
if (g_cbs_head) {
do {
closure = g_cbs_head;

@ -45,11 +45,14 @@
#include "src/core/iomgr/socket_windows.h"
grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) {
char *final_name;
grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket));
memset(r, 0, sizeof(grpc_winsocket));
r->socket = socket;
gpr_mu_init(&r->state_mu);
grpc_iomgr_register_object(&r->iomgr_object, name);
gpr_asprintf(&final_name, "%s:socket=0x%p", name, r);
grpc_iomgr_register_object(&r->iomgr_object, final_name);
gpr_free(final_name);
grpc_iocp_add_socket(r);
return r;
}

@ -313,9 +313,7 @@ static void call_read_cb(grpc_tcp *tcp, gpr_slice *slices, size_t nslices,
size_t i;
gpr_log(GPR_DEBUG, "read: status=%d", status);
for (i = 0; i < nslices; i++) {
char *dump =
gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]),
GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT);
char *dump = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_log(GPR_DEBUG, "READ: %s", dump);
gpr_free(dump);
}
@ -540,9 +538,7 @@ static grpc_endpoint_write_status grpc_tcp_write(grpc_endpoint *ep,
size_t i;
for (i = 0; i < nslices; i++) {
char *data =
gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]),
GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT);
char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_log(GPR_DEBUG, "WRITE %p: %s", tcp, data);
gpr_free(data);
}

@ -53,7 +53,7 @@ typedef struct grpc_json {
} grpc_json;
/* The next two functions are going to parse the input string, and
* destroy it in the process, in order to use its space to store
* modify it in the process, in order to use its space to store
* all of the keys and values for the returned object tree.
*
* They assume UTF-8 input stream, and will output UTF-8 encoded

@ -61,6 +61,7 @@ typedef struct {
grpc_transport_stream_op op;
size_t op_md_idx;
int sent_initial_metadata;
gpr_uint8 security_context_set;
grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
} call_data;
@ -199,8 +200,22 @@ static void auth_start_transport_op(grpc_call_element *elem,
channel_data *chand = elem->channel_data;
grpc_linked_mdelem *l;
size_t i;
grpc_client_security_context* sec_ctx = NULL;
/* TODO(jboeuf): write the call auth context. */
if (calld->security_context_set == 0) {
calld->security_context_set = 1;
GPR_ASSERT(op->context);
if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) {
op->context[GRPC_CONTEXT_SECURITY].value =
grpc_client_security_context_create();
op->context[GRPC_CONTEXT_SECURITY].destroy =
grpc_client_security_context_destroy;
}
sec_ctx = op->context[GRPC_CONTEXT_SECURITY].value;
GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter");
sec_ctx->auth_context = GRPC_AUTH_CONTEXT_REF(
chand->security_connector->base.auth_context, "client_auth_filter");
}
if (op->bind_pollset) {
calld->pollset = op->bind_pollset;
@ -219,11 +234,11 @@ static void auth_start_transport_op(grpc_call_element *elem,
/* Pointer comparison is OK for md_elems created from the same context.
*/
if (md->key == chand->authority_string) {
if (calld->host != NULL) grpc_mdstr_unref(calld->host);
calld->host = grpc_mdstr_ref(md->value);
if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host);
calld->host = GRPC_MDSTR_REF(md->value);
} else if (md->key == chand->path_string) {
if (calld->method != NULL) grpc_mdstr_unref(calld->method);
calld->method = grpc_mdstr_ref(md->value);
if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method);
calld->method = GRPC_MDSTR_REF(md->value);
}
}
if (calld->host != NULL) {
@ -263,6 +278,7 @@ static void init_call_elem(grpc_call_element *elem,
calld->method = NULL;
calld->pollset = NULL;
calld->sent_initial_metadata = 0;
calld->security_context_set = 0;
GPR_ASSERT(!initial_op || !initial_op->send_ops);
}
@ -272,10 +288,10 @@ static void destroy_call_elem(grpc_call_element *elem) {
call_data *calld = elem->call_data;
grpc_credentials_unref(calld->creds);
if (calld->host != NULL) {
grpc_mdstr_unref(calld->host);
GRPC_MDSTR_UNREF(calld->host);
}
if (calld->method != NULL) {
grpc_mdstr_unref(calld->method);
GRPC_MDSTR_UNREF(calld->method);
}
}
@ -314,16 +330,16 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
if (ctx != NULL)
GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter");
if (chand->authority_string != NULL) {
grpc_mdstr_unref(chand->authority_string);
GRPC_MDSTR_UNREF(chand->authority_string);
}
if (chand->error_msg_key != NULL) {
grpc_mdstr_unref(chand->error_msg_key);
GRPC_MDSTR_UNREF(chand->error_msg_key);
}
if (chand->status_key != NULL) {
grpc_mdstr_unref(chand->status_key);
GRPC_MDSTR_UNREF(chand->status_key);
}
if (chand->path_string != NULL) {
grpc_mdstr_unref(chand->path_string);
GRPC_MDSTR_UNREF(chand->path_string);
}
}

@ -594,7 +594,7 @@ static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
gpr_mu_init(&c->mu);
c->token_expiration = gpr_inf_past;
c->fetch_func = fetch_func;
grpc_pollset_set_init(&c->pollset_set);
grpc_httpcli_context_init(&c->httpcli_context);
}
/* -- ComputeEngine credentials. -- */
@ -820,6 +820,54 @@ grpc_credentials *grpc_fake_oauth2_credentials_create(
return &c->base;
}
/* -- Oauth2 Access Token credentials. -- */
static void access_token_destroy(grpc_credentials *creds) {
grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
grpc_credentials_md_store_unref(c->access_token_md);
gpr_free(c);
}
static int access_token_has_request_metadata(const grpc_credentials *creds) {
return 1;
}
static int access_token_has_request_metadata_only(
const grpc_credentials *creds) {
return 1;
}
static void access_token_get_request_metadata(grpc_credentials *creds,
grpc_pollset *pollset,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data) {
grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
cb(user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK);
}
static grpc_credentials_vtable access_token_vtable = {
access_token_destroy, access_token_has_request_metadata,
access_token_has_request_metadata_only, access_token_get_request_metadata,
NULL};
grpc_credentials *grpc_access_token_credentials_create(
const char *access_token) {
grpc_access_token_credentials *c =
gpr_malloc(sizeof(grpc_access_token_credentials));
char *token_md_value;
memset(c, 0, sizeof(grpc_access_token_credentials));
c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
c->base.vtable = &access_token_vtable;
gpr_ref_init(&c->base.refcount, 1);
c->access_token_md = grpc_credentials_md_store_create(1);
gpr_asprintf(&token_md_value, "Bearer %s", access_token);
grpc_credentials_md_store_add_cstrings(
c->access_token_md, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
gpr_free(token_md_value);
return &c->base;
}
/* -- Fake transport security credentials. -- */
static void fake_transport_security_credentials_destroy(

@ -262,7 +262,6 @@ typedef struct {
grpc_credentials_md_store *access_token_md;
gpr_timespec token_expiration;
grpc_httpcli_context httpcli_context;
grpc_pollset_set pollset_set;
grpc_fetch_oauth2_func fetch_func;
} grpc_oauth2_token_fetcher_credentials;
@ -282,6 +281,13 @@ typedef struct {
grpc_auth_refresh_token refresh_token;
} grpc_refresh_token_credentials;
/* -- Oauth2 Access Token credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_md_store *access_token_md;
} grpc_access_token_credentials;
/* -- Fake Oauth2 credentials. -- */
typedef struct {

@ -0,0 +1,830 @@
/*
*
* 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 disclaimser.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimser
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/security/jwt_verifier.h"
#include <string.h>
#include "src/core/httpcli/httpcli.h"
#include "src/core/security/base64.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
#include <openssl/pem.h>
/* --- Utils. --- */
const char *grpc_jwt_verifier_status_to_string(
grpc_jwt_verifier_status status) {
switch (status) {
case GRPC_JWT_VERIFIER_OK:
return "OK";
case GRPC_JWT_VERIFIER_BAD_SIGNATURE:
return "BAD_SIGNATURE";
case GRPC_JWT_VERIFIER_BAD_FORMAT:
return "BAD_FORMAT";
case GRPC_JWT_VERIFIER_BAD_AUDIENCE:
return "BAD_AUDIENCE";
case GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR:
return "KEY_RETRIEVAL_ERROR";
case GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE:
return "TIME_CONSTRAINT_FAILURE";
case GRPC_JWT_VERIFIER_GENERIC_ERROR:
return "GENERIC_ERROR";
default:
return "UNKNOWN";
}
}
static const EVP_MD *evp_md_from_alg(const char *alg) {
if (strcmp(alg, "RS256") == 0) {
return EVP_sha256();
} else if (strcmp(alg, "RS384") == 0) {
return EVP_sha384();
} else if (strcmp(alg, "RS512") == 0) {
return EVP_sha512();
} else {
return NULL;
}
}
static grpc_json *parse_json_part_from_jwt(const char *str, size_t len,
gpr_slice *buffer) {
grpc_json *json;
*buffer = grpc_base64_decode_with_len(str, len, 1);
if (GPR_SLICE_IS_EMPTY(*buffer)) {
gpr_log(GPR_ERROR, "Invalid base64.");
return NULL;
}
json = grpc_json_parse_string_with_len((char *)GPR_SLICE_START_PTR(*buffer),
GPR_SLICE_LENGTH(*buffer));
if (json == NULL) {
gpr_slice_unref(*buffer);
gpr_log(GPR_ERROR, "JSON parsing error.");
}
return json;
}
static const char *validate_string_field(const grpc_json *json,
const char *key) {
if (json->type != GRPC_JSON_STRING) {
gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value);
return NULL;
}
return json->value;
}
static gpr_timespec validate_time_field(const grpc_json *json,
const char *key) {
gpr_timespec result = gpr_time_0;
if (json->type != GRPC_JSON_NUMBER) {
gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value);
return result;
}
result.tv_sec = strtol(json->value, NULL, 10);
return result;
}
/* --- JOSE header. see http://tools.ietf.org/html/rfc7515#section-4 --- */
typedef struct {
const char *alg;
const char *kid;
const char *typ;
/* TODO(jboeuf): Add others as needed (jku, jwk, x5u, x5c and so on...). */
gpr_slice buffer;
} jose_header;
static void jose_header_destroy(jose_header *h) {
gpr_slice_unref(h->buffer);
gpr_free(h);
}
/* Takes ownership of json and buffer. */
static jose_header *jose_header_from_json(grpc_json *json, gpr_slice buffer) {
grpc_json *cur;
jose_header *h = gpr_malloc(sizeof(jose_header));
memset(h, 0, sizeof(jose_header));
h->buffer = buffer;
for (cur = json->child; cur != NULL; cur = cur->next) {
if (strcmp(cur->key, "alg") == 0) {
/* We only support RSA-1.5 signatures for now.
Beware of this if we add HMAC support:
https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
*/
if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) ||
evp_md_from_alg(cur->value) == NULL) {
gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value);
goto error;
}
h->alg = cur->value;
} else if (strcmp(cur->key, "typ") == 0) {
h->typ = validate_string_field(cur, "typ");
if (h->typ == NULL) goto error;
} else if (strcmp(cur->key, "kid") == 0) {
h->kid = validate_string_field(cur, "kid");
if (h->kid == NULL) goto error;
}
}
if (h->alg == NULL) {
gpr_log(GPR_ERROR, "Missing alg field.");
goto error;
}
grpc_json_destroy(json);
h->buffer = buffer;
return h;
error:
grpc_json_destroy(json);
jose_header_destroy(h);
return NULL;
}
/* --- JWT claims. see http://tools.ietf.org/html/rfc7519#section-4.1 */
struct grpc_jwt_claims {
/* Well known properties already parsed. */
const char *sub;
const char *iss;
const char *aud;
const char *jti;
gpr_timespec iat;
gpr_timespec exp;
gpr_timespec nbf;
grpc_json *json;
gpr_slice buffer;
};
void grpc_jwt_claims_destroy(grpc_jwt_claims *claims) {
grpc_json_destroy(claims->json);
gpr_slice_unref(claims->buffer);
gpr_free(claims);
}
const grpc_json *grpc_jwt_claims_json(const grpc_jwt_claims *claims) {
if (claims == NULL) return NULL;
return claims->json;
}
const char *grpc_jwt_claims_subject(const grpc_jwt_claims *claims) {
if (claims == NULL) return NULL;
return claims->sub;
}
const char *grpc_jwt_claims_issuer(const grpc_jwt_claims *claims) {
if (claims == NULL) return NULL;
return claims->iss;
}
const char *grpc_jwt_claims_id(const grpc_jwt_claims *claims) {
if (claims == NULL) return NULL;
return claims->jti;
}
const char *grpc_jwt_claims_audience(const grpc_jwt_claims *claims) {
if (claims == NULL) return NULL;
return claims->aud;
}
gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims *claims) {
if (claims == NULL) return gpr_inf_past;
return claims->iat;
}
gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims *claims) {
if (claims == NULL) return gpr_inf_future;
return claims->exp;
}
gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims) {
if (claims == NULL) return gpr_inf_past;
return claims->nbf;
}
/* Takes ownership of json and buffer even in case of failure. */
grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, gpr_slice buffer) {
grpc_json *cur;
grpc_jwt_claims *claims = gpr_malloc(sizeof(grpc_jwt_claims));
memset(claims, 0, sizeof(grpc_jwt_claims));
claims->json = json;
claims->buffer = buffer;
claims->iat = gpr_inf_past;
claims->nbf = gpr_inf_past;
claims->exp = gpr_inf_future;
/* Per the spec, all fields are optional. */
for (cur = json->child; cur != NULL; cur = cur->next) {
if (strcmp(cur->key, "sub") == 0) {
claims->sub = validate_string_field(cur, "sub");
if (claims->sub == NULL) goto error;
} else if (strcmp(cur->key, "iss") == 0) {
claims->iss = validate_string_field(cur, "iss");
if (claims->iss == NULL) goto error;
} else if (strcmp(cur->key, "aud") == 0) {
claims->aud = validate_string_field(cur, "aud");
if (claims->aud == NULL) goto error;
} else if (strcmp(cur->key, "jti") == 0) {
claims->jti = validate_string_field(cur, "jti");
if (claims->jti == NULL) goto error;
} else if (strcmp(cur->key, "iat") == 0) {
claims->iat = validate_time_field(cur, "iat");
if (gpr_time_cmp(claims->iat, gpr_time_0) == 0) goto error;
} else if (strcmp(cur->key, "exp") == 0) {
claims->exp = validate_time_field(cur, "exp");
if (gpr_time_cmp(claims->exp, gpr_time_0) == 0) goto error;
} else if (strcmp(cur->key, "nbf") == 0) {
claims->nbf = validate_time_field(cur, "nbf");
if (gpr_time_cmp(claims->nbf, gpr_time_0) == 0) goto error;
}
}
return claims;
error:
grpc_jwt_claims_destroy(claims);
return NULL;
}
grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims,
const char *audience) {
gpr_timespec skewed_now;
int audience_ok;
GPR_ASSERT(claims != NULL);
skewed_now = gpr_time_add(gpr_now(), grpc_jwt_verifier_clock_skew);
if (gpr_time_cmp(skewed_now, claims->nbf) < 0) {
gpr_log(GPR_ERROR, "JWT is not valid yet.");
return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
}
skewed_now = gpr_time_sub(gpr_now(), grpc_jwt_verifier_clock_skew);
if (gpr_time_cmp(skewed_now, claims->exp) > 0) {
gpr_log(GPR_ERROR, "JWT is expired.");
return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
}
if (audience == NULL) {
audience_ok = claims->aud == NULL;
} else {
audience_ok = claims->aud != NULL && strcmp(audience, claims->aud) == 0;
}
if (!audience_ok) {
gpr_log(GPR_ERROR, "Audience mismatch: expected %s and found %s.",
audience == NULL ? "NULL" : audience,
claims->aud == NULL ? "NULL" : claims->aud);
return GRPC_JWT_VERIFIER_BAD_AUDIENCE;
}
return GRPC_JWT_VERIFIER_OK;
}
/* --- verifier_cb_ctx object. --- */
typedef struct {
grpc_jwt_verifier *verifier;
grpc_pollset *pollset;
jose_header *header;
grpc_jwt_claims *claims;
char *audience;
gpr_slice signature;
gpr_slice signed_data;
void *user_data;
grpc_jwt_verification_done_cb user_cb;
} verifier_cb_ctx;
/* Takes ownership of the header, claims and signature. */
static verifier_cb_ctx *verifier_cb_ctx_create(
grpc_jwt_verifier *verifier, grpc_pollset *pollset,
jose_header * header, grpc_jwt_claims *claims, const char *audience,
gpr_slice signature, const char *signed_jwt, size_t signed_jwt_len,
void *user_data, grpc_jwt_verification_done_cb cb) {
verifier_cb_ctx *ctx = gpr_malloc(sizeof(verifier_cb_ctx));
memset(ctx, 0, sizeof(verifier_cb_ctx));
ctx->verifier = verifier;
ctx->pollset = pollset;
ctx->header = header;
ctx->audience = gpr_strdup(audience);
ctx->claims = claims;
ctx->signature = signature;
ctx->signed_data = gpr_slice_from_copied_buffer(signed_jwt, signed_jwt_len);
ctx->user_data = user_data;
ctx->user_cb = cb;
return ctx;
}
void verifier_cb_ctx_destroy(verifier_cb_ctx *ctx) {
if (ctx->audience != NULL) gpr_free(ctx->audience);
if (ctx->claims != NULL) grpc_jwt_claims_destroy(ctx->claims);
gpr_slice_unref(ctx->signature);
gpr_slice_unref(ctx->signed_data);
jose_header_destroy(ctx->header);
/* TODO: see what to do with claims... */
gpr_free(ctx);
}
/* --- grpc_jwt_verifier object. --- */
/* Clock skew defaults to one minute. */
gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0};
/* Max delay defaults to one minute. */
gpr_timespec grpc_jwt_verifier_max_delay = {60, 0};
typedef struct {
char *email_domain;
char *key_url_prefix;
} email_key_mapping;
struct grpc_jwt_verifier {
email_key_mapping *mappings;
size_t num_mappings; /* Should be very few, linear search ok. */
size_t allocated_mappings;
grpc_httpcli_context http_ctx;
};
static grpc_json *json_from_http(const grpc_httpcli_response *response) {
grpc_json *json = NULL;
if (response == NULL) {
gpr_log(GPR_ERROR, "HTTP response is NULL.");
return NULL;
}
if (response->status != 200) {
gpr_log(GPR_ERROR, "Call to http server failed with error %d.",
response->status);
return NULL;
}
json = grpc_json_parse_string_with_len(response->body, response->body_length);
if (json == NULL) {
gpr_log(GPR_ERROR, "Invalid JSON found in response.");
}
return json;
}
static const grpc_json *find_property_by_name(const grpc_json *json,
const char *name) {
const grpc_json *cur;
for (cur = json->child; cur != NULL; cur = cur->next) {
if (strcmp(cur->key, name) == 0) return cur;
}
return NULL;
}
static EVP_PKEY *extract_pkey_from_x509(const char *x509_str) {
X509 *x509 = NULL;
EVP_PKEY *result = NULL;
BIO *bio = BIO_new(BIO_s_mem());
BIO_write(bio, x509_str, strlen(x509_str));
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (x509 == NULL) {
gpr_log(GPR_ERROR, "Unable to parse x509 cert.");
goto end;
}
result = X509_get_pubkey(x509);
if (result == NULL) {
gpr_log(GPR_ERROR, "Cannot find public key in X509 cert.");
}
end:
BIO_free(bio);
if (x509 != NULL) X509_free(x509);
return result;
}
static BIGNUM *bignum_from_base64(const char *b64) {
BIGNUM *result = NULL;
gpr_slice bin;
if (b64 == NULL) return NULL;
bin = grpc_base64_decode(b64, 1);
if (GPR_SLICE_IS_EMPTY(bin)) {
gpr_log(GPR_ERROR, "Invalid base64 for big num.");
return NULL;
}
result = BN_bin2bn(GPR_SLICE_START_PTR(bin), GPR_SLICE_LENGTH(bin), NULL);
gpr_slice_unref(bin);
return result;
}
static EVP_PKEY *pkey_from_jwk(const grpc_json *json, const char *kty) {
const grpc_json *key_prop;
RSA *rsa = NULL;
EVP_PKEY *result = NULL;
GPR_ASSERT(kty != NULL && json != NULL);
if (strcmp(kty, "RSA") != 0) {
gpr_log(GPR_ERROR, "Unsupported key type %s.", kty);
goto end;
}
rsa = RSA_new();
if (rsa == NULL) {
gpr_log(GPR_ERROR, "Could not create rsa key.");
goto end;
}
for (key_prop = json->child; key_prop != NULL; key_prop = key_prop->next) {
if (strcmp(key_prop->key, "n") == 0) {
rsa->n = bignum_from_base64(validate_string_field(key_prop, "n"));
if (rsa->n == NULL) goto end;
} else if (strcmp(key_prop->key, "e") == 0) {
rsa->e = bignum_from_base64(validate_string_field(key_prop, "e"));
if (rsa->e == NULL) goto end;
}
}
if (rsa->e == NULL || rsa->n == NULL) {
gpr_log(GPR_ERROR, "Missing RSA public key field.");
goto end;
}
result = EVP_PKEY_new();
EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */
end:
if (rsa != NULL) RSA_free(rsa);
return result;
}
static EVP_PKEY *find_verification_key(const grpc_json *json,
const char *header_alg,
const char *header_kid) {
const grpc_json *jkey;
const grpc_json *jwk_keys;
/* Try to parse the json as a JWK set:
https://tools.ietf.org/html/rfc7517#section-5. */
jwk_keys = find_property_by_name(json, "keys");
if (jwk_keys == NULL) {
/* Use the google proprietary format which is:
{ <kid1>: <x5091>, <kid2>: <x5092>, ... } */
const grpc_json *cur = find_property_by_name(json, header_kid);
if (cur == NULL) return NULL;
return extract_pkey_from_x509(cur->value);
}
if (jwk_keys->type != GRPC_JSON_ARRAY) {
gpr_log(GPR_ERROR,
"Unexpected value type of keys property in jwks key set.");
return NULL;
}
/* Key format is specified in:
https://tools.ietf.org/html/rfc7518#section-6. */
for (jkey = jwk_keys->child; jkey != NULL; jkey = jkey->next) {
grpc_json *key_prop;
const char *alg = NULL;
const char *kid = NULL;
const char *kty = NULL;
if (jkey->type != GRPC_JSON_OBJECT) continue;
for (key_prop = jkey->child; key_prop != NULL; key_prop = key_prop->next) {
if (strcmp(key_prop->key, "alg") == 0 &&
key_prop->type == GRPC_JSON_STRING) {
alg = key_prop->value;
} else if (strcmp(key_prop->key, "kid") == 0 &&
key_prop->type == GRPC_JSON_STRING) {
kid = key_prop->value;
} else if (strcmp(key_prop->key, "kty") == 0 &&
key_prop->type == GRPC_JSON_STRING) {
kty = key_prop->value;
}
}
if (alg != NULL && kid != NULL && kty != NULL &&
strcmp(kid, header_kid) == 0 && strcmp(alg, header_alg) == 0) {
return pkey_from_jwk(jkey, kty);
}
}
gpr_log(GPR_ERROR,
"Could not find matching key in key set for kid=%s and alg=%s",
header_kid, header_alg);
return NULL;
}
static int verify_jwt_signature(EVP_PKEY *key, const char *alg,
gpr_slice signature, gpr_slice signed_data) {
EVP_MD_CTX *md_ctx = EVP_MD_CTX_create();
const EVP_MD *md = evp_md_from_alg(alg);
int result = 0;
GPR_ASSERT(md != NULL); /* Checked before. */
if (md_ctx == NULL) {
gpr_log(GPR_ERROR, "Could not create EVP_MD_CTX.");
goto end;
}
if (EVP_DigestVerifyInit(md_ctx, NULL, md, NULL, key) != 1) {
gpr_log(GPR_ERROR, "EVP_DigestVerifyInit failed.");
goto end;
}
if (EVP_DigestVerifyUpdate(md_ctx, GPR_SLICE_START_PTR(signed_data),
GPR_SLICE_LENGTH(signed_data)) != 1) {
gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed.");
goto end;
}
if (EVP_DigestVerifyFinal(md_ctx, GPR_SLICE_START_PTR(signature),
GPR_SLICE_LENGTH(signature)) != 1) {
gpr_log(GPR_ERROR, "JWT signature verification failed.");
goto end;
}
result = 1;
end:
if (md_ctx != NULL) EVP_MD_CTX_destroy(md_ctx);
return result;
}
static void on_keys_retrieved(void *user_data,
const grpc_httpcli_response *response) {
grpc_json *json = json_from_http(response);
verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
EVP_PKEY *verification_key = NULL;
grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR;
grpc_jwt_claims *claims = NULL;
if (json == NULL) {
status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
goto end;
}
verification_key =
find_verification_key(json, ctx->header->alg, ctx->header->kid);
if (verification_key == NULL) {
gpr_log(GPR_ERROR, "Could not find verification key with kid %s.",
ctx->header->kid);
status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
goto end;
}
if (!verify_jwt_signature(verification_key, ctx->header->alg, ctx->signature,
ctx->signed_data)) {
status = GRPC_JWT_VERIFIER_BAD_SIGNATURE;
goto end;
}
status = grpc_jwt_claims_check(ctx->claims, ctx->audience);
if (status == GRPC_JWT_VERIFIER_OK) {
/* Pass ownership. */
claims = ctx->claims;
ctx->claims = NULL;
}
end:
if (json != NULL) grpc_json_destroy(json);
if (verification_key != NULL) EVP_PKEY_free(verification_key);
ctx->user_cb(ctx->user_data, status, claims);
verifier_cb_ctx_destroy(ctx);
}
static void on_openid_config_retrieved(void *user_data,
const grpc_httpcli_response *response) {
const grpc_json* cur;
grpc_json *json = json_from_http(response);
verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
grpc_httpcli_request req;
const char *jwks_uri;
/* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time.*/
if (json == NULL) goto error;
cur = find_property_by_name(json, "jwks_uri");
if (cur == NULL) {
gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config.");
goto error;
}
jwks_uri = validate_string_field(cur, "jwks_uri");
if (jwks_uri == NULL) goto error;
if (strstr(jwks_uri, "https://") != jwks_uri) {
gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri);
goto error;
}
jwks_uri += 8;
req.use_ssl = 1;
req.host = gpr_strdup(jwks_uri);
req.path = strchr(jwks_uri, '/');
if (req.path == NULL) {
req.path = "";
} else {
*(req.host + (req.path - jwks_uri)) = '\0';
}
grpc_httpcli_get(&ctx->verifier->http_ctx, ctx->pollset, &req,
gpr_time_add(gpr_now(), grpc_jwt_verifier_max_delay),
on_keys_retrieved, ctx);
grpc_json_destroy(json);
gpr_free(req.host);
return;
error:
if (json != NULL) grpc_json_destroy(json);
ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL);
verifier_cb_ctx_destroy(ctx);
}
static email_key_mapping *verifier_get_mapping(
grpc_jwt_verifier *v, const char *email_domain) {
size_t i;
if (v->mappings == NULL) return NULL;
for (i = 0; i < v->num_mappings; i++) {
if (strcmp(email_domain, v->mappings[i].email_domain) == 0) {
return &v->mappings[i];
}
}
return NULL;
}
static void verifier_put_mapping(grpc_jwt_verifier *v, const char *email_domain,
const char *key_url_prefix) {
email_key_mapping *mapping = verifier_get_mapping(v, email_domain);
GPR_ASSERT(v->num_mappings < v->allocated_mappings);
if (mapping != NULL) {
gpr_free(mapping->key_url_prefix);
mapping->key_url_prefix = gpr_strdup(key_url_prefix);
return;
}
v->mappings[v->num_mappings].email_domain = gpr_strdup(email_domain);
v->mappings[v->num_mappings].key_url_prefix = gpr_strdup(key_url_prefix);
v->num_mappings++;
GPR_ASSERT(v->num_mappings <= v->allocated_mappings);
}
/* Takes ownership of ctx. */
static void retrieve_key_and_verify(verifier_cb_ctx *ctx) {
const char *at_sign;
grpc_httpcli_response_cb http_cb;
char *path_prefix = NULL;
const char *iss;
grpc_httpcli_request req;
memset(&req, 0, sizeof(grpc_httpcli_request));
req.use_ssl = 1;
GPR_ASSERT(ctx != NULL && ctx->header != NULL && ctx->claims != NULL);
iss = ctx->claims->iss;
if (ctx->header->kid == NULL) {
gpr_log(GPR_ERROR, "Missing kid in jose header.");
goto error;
}
if (iss == NULL) {
gpr_log(GPR_ERROR, "Missing iss in claims.");
goto error;
}
/* This code relies on:
https://openid.net/specs/openid-connect-discovery-1_0.html
Nobody seems to implement the account/email/webfinger part 2. of the spec
so we will rely instead on email/url mappings if we detect such an issuer.
Part 4, on the other hand is implemented by both google and salesforce. */
/* Very non-sophisticated way to detect an email address. Should be good
enough for now... */
at_sign = strchr(iss, '@');
if (at_sign != NULL) {
email_key_mapping *mapping;
const char *email_domain = at_sign + 1;
GPR_ASSERT(ctx->verifier != NULL);
mapping = verifier_get_mapping(ctx->verifier, email_domain);
if (mapping == NULL) {
gpr_log(GPR_ERROR, "Missing mapping for issuer email.");
goto error;
}
req.host = gpr_strdup(mapping->key_url_prefix);
path_prefix = strchr(req.host, '/');
if (path_prefix == NULL) {
gpr_asprintf(&req.path, "/%s", iss);
} else {
*(path_prefix++) = '\0';
gpr_asprintf(&req.path, "/%s/%s", path_prefix, iss);
}
http_cb = on_keys_retrieved;
} else {
req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
path_prefix = strchr(req.host, '/');
if (path_prefix == NULL) {
req.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX);
} else {
*(path_prefix++) = 0;
gpr_asprintf(&req.path, "/%s%s", path_prefix,
GRPC_OPENID_CONFIG_URL_SUFFIX);
}
http_cb = on_openid_config_retrieved;
}
grpc_httpcli_get(&ctx->verifier->http_ctx, ctx->pollset, &req,
gpr_time_add(gpr_now(), grpc_jwt_verifier_max_delay),
http_cb, ctx);
gpr_free(req.host);
gpr_free(req.path);
return;
error:
ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL);
verifier_cb_ctx_destroy(ctx);
}
void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier,
grpc_pollset *pollset, const char *jwt,
const char *audience,
grpc_jwt_verification_done_cb cb,
void *user_data) {
const char *dot = NULL;
grpc_json *json;
jose_header *header = NULL;
grpc_jwt_claims *claims = NULL;
gpr_slice header_buffer;
gpr_slice claims_buffer;
gpr_slice signature;
size_t signed_jwt_len;
const char *cur = jwt;
GPR_ASSERT(verifier != NULL && jwt != NULL && audience != NULL && cb != NULL);
dot = strchr(cur, '.');
if (dot == NULL) goto error;
json = parse_json_part_from_jwt(cur, dot - cur, &header_buffer);
if (json == NULL) goto error;
header = jose_header_from_json(json, header_buffer);
if (header == NULL) goto error;
cur = dot + 1;
dot = strchr(cur, '.');
if (dot == NULL) goto error;
json = parse_json_part_from_jwt(cur, dot - cur, &claims_buffer);
if (json == NULL) goto error;
claims = grpc_jwt_claims_from_json(json, claims_buffer);
if (claims == NULL) goto error;
signed_jwt_len = (size_t)(dot - jwt);
cur = dot + 1;
signature = grpc_base64_decode(cur, 1);
if (GPR_SLICE_IS_EMPTY(signature)) goto error;
retrieve_key_and_verify(
verifier_cb_ctx_create(verifier, pollset, header, claims, audience,
signature, jwt, signed_jwt_len, user_data, cb));
return;
error:
if (header != NULL) jose_header_destroy(header);
if (claims != NULL) grpc_jwt_claims_destroy(claims);
cb(user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, NULL);
}
grpc_jwt_verifier *grpc_jwt_verifier_create(
const grpc_jwt_verifier_email_domain_key_url_mapping *mappings,
size_t num_mappings) {
grpc_jwt_verifier *v = gpr_malloc(sizeof(grpc_jwt_verifier));
memset(v, 0, sizeof(grpc_jwt_verifier));
grpc_httpcli_context_init(&v->http_ctx);
/* We know at least of one mapping. */
v->allocated_mappings = 1 + num_mappings;
v->mappings = gpr_malloc(v->allocated_mappings * sizeof(email_key_mapping));
verifier_put_mapping(v, GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN,
GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX);
/* User-Provided mappings. */
if (mappings != NULL) {
size_t i;
for (i = 0; i < num_mappings; i++) {
verifier_put_mapping(v, mappings[i].email_domain,
mappings[i].key_url_prefix);
}
}
return v;
}
void grpc_jwt_verifier_destroy(grpc_jwt_verifier *v) {
size_t i;
if (v == NULL) return;
grpc_httpcli_context_destroy(&v->http_ctx);
if (v->mappings != NULL) {
for (i = 0; i < v->num_mappings; i++) {
gpr_free(v->mappings[i].email_domain);
gpr_free(v->mappings[i].key_url_prefix);
}
gpr_free(v->mappings);
}
gpr_free(v);
}

@ -0,0 +1,136 @@
/*
*
* 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 disclaimser.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimser
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H
#define GRPC_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H
#include "src/core/iomgr/pollset.h"
#include "src/core/json/json.h"
#include <grpc/support/slice.h>
#include <grpc/support/time.h>
/* --- Constants. --- */
#define GRPC_OPENID_CONFIG_URL_SUFFIX "/.well-known/openid-configuration"
#define GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN \
"developer.gserviceaccount.com"
#define GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX \
"www.googleapis.com/robot/v1/metadata/x509"
/* --- grpc_jwt_verifier_status. --- */
typedef enum {
GRPC_JWT_VERIFIER_OK = 0,
GRPC_JWT_VERIFIER_BAD_SIGNATURE,
GRPC_JWT_VERIFIER_BAD_FORMAT,
GRPC_JWT_VERIFIER_BAD_AUDIENCE,
GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR,
GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE,
GRPC_JWT_VERIFIER_GENERIC_ERROR
} grpc_jwt_verifier_status;
const char *grpc_jwt_verifier_status_to_string(grpc_jwt_verifier_status status);
/* --- grpc_jwt_claims. --- */
typedef struct grpc_jwt_claims grpc_jwt_claims;
void grpc_jwt_claims_destroy(grpc_jwt_claims *claims);
/* Returns the whole JSON tree of the claims. */
const grpc_json *grpc_jwt_claims_json(const grpc_jwt_claims *claims);
/* Access to registered claims in https://tools.ietf.org/html/rfc7519#page-9 */
const char *grpc_jwt_claims_subject(const grpc_jwt_claims *claims);
const char *grpc_jwt_claims_issuer(const grpc_jwt_claims *claims);
const char *grpc_jwt_claims_id(const grpc_jwt_claims *claims);
const char *grpc_jwt_claims_audience(const grpc_jwt_claims *claims);
gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims *claims);
gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims *claims);
gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims);
/* --- grpc_jwt_verifier. --- */
typedef struct grpc_jwt_verifier grpc_jwt_verifier;
typedef struct {
/* The email domain is the part after the @ sign. */
const char *email_domain;
/* The key url prefix will be used to get the public key from the issuer:
https://<key_url_prefix>/<issuer_email>
Therefore the key_url_prefix must NOT contain https://. */
const char *key_url_prefix;
} grpc_jwt_verifier_email_domain_key_url_mapping;
/* Globals to control the verifier. Not thread-safe. */
extern gpr_timespec grpc_jwt_verifier_clock_skew;
extern gpr_timespec grpc_jwt_verifier_max_delay;
/* The verifier can be created with some custom mappings to help with key
discovery in the case where the issuer is an email address.
mappings can be NULL in which case num_mappings MUST be 0.
A verifier object has one built-in mapping (unless overridden):
GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN ->
GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX.*/
grpc_jwt_verifier *grpc_jwt_verifier_create(
const grpc_jwt_verifier_email_domain_key_url_mapping *mappings,
size_t num_mappings);
/*The verifier must not be destroyed if there are still outstanding callbacks.*/
void grpc_jwt_verifier_destroy(grpc_jwt_verifier *verifier);
/* User provided callback that will be called when the verification of the JWT
is done (maybe in another thread).
It is the responsibility of the callee to call grpc_jwt_claims_destroy on
the claims. */
typedef void (*grpc_jwt_verification_done_cb)(void *user_data,
grpc_jwt_verifier_status status,
grpc_jwt_claims *claims);
/* Verifies for the JWT for the given expected audience. */
void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier,
grpc_pollset *pollset, const char *jwt,
const char *audience,
grpc_jwt_verification_done_cb cb,
void *user_data);
/* --- TESTING ONLY exposed functions. --- */
grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, gpr_slice buffer);
grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims,
const char *audience);
#endif /* GRPC_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H */

@ -101,9 +101,7 @@ static void call_read_cb(secure_endpoint *ep, gpr_slice *slices, size_t nslices,
if (grpc_trace_secure_endpoint) {
size_t i;
for (i = 0; i < nslices; i++) {
char *data =
gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]),
GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT);
char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_log(GPR_DEBUG, "READ %p: %s", ep, data);
gpr_free(data);
}
@ -235,9 +233,7 @@ static grpc_endpoint_write_status endpoint_write(grpc_endpoint *secure_ep,
if (grpc_trace_secure_endpoint) {
for (i = 0; i < nslices; i++) {
char *data =
gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]),
GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT);
char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_log(GPR_DEBUG, "WRITE %p: %s", ep, data);
gpr_free(data);
}

@ -69,12 +69,20 @@ grpc_call_error grpc_call_set_credentials(grpc_call *call,
return GRPC_CALL_OK;
}
const grpc_auth_context *grpc_call_auth_context(grpc_call *call) {
grpc_auth_context *grpc_call_auth_context(grpc_call *call) {
void *sec_ctx = grpc_call_context_get(call, GRPC_CONTEXT_SECURITY);
if (sec_ctx == NULL) return NULL;
return grpc_call_is_client(call)
? ((grpc_client_security_context *)sec_ctx)->auth_context
: ((grpc_server_security_context *)sec_ctx)->auth_context;
? GRPC_AUTH_CONTEXT_REF(
((grpc_client_security_context *)sec_ctx)->auth_context,
"grpc_call_auth_context client")
: GRPC_AUTH_CONTEXT_REF(
((grpc_server_security_context *)sec_ctx)->auth_context,
"grpc_call_auth_context server");
}
void grpc_auth_context_release(grpc_auth_context *context) {
GRPC_AUTH_CONTEXT_UNREF(context, "grpc_auth_context_unref");
}
/* --- grpc_client_security_context --- */

@ -36,6 +36,10 @@
#include "src/core/security/credentials.h"
#ifdef __cplusplus
extern "C" {
#endif
/* --- grpc_auth_context ---
High level authentication context object. Can optionally be chained. */
@ -103,5 +107,9 @@ typedef struct {
grpc_server_security_context *grpc_server_security_context_create(void);
void grpc_server_security_context_destroy(void *ctx);
#ifdef __cplusplus
}
#endif
#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */

@ -325,3 +325,10 @@ int gpr_slice_str_cmp(gpr_slice a, const char *b) {
if (d != 0) return d;
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;
}

@ -61,14 +61,14 @@ typedef struct {
size_t capacity;
size_t length;
char *data;
} hexout;
} dump_out;
static hexout hexout_create(void) {
hexout r = {0, 0, NULL};
static dump_out dump_out_create(void) {
dump_out r = {0, 0, NULL};
return r;
}
static void hexout_append(hexout *out, char c) {
static void dump_out_append(dump_out *out, char c) {
if (out->length == out->capacity) {
out->capacity = GPR_MAX(8, 2 * out->capacity);
out->data = gpr_realloc(out->data, out->capacity);
@ -76,34 +76,55 @@ static void hexout_append(hexout *out, char c) {
out->data[out->length++] = c;
}
char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags) {
static void hexdump(dump_out *out, const char *buf, size_t len) {
static const char hex[16] = "0123456789abcdef";
hexout out = hexout_create();
const gpr_uint8 *const beg = (const gpr_uint8 *)buf;
const gpr_uint8 *const end = beg + len;
const gpr_uint8 *cur;
for (cur = beg; cur != end; ++cur) {
if (cur != beg) hexout_append(&out, ' ');
hexout_append(&out, hex[*cur >> 4]);
hexout_append(&out, hex[*cur & 0xf]);
if (cur != beg) dump_out_append(out, ' ');
dump_out_append(out, hex[*cur >> 4]);
dump_out_append(out, hex[*cur & 0xf]);
}
}
if (flags & GPR_HEXDUMP_PLAINTEXT) {
if (len) hexout_append(&out, ' ');
hexout_append(&out, '\'');
for (cur = beg; cur != end; ++cur) {
hexout_append(&out, isprint(*cur) ? *(char*)cur : '.');
}
hexout_append(&out, '\'');
static void asciidump(dump_out *out, const char *buf, size_t len) {
const gpr_uint8 *const beg = (const gpr_uint8 *)buf;
const gpr_uint8 *const end = beg + len;
const gpr_uint8 *cur;
int out_was_empty = (out->length == 0);
if (!out_was_empty) {
dump_out_append(out, ' ');
dump_out_append(out, '\'');
}
for (cur = beg; cur != end; ++cur) {
dump_out_append(out, isprint(*cur) ? *(char *)cur : '.');
}
if (!out_was_empty) {
dump_out_append(out, '\'');
}
}
hexout_append(&out, 0);
char *gpr_dump(const char *buf, size_t len, gpr_uint32 flags) {
dump_out out = dump_out_create();
if (flags & GPR_DUMP_HEX) {
hexdump(&out, buf, len);
}
if (flags & GPR_DUMP_ASCII) {
asciidump(&out, buf, len);
}
dump_out_append(&out, 0);
return out.data;
}
char *gpr_dump_slice(gpr_slice s, gpr_uint32 flags) {
return gpr_dump((const char *)GPR_SLICE_START_PTR(s), GPR_SLICE_LENGTH(s),
flags);
}
int gpr_parse_bytes_to_uint32(const char *buf, size_t len, gpr_uint32 *result) {
gpr_uint32 out = 0;
gpr_uint32 new;

@ -37,6 +37,7 @@
#include <stddef.h>
#include <grpc/support/port_platform.h>
#include <grpc/support/slice.h>
#ifdef __cplusplus
extern "C" {
@ -44,12 +45,16 @@ extern "C" {
/* String utility functions */
/* flag to include plaintext after a hexdump */
#define GPR_HEXDUMP_PLAINTEXT 0x00000001
/* Flags for gpr_dump function. */
#define GPR_DUMP_HEX 0x00000001
#define GPR_DUMP_ASCII 0x00000002
/* Converts array buf, of length len, into a hexadecimal dump. Result should
be freed with gpr_free() */
char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags);
/* Converts array buf, of length len, into a C string according to the flags.
Result should be freed with gpr_free() */
char *gpr_dump(const char *buf, size_t len, gpr_uint32 flags);
/* Calls gpr_dump on a slice. */
char *gpr_dump_slice(gpr_slice slice, gpr_uint32 flags);
/* Parses an array of bytes into an integer (base 10). Returns 1 on success,
0 on failure. */

@ -298,8 +298,6 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
if (call->is_client) {
call->request_set[GRPC_IOREQ_SEND_TRAILING_METADATA] = REQSET_DONE;
call->request_set[GRPC_IOREQ_SEND_STATUS] = REQSET_DONE;
call->context[GRPC_CONTEXT_TRACING].value = grpc_census_context_create();
call->context[GRPC_CONTEXT_TRACING].destroy = grpc_census_context_destroy;
}
GPR_ASSERT(add_initial_metadata_count < MAX_SEND_INITIAL_METADATA_COUNT);
for (i = 0; i < add_initial_metadata_count; i++) {
@ -369,18 +367,18 @@ static void destroy_call(void *call, int ignored_success) {
gpr_mu_destroy(&c->mu);
for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
if (c->status[i].details) {
grpc_mdstr_unref(c->status[i].details);
GRPC_MDSTR_UNREF(c->status[i].details);
}
}
for (i = 0; i < c->owned_metadata_count; i++) {
grpc_mdelem_unref(c->owned_metadata[i]);
GRPC_MDELEM_UNREF(c->owned_metadata[i]);
}
gpr_free(c->owned_metadata);
for (i = 0; i < GPR_ARRAY_SIZE(c->buffered_metadata); i++) {
gpr_free(c->buffered_metadata[i].metadata);
}
for (i = 0; i < c->send_initial_metadata_count; i++) {
grpc_mdelem_unref(c->send_initial_metadata[i].md);
GRPC_MDELEM_UNREF(c->send_initial_metadata[i].md);
}
for (i = 0; i < GRPC_CONTEXT_COUNT; i++) {
if (c->context[i].destroy) {
@ -437,7 +435,7 @@ static void set_decode_compression_level(grpc_call *call,
static void set_status_details(grpc_call *call, status_source source,
grpc_mdstr *status) {
if (call->status[source].details != NULL) {
grpc_mdstr_unref(call->status[source].details);
GRPC_MDSTR_UNREF(call->status[source].details);
}
call->status[source].details = status;
}
@ -616,7 +614,7 @@ static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op,
case GRPC_IOREQ_SEND_STATUS:
if (call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details !=
NULL) {
grpc_mdstr_unref(
GRPC_MDSTR_UNREF(
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details);
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
NULL;
@ -945,7 +943,7 @@ static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op) {
&mdb, &call->details_link,
grpc_mdelem_from_metadata_strings(
call->metadata_context,
grpc_mdstr_ref(
GRPC_MDSTR_REF(
grpc_channel_get_message_string(call->channel)),
data.send_status.details));
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
@ -1053,7 +1051,7 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs,
reqs[i].data.send_status.code);
if (reqs[i].data.send_status.details) {
set_status_details(call, STATUS_FROM_SERVER_STATUS,
grpc_mdstr_ref(reqs[i].data.send_status.details));
GRPC_MDSTR_REF(reqs[i].data.send_status.details));
}
}
have_ops |= 1u << op;
@ -1257,7 +1255,7 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) {
if (key == grpc_channel_get_status_string(call->channel)) {
set_status_code(call, STATUS_FROM_WIRE, decode_status(md));
} else if (key == grpc_channel_get_message_string(call->channel)) {
set_status_details(call, STATUS_FROM_WIRE, grpc_mdstr_ref(md->value));
set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(md->value));
} else if (key ==
grpc_channel_get_compresssion_level_string(call->channel)) {
set_decode_compression_level(call, decode_compression(md));
@ -1293,10 +1291,10 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) {
grpc_mdctx_lock(mdctx);
for (l = md->list.head; l; l = l->next) {
if (l->md) grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
if (l->md) GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
}
for (l = md->garbage.head; l; l = l->next) {
grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
}
grpc_mdctx_unlock(mdctx);
}

@ -46,8 +46,8 @@ static void add_metadata(gpr_strvec *b, const grpc_metadata *md, size_t count) {
gpr_strvec_add(b, gpr_strdup(md[i].key));
gpr_strvec_add(b, gpr_strdup(" value="));
gpr_strvec_add(b, gpr_hexdump(md[i].value, md[i].value_length,
GPR_HEXDUMP_PLAINTEXT));
gpr_strvec_add(b, gpr_dump(md[i].value, md[i].value_length,
GPR_DUMP_HEX | GPR_DUMP_ASCII));
}
}

@ -104,7 +104,7 @@ grpc_channel *grpc_channel_create_from_filters(
char buf[GPR_LTOA_MIN_BUFSIZE];
gpr_ltoa(i, buf);
channel->grpc_status_elem[i] = grpc_mdelem_from_metadata_strings(
mdctx, grpc_mdstr_ref(channel->grpc_status_string),
mdctx, GRPC_MDSTR_REF(channel->grpc_status_string),
grpc_mdstr_from_string(mdctx, buf));
}
channel->path_string = grpc_mdstr_from_string(mdctx, ":path");
@ -157,10 +157,10 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel,
return grpc_channel_create_call_internal(
channel, cq,
grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->path_string),
channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
grpc_mdstr_from_string(channel->metadata_context, method)),
grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->authority_string),
channel->metadata_context, GRPC_MDSTR_REF(channel->authority_string),
grpc_mdstr_from_string(channel->metadata_context, host)),
deadline);
}
@ -169,10 +169,10 @@ void *grpc_channel_register_call(grpc_channel *channel, const char *method,
const char *host) {
registered_call *rc = gpr_malloc(sizeof(registered_call));
rc->path = grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->path_string),
channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
grpc_mdstr_from_string(channel->metadata_context, method));
rc->authority = grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->authority_string),
channel->metadata_context, GRPC_MDSTR_REF(channel->authority_string),
grpc_mdstr_from_string(channel->metadata_context, host));
gpr_mu_lock(&channel->registered_call_mu);
rc->next = channel->registered_calls;
@ -186,8 +186,8 @@ grpc_call *grpc_channel_create_registered_call(
void *registered_call_handle, gpr_timespec deadline) {
registered_call *rc = registered_call_handle;
return grpc_channel_create_call_internal(
channel, completion_queue, grpc_mdelem_ref(rc->path),
grpc_mdelem_ref(rc->authority), deadline);
channel, completion_queue, GRPC_MDELEM_REF(rc->path),
GRPC_MDELEM_REF(rc->authority), deadline);
}
#ifdef GRPC_CHANNEL_REF_COUNT_DEBUG
@ -205,18 +205,18 @@ static void destroy_channel(void *p, int ok) {
size_t i;
grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel));
for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) {
grpc_mdelem_unref(channel->grpc_status_elem[i]);
GRPC_MDELEM_UNREF(channel->grpc_status_elem[i]);
}
grpc_mdstr_unref(channel->grpc_status_string);
grpc_mdstr_unref(channel->grpc_compression_level_string);
grpc_mdstr_unref(channel->grpc_message_string);
grpc_mdstr_unref(channel->path_string);
grpc_mdstr_unref(channel->authority_string);
GRPC_MDSTR_UNREF(channel->grpc_status_string);
GRPC_MDSTR_UNREF(channel->grpc_compression_level_string);
GRPC_MDSTR_UNREF(channel->grpc_message_string);
GRPC_MDSTR_UNREF(channel->path_string);
GRPC_MDSTR_UNREF(channel->authority_string);
while (channel->registered_calls) {
registered_call *rc = channel->registered_calls;
channel->registered_calls = rc->next;
grpc_mdelem_unref(rc->path);
grpc_mdelem_unref(rc->authority);
GRPC_MDELEM_UNREF(rc->path);
GRPC_MDELEM_UNREF(rc->authority);
gpr_free(rc);
}
grpc_mdctx_unref(channel->metadata_context);
@ -267,12 +267,12 @@ grpc_mdstr *grpc_channel_get_compresssion_level_string(grpc_channel *channel) {
grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
if (i >= 0 && i < NUM_CACHED_STATUS_ELEMS) {
return grpc_mdelem_ref(channel->grpc_status_elem[i]);
return GRPC_MDELEM_REF(channel->grpc_status_elem[i]);
} else {
char tmp[GPR_LTOA_MIN_BUFSIZE];
gpr_ltoa(i, tmp);
return grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->grpc_status_string),
channel->metadata_context, GRPC_MDSTR_REF(channel->grpc_status_string),
grpc_mdstr_from_string(channel->metadata_context, tmp));
}
}

@ -51,7 +51,7 @@
#include <grpc/support/string_util.h>
#include <grpc/support/useful.h>
typedef enum { PENDING_START, ALL_CALLS, CALL_LIST_COUNT } call_list;
typedef enum { PENDING_START, CALL_LIST_COUNT } call_list;
typedef struct listener {
void *arg;
@ -114,7 +114,6 @@ typedef struct channel_registered_method {
struct channel_data {
grpc_server *server;
size_t num_calls;
grpc_connectivity_state connectivity_state;
grpc_channel *channel;
grpc_mdstr *path_key;
@ -167,6 +166,9 @@ struct grpc_server {
listener *listeners;
int listeners_destroyed;
gpr_refcount internal_refcount;
/** when did we print the last shutdown progress message */
gpr_timespec last_shutdown_message_time;
};
typedef enum {
@ -183,7 +185,11 @@ typedef enum {
struct call_data {
grpc_call *call;
/** protects state */
gpr_mu mu_state;
/** the current state of a call - see call_state */
call_state state;
grpc_mdstr *path;
grpc_mdstr *host;
gpr_timespec deadline;
@ -204,9 +210,7 @@ struct call_data {
typedef struct {
grpc_channel **channels;
grpc_channel **disconnects;
size_t num_channels;
size_t num_disconnects;
} channel_broadcaster;
#define SERVER_FROM_CALL_ELEM(elem) \
@ -225,26 +229,15 @@ static void maybe_finish_shutdown(grpc_server *server);
static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) {
channel_data *c;
size_t count = 0;
size_t dc_count = 0;
for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) {
count++;
if (c->num_calls == 0) {
dc_count++;
}
}
cb->num_channels = count;
cb->num_disconnects = dc_count;
cb->channels = gpr_malloc(sizeof(*cb->channels) * cb->num_channels);
cb->disconnects = gpr_malloc(sizeof(*cb->channels) * cb->num_disconnects);
count = 0;
dc_count = 0;
for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) {
cb->channels[count++] = c->channel;
GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast");
if (c->num_calls == 0) {
cb->disconnects[dc_count++] = c->channel;
GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast-disconnect");
}
}
}
@ -280,19 +273,14 @@ static void send_shutdown(grpc_channel *channel, int send_goaway,
}
static void channel_broadcaster_shutdown(channel_broadcaster *cb,
int send_goaway, int send_disconnect) {
int send_goaway, int force_disconnect) {
size_t i;
for (i = 0; i < cb->num_channels; i++) {
send_shutdown(cb->channels[i], 1, 0);
send_shutdown(cb->channels[i], send_goaway, force_disconnect);
GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast");
}
for (i = 0; i < cb->num_disconnects; i++) {
send_shutdown(cb->disconnects[i], 0, 1);
GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast-disconnect");
}
gpr_free(cb->channels);
gpr_free(cb->disconnects);
}
/* call list */
@ -422,19 +410,23 @@ static void destroy_channel(channel_data *chand) {
grpc_iomgr_add_callback(&chand->finish_destroy_channel_closure);
}
static void finish_start_new_rpc_and_unlock(grpc_server *server,
grpc_call_element *elem,
call_data **pending_root,
requested_call_array *array) {
static void finish_start_new_rpc(grpc_server *server, grpc_call_element *elem,
call_data **pending_root,
requested_call_array *array) {
requested_call rc;
call_data *calld = elem->call_data;
gpr_mu_lock(&server->mu_call);
if (array->count == 0) {
gpr_mu_lock(&calld->mu_state);
calld->state = PENDING;
gpr_mu_unlock(&calld->mu_state);
call_list_join(pending_root, calld, PENDING_START);
gpr_mu_unlock(&server->mu_call);
} else {
rc = array->calls[--array->count];
gpr_mu_lock(&calld->mu_state);
calld->state = ACTIVATED;
gpr_mu_unlock(&calld->mu_state);
gpr_mu_unlock(&server->mu_call);
begin_call(server, calld, &rc);
}
@ -448,20 +440,18 @@ static void start_new_rpc(grpc_call_element *elem) {
gpr_uint32 hash;
channel_registered_method *rm;
gpr_mu_lock(&server->mu_call);
if (chand->registered_methods && calld->path && calld->host) {
/* TODO(ctiller): unify these two searches */
/* check for an exact match with host */
hash = GRPC_MDSTR_KV_HASH(calld->host->hash, calld->path->hash);
for (i = 0; i < chand->registered_method_max_probes; i++) {
for (i = 0; i <= chand->registered_method_max_probes; i++) {
rm = &chand->registered_methods[(hash + i) %
chand->registered_method_slots];
if (!rm) break;
if (rm->host != calld->host) continue;
if (rm->method != calld->path) continue;
finish_start_new_rpc_and_unlock(server, elem,
&rm->server_registered_method->pending,
&rm->server_registered_method->requested);
finish_start_new_rpc(server, elem, &rm->server_registered_method->pending,
&rm->server_registered_method->requested);
return;
}
/* check for a wildcard method definition (no host set) */
@ -472,14 +462,13 @@ static void start_new_rpc(grpc_call_element *elem) {
if (!rm) break;
if (rm->host != NULL) continue;
if (rm->method != calld->path) continue;
finish_start_new_rpc_and_unlock(server, elem,
&rm->server_registered_method->pending,
&rm->server_registered_method->requested);
finish_start_new_rpc(server, elem, &rm->server_registered_method->pending,
&rm->server_registered_method->requested);
return;
}
}
finish_start_new_rpc_and_unlock(server, elem, &server->lists[PENDING_START],
&server->requested_calls);
finish_start_new_rpc(server, elem, &server->lists[PENDING_START],
&server->requested_calls);
}
static void kill_zombie(void *elem, int success) {
@ -495,29 +484,35 @@ static int num_listeners(grpc_server *server) {
return n;
}
static int num_channels(grpc_server *server) {
channel_data *chand;
int n = 0;
for (chand = server->root_channel_data.next;
chand != &server->root_channel_data; chand = chand->next) {
n++;
}
return n;
}
static void maybe_finish_shutdown(grpc_server *server) {
size_t i;
if (!server->shutdown || server->shutdown_published) {
return;
}
gpr_mu_lock(&server->mu_call);
if (server->lists[ALL_CALLS] != NULL) {
gpr_log(GPR_DEBUG,
"Waiting for all calls to finish before destroying server");
gpr_mu_unlock(&server->mu_call);
return;
}
gpr_mu_unlock(&server->mu_call);
if (server->root_channel_data.next != &server->root_channel_data) {
gpr_log(GPR_DEBUG,
"Waiting for all channels to close before destroying server");
return;
}
if (server->listeners_destroyed < num_listeners(server)) {
gpr_log(GPR_DEBUG, "Waiting for all listeners to be destroyed (@ %d/%d)",
server->listeners_destroyed, num_listeners(server));
if (server->root_channel_data.next != &server->root_channel_data ||
server->listeners_destroyed < num_listeners(server)) {
if (gpr_time_cmp(
gpr_time_sub(gpr_now(), server->last_shutdown_message_time),
gpr_time_from_seconds(1)) >= 0) {
server->last_shutdown_message_time = gpr_now();
gpr_log(GPR_DEBUG,
"Waiting for %d channels and %d/%d listeners to be destroyed"
" before shutting down server",
num_channels(server),
num_listeners(server) - server->listeners_destroyed,
num_listeners(server));
}
return;
}
server->shutdown_published = 1;
@ -532,31 +527,19 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
if (md->key == chand->path_key) {
calld->path = grpc_mdstr_ref(md->value);
calld->path = GRPC_MDSTR_REF(md->value);
return NULL;
} else if (md->key == chand->authority_key) {
calld->host = grpc_mdstr_ref(md->value);
calld->host = GRPC_MDSTR_REF(md->value);
return NULL;
}
return md;
}
static int decrement_call_count(channel_data *chand) {
int disconnect = 0;
chand->num_calls--;
if (0 == chand->num_calls && chand->server->shutdown) {
disconnect = 1;
}
maybe_finish_shutdown(chand->server);
return disconnect;
}
static void server_on_recv(void *ptr, int success) {
grpc_call_element *elem = ptr;
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
int remove_res;
int disconnect = 0;
if (success && !calld->got_initial_metadata) {
size_t i;
@ -581,39 +564,33 @@ static void server_on_recv(void *ptr, int success) {
case GRPC_STREAM_SEND_CLOSED:
break;
case GRPC_STREAM_RECV_CLOSED:
gpr_mu_lock(&chand->server->mu_call);
gpr_mu_lock(&calld->mu_state);
if (calld->state == NOT_STARTED) {
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
grpc_iomgr_add_callback(&calld->kill_zombie_closure);
} else {
gpr_mu_unlock(&calld->mu_state);
}
gpr_mu_unlock(&chand->server->mu_call);
break;
case GRPC_STREAM_CLOSED:
gpr_mu_lock(&chand->server->mu_call);
gpr_mu_lock(&calld->mu_state);
if (calld->state == NOT_STARTED) {
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
grpc_iomgr_add_callback(&calld->kill_zombie_closure);
} else if (calld->state == PENDING) {
call_list_remove(calld, PENDING_START);
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
gpr_mu_lock(&chand->server->mu_call);
call_list_remove(calld, PENDING_START);
gpr_mu_unlock(&chand->server->mu_call);
grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
grpc_iomgr_add_callback(&calld->kill_zombie_closure);
}
remove_res = call_list_remove(calld, ALL_CALLS);
gpr_mu_unlock(&chand->server->mu_call);
gpr_mu_lock(&chand->server->mu_global);
if (remove_res) {
disconnect = decrement_call_count(chand);
if (disconnect) {
GRPC_CHANNEL_INTERNAL_REF(chand->channel, "send-disconnect");
}
}
gpr_mu_unlock(&chand->server->mu_global);
if (disconnect) {
send_shutdown(chand->channel, 0, 1);
GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "send-disconnect");
} else {
gpr_mu_unlock(&calld->mu_state);
}
break;
}
@ -676,17 +653,10 @@ static void init_call_elem(grpc_call_element *elem,
memset(calld, 0, sizeof(call_data));
calld->deadline = gpr_inf_future;
calld->call = grpc_call_from_top_element(elem);
gpr_mu_init(&calld->mu_state);
grpc_iomgr_closure_init(&calld->server_on_recv, server_on_recv, elem);
gpr_mu_lock(&chand->server->mu_call);
call_list_join(&chand->server->lists[ALL_CALLS], calld, ALL_CALLS);
gpr_mu_unlock(&chand->server->mu_call);
gpr_mu_lock(&chand->server->mu_global);
chand->num_calls++;
gpr_mu_unlock(&chand->server->mu_global);
server_ref(chand->server);
if (initial_op) server_mutate_op(elem, initial_op);
@ -695,27 +665,22 @@ static void init_call_elem(grpc_call_element *elem,
static void destroy_call_elem(grpc_call_element *elem) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
int removed[CALL_LIST_COUNT];
size_t i;
gpr_mu_lock(&chand->server->mu_call);
for (i = 0; i < CALL_LIST_COUNT; i++) {
removed[i] = call_list_remove(elem->call_data, i);
}
gpr_mu_unlock(&chand->server->mu_call);
if (removed[ALL_CALLS]) {
gpr_mu_lock(&chand->server->mu_global);
decrement_call_count(chand);
gpr_mu_unlock(&chand->server->mu_global);
if (calld->state == PENDING) {
gpr_mu_lock(&chand->server->mu_call);
call_list_remove(elem->call_data, PENDING_START);
gpr_mu_unlock(&chand->server->mu_call);
}
if (calld->host) {
grpc_mdstr_unref(calld->host);
GRPC_MDSTR_UNREF(calld->host);
}
if (calld->path) {
grpc_mdstr_unref(calld->path);
GRPC_MDSTR_UNREF(calld->path);
}
gpr_mu_destroy(&calld->mu_state);
server_unref(chand->server);
}
@ -727,7 +692,6 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master,
GPR_ASSERT(is_first);
GPR_ASSERT(!is_last);
chand->server = NULL;
chand->num_calls = 0;
chand->channel = NULL;
chand->path_key = grpc_mdstr_from_string(metadata_context, ":path");
chand->authority_key = grpc_mdstr_from_string(metadata_context, ":authority");
@ -744,10 +708,10 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
if (chand->registered_methods) {
for (i = 0; i < chand->registered_method_slots; i++) {
if (chand->registered_methods[i].method) {
grpc_mdstr_unref(chand->registered_methods[i].method);
GRPC_MDSTR_UNREF(chand->registered_methods[i].method);
}
if (chand->registered_methods[i].host) {
grpc_mdstr_unref(chand->registered_methods[i].host);
GRPC_MDSTR_UNREF(chand->registered_methods[i].host);
}
}
gpr_free(chand->registered_methods);
@ -759,8 +723,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
chand->next = chand->prev = chand;
maybe_finish_shutdown(chand->server);
gpr_mu_unlock(&chand->server->mu_global);
grpc_mdstr_unref(chand->path_key);
grpc_mdstr_unref(chand->authority_key);
GRPC_MDSTR_UNREF(chand->path_key);
GRPC_MDSTR_UNREF(chand->authority_key);
server_unref(chand->server);
}
}
@ -998,6 +962,8 @@ void grpc_server_shutdown_and_notify(grpc_server *server,
return;
}
server->last_shutdown_message_time = gpr_now();
channel_broadcaster_init(server, &broadcaster);
/* collect all unregistered then registered calls */
@ -1049,47 +1015,13 @@ void grpc_server_listener_destroy_done(void *s) {
}
void grpc_server_cancel_all_calls(grpc_server *server) {
call_data *calld;
grpc_call **calls;
size_t call_count;
size_t call_capacity;
int is_first = 1;
size_t i;
gpr_mu_lock(&server->mu_call);
GPR_ASSERT(server->shutdown);
if (!server->lists[ALL_CALLS]) {
gpr_mu_unlock(&server->mu_call);
return;
}
call_capacity = 8;
call_count = 0;
calls = gpr_malloc(sizeof(grpc_call *) * call_capacity);
for (calld = server->lists[ALL_CALLS];
calld != server->lists[ALL_CALLS] || is_first;
calld = calld->links[ALL_CALLS].next) {
if (call_count == call_capacity) {
call_capacity *= 2;
calls = gpr_realloc(calls, sizeof(grpc_call *) * call_capacity);
}
calls[call_count++] = calld->call;
GRPC_CALL_INTERNAL_REF(calld->call, "cancel_all");
is_first = 0;
}
gpr_mu_unlock(&server->mu_call);
channel_broadcaster broadcaster;
for (i = 0; i < call_count; i++) {
grpc_call_cancel_with_status(calls[i], GRPC_STATUS_UNAVAILABLE,
"Unavailable");
GRPC_CALL_INTERNAL_UNREF(calls[i], "cancel_all", 1);
}
gpr_mu_lock(&server->mu_global);
channel_broadcaster_init(server, &broadcaster);
gpr_mu_unlock(&server->mu_global);
gpr_free(calls);
channel_broadcaster_shutdown(&broadcaster, 0, 1);
}
void grpc_server_destroy(grpc_server *server) {
@ -1145,10 +1077,12 @@ static grpc_call_error queue_call_request(grpc_server *server,
requested_calls = &rc->data.registered.registered_method->requested;
break;
}
if (calld) {
if (calld != NULL) {
gpr_mu_unlock(&server->mu_call);
gpr_mu_lock(&calld->mu_state);
GPR_ASSERT(calld->state == PENDING);
calld->state = ACTIVATED;
gpr_mu_unlock(&server->mu_call);
gpr_mu_unlock(&calld->mu_state);
begin_call(server, calld, rc);
return GRPC_CALL_OK;
} else {

@ -622,7 +622,7 @@ static const gpr_uint8 inverse_base64[256] = {
static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
int add_to_table) {
if (add_to_table) {
grpc_mdelem_ref(md);
GRPC_MDELEM_REF(md);
grpc_chttp2_hptbl_add(&p->table, md);
}
p->on_header(p->on_header_user_data, md);
@ -711,7 +711,7 @@ static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
static int finish_indexed_field(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
grpc_mdelem_ref(md);
GRPC_MDELEM_REF(md);
on_hdr(p, md, 0);
return parse_begin(p, cur, end);
}
@ -740,7 +740,7 @@ static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key),
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
1);
return parse_begin(p, cur, end);
@ -793,7 +793,7 @@ static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key),
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0);
return parse_begin(p, cur, end);
@ -846,7 +846,7 @@ static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key),
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0);
return parse_begin(p, cur, end);
@ -1329,17 +1329,14 @@ static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p,
/* PUBLIC INTERFACE */
static void on_header_not_set(void *user_data, grpc_mdelem *md) {
char *keyhex =
gpr_hexdump(grpc_mdstr_as_c_string(md->key),
GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT);
char *keyhex = gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
char *valuehex =
gpr_hexdump(grpc_mdstr_as_c_string(md->value),
GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT);
gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_log(GPR_ERROR, "on_header callback not set; key=%s value=%s", keyhex,
valuehex);
gpr_free(keyhex);
gpr_free(valuehex);
grpc_mdelem_unref(md);
GRPC_MDELEM_UNREF(md);
abort();
}

@ -122,10 +122,10 @@ void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) {
void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) {
size_t i;
for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
grpc_mdelem_unref(tbl->static_ents[i]);
GRPC_MDELEM_UNREF(tbl->static_ents[i]);
}
for (i = 0; i < tbl->num_ents; i++) {
grpc_mdelem_unref(
GRPC_MDELEM_UNREF(
tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]);
}
}
@ -155,7 +155,7 @@ static void evict1(grpc_chttp2_hptbl *tbl) {
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
tbl->first_ent = (tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT;
tbl->num_ents--;
grpc_mdelem_unref(first_ent);
GRPC_MDELEM_UNREF(first_ent);
}
void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {

@ -49,7 +49,7 @@ void grpc_chttp2_incoming_metadata_buffer_destroy(
grpc_chttp2_incoming_metadata_buffer *buffer) {
size_t i;
for (i = 0; i < buffer->count; i++) {
grpc_mdelem_unref(buffer->elems[i].md);
GRPC_MDELEM_UNREF(buffer->elems[i].md);
}
gpr_free(buffer->elems);
}

@ -173,6 +173,8 @@ typedef struct {
/** have we seen a goaway */
gpr_uint8 seen_goaway;
/** have we sent a goaway */
gpr_uint8 sent_goaway;
/** is this transport a client? */
gpr_uint8 is_client;
@ -557,8 +559,10 @@ void grpc_chttp2_add_incoming_goaway(
void grpc_chttp2_register_stream(grpc_chttp2_transport *t,
grpc_chttp2_stream *s);
void grpc_chttp2_unregister_stream(grpc_chttp2_transport *t,
grpc_chttp2_stream *s);
/* returns 1 if this is the last stream, 0 otherwise */
int grpc_chttp2_unregister_stream(grpc_chttp2_transport *t,
grpc_chttp2_stream *s) GRPC_MUST_USE_RESULT;
int grpc_chttp2_has_streams(grpc_chttp2_transport *t);
void grpc_chttp2_for_all_streams(
grpc_chttp2_transport_global *transport_global, void *user_data,
void (*cb)(grpc_chttp2_transport_global *transport_global, void *user_data,

@ -463,7 +463,7 @@ static grpc_chttp2_parse_error skip_parser(
return GRPC_CHTTP2_PARSE_OK;
}
static void skip_header(void *tp, grpc_mdelem *md) { grpc_mdelem_unref(md); }
static void skip_header(void *tp, grpc_mdelem *md) { GRPC_MDELEM_UNREF(md); }
static int init_skip_frame_parser(
grpc_chttp2_transport_parsing *transport_parsing, int is_header) {
@ -600,7 +600,7 @@ static void on_header(void *tp, grpc_mdelem *md) {
grpc_chttp2_incoming_metadata_buffer_set_deadline(
&stream_parsing->incoming_metadata,
gpr_time_add(gpr_now(), *cached_timeout));
grpc_mdelem_unref(md);
GRPC_MDELEM_UNREF(md);
} else {
grpc_chttp2_incoming_metadata_buffer_add(&stream_parsing->incoming_metadata,
md);

@ -247,19 +247,19 @@ static grpc_mdelem *add_elem(grpc_chttp2_hpack_compressor *c,
} else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key) {
c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
} else if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == NULL) {
c->entries_keys[HASH_FRAGMENT_2(key_hash)] = grpc_mdstr_ref(elem->key);
c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key);
c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index;
} else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == NULL) {
c->entries_keys[HASH_FRAGMENT_3(key_hash)] = grpc_mdstr_ref(elem->key);
c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key);
c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
} else if (c->indices_keys[HASH_FRAGMENT_2(key_hash)] <
c->indices_keys[HASH_FRAGMENT_3(key_hash)]) {
grpc_mdstr_unref(c->entries_keys[HASH_FRAGMENT_2(key_hash)]);
c->entries_keys[HASH_FRAGMENT_2(key_hash)] = grpc_mdstr_ref(elem->key);
GRPC_MDSTR_UNREF(c->entries_keys[HASH_FRAGMENT_2(key_hash)]);
c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key);
c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index;
} else {
grpc_mdstr_unref(c->entries_keys[HASH_FRAGMENT_3(key_hash)]);
c->entries_keys[HASH_FRAGMENT_3(key_hash)] = grpc_mdstr_ref(elem->key);
GRPC_MDSTR_UNREF(c->entries_keys[HASH_FRAGMENT_3(key_hash)]);
c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key);
c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
}
@ -439,10 +439,10 @@ static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
grpc_mdelem *mdelem;
grpc_chttp2_encode_timeout(gpr_time_sub(deadline, gpr_now()), timeout_str);
mdelem = grpc_mdelem_from_metadata_strings(
c->mdctx, grpc_mdstr_ref(c->timeout_key_str),
c->mdctx, GRPC_MDSTR_REF(c->timeout_key_str),
grpc_mdstr_from_string(c->mdctx, timeout_str));
mdelem = hpack_enc(c, mdelem, st);
if (mdelem) grpc_mdelem_unref(mdelem);
if (mdelem) GRPC_MDELEM_UNREF(mdelem);
}
gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) {
@ -461,10 +461,10 @@ void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
int i;
for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) {
if (c->entries_keys[i]) grpc_mdstr_unref(c->entries_keys[i]);
if (c->entries_elems[i]) grpc_mdelem_unref(c->entries_elems[i]);
if (c->entries_keys[i]) GRPC_MDSTR_UNREF(c->entries_keys[i]);
if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]);
}
grpc_mdstr_unref(c->timeout_key_str);
GRPC_MDSTR_UNREF(c->timeout_key_str);
}
gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count,
@ -620,10 +620,10 @@ void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof,
op = &ops[unref_op];
if (op->type != GRPC_OP_METADATA) continue;
for (l = op->data.metadata.list.head; l; l = l->next) {
if (l->md) grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
if (l->md) GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
}
for (l = op->data.metadata.garbage.head; l; l = l->next) {
grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
}
}
grpc_mdctx_unlock(mdctx);

@ -354,9 +354,14 @@ void grpc_chttp2_register_stream(grpc_chttp2_transport *t,
stream_list_add_tail(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS);
}
void grpc_chttp2_unregister_stream(grpc_chttp2_transport *t,
int grpc_chttp2_unregister_stream(grpc_chttp2_transport *t,
grpc_chttp2_stream *s) {
stream_list_remove(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS);
stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS);
return stream_list_empty(t, GRPC_CHTTP2_LIST_ALL_STREAMS);
}
int grpc_chttp2_has_streams(grpc_chttp2_transport *t) {
return !stream_list_empty(t, GRPC_CHTTP2_LIST_ALL_STREAMS);
}
void grpc_chttp2_for_all_streams(

@ -139,7 +139,7 @@ static void destruct_transport(grpc_chttp2_transport *t) {
grpc_chttp2_hpack_parser_destroy(&t->parsing.hpack_parser);
grpc_chttp2_goaway_parser_destroy(&t->parsing.goaway_parser);
grpc_mdstr_unref(t->parsing.str_grpc_timeout);
GRPC_MDSTR_UNREF(t->parsing.str_grpc_timeout);
for (i = 0; i < STREAM_LIST_COUNT; i++) {
GPR_ASSERT(t->lists[i].head == NULL);
@ -382,7 +382,9 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) {
GPR_ASSERT(s->global.published_state == GRPC_STREAM_CLOSED ||
s->global.id == 0);
GPR_ASSERT(!s->global.in_stream_map);
grpc_chttp2_unregister_stream(t, s);
if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) {
close_transport_locked(t);
}
if (!t->parsing_active && s->global.id) {
GPR_ASSERT(grpc_chttp2_stream_map_find(&t->parsing_stream_map,
s->global.id) == NULL);
@ -521,8 +523,7 @@ static void writing_action(void *gt, int iomgr_success_ignored) {
void grpc_chttp2_add_incoming_goaway(
grpc_chttp2_transport_global *transport_global, gpr_uint32 goaway_error,
gpr_slice goaway_text) {
char *msg = gpr_hexdump((char *)GPR_SLICE_START_PTR(goaway_text),
GPR_SLICE_LENGTH(goaway_text), GPR_HEXDUMP_PLAINTEXT);
char *msg = gpr_dump_slice(goaway_text, GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_log(GPR_DEBUG, "got goaway [%d]: %s", goaway_error, msg);
gpr_free(msg);
gpr_slice_unref(goaway_text);
@ -681,10 +682,14 @@ static void perform_transport_op(grpc_transport *gt, grpc_transport_op *op) {
}
if (op->send_goaway) {
t->global.sent_goaway = 1;
grpc_chttp2_goaway_append(
t->global.last_incoming_stream_id,
grpc_chttp2_grpc_status_to_http2_error(op->goaway_status),
gpr_slice_ref(*op->goaway_message), &t->global.qbuf);
if (!grpc_chttp2_has_streams(t)) {
close_transport_locked(t);
}
}
if (op->set_accept_stream != NULL) {
@ -733,6 +738,9 @@ static void remove_stream(grpc_chttp2_transport *t, gpr_uint32 id) {
t->parsing.incoming_stream = NULL;
grpc_chttp2_parsing_become_skip_parser(&t->parsing);
}
if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) {
close_transport_locked(t);
}
new_stream_count = grpc_chttp2_stream_map_size(&t->parsing_stream_map) +
grpc_chttp2_stream_map_size(&t->new_stream_map);
@ -868,11 +876,19 @@ static void update_global_window(void *args, gpr_uint32 id, void *stream) {
grpc_chttp2_stream *s = stream;
grpc_chttp2_transport_global *transport_global = &t->global;
grpc_chttp2_stream_global *stream_global = &s->global;
int was_zero;
int is_zero;
GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("settings", transport_global, stream_global,
outgoing_window,
t->parsing.initial_window_update);
was_zero = stream_global->outgoing_window <= 0;
stream_global->outgoing_window += t->parsing.initial_window_update;
is_zero = stream_global->outgoing_window <= 0;
if (was_zero && !is_zero) {
grpc_chttp2_list_add_writable_stream(transport_global, stream_global);
}
}
static void read_error_locked(grpc_chttp2_transport *t) {

@ -48,6 +48,20 @@
#define INITIAL_STRTAB_CAPACITY 4
#define INITIAL_MDTAB_CAPACITY 4
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
#define DEBUG_ARGS , const char *file, int line
#define FWD_DEBUG_ARGS , file, line
#define INTERNAL_STRING_REF(s) internal_string_ref((s), __FILE__, __LINE__)
#define INTERNAL_STRING_UNREF(s) internal_string_unref((s), __FILE__, __LINE__)
#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
#else
#define DEBUG_ARGS
#define FWD_DEBUG_ARGS
#define INTERNAL_STRING_REF(s) internal_string_ref((s))
#define INTERNAL_STRING_UNREF(s) internal_string_unref((s))
#define REF_MD_LOCKED(s) ref_md_locked((s))
#endif
typedef struct internal_string {
/* must be byte compatible with grpc_mdstr */
gpr_slice slice;
@ -96,8 +110,8 @@ struct grpc_mdctx {
size_t mdtab_capacity;
};
static void internal_string_ref(internal_string *s);
static void internal_string_unref(internal_string *s);
static void internal_string_ref(internal_string *s DEBUG_ARGS);
static void internal_string_unref(internal_string *s DEBUG_ARGS);
static void discard_metadata(grpc_mdctx *ctx);
static void gc_mdtab(grpc_mdctx *ctx);
static void metadata_context_destroy_locked(grpc_mdctx *ctx);
@ -132,7 +146,15 @@ static void unlock(grpc_mdctx *ctx) {
gpr_mu_unlock(&ctx->mu);
}
static void ref_md_locked(internal_metadata *md) {
static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM REF:%p:%d->%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
gpr_atm_no_barrier_load(&md->refcnt) + 1,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
md->context->mdtab_free--;
}
@ -173,8 +195,8 @@ static void discard_metadata(grpc_mdctx *ctx) {
while (cur) {
GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
next = cur->bucket_next;
internal_string_unref(cur->key);
internal_string_unref(cur->value);
INTERNAL_STRING_UNREF(cur->key);
INTERNAL_STRING_UNREF(cur->value);
if (cur->user_data) {
cur->destroy_user_data(cur->user_data);
}
@ -248,9 +270,19 @@ static void internal_destroy_string(internal_string *is) {
gpr_free(is);
}
static void internal_string_ref(internal_string *s) { ++s->refs; }
static void internal_string_ref(internal_string *s DEBUG_ARGS) {
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR REF:%p:%d->%d: '%s'", s,
s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
#endif
++s->refs;
}
static void internal_string_unref(internal_string *s) {
static void internal_string_unref(internal_string *s DEBUG_ARGS) {
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s,
s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
#endif
GPR_ASSERT(s->refs > 0);
if (0 == --s->refs) {
internal_destroy_string(s);
@ -262,7 +294,7 @@ static void slice_ref(void *p) {
(internal_string *)((char *)p - offsetof(internal_string, refcount));
grpc_mdctx *ctx = is->context;
lock(ctx);
internal_string_ref(is);
INTERNAL_STRING_REF(is);
unlock(ctx);
}
@ -271,7 +303,7 @@ static void slice_unref(void *p) {
(internal_string *)((char *)p - offsetof(internal_string, refcount));
grpc_mdctx *ctx = is->context;
lock(ctx);
internal_string_unref(is);
INTERNAL_STRING_UNREF(is);
unlock(ctx);
}
@ -297,7 +329,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) {
if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
internal_string_ref(s);
INTERNAL_STRING_REF(s);
unlock(ctx);
return (grpc_mdstr *)s;
}
@ -353,8 +385,8 @@ static void gc_mdtab(grpc_mdctx *ctx) {
for (md = ctx->mdtab[i]; md; md = next) {
next = md->bucket_next;
if (gpr_atm_acq_load(&md->refcnt) == 0) {
internal_string_unref(md->key);
internal_string_unref(md->value);
INTERNAL_STRING_UNREF(md->key);
INTERNAL_STRING_UNREF(md->value);
if (md->user_data) {
md->destroy_user_data(md->user_data);
}
@ -418,9 +450,9 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
/* search for an existing pair */
for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) {
if (md->key == key && md->value == value) {
ref_md_locked(md);
internal_string_unref(key);
internal_string_unref(value);
REF_MD_LOCKED(md);
INTERNAL_STRING_UNREF(key);
INTERNAL_STRING_UNREF(value);
unlock(ctx);
return (grpc_mdelem *)md;
}
@ -435,6 +467,12 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
md->user_data = NULL;
md->destroy_user_data = NULL;
md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity];
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
ctx->mdtab[hash % ctx->mdtab_capacity] = md;
ctx->mdtab_count++;
@ -469,8 +507,16 @@ grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
grpc_mdstr_from_buffer(ctx, value, value_length));
}
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd) {
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
internal_metadata *md = (internal_metadata *)gmd;
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM REF:%p:%d->%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
gpr_atm_no_barrier_load(&md->refcnt) + 1,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
/* we can assume the ref count is >= 1 as the application is calling
this function - meaning that no adjustment to mdtab_free is necessary,
simplifying the logic here to be just an atomic increment */
@ -480,10 +526,18 @@ grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd) {
return gmd;
}
void grpc_mdelem_unref(grpc_mdelem *gmd) {
void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
internal_metadata *md = (internal_metadata *)gmd;
grpc_mdctx *ctx = md->context;
lock(ctx);
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
gpr_atm_no_barrier_load(&md->refcnt) - 1,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
ctx->mdtab_free++;
@ -495,20 +549,20 @@ const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
return (const char *)GPR_SLICE_START_PTR(s->slice);
}
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs) {
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
internal_string *s = (internal_string *)gs;
grpc_mdctx *ctx = s->context;
lock(ctx);
internal_string_ref(s);
internal_string_ref(s FWD_DEBUG_ARGS);
unlock(ctx);
return gs;
}
void grpc_mdstr_unref(grpc_mdstr *gs) {
void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
internal_string *s = (internal_string *)gs;
grpc_mdctx *ctx = s->context;
lock(ctx);
internal_string_unref(s);
internal_string_unref(s FWD_DEBUG_ARGS);
unlock(ctx);
}
@ -558,10 +612,19 @@ gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
void grpc_mdctx_lock(grpc_mdctx *ctx) { lock(ctx); }
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *gmd) {
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx,
grpc_mdelem *gmd DEBUG_ARGS) {
internal_metadata *md = (internal_metadata *)gmd;
grpc_mdctx *elem_ctx = md->context;
GPR_ASSERT(ctx == elem_ctx);
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
gpr_atm_no_barrier_load(&md->refcnt) - 1,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
ctx->mdtab_free++;

@ -127,11 +127,25 @@ void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
void *user_data);
/* Reference counting */
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
#define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s), __FILE__, __LINE__)
#define GRPC_MDSTR_UNREF(s) grpc_mdstr_unref((s), __FILE__, __LINE__)
#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__)
#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __LINE__)
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s, const char *file, int line);
void grpc_mdstr_unref(grpc_mdstr *s, const char *file, int line);
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md, const char *file, int line);
void grpc_mdelem_unref(grpc_mdelem *md, const char *file, int line);
#else
#define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s))
#define GRPC_MDSTR_UNREF(s) grpc_mdstr_unref((s))
#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s))
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s);
void grpc_mdstr_unref(grpc_mdstr *s);
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md);
void grpc_mdelem_unref(grpc_mdelem *md);
#endif
/* Recover a char* from a grpc_mdstr. The returned string is null terminated.
Does not promise that the returned string has no embedded nulls however. */
@ -147,8 +161,18 @@ int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s);
/* Lock the metadata context: it's only safe to call _locked_ functions against
this context from the calling thread until grpc_mdctx_unlock is called */
void grpc_mdctx_lock(grpc_mdctx *ctx);
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
#define GRPC_MDCTX_LOCKED_MDELEM_UNREF(ctx, elem) \
grpc_mdctx_locked_mdelem_unref((ctx), (elem), __FILE__, __LINE__)
/* Unref a metadata element */
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem,
const char *file, int line);
#else
#define GRPC_MDCTX_LOCKED_MDELEM_UNREF(ctx, elem) \
grpc_mdctx_locked_mdelem_unref((ctx), (elem))
/* Unref a metadata element */
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem);
#endif
/* Unlock the metadata context */
void grpc_mdctx_unlock(grpc_mdctx *ctx);

@ -211,10 +211,10 @@ void grpc_metadata_batch_init(grpc_metadata_batch *batch) {
void grpc_metadata_batch_destroy(grpc_metadata_batch *batch) {
grpc_linked_mdelem *l;
for (l = batch->list.head; l; l = l->next) {
grpc_mdelem_unref(l->md);
GRPC_MDELEM_UNREF(l->md);
}
for (l = batch->garbage.head; l; l = l->next) {
grpc_mdelem_unref(l->md);
GRPC_MDELEM_UNREF(l->md);
}
}
@ -315,7 +315,7 @@ void grpc_metadata_batch_filter(grpc_metadata_batch *batch,
assert_valid_list(&batch->list);
link_head(&batch->garbage, l);
} else if (filt != orig) {
grpc_mdelem_unref(orig);
GRPC_MDELEM_UNREF(orig);
l->md = filt;
}
}

@ -85,6 +85,6 @@ void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
op->cancel_with_status = status;
}
if (message) {
grpc_mdstr_unref(message);
GRPC_MDSTR_UNREF(message);
}
}

@ -91,7 +91,9 @@ typedef struct grpc_transport_op {
grpc_connectivity_state *connectivity_state;
/** should the transport be disconnected */
int disconnect;
/** should we send a goaway? */
/** should we send a goaway?
after a goaway is sent, once there are no more active calls on
the transport, the transport should disconnect */
int send_goaway;
/** what should the goaway contain? */
grpc_status_code goaway_status;

@ -47,14 +47,12 @@
static void put_metadata(gpr_strvec *b, grpc_mdelem *md) {
gpr_strvec_add(b, gpr_strdup("key="));
gpr_strvec_add(
b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->key->slice),
GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT));
gpr_strvec_add(b,
gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII));
gpr_strvec_add(b, gpr_strdup(" value="));
gpr_strvec_add(b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->value->slice),
GPR_SLICE_LENGTH(md->value->slice),
GPR_HEXDUMP_PLAINTEXT));
gpr_strvec_add(
b, gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII));
}
static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) {

@ -39,6 +39,7 @@
#include <grpc/support/log.h>
#include <grpc/support/slice.h>
#include "src/core/census/grpc_context.h"
#include "src/core/profiling/timers.h"
#include <grpc++/channel_arguments.h>
#include <grpc++/client_context.h>
@ -59,7 +60,7 @@ Channel::~Channel() { grpc_channel_destroy(c_channel_); }
Call Channel::CreateCall(const RpcMethod& method, ClientContext* context,
CompletionQueue* cq) {
auto c_call =
method.channel_tag()
method.channel_tag() && context->authority().empty()
? grpc_channel_create_registered_call(c_channel_, cq->cq(),
method.channel_tag(),
context->raw_deadline())
@ -68,6 +69,7 @@ Call Channel::CreateCall(const RpcMethod& method, ClientContext* context,
? target_.c_str()
: context->authority().c_str(),
context->raw_deadline());
grpc_census_call_set_context(c_call, context->get_census_context());
GRPC_TIMER_MARK(GRPC_PTAG_CPP_CALL_CREATED, c_call);
context->set_call(c_call, shared_from_this());
return Call(c_call, this, cq);

@ -36,6 +36,7 @@
#include <grpc/grpc.h>
#include <grpc++/credentials.h>
#include <grpc++/time.h>
#include "src/cpp/common/create_auth_context.h"
namespace grpc {
@ -75,6 +76,13 @@ void ClientContext::set_call(grpc_call* call,
}
}
std::shared_ptr<const AuthContext> ClientContext::auth_context() const {
if (auth_context_.get() == nullptr) {
auth_context_ = CreateAuthContext(call_);
}
return auth_context_;
}
void ClientContext::TryCancel() {
if (call_) {
grpc_call_cancel(call_);

@ -117,6 +117,13 @@ std::shared_ptr<Credentials> RefreshTokenCredentials(
grpc_refresh_token_credentials_create(json_refresh_token.c_str()));
}
// Builds access token credentials.
std::shared_ptr<Credentials> AccessTokenCredentials(
const grpc::string& access_token) {
return WrapCredentials(
grpc_access_token_credentials_create(access_token.c_str()));
}
// Builds IAM credentials.
std::shared_ptr<Credentials> IAMCredentials(
const grpc::string& authorization_token,

@ -0,0 +1,42 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <memory>
#include <grpc/grpc.h>
#include <grpc++/auth_context.h>
namespace grpc {
std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call);
} // namespace grpc

@ -0,0 +1,45 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <memory>
#include <grpc/grpc.h>
#include <grpc++/auth_context.h>
namespace grpc {
std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call) {
(void)call;
return std::shared_ptr<const AuthContext>();
}
} // namespace grpc

@ -0,0 +1,80 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/cpp/common/secure_auth_context.h"
#include <grpc/grpc_security.h>
namespace grpc {
SecureAuthContext::SecureAuthContext(grpc_auth_context* ctx) : ctx_(ctx) {}
SecureAuthContext::~SecureAuthContext() { grpc_auth_context_release(ctx_); }
std::vector<grpc::string> SecureAuthContext::GetPeerIdentity() const {
if (!ctx_) {
return std::vector<grpc::string>();
}
grpc_auth_property_iterator iter = grpc_auth_context_peer_identity(ctx_);
std::vector<grpc::string> identity;
const grpc_auth_property* property = nullptr;
while ((property = grpc_auth_property_iterator_next(&iter))) {
identity.push_back(grpc::string(property->value, property->value_length));
}
return identity;
}
grpc::string SecureAuthContext::GetPeerIdentityPropertyName() const {
if (!ctx_) {
return "";
}
const char* name = grpc_auth_context_peer_identity_property_name(ctx_);
return name == nullptr ? "" : name;
}
std::vector<grpc::string> SecureAuthContext::FindPropertyValues(
const grpc::string& name) const {
if (!ctx_) {
return std::vector<grpc::string>();
}
grpc_auth_property_iterator iter =
grpc_auth_context_find_properties_by_name(ctx_, name.c_str());
const grpc_auth_property* property = nullptr;
std::vector<grpc::string> values;
while ((property = grpc_auth_property_iterator_next(&iter))) {
values.push_back(grpc::string(property->value, property->value_length));
}
return values;
}
} // namespace grpc

@ -0,0 +1,62 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H
#define GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H
#include <grpc++/auth_context.h>
struct grpc_auth_context;
namespace grpc {
class SecureAuthContext GRPC_FINAL : public AuthContext {
public:
SecureAuthContext(grpc_auth_context* ctx);
~SecureAuthContext() GRPC_OVERRIDE;
std::vector<grpc::string> GetPeerIdentity() const GRPC_OVERRIDE;
grpc::string GetPeerIdentityPropertyName() const GRPC_OVERRIDE;
std::vector<grpc::string> FindPropertyValues(const grpc::string& name) const
GRPC_OVERRIDE;
private:
grpc_auth_context* ctx_;
};
} // namespace grpc
#endif // GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H

@ -0,0 +1,50 @@
/*
*
* 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 <memory>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc++/auth_context.h>
#include "src/cpp/common/secure_auth_context.h"
namespace grpc {
std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call) {
if (call == nullptr) {
return std::shared_ptr<const AuthContext>();
}
return std::shared_ptr<const AuthContext>(
new SecureAuthContext(grpc_call_auth_context(call)));
}
} // namespace grpc

@ -118,7 +118,7 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
has_request_payload_(mrd->has_request_payload_),
request_payload_(mrd->request_payload_),
method_(mrd->method_) {
ctx_.call_ = mrd->call_;
ctx_.set_call(mrd->call_);
ctx_.cq_ = &cq_;
GPR_ASSERT(mrd->in_flight_);
mrd->in_flight_ = false;
@ -207,10 +207,11 @@ Server::~Server() {
delete sync_methods_;
}
bool Server::RegisterService(RpcService* service) {
bool Server::RegisterService(const grpc::string *host, RpcService* service) {
for (int i = 0; i < service->GetMethodCount(); ++i) {
RpcServiceMethod* method = service->GetMethod(i);
void* tag = grpc_server_register_method(server_, method->name(), nullptr);
void* tag = grpc_server_register_method(
server_, method->name(), host ? host->c_str() : nullptr);
if (!tag) {
gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
method->name());
@ -222,14 +223,14 @@ bool Server::RegisterService(RpcService* service) {
return true;
}
bool Server::RegisterAsyncService(AsynchronousService* service) {
bool Server::RegisterAsyncService(const grpc::string *host, AsynchronousService* service) {
GPR_ASSERT(service->server_ == nullptr &&
"Can only register an asynchronous service against one server.");
service->server_ = this;
service->request_args_ = new void*[service->method_count_];
for (size_t i = 0; i < service->method_count_; ++i) {
void* tag = grpc_server_register_method(server_, service->method_names_[i],
nullptr);
host ? host->c_str() : nullptr);
if (!tag) {
gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
service->method_names_[i]);
@ -325,7 +326,7 @@ bool Server::BaseAsyncRequest::FinalizeResult(void** tag, bool* status) {
}
}
grpc_metadata_array_destroy(&initial_metadata_array_);
context_->call_ = call_;
context_->set_call(call_);
context_->cq_ = call_cq_;
Call call(call_, server_, call_cq_, server_->max_message_size_);
if (*status && call_) {

@ -51,11 +51,21 @@ std::unique_ptr<ServerCompletionQueue> ServerBuilder::AddCompletionQueue() {
}
void ServerBuilder::RegisterService(SynchronousService* service) {
services_.push_back(service->service());
services_.emplace_back(new NamedService<RpcService>(service->service()));
}
void ServerBuilder::RegisterAsyncService(AsynchronousService* service) {
async_services_.push_back(service);
async_services_.emplace_back(new NamedService<AsynchronousService>(service));
}
void ServerBuilder::RegisterService(
const grpc::string& addr, SynchronousService* service) {
services_.emplace_back(new NamedService<RpcService>(addr, service->service()));
}
void ServerBuilder::RegisterAsyncService(
const grpc::string& addr, AsynchronousService* service) {
async_services_.emplace_back(new NamedService<AsynchronousService>(addr, service));
}
void ServerBuilder::RegisterAsyncGenericService(AsyncGenericService* service) {
@ -97,13 +107,13 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
}
for (auto service = services_.begin(); service != services_.end();
service++) {
if (!server->RegisterService(*service)) {
if (!server->RegisterService((*service)->host.get(), (*service)->service)) {
return nullptr;
}
}
for (auto service = async_services_.begin();
service != async_services_.end(); service++) {
if (!server->RegisterAsyncService(*service)) {
if (!server->RegisterAsyncService((*service)->host.get(), (*service)->service)) {
return nullptr;
}
}

@ -39,6 +39,8 @@
#include <grpc++/impl/sync.h>
#include <grpc++/time.h>
#include "src/cpp/common/create_auth_context.h"
namespace grpc {
// CompletionOp
@ -146,4 +148,9 @@ bool ServerContext::IsCancelled() {
return completion_op_ && completion_op_->CheckCancelled(cq_);
}
void ServerContext::set_call(grpc_call* call) {
call_ = call;
auth_context_ = CreateAuthContext(call);
}
} // namespace grpc

@ -10,20 +10,54 @@
'-pthread',
'-pedantic',
'-g',
'-zdefs'
'-zdefs',
'-Werror'
],
'ldflags': [
'-g'
],
'link_settings': {
'libraries': [
'-lpthread',
'-lgrpc',
'-lgpr'
]
},
"conditions": [
['OS != "win"', {
'variables': {
'pkg_config_grpc': '<!(pkg-config --exists grpc >/dev/null 2>&1 && echo true || echo false)'
},
'conditions': [
['pkg_config_grpc == "true"', {
'link_settings': {
'libraries': [
'<!@(pkg-config --libs-only-l --static grpc)'
]
},
'cflags': [
'<!@(pkg-config --cflags grpc)'
],
'libraries': [
'<!@(pkg-config --libs-only-L --static grpc)'
],
'ldflags': [
'<!@(pkg-config --libs-only-other --static grpc)'
]
}, {
'link_settings': {
'libraries': [
'-lpthread',
'-lgrpc',
'-lgpr'
],
},
'conditions':[
['OS != "mac"', {
'link_settings': {
'libraries': [
'-lrt'
]
}
}]
]
}
]
]
}],
['OS == "mac"', {
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.9',
@ -32,13 +66,6 @@
'-stdlib=libc++'
]
}
}],
['OS != "mac"', {
'link_settings': {
'libraries': [
'-lrt'
]
}
}]
],
"target_name": "grpc",

@ -27,7 +27,7 @@
"bindings": "^1.2.0",
"lodash": "^3.9.3",
"nan": "^1.5.0",
"protobufjs": "dcodeIO/ProtoBuf.js"
"protobufjs": "^4.0.0"
},
"devDependencies": {
"async": "^0.9.0",

@ -60,7 +60,7 @@
}
- (instancetype)initWithHost:(NSString *)host {
if (![host containsString:@"://"]) {
if (![host rangeOfString:@"://"].length) {
// No scheme provided; assume https.
host = [@"https://" stringByAppendingString:host];
}

@ -1,6 +1,7 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'Protobuf', :path => "../../../third_party/protobuf"
pod 'gRPC', :path => "../../.."
pod 'RemoteTest', :path => "../generated_libraries/RemoteTestClient"
pod 'RouteGuide', :path => "../generated_libraries/RouteGuideClient"

@ -1,3 +1,4 @@
enum34==1.0.4
futures==2.2.0
protobuf==3.0.0a3
cython>=0.22

@ -1,3 +1,4 @@
MANIFEST
grpcio.egg-info/
build/
dist/

@ -0,0 +1,5 @@
*.a
*.so
*.dll
*.pyc
*.pyd

@ -0,0 +1,7 @@
*.h
*.c
*.a
*.so
*.dll
*.pyc
*.pyd

@ -0,0 +1,52 @@
GRPC Python Cython layer
========================
Package for the GRPC Python Cython layer.
What is Cython?
---------------
Cython is both a superset of the Python language with extensions for dealing
with C types and a tool that transpiles this superset into C code. It provides
convenient means of statically typing expressions and of converting Python
strings to pointers (among other niceties), thus dramatically smoothing the
Python/C interop by allowing fluid use of APIs in both from the same source.
See the wonderful `Cython website`_.
Why Cython?
-----------
- **Python 2 and 3 support**
Cython generated C code has precompiler macros to target both Python 2 and
Python 3 C APIs, even while acting as a superset of just the Python 2
language (e.g. using ``basestring``).
- **Significantly less semantic noise**
A lot of CPython code is just glue, especially human-error-prone
``Py_INCREF``-ing and ``Py_DECREF``-ing around error handlers and such.
Cython takes care of that automagically.
- **Possible PyPy support**
One of the major developments in Cython over the past few years was the
addition of support for PyPy. We might soon be able to provide such support
ourselves through our use of Cython.
- **Less Python glue code**
There existed several adapter layers in and around the original CPython code
to smooth the surface exposed to Python due to how much trouble it was to
make such a smooth surface via the CPython API alone. Cython makes writing
such a surface incredibly easy, so these adapter layers may be removed.
Implications for Users
----------------------
Nothing additional will be required for users. PyPI packages will contain
Cython generated C code and thus not necessitate a Cython installation.
Implications for GRPC Developers
--------------------------------
A typical edit-compile-debug cycle now requires Cython. We install Cython in
the ``virtualenv`` generated for the Python tests in this repository, so
initial test runs may take an extra 2+ minutes to complete. Subsequent test
runs won't reinstall ``Cython`` (unless required versions change and the
``virtualenv`` doesn't have installed versions that satisfy the change).
.. _`Cython website`: http://cython.org/

@ -0,0 +1,28 @@
# 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.

@ -0,0 +1,28 @@
# 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.

@ -0,0 +1,37 @@
# 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.
from grpc._cython._cygrpc cimport grpc
cdef class Call:
cdef grpc.grpc_call *c_call
cdef list references

@ -0,0 +1,82 @@
# 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.
cimport cpython
from grpc._cython._cygrpc cimport records
cdef class Call:
def __cinit__(self):
# Create an *empty* call
self.c_call = NULL
self.references = []
def start_batch(self, operations, tag):
if not self.is_valid:
raise ValueError("invalid call object cannot be used from Python")
cdef records.Operations cy_operations = records.Operations(operations)
cdef records.OperationTag operation_tag = records.OperationTag(tag)
operation_tag.operation_call = self
operation_tag.batch_operations = cy_operations
cpython.Py_INCREF(operation_tag)
return grpc.grpc_call_start_batch(
self.c_call, cy_operations.c_ops, cy_operations.c_nops,
<cpython.PyObject *>operation_tag)
def cancel(self,
grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE,
details=None):
if not self.is_valid:
raise ValueError("invalid call object cannot be used from Python")
if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE):
raise ValueError("if error_code is specified, so must details "
"(and vice-versa)")
if isinstance(details, bytes):
pass
elif isinstance(details, basestring):
details = details.encode()
else:
raise TypeError("expected details to be str or bytes")
if error_code != grpc.GRPC_STATUS__DO_NOT_USE:
self.references.append(details)
return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details)
else:
return grpc.grpc_call_cancel(self.c_call)
def __dealloc__(self):
if self.c_call != NULL:
grpc.grpc_call_destroy(self.c_call)
# The object *should* always be valid from Python. Used for debugging.
@property
def is_valid(self):
return self.c_call != NULL

@ -0,0 +1,36 @@
# 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.
from grpc._cython._cygrpc cimport grpc
cdef class Channel:
cdef grpc.grpc_channel *c_channel
cdef list references

@ -0,0 +1,84 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport completion_queue
from grpc._cython._cygrpc cimport credentials
from grpc._cython._cygrpc cimport records
cdef class Channel:
def __cinit__(self, target, records.ChannelArgs arguments=None,
credentials.ClientCredentials client_credentials=None):
cdef grpc.grpc_channel_args *c_arguments = NULL
self.c_channel = NULL
self.references = []
if arguments is not None:
c_arguments = &arguments.c_args
if isinstance(target, bytes):
pass
elif isinstance(target, basestring):
target = target.encode()
else:
raise TypeError("expected target to be str or bytes")
if client_credentials is None:
self.c_channel = grpc.grpc_channel_create(target, c_arguments)
else:
self.c_channel = grpc.grpc_secure_channel_create(
client_credentials.c_credentials, target, c_arguments)
self.references.append(client_credentials)
self.references.append(target)
self.references.append(arguments)
def create_call(self, completion_queue.CompletionQueue queue not None,
method, host, records.Timespec deadline not None):
if queue.is_shutting_down:
raise ValueError("queue must not be shutting down or shutdown")
if isinstance(method, bytes):
pass
elif isinstance(method, basestring):
method = method.encode()
else:
raise TypeError("expected method to be str or bytes")
if isinstance(host, bytes):
pass
elif isinstance(host, basestring):
host = host.encode()
else:
raise TypeError("expected host to be str or bytes")
cdef call.Call operation_call = call.Call()
operation_call.references = [self, method, host, queue]
operation_call.c_call = grpc.grpc_channel_create_call(
self.c_channel, queue.c_completion_queue, method, host, deadline.c_time)
return operation_call
def __dealloc__(self):
if self.c_channel != NULL:
grpc.grpc_channel_destroy(self.c_channel)

@ -0,0 +1,39 @@
# 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.
from grpc._cython._cygrpc cimport grpc
cdef class CompletionQueue:
cdef grpc.grpc_completion_queue *c_completion_queue
cdef object poll_condition
cdef bint is_polling
cdef bint is_shutting_down
cdef bint is_shutdown

@ -0,0 +1,117 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport records
import threading
import time
cdef class CompletionQueue:
def __cinit__(self):
self.c_completion_queue = grpc.grpc_completion_queue_create()
self.is_shutting_down = False
self.is_shutdown = False
self.poll_condition = threading.Condition()
self.is_polling = False
def poll(self, records.Timespec deadline=None):
# We name this 'poll' to avoid problems with CPython's expectations for
# 'special' methods (like next and __next__).
cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future
cdef records.OperationTag tag = None
cdef object user_tag = None
cdef call.Call operation_call = None
cdef records.CallDetails request_call_details = None
cdef records.Metadata request_metadata = None
cdef records.Operations batch_operations = None
if deadline is not None:
c_deadline = deadline.c_time
cdef grpc.grpc_event event
# Poll within a critical section
with self.poll_condition:
while self.is_polling:
self.poll_condition.wait(float(deadline) - time.time())
self.is_polling = True
with nogil:
event = grpc.grpc_completion_queue_next(
self.c_completion_queue, c_deadline)
with self.poll_condition:
self.is_polling = False
self.poll_condition.notify()
if event.type == grpc.GRPC_QUEUE_TIMEOUT:
return records.Event(event.type, False, None, None, None, None, None)
elif event.type == grpc.GRPC_QUEUE_SHUTDOWN:
self.is_shutdown = True
return records.Event(event.type, True, None, None, None, None, None)
else:
if event.tag != NULL:
tag = <records.OperationTag>event.tag
# We receive event tags only after they've been inc-ref'd elsewhere in
# the code.
cpython.Py_DECREF(tag)
if tag.shutting_down_server is not None:
tag.shutting_down_server.notify_shutdown_complete()
user_tag = tag.user_tag
operation_call = tag.operation_call
request_call_details = tag.request_call_details
request_metadata = tag.request_metadata
batch_operations = tag.batch_operations
if tag.is_new_request:
# Stuff in the tag not explicitly handled by us needs to live through
# the life of the call
operation_call.references.extend(tag.references)
return records.Event(
event.type, event.success, user_tag, operation_call,
request_call_details, request_metadata, batch_operations)
def shutdown(self):
grpc.grpc_completion_queue_shutdown(self.c_completion_queue)
self.is_shutting_down = True
def clear(self):
if not self.is_shutting_down:
raise ValueError('queue must be shutting down to be cleared')
while self.poll().type != grpc.GRPC_QUEUE_SHUTDOWN:
pass
def __dealloc__(self):
if self.c_completion_queue != NULL:
# Ensure shutdown, pump the queue
if not self.is_shutting_down:
self.shutdown()
while not self.is_shutdown:
self.poll()
grpc.grpc_completion_queue_destroy(self.c_completion_queue)

@ -0,0 +1,45 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from grpc._cython._cygrpc cimport grpc
cdef class ClientCredentials:
cdef grpc.grpc_credentials *c_credentials
cdef grpc.grpc_ssl_pem_key_cert_pair c_ssl_pem_key_cert_pair
cdef list references
cdef class ServerCredentials:
cdef grpc.grpc_server_credentials *c_credentials
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 list references

@ -0,0 +1,217 @@
# 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.
from grpc._cython._cygrpc cimport records
cdef class ClientCredentials:
def __cinit__(self):
self.c_credentials = NULL
self.c_ssl_pem_key_cert_pair.private_key = NULL
self.c_ssl_pem_key_cert_pair.certificate_chain = NULL
self.references = []
# The object *can* be invalid in Python if we fail to make the credentials
# (and the core thus returns NULL credentials). Used primarily for debugging.
@property
def is_valid(self):
return self.c_credentials != NULL
def __dealloc__(self):
if self.c_credentials != NULL:
grpc.grpc_credentials_release(self.c_credentials)
cdef class ServerCredentials:
def __cinit__(self):
self.c_credentials = NULL
def __dealloc__(self):
if self.c_credentials != NULL:
grpc.grpc_server_credentials_release(self.c_credentials)
def client_credentials_google_default():
cdef ClientCredentials credentials = ClientCredentials();
credentials.c_credentials = grpc.grpc_google_default_credentials_create()
return credentials
def client_credentials_ssl(pem_root_certificates,
records.SslPemKeyCertPair ssl_pem_key_cert_pair):
if pem_root_certificates is None:
pass
elif isinstance(pem_root_certificates, bytes):
pass
elif isinstance(pem_root_certificates, basestring):
pem_root_certificates = pem_root_certificates.encode()
else:
raise TypeError("expected str or bytes for pem_root_certificates")
cdef ClientCredentials credentials = ClientCredentials()
cdef const char *c_pem_root_certificates = NULL
if pem_root_certificates is not None:
c_pem_root_certificates = pem_root_certificates
credentials.references.append(pem_root_certificates)
if ssl_pem_key_cert_pair is not None:
credentials.c_credentials = grpc.grpc_ssl_credentials_create(
c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair
)
credentials.references.append(ssl_pem_key_cert_pair)
else:
credentials.c_credentials = grpc.grpc_ssl_credentials_create(
c_pem_root_certificates, NULL
)
def client_credentials_composite_credentials(
ClientCredentials credentials_1 not None,
ClientCredentials credentials_2 not None):
if not credentials_1.is_valid or not credentials_2.is_valid:
raise ValueError("passed credentials must both be valid")
cdef ClientCredentials credentials = ClientCredentials()
credentials.c_credentials = grpc.grpc_composite_credentials_create(
credentials_1.c_credentials, credentials_2.c_credentials)
credentials.references.append(credentials_1)
credentials.references.append(credentials_2)
return credentials
def client_credentials_compute_engine():
cdef ClientCredentials credentials = ClientCredentials()
credentials.c_credentials = grpc.grpc_compute_engine_credentials_create()
return credentials
def client_credentials_service_account(
json_key, scope, records.Timespec token_lifetime not None):
if isinstance(json_key, bytes):
pass
elif isinstance(json_key, basestring):
json_key = json_key.encode()
else:
raise TypeError("expected json_key to be str or bytes")
if isinstance(scope, bytes):
pass
elif isinstance(scope, basestring):
scope = scope.encode()
else:
raise TypeError("expected scope to be str or bytes")
cdef ClientCredentials credentials = ClientCredentials()
credentials.c_credentials = grpc.grpc_service_account_credentials_create(
json_key, scope, token_lifetime.c_time)
credentials.references.extend([json_key, scope])
return credentials
def client_credentials_jwt(json_key, records.Timespec token_lifetime not None):
if isinstance(json_key, bytes):
pass
elif isinstance(json_key, basestring):
json_key = json_key.encode()
else:
raise TypeError("expected json_key to be str or bytes")
cdef ClientCredentials credentials = ClientCredentials()
credentials.c_credentials = grpc.grpc_jwt_credentials_create(
json_key, token_lifetime.c_time)
credentials.references.append(json_key)
return credentials
def client_credentials_refresh_token(json_refresh_token):
if isinstance(json_refresh_token, bytes):
pass
elif isinstance(json_refresh_token, basestring):
json_refresh_token = json_refresh_token.encode()
else:
raise TypeError("expected json_refresh_token to be str or bytes")
cdef ClientCredentials credentials = ClientCredentials()
credentials.c_credentials = grpc.grpc_refresh_token_credentials_create(
json_refresh_token)
credentials.references.append(json_refresh_token)
return credentials
def client_credentials_fake_transport_security():
cdef ClientCredentials credentials = ClientCredentials()
credentials.c_credentials = (
grpc.grpc_fake_transport_security_credentials_create())
return credentials
def client_credentials_iam(authorization_token, authority_selector):
if isinstance(authorization_token, bytes):
pass
elif isinstance(authorization_token, basestring):
authorization_token = authorization_token.encode()
else:
raise TypeError("expected authorization_token to be str or bytes")
if isinstance(authority_selector, bytes):
pass
elif isinstance(authority_selector, basestring):
authority_selector = authority_selector.encode()
else:
raise TypeError("expected authority_selector to be str or bytes")
cdef ClientCredentials credentials = ClientCredentials()
credentials.c_credentials = grpc.grpc_iam_credentials_create(
authorization_token, authority_selector)
credentials.references.append(authorization_token)
credentials.references.append(authority_selector)
return credentials
def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs):
if pem_root_certs is None:
pass
elif isinstance(pem_root_certs, bytes):
pass
elif isinstance(pem_root_certs, basestring):
pem_root_certs = pem_root_certs.encode()
else:
raise TypeError("expected pem_root_certs to be str or bytes")
pem_key_cert_pairs = list(pem_key_cert_pairs)
for pair in pem_key_cert_pairs:
if not isinstance(pair, records.SslPemKeyCertPair):
raise TypeError("expected pem_key_cert_pairs to be sequence of "
"records.SslPemKeyCertPair")
cdef ServerCredentials credentials = ServerCredentials()
credentials.references.append(pem_key_cert_pairs)
credentials.references.append(pem_root_certs)
credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs)
credentials.c_ssl_pem_key_cert_pairs = (
<grpc.grpc_ssl_pem_key_cert_pair *>grpc.gpr_malloc(
sizeof(grpc.grpc_ssl_pem_key_cert_pair) *
credentials.c_ssl_pem_key_cert_pairs_count
))
for i in range(credentials.c_ssl_pem_key_cert_pairs_count):
credentials.c_ssl_pem_key_cert_pairs[i] = (
(<records.SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair)
credentials.c_credentials = grpc.grpc_ssl_server_credentials_create(
pem_root_certs, credentials.c_ssl_pem_key_cert_pairs,
credentials.c_ssl_pem_key_cert_pairs_count
)
return credentials
def server_credentials_fake_transport_security():
cdef ServerCredentials credentials = ServerCredentials()
credentials.c_credentials = (
grpc.grpc_fake_transport_security_server_credentials_create())
return credentials

@ -0,0 +1,344 @@
# 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.
cimport libc.time
cdef extern from "grpc/support/alloc.h":
void *gpr_malloc(size_t size)
void gpr_free(void *ptr)
void *gpr_realloc(void *p, size_t size)
cdef extern from "grpc/support/slice.h":
ctypedef struct gpr_slice:
# don't worry about writing out the members of gpr_slice; we never access
# them directly.
pass
gpr_slice gpr_slice_ref(gpr_slice s)
void gpr_slice_unref(gpr_slice s)
gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *))
gpr_slice gpr_slice_new_with_len(
void *p, size_t len, void (*destroy)(void *, size_t))
gpr_slice gpr_slice_malloc(size_t length)
gpr_slice gpr_slice_from_copied_string(const char *source)
gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len)
# Declare functions for function-like macros (because Cython)...
void *gpr_slice_start_ptr "GPR_SLICE_START_PTR" (gpr_slice s)
size_t gpr_slice_length "GPR_SLICE_LENGTH" (gpr_slice s)
cdef extern from "grpc/support/port_platform.h":
# As long as the header file gets this type right, we don't need to get this
# type exactly; just close enough that the operations will be supported in the
# underlying C layers.
ctypedef unsigned int gpr_uint32
cdef extern from "grpc/support/time.h":
ctypedef struct gpr_timespec:
libc.time.time_t seconds "tv_sec"
int nanoseconds "tv_nsec"
cdef gpr_timespec gpr_time_0
cdef gpr_timespec gpr_inf_future
cdef gpr_timespec gpr_inf_past
gpr_timespec gpr_now()
cdef extern from "grpc/status.h":
ctypedef enum grpc_status_code:
GRPC_STATUS_OK
GRPC_STATUS_CANCELLED
GRPC_STATUS_UNKNOWN
GRPC_STATUS_INVALID_ARGUMENT
GRPC_STATUS_DEADLINE_EXCEEDED
GRPC_STATUS_NOT_FOUND
GRPC_STATUS_ALREADY_EXISTS
GRPC_STATUS_PERMISSION_DENIED
GRPC_STATUS_UNAUTHENTICATED
GRPC_STATUS_RESOURCE_EXHAUSTED
GRPC_STATUS_FAILED_PRECONDITION
GRPC_STATUS_ABORTED
GRPC_STATUS_OUT_OF_RANGE
GRPC_STATUS_UNIMPLEMENTED
GRPC_STATUS_INTERNAL
GRPC_STATUS_UNAVAILABLE
GRPC_STATUS_DATA_LOSS
GRPC_STATUS__DO_NOT_USE
cdef extern from "grpc/byte_buffer_reader.h":
struct grpc_byte_buffer_reader:
# We don't care about the internals
pass
cdef extern from "grpc/byte_buffer.h":
ctypedef struct grpc_byte_buffer:
# We don't care about the internals.
pass
grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices,
size_t nslices)
size_t grpc_byte_buffer_length(grpc_byte_buffer *bb)
void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer)
void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
grpc_byte_buffer *buffer)
int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
gpr_slice *slice)
void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader)
cdef extern from "grpc/grpc.h":
ctypedef struct grpc_completion_queue:
# We don't care about the internals (and in fact don't know them)
pass
ctypedef struct grpc_channel:
# We don't care about the internals (and in fact don't know them)
pass
ctypedef struct grpc_server:
# We don't care about the internals (and in fact don't know them)
pass
ctypedef struct grpc_call:
# We don't care about the internals (and in fact don't know them)
pass
ctypedef enum grpc_arg_type:
grpc_arg_string "GRPC_ARG_STRING"
grpc_arg_integer "GRPC_ARG_INTEGER"
grpc_arg_pointer "GRPC_ARG_POINTER"
ctypedef struct grpc_arg_value_pointer:
void *address "p"
void *(*copy)(void *)
void (*destroy)(void *)
union grpc_arg_value:
char *string
int integer
grpc_arg_value_pointer pointer
ctypedef struct grpc_arg:
grpc_arg_type type
char *key
grpc_arg_value value
ctypedef struct grpc_channel_args:
size_t arguments_length "num_args"
grpc_arg *arguments "args"
ctypedef enum grpc_call_error:
GRPC_CALL_OK
GRPC_CALL_ERROR
GRPC_CALL_ERROR_NOT_ON_SERVER
GRPC_CALL_ERROR_NOT_ON_CLIENT
GRPC_CALL_ERROR_ALREADY_ACCEPTED
GRPC_CALL_ERROR_ALREADY_INVOKED
GRPC_CALL_ERROR_NOT_INVOKED
GRPC_CALL_ERROR_ALREADY_FINISHED
GRPC_CALL_ERROR_TOO_MANY_OPERATIONS
GRPC_CALL_ERROR_INVALID_FLAGS
GRPC_CALL_ERROR_INVALID_METADATA
ctypedef struct grpc_metadata:
const char *key
const char *value
size_t value_length
# ignore the 'internal_data.obfuscated' fields.
ctypedef enum grpc_completion_type:
GRPC_QUEUE_SHUTDOWN
GRPC_QUEUE_TIMEOUT
GRPC_OP_COMPLETE
ctypedef struct grpc_event:
grpc_completion_type type
int success
void *tag
ctypedef struct grpc_metadata_array:
size_t count
size_t capacity
grpc_metadata *metadata
void grpc_metadata_array_init(grpc_metadata_array *array)
void grpc_metadata_array_destroy(grpc_metadata_array *array)
ctypedef struct grpc_call_details:
char *method
size_t method_capacity
char *host
size_t host_capacity
gpr_timespec deadline
void grpc_call_details_init(grpc_call_details *details)
void grpc_call_details_destroy(grpc_call_details *details)
ctypedef enum grpc_op_type:
GRPC_OP_SEND_INITIAL_METADATA
GRPC_OP_SEND_MESSAGE
GRPC_OP_SEND_CLOSE_FROM_CLIENT
GRPC_OP_SEND_STATUS_FROM_SERVER
GRPC_OP_RECV_INITIAL_METADATA
GRPC_OP_RECV_MESSAGE
GRPC_OP_RECV_STATUS_ON_CLIENT
GRPC_OP_RECV_CLOSE_ON_SERVER
ctypedef struct grpc_op_data_send_initial_metadata:
size_t count
grpc_metadata *metadata
ctypedef struct grpc_op_data_send_status_from_server:
size_t trailing_metadata_count
grpc_metadata *trailing_metadata
grpc_status_code status
const char *status_details
ctypedef struct grpc_op_data_recv_status_on_client:
grpc_metadata_array *trailing_metadata
grpc_status_code *status
char **status_details
size_t *status_details_capacity
ctypedef struct grpc_op_data_recv_close_on_server:
int *cancelled
union grpc_op_data:
grpc_op_data_send_initial_metadata send_initial_metadata
grpc_byte_buffer *send_message
grpc_op_data_send_status_from_server send_status_from_server
grpc_metadata_array *receive_initial_metadata "recv_initial_metadata"
grpc_byte_buffer **receive_message "recv_message"
grpc_op_data_recv_status_on_client receive_status_on_client "recv_status_on_client"
grpc_op_data_recv_close_on_server receive_close_on_server "recv_close_on_server"
ctypedef struct grpc_op:
grpc_op_type type "op"
gpr_uint32 flags
grpc_op_data data
void grpc_init()
void grpc_shutdown()
grpc_completion_queue *grpc_completion_queue_create()
grpc_event grpc_completion_queue_next(grpc_completion_queue *cq,
gpr_timespec deadline) nogil
void grpc_completion_queue_shutdown(grpc_completion_queue *cq)
void grpc_completion_queue_destroy(grpc_completion_queue *cq)
grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
size_t nops, void *tag)
grpc_call_error grpc_call_cancel(grpc_call *call)
grpc_call_error grpc_call_cancel_with_status(grpc_call *call,
grpc_status_code status,
const char *description)
void grpc_call_destroy(grpc_call *call)
grpc_channel *grpc_channel_create(const char *target,
const grpc_channel_args *args)
grpc_call *grpc_channel_create_call(grpc_channel *channel,
grpc_completion_queue *completion_queue,
const char *method, const char *host,
gpr_timespec deadline)
void grpc_channel_destroy(grpc_channel *channel)
grpc_server *grpc_server_create(const grpc_channel_args *args)
grpc_call_error grpc_server_request_call(
grpc_server *server, grpc_call **call, grpc_call_details *details,
grpc_metadata_array *request_metadata, grpc_completion_queue
*cq_bound_to_call, grpc_completion_queue *cq_for_notification, void
*tag_new)
void grpc_server_register_completion_queue(grpc_server *server,
grpc_completion_queue *cq)
int grpc_server_add_http2_port(grpc_server *server, const char *addr)
void grpc_server_start(grpc_server *server)
void grpc_server_shutdown_and_notify(
grpc_server *server, grpc_completion_queue *cq, void *tag)
void grpc_server_cancel_all_calls(grpc_server *server)
void grpc_server_destroy(grpc_server *server)
cdef extern from "grpc/grpc_security.h":
ctypedef struct grpc_ssl_pem_key_cert_pair:
const char *private_key
const char *certificate_chain "cert_chain"
ctypedef struct grpc_credentials:
# We don't care about the internals (and in fact don't know them)
pass
grpc_credentials *grpc_google_default_credentials_create()
grpc_credentials *grpc_ssl_credentials_create(
const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair)
grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
grpc_credentials *creds2)
grpc_credentials *grpc_compute_engine_credentials_create()
grpc_credentials *grpc_service_account_credentials_create(
const char *json_key, const char *scope, gpr_timespec token_lifetime)
grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
gpr_timespec token_lifetime)
grpc_credentials *grpc_refresh_token_credentials_create(
const char *json_refresh_token)
grpc_credentials *grpc_fake_transport_security_credentials_create()
grpc_credentials *grpc_iam_credentials_create(const char *authorization_token,
const char *authority_selector)
void grpc_credentials_release(grpc_credentials *creds)
grpc_channel *grpc_secure_channel_create(
grpc_credentials *creds, const char *target,
const grpc_channel_args *args)
ctypedef struct grpc_server_credentials:
# We don't care about the internals (and in fact don't know them)
pass
grpc_server_credentials *grpc_ssl_server_credentials_create(
const char *pem_root_certs,
grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
size_t num_key_cert_pairs);
grpc_server_credentials *grpc_fake_transport_security_server_credentials_create()
void grpc_server_credentials_release(grpc_server_credentials *creds)
int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
grpc_server_credentials *creds)
grpc_call_error grpc_call_set_credentials(grpc_call *call,
grpc_credentials *creds)

@ -0,0 +1,129 @@
# 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.
from grpc._cython._cygrpc cimport grpc
from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport server
cdef class Timespec:
cdef grpc.gpr_timespec c_time
cdef class CallDetails:
cdef grpc.grpc_call_details c_details
cdef class OperationTag:
cdef object user_tag
cdef list references
# This allows CompletionQueue to notify the Python Server object that the
# underlying GRPC core server has shutdown
cdef server.Server shutting_down_server
cdef call.Call operation_call
cdef CallDetails request_call_details
cdef Metadata request_metadata
cdef Operations batch_operations
cdef bint is_new_request
cdef class Event:
cdef readonly grpc.grpc_completion_type type
cdef readonly bint success
cdef readonly object tag
# For operations with calls
cdef readonly call.Call operation_call
# For Server.request_call
cdef readonly CallDetails request_call_details
cdef readonly Metadata request_metadata
# For Call.start_batch
cdef readonly Operations batch_operations
cdef class ByteBuffer:
cdef grpc.grpc_byte_buffer *c_byte_buffer
cdef class SslPemKeyCertPair:
cdef grpc.grpc_ssl_pem_key_cert_pair c_pair
cdef readonly object private_key, certificate_chain
cdef class ChannelArg:
cdef grpc.grpc_arg c_arg
cdef readonly object key, value
cdef class ChannelArgs:
cdef grpc.grpc_channel_args c_args
cdef list args
cdef class Metadatum:
cdef grpc.grpc_metadata c_metadata
cdef object _key, _value
cdef class Metadata:
cdef grpc.grpc_metadata_array c_metadata_array
cdef object metadata
cdef class Operation:
cdef grpc.grpc_op c_op
cdef ByteBuffer _received_message
cdef Metadata _received_metadata
cdef grpc.grpc_status_code _received_status_code
cdef char *_received_status_details
cdef size_t _received_status_details_capacity
cdef int _received_cancelled
cdef readonly bint is_valid
cdef object references
cdef class Operations:
cdef grpc.grpc_op *c_ops
cdef size_t c_nops
cdef list operations

@ -0,0 +1,575 @@
# 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.
from grpc._cython._cygrpc cimport grpc
from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport server
class StatusCode:
ok = grpc.GRPC_STATUS_OK
cancelled = grpc.GRPC_STATUS_CANCELLED
unknown = grpc.GRPC_STATUS_UNKNOWN
invalid_argument = grpc.GRPC_STATUS_INVALID_ARGUMENT
deadline_exceeded = grpc.GRPC_STATUS_DEADLINE_EXCEEDED
not_found = grpc.GRPC_STATUS_NOT_FOUND
already_exists = grpc.GRPC_STATUS_ALREADY_EXISTS
permission_denied = grpc.GRPC_STATUS_PERMISSION_DENIED
unauthenticated = grpc.GRPC_STATUS_UNAUTHENTICATED
resource_exhausted = grpc.GRPC_STATUS_RESOURCE_EXHAUSTED
failed_precondition = grpc.GRPC_STATUS_FAILED_PRECONDITION
aborted = grpc.GRPC_STATUS_ABORTED
out_of_range = grpc.GRPC_STATUS_OUT_OF_RANGE
unimplemented = grpc.GRPC_STATUS_UNIMPLEMENTED
internal = grpc.GRPC_STATUS_INTERNAL
unavailable = grpc.GRPC_STATUS_UNAVAILABLE
data_loss = grpc.GRPC_STATUS_DATA_LOSS
class CallError:
ok = grpc.GRPC_CALL_OK
error = grpc.GRPC_CALL_ERROR
not_on_server = grpc.GRPC_CALL_ERROR_NOT_ON_SERVER
not_on_client = grpc.GRPC_CALL_ERROR_NOT_ON_CLIENT
already_accepted = grpc.GRPC_CALL_ERROR_ALREADY_ACCEPTED
already_invoked = grpc.GRPC_CALL_ERROR_ALREADY_INVOKED
not_invoked = grpc.GRPC_CALL_ERROR_NOT_INVOKED
already_finished = grpc.GRPC_CALL_ERROR_ALREADY_FINISHED
too_many_operations = grpc.GRPC_CALL_ERROR_TOO_MANY_OPERATIONS
invalid_flags = grpc.GRPC_CALL_ERROR_INVALID_FLAGS
invalid_metadata = grpc.GRPC_CALL_ERROR_INVALID_METADATA
class CompletionType:
queue_shutdown = grpc.GRPC_QUEUE_SHUTDOWN
queue_timeout = grpc.GRPC_QUEUE_TIMEOUT
operation_complete = grpc.GRPC_OP_COMPLETE
class OperationType:
send_initial_metadata = grpc.GRPC_OP_SEND_INITIAL_METADATA
send_message = grpc.GRPC_OP_SEND_MESSAGE
send_close_from_client = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT
send_status_from_server = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER
receive_initial_metadata = grpc.GRPC_OP_RECV_INITIAL_METADATA
receive_message = grpc.GRPC_OP_RECV_MESSAGE
receive_status_on_client = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
receive_close_on_server = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER
cdef class Timespec:
def __cinit__(self, time):
if time is None:
self.c_time = grpc.gpr_now()
elif isinstance(time, float):
if time == float("+inf"):
self.c_time = grpc.gpr_inf_future
elif time == float("-inf"):
self.c_time = grpc.gpr_inf_past
else:
self.c_time.seconds = time
self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9
else:
raise TypeError("expected time to be float")
@property
def seconds(self):
return self.c_time.seconds
@property
def nanoseconds(self):
return self.c_time.nanoseconds
def __float__(self):
return <double>self.c_time.seconds + <double>self.c_time.nanoseconds / 1e9
infinite_future = Timespec(float("+inf"))
infinite_past = Timespec(float("-inf"))
cdef class CallDetails:
def __cinit__(self):
grpc.grpc_call_details_init(&self.c_details)
def __dealloc__(self):
grpc.grpc_call_details_destroy(&self.c_details)
@property
def method(self):
if self.c_details.method != NULL:
return <bytes>self.c_details.method
else:
return None
@property
def host(self):
if self.c_details.host != NULL:
return <bytes>self.c_details.host
else:
return None
@property
def deadline(self):
timespec = Timespec(float("-inf"))
timespec.c_time = self.c_details.deadline
return timespec
cdef class OperationTag:
def __cinit__(self, user_tag):
self.user_tag = user_tag
self.references = []
cdef class Event:
def __cinit__(self, grpc.grpc_completion_type type, bint success,
object tag, call.Call operation_call,
CallDetails request_call_details,
Metadata request_metadata,
Operations batch_operations):
self.type = type
self.success = success
self.tag = tag
self.operation_call = operation_call
self.request_call_details = request_call_details
self.request_metadata = request_metadata
self.batch_operations = batch_operations
cdef class ByteBuffer:
def __cinit__(self, data):
if data is None:
self.c_byte_buffer = NULL
return
if isinstance(data, bytes):
pass
elif isinstance(data, basestring):
data = data.encode()
else:
raise TypeError("expected value to be of type str or bytes")
cdef char *c_data = data
data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data))
self.c_byte_buffer = grpc.grpc_raw_byte_buffer_create(
&data_slice, 1)
grpc.gpr_slice_unref(data_slice)
def bytes(self):
cdef grpc.grpc_byte_buffer_reader reader
cdef grpc.gpr_slice data_slice
cdef size_t data_slice_length
cdef void *data_slice_pointer
if self.c_byte_buffer != NULL:
grpc.grpc_byte_buffer_reader_init(&reader, self.c_byte_buffer)
result = b""
while grpc.grpc_byte_buffer_reader_next(&reader, &data_slice):
data_slice_pointer = grpc.gpr_slice_start_ptr(data_slice)
data_slice_length = grpc.gpr_slice_length(data_slice)
result += (<char *>data_slice_pointer)[:data_slice_length]
grpc.grpc_byte_buffer_reader_destroy(&reader)
return result
else:
return None
def __len__(self):
if self.c_byte_buffer != NULL:
return grpc.grpc_byte_buffer_length(self.c_byte_buffer)
else:
return 0
def __str__(self):
return self.bytes()
def __dealloc__(self):
if self.c_byte_buffer != NULL:
grpc.grpc_byte_buffer_destroy(self.c_byte_buffer)
cdef class SslPemKeyCertPair:
def __cinit__(self, private_key, certificate_chain):
if isinstance(private_key, bytes):
self.private_key = private_key
elif isinstance(private_key, basestring):
self.private_key = private_key.encode()
else:
raise TypeError("expected private_key to be of type str or bytes")
if isinstance(certificate_chain, bytes):
self.certificate_chain = certificate_chain
elif isinstance(certificate_chain, basestring):
self.certificate_chain = certificate_chain.encode()
else:
raise TypeError("expected certificate_chain to be of type str or bytes "
"or int")
self.c_pair.private_key = self.private_key
self.c_pair.certificate_chain = self.certificate_chain
cdef class ChannelArg:
def __cinit__(self, key, value):
if isinstance(key, bytes):
self.key = key
elif isinstance(key, basestring):
self.key = key.encode()
else:
raise TypeError("expected key to be of type str or bytes")
if isinstance(value, bytes):
self.value = value
self.c_arg.type = grpc.GRPC_ARG_STRING
self.c_arg.value.string = self.value
elif isinstance(value, basestring):
self.value = value.encode()
self.c_arg.type = grpc.GRPC_ARG_STRING
self.c_arg.value.string = self.value
elif isinstance(value, int):
self.value = int(value)
self.c_arg.type = grpc.GRPC_ARG_INTEGER
self.c_arg.value.integer = self.value
else:
raise TypeError("expected value to be of type str or bytes or int")
self.c_arg.key = self.key
cdef class ChannelArgs:
def __cinit__(self, args):
self.args = list(args)
for arg in self.args:
if not isinstance(arg, ChannelArg):
raise TypeError("expected list of ChannelArg")
self.c_args.arguments_length = len(self.args)
self.c_args.arguments = <grpc.grpc_arg *>grpc.gpr_malloc(
self.c_args.arguments_length*sizeof(grpc.grpc_arg)
)
for i in range(self.c_args.arguments_length):
self.c_args.arguments[i] = (<ChannelArg>self.args[i]).c_arg
def __dealloc__(self):
grpc.gpr_free(self.c_args.arguments)
def __len__(self):
# self.args is never stale; it's only updated from this file
return len(self.args)
def __getitem__(self, size_t i):
# self.args is never stale; it's only updated from this file
return self.args[i]
cdef class Metadatum:
def __cinit__(self, key, value):
if isinstance(key, bytes):
self._key = key
elif isinstance(key, basestring):
self._key = key.encode()
else:
raise TypeError("expected key to be of type str or bytes")
if isinstance(value, bytes):
self._value = value
elif isinstance(value, basestring):
self._value = value.encode()
else:
raise TypeError("expected value to be of type str or bytes")
self.c_metadata.key = self._key
self.c_metadata.value = self._value
self.c_metadata.value_length = len(self._value)
@property
def key(self):
return <bytes>self.c_metadata.key
@property
def value(self):
return <bytes>self.c_metadata.value[:self.c_metadata.value_length]
def __len__(self):
return 2
def __getitem__(self, size_t i):
if i == 0:
return self.key
elif i == 1:
return self.value
else:
raise IndexError("index must be 0 (key) or 1 (value)")
def __iter__(self):
return iter((self.key, self.value))
cdef class _MetadataIterator:
cdef size_t i
cdef Metadata metadata
def __cinit__(self, Metadata metadata not None):
self.i = 0
self.metadata = metadata
def __next__(self):
if self.i < len(self.metadata):
result = self.metadata[self.i]
self.i = self.i + 1
return result
else:
raise StopIteration()
cdef class Metadata:
def __cinit__(self, metadata):
self.metadata = list(metadata)
for metadatum in metadata:
if not isinstance(metadatum, Metadatum):
raise TypeError("expected list of Metadatum")
grpc.grpc_metadata_array_init(&self.c_metadata_array)
self.c_metadata_array.count = len(self.metadata)
self.c_metadata_array.capacity = len(self.metadata)
self.c_metadata_array.metadata = <grpc.grpc_metadata *>grpc.gpr_malloc(
self.c_metadata_array.count*sizeof(grpc.grpc_metadata)
)
for i in range(self.c_metadata_array.count):
self.c_metadata_array.metadata[i] = (
(<Metadatum>self.metadata[i]).c_metadata)
def __dealloc__(self):
# this frees the allocated memory for the grpc_metadata_array (although
# it'd be nice if that were documented somewhere...) TODO(atash): document
# this in the C core
grpc.grpc_metadata_array_destroy(&self.c_metadata_array)
def __len__(self):
return self.c_metadata_array.count
def __getitem__(self, size_t i):
return Metadatum(
key=<bytes>self.c_metadata_array.metadata[i].key,
value=<bytes>self.c_metadata_array.metadata[i].value[
:self.c_metadata_array.metadata[i].value_length])
def __iter__(self):
return _MetadataIterator(self)
cdef class Operation:
def __cinit__(self):
self.references = []
self._received_status_details = NULL
self._received_status_details_capacity = 0
self.is_valid = False
@property
def type(self):
return self.c_op.type
@property
def received_message(self):
if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE:
raise TypeError("self must be an operation receiving a message")
return self._received_message
@property
def received_metadata(self):
if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and
self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT):
raise TypeError("self must be an operation receiving metadata")
return self._received_metadata
@property
def received_status_code(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
raise TypeError("self must be an operation receiving a status code")
return self._received_status_code
@property
def received_status_details(self):
if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
raise TypeError("self must be an operation receiving status details")
if self._received_status_details:
return self._received_status_details
else:
return None
@property
def received_cancelled(self):
if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER:
raise TypeError("self must be an operation receiving cancellation "
"information")
return False if self._received_cancelled == 0 else True
def __dealloc__(self):
# 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.
# This means that we need to clean up after receive_status_on_client.
if self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
grpc.gpr_free(self._received_status_details)
def operation_send_initial_metadata(Metadata metadata):
cdef Operation op = Operation()
op.c_op.type = grpc.GRPC_OP_SEND_INITIAL_METADATA
op.c_op.data.send_initial_metadata.count = metadata.c_metadata_array.count
op.c_op.data.send_initial_metadata.metadata = (
metadata.c_metadata_array.metadata)
op.references.append(metadata)
op.is_valid = True
return op
def operation_send_message(data):
cdef Operation op = Operation()
op.c_op.type = grpc.GRPC_OP_SEND_MESSAGE
byte_buffer = ByteBuffer(data)
op.c_op.data.send_message = byte_buffer.c_byte_buffer
op.references.append(byte_buffer)
op.is_valid = True
return op
def operation_send_close_from_client():
cdef Operation op = Operation()
op.c_op.type = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT
op.is_valid = True
return op
def operation_send_status_from_server(
Metadata metadata, grpc.grpc_status_code code, details):
if isinstance(details, bytes):
pass
elif isinstance(details, basestring):
details = details.encode()
else:
raise TypeError("expected a str or bytes object for details")
cdef Operation op = Operation()
op.c_op.type = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER
op.c_op.data.send_status_from_server.trailing_metadata_count = (
metadata.c_metadata_array.count)
op.c_op.data.send_status_from_server.trailing_metadata = (
metadata.c_metadata_array.metadata)
op.c_op.data.send_status_from_server.status = code
op.c_op.data.send_status_from_server.status_details = details
op.references.append(metadata)
op.references.append(details)
op.is_valid = True
return op
def operation_receive_initial_metadata():
cdef Operation op = Operation()
op.c_op.type = grpc.GRPC_OP_RECV_INITIAL_METADATA
op._received_metadata = Metadata([])
op.c_op.data.receive_initial_metadata = (
&op._received_metadata.c_metadata_array)
op.is_valid = True
return op
def operation_receive_message():
cdef Operation op = Operation()
op.c_op.type = grpc.GRPC_OP_RECV_MESSAGE
op._received_message = ByteBuffer(None)
# n.b. the c_op.data.receive_message field needs to be deleted by us,
# anyway, so we just let that be handled by the ByteBuffer() we allocated
# the line before.
op.c_op.data.receive_message = &op._received_message.c_byte_buffer
op.is_valid = True
return op
def operation_receive_status_on_client():
cdef Operation op = Operation()
op.c_op.type = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
op._received_metadata = Metadata([])
op.c_op.data.receive_status_on_client.trailing_metadata = (
&op._received_metadata.c_metadata_array)
op.c_op.data.receive_status_on_client.status = (
&op._received_status_code)
op.c_op.data.receive_status_on_client.status_details = (
&op._received_status_details)
op.c_op.data.receive_status_on_client.status_details_capacity = (
&op._received_status_details_capacity)
op.is_valid = True
return op
def operation_receive_close_on_server():
cdef Operation op = Operation()
op.c_op.type = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER
op.c_op.data.receive_close_on_server.cancelled = &op._received_cancelled
op.is_valid = True
return op
cdef class _OperationsIterator:
cdef size_t i
cdef Operations operations
def __cinit__(self, Operations operations not None):
self.i = 0
self.operations = operations
def __next__(self):
if self.i < len(self.operations):
result = self.operations[self.i]
self.i = self.i + 1
return result
else:
raise StopIteration()
cdef class Operations:
def __cinit__(self, operations):
self.operations = list(operations) # normalize iterable
self.c_ops = NULL
self.c_nops = 0
for operation in self.operations:
if not isinstance(operation, Operation):
raise TypeError("expected operations to be iterable of Operation")
self.c_nops = len(self.operations)
self.c_ops = <grpc.grpc_op *>grpc.gpr_malloc(
sizeof(grpc.grpc_op)*self.c_nops)
for i in range(self.c_nops):
self.c_ops[i] = (<Operation>(self.operations[i])).c_op
def __len__(self):
return self.c_nops
def __getitem__(self, size_t i):
# self.operations is never stale; it's only updated from this file
return self.operations[i]
def __dealloc__(self):
grpc.gpr_free(self.c_ops)
def __iter__(self):
return _OperationsIterator(self)

@ -0,0 +1,45 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from grpc._cython._cygrpc cimport grpc
from grpc._cython._cygrpc cimport completion_queue
cdef class Server:
cdef grpc.grpc_server *c_server
cdef bint is_started # start has been called
cdef bint is_shutting_down # shutdown has been called
cdef bint is_shutdown # notification of complete shutdown received
# used at dealloc when user forgets to shutdown
cdef completion_queue.CompletionQueue backup_shutdown_queue
cdef list references
cdef list registered_completion_queues
cdef notify_shutdown_complete(self)

@ -0,0 +1,158 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport completion_queue
from grpc._cython._cygrpc cimport credentials
from grpc._cython._cygrpc cimport records
import time
cdef class Server:
def __cinit__(self, records.ChannelArgs arguments=None):
cdef grpc.grpc_channel_args *c_arguments = NULL
self.references = []
self.registered_completion_queues = []
if arguments is not None:
c_arguments = &arguments.c_args
self.references.append(arguments)
self.c_server = grpc.grpc_server_create(c_arguments)
self.is_started = False
self.is_shutting_down = False
self.is_shutdown = False
def request_call(
self, completion_queue.CompletionQueue call_queue not None,
completion_queue.CompletionQueue server_queue not None, tag):
if not self.is_started or self.is_shutting_down:
raise ValueError("server must be started and not shutting down")
if server_queue not in self.registered_completion_queues:
raise ValueError("server_queue must be a registered completion queue")
cdef records.OperationTag operation_tag = records.OperationTag(tag)
operation_tag.operation_call = call.Call()
operation_tag.request_call_details = records.CallDetails()
operation_tag.request_metadata = records.Metadata([])
operation_tag.references.extend([self, call_queue, server_queue])
operation_tag.is_new_request = True
operation_tag.batch_operations = records.Operations([])
cpython.Py_INCREF(operation_tag)
return grpc.grpc_server_request_call(
self.c_server, &operation_tag.operation_call.c_call,
&operation_tag.request_call_details.c_details,
&operation_tag.request_metadata.c_metadata_array,
call_queue.c_completion_queue, server_queue.c_completion_queue,
<cpython.PyObject *>operation_tag)
def register_completion_queue(
self, completion_queue.CompletionQueue queue not None):
if self.is_started:
raise ValueError("cannot register completion queues after start")
grpc.grpc_server_register_completion_queue(
self.c_server, queue.c_completion_queue)
self.registered_completion_queues.append(queue)
def start(self):
if self.is_started:
raise ValueError("the server has already started")
self.backup_shutdown_queue = completion_queue.CompletionQueue()
self.register_completion_queue(self.backup_shutdown_queue)
self.is_started = True
grpc.grpc_server_start(self.c_server)
def add_http2_port(self, address,
credentials.ServerCredentials server_credentials=None):
if isinstance(address, bytes):
pass
elif isinstance(address, basestring):
address = address.encode()
else:
raise TypeError("expected address to be a str or bytes")
self.references.append(address)
if server_credentials is not None:
self.references.append(server_credentials)
return grpc.grpc_server_add_secure_http2_port(
self.c_server, address, server_credentials.c_credentials)
else:
return grpc.grpc_server_add_http2_port(self.c_server, address)
def shutdown(self, completion_queue.CompletionQueue queue not None, tag):
cdef records.OperationTag operation_tag
if queue.is_shutting_down:
raise ValueError("queue must be live")
elif not self.is_started:
raise ValueError("the server hasn't started yet")
elif self.is_shutting_down:
return
elif queue not in self.registered_completion_queues:
raise ValueError("expected registered completion queue")
else:
self.is_shutting_down = True
operation_tag = records.OperationTag(tag)
operation_tag.shutting_down_server = self
operation_tag.references.extend([self, queue])
cpython.Py_INCREF(operation_tag)
grpc.grpc_server_shutdown_and_notify(
self.c_server, queue.c_completion_queue,
<cpython.PyObject *>operation_tag)
cdef notify_shutdown_complete(self):
# called only by a completion queue on receiving our shutdown operation tag
self.is_shutdown = True
def cancel_all_calls(self):
if not self.is_shutting_down:
raise ValueError("the server must be shutting down to cancel all calls")
elif self.is_shutdown:
return
else:
grpc.grpc_server_cancel_all_calls(self.c_server)
def __dealloc__(self):
if self.c_server != NULL:
if not self.is_started:
pass
elif self.is_shutdown:
pass
elif not self.is_shutting_down:
# the user didn't call shutdown - use our backup queue
self.shutdown(self.backup_shutdown_queue, None)
# and now we wait
while not self.is_shutdown:
self.backup_shutdown_queue.poll()
else:
# We're in the process of shutting down, but have not shutdown; can't do
# much but repeatedly release the GIL and wait
while not self.is_shutdown:
time.sleep(0)
grpc.grpc_server_destroy(self.c_server)

@ -0,0 +1,114 @@
# 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.
# Adapter from grpc._cython.types to the surface expected by
# grpc._adapter._intermediary_low.
#
# 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 service_account():
raise NotImplementedError()
@staticmethod
def jwt():
raise NotImplementedError()
@staticmethod
def refresh_token():
raise NotImplementedError()
@staticmethod
def fake_transport_security():
raise NotImplementedError()
@staticmethod
def iam():
raise NotImplementedError()
class ServerCredentials(object):
def __init__(self):
raise NotImplementedError()
@staticmethod
def ssl():
raise NotImplementedError()
@staticmethod
def fake_transport_security():
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()
class Server(type_interfaces.Server):
def __init__(self):
raise NotImplementedError()

@ -0,0 +1,187 @@
# 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)

@ -0,0 +1,111 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cimport cpython
from grpc._cython._cygrpc cimport grpc
from grpc._cython._cygrpc cimport call
from grpc._cython._cygrpc cimport channel
from grpc._cython._cygrpc cimport credentials
from grpc._cython._cygrpc cimport completion_queue
from grpc._cython._cygrpc cimport records
from grpc._cython._cygrpc cimport server
from grpc._cython._cygrpc import call
from grpc._cython._cygrpc import channel
from grpc._cython._cygrpc import credentials
from grpc._cython._cygrpc import completion_queue
from grpc._cython._cygrpc import records
from grpc._cython._cygrpc import server
StatusCode = records.StatusCode
CallError = records.CallError
CompletionType = records.CompletionType
OperationType = records.OperationType
Timespec = records.Timespec
CallDetails = records.CallDetails
Event = records.Event
ByteBuffer = records.ByteBuffer
SslPemKeyCertPair = records.SslPemKeyCertPair
ChannelArg = records.ChannelArg
ChannelArgs = records.ChannelArgs
Metadatum = records.Metadatum
Metadata = records.Metadata
Operation = records.Operation
operation_send_initial_metadata = records.operation_send_initial_metadata
operation_send_message = records.operation_send_message
operation_send_close_from_client = records.operation_send_close_from_client
operation_send_status_from_server = records.operation_send_status_from_server
operation_receive_initial_metadata = records.operation_receive_initial_metadata
operation_receive_message = records.operation_receive_message
operation_receive_status_on_client = records.operation_receive_status_on_client
operation_receive_close_on_server = records.operation_receive_close_on_server
Operations = records.Operations
ClientCredentials = credentials.ClientCredentials
ServerCredentials = credentials.ServerCredentials
client_credentials_google_default = (
credentials.client_credentials_google_default)
client_credentials_ssl = credentials.client_credentials_ssl
client_credentials_composite_credentials = (
credentials.client_credentials_composite_credentials)
client_credentials_compute_engine = (
credentials.client_credentials_compute_engine)
client_credentials_jwt = credentials.client_credentials_jwt
client_credentials_refresh_token = credentials.client_credentials_refresh_token
client_credentials_fake_transport_security = (
credentials.client_credentials_fake_transport_security)
client_credentials_iam = credentials.client_credentials_iam
server_credentials_ssl = credentials.server_credentials_ssl
server_credentials_fake_transport_security = (
credentials.server_credentials_fake_transport_security)
CompletionQueue = completion_queue.CompletionQueue
Channel = channel.Channel
Server = server.Server
Call = call.Call
#
# Global state
#
cdef class _ModuleState:
def __cinit__(self):
grpc.grpc_init()
def __dealloc__(self):
grpc.grpc_shutdown()
_module_state = _ModuleState()

@ -0,0 +1,276 @@
# 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.
import time
import unittest
from grpc._cython import cygrpc
from grpc._cython import test_utilities
class TypeSmokeTest(unittest.TestCase):
def testStringsInUtilitiesUpDown(self):
self.assertEqual(0, cygrpc.StatusCode.ok)
metadatum = cygrpc.Metadatum('a', 'b')
self.assertEqual('a'.encode(), metadatum.key)
self.assertEqual('b'.encode(), metadatum.value)
metadata = cygrpc.Metadata([metadatum])
self.assertEqual(1, len(metadata))
self.assertEqual(metadatum.key, metadata[0].key)
def testMetadataIteration(self):
metadata = cygrpc.Metadata([
cygrpc.Metadatum('a', 'b'), cygrpc.Metadatum('c', 'd')])
iterator = iter(metadata)
metadatum = next(iterator)
self.assertIsInstance(metadatum, cygrpc.Metadatum)
self.assertEqual(metadatum.key, 'a'.encode())
self.assertEqual(metadatum.value, 'b'.encode())
metadatum = next(iterator)
self.assertIsInstance(metadatum, cygrpc.Metadatum)
self.assertEqual(metadatum.key, 'c'.encode())
self.assertEqual(metadatum.value, 'd'.encode())
with self.assertRaises(StopIteration):
next(iterator)
def testOperationsIteration(self):
operations = cygrpc.Operations([
cygrpc.operation_send_message('asdf')])
iterator = iter(operations)
operation = next(iterator)
self.assertIsInstance(operation, cygrpc.Operation)
# `Operation`s are write-only structures; can't directly debug anything out
# of them. Just check that we stop iterating.
with self.assertRaises(StopIteration):
next(iterator)
def testTimespec(self):
now = time.time()
timespec = cygrpc.Timespec(now)
self.assertAlmostEqual(now, float(timespec), places=8)
def testClientCredentialsUpDown(self):
credentials = cygrpc.client_credentials_fake_transport_security()
del credentials
def testServerCredentialsUpDown(self):
credentials = cygrpc.server_credentials_fake_transport_security()
del credentials
def testCompletionQueueUpDown(self):
completion_queue = cygrpc.CompletionQueue()
del completion_queue
def testServerUpDown(self):
server = cygrpc.Server(cygrpc.ChannelArgs([]))
del server
def testChannelUpDown(self):
channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([]))
del channel
def testSecureChannelUpDown(self):
channel = cygrpc.Channel(
'[::]:0', cygrpc.ChannelArgs([]),
cygrpc.client_credentials_fake_transport_security())
del channel
@unittest.skip('TODO(atash): undo skip after #2229 is merged')
def testServerStartNoExplicitShutdown(self):
server = cygrpc.Server()
completion_queue = cygrpc.CompletionQueue()
server.register_completion_queue(completion_queue)
port = server.add_http2_port('[::]:0')
self.assertIsInstance(port, int)
server.start()
del server
@unittest.skip('TODO(atash): undo skip after #2229 is merged')
def testServerStartShutdown(self):
completion_queue = cygrpc.CompletionQueue()
server = cygrpc.Server()
server.add_http2_port('[::]:0')
server.register_completion_queue(completion_queue)
server.start()
shutdown_tag = object()
server.shutdown(completion_queue, shutdown_tag)
event = completion_queue.poll()
self.assertEqual(cygrpc.CompletionType.operation_complete, event.type)
self.assertIs(shutdown_tag, event.tag)
del server
del completion_queue
class InsecureServerInsecureClient(unittest.TestCase):
def setUp(self):
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')
self.server.start()
self.client_completion_queue = cygrpc.CompletionQueue()
self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port))
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 = b'hostess'
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)
client_call_tag = object()
client_call = self.client_channel.create_call(self.client_completion_queue,
METHOD, HOST, cygrpc_deadline)
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))
self.assertEqual(dict(client_initial_metadata),
dict(request_event.request_metadata))
self.assertEqual(METHOD, request_event.request_call_details.method)
self.assertEqual(HOST, 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.assertEqual(dict(server_initial_metadata),
dict(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.assertEqual(dict(server_trailing_metadata),
dict(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__':
unittest.main(verbosity=2)

@ -0,0 +1,46 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import threading
from grpc._cython._cygrpc import completion_queue
class CompletionQueuePollFuture:
def __init__(self, completion_queue, deadline):
def poller_function():
self._event_result = completion_queue.poll(deadline)
self._event_result = None
self._thread = threading.Thread(target=poller_function)
self._thread.start()
def result(self):
self._thread.join()
return self._event_result

@ -29,11 +29,20 @@
"""A setup module for the GRPC Python package."""
import os
import sys
from distutils import core as _core
from distutils import extension as _extension
import setuptools
import sys
_EXTENSION_SOURCES = (
# Use environment variables to determine whether or not the Cython extension
# should *use* Cython or use the generated C files. Note that this requires the
# C files to have been generated by building first *with* Cython support.
_BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False)
_C_EXTENSION_SOURCES = (
'grpc/_adapter/_c/module.c',
'grpc/_adapter/_c/types.c',
'grpc/_adapter/_c/utility.c',
@ -45,6 +54,19 @@ _EXTENSION_SOURCES = (
'grpc/_adapter/_c/types/server.c',
)
_CYTHON_EXTENSION_PACKAGE_NAMES = (
)
_CYTHON_EXTENSION_MODULE_NAMES = (
'grpc._cython.cygrpc',
'grpc._cython._cygrpc.call',
'grpc._cython._cygrpc.channel',
'grpc._cython._cygrpc.completion_queue',
'grpc._cython._cygrpc.credentials',
'grpc._cython._cygrpc.records',
'grpc._cython._cygrpc.server',
)
_EXTENSION_INCLUDE_DIRECTORIES = (
'.',
)
@ -56,15 +78,50 @@ _EXTENSION_LIBRARIES = (
if not "darwin" in sys.platform:
_EXTENSION_LIBRARIES += ('rt',)
_EXTENSION_MODULE = _core.Extension(
'grpc._adapter._c', sources=list(_EXTENSION_SOURCES),
_C_EXTENSION_MODULE = _core.Extension(
'grpc._adapter._c', sources=list(_C_EXTENSION_SOURCES),
include_dirs=list(_EXTENSION_INCLUDE_DIRECTORIES),
libraries=list(_EXTENSION_LIBRARIES),
)
)
_C_EXTENSION_MODULES = [_C_EXTENSION_MODULE]
def cython_extensions(package_names, module_names, include_dirs, libraries,
build_with_cython=False):
file_extension = 'pyx' if build_with_cython else 'c'
module_files = [name.replace('.', '/') + '.' + file_extension
for name in module_names]
extensions = [
_extension.Extension(
name=module_name, sources=[module_file],
include_dirs=include_dirs, libraries=libraries
) for (module_name, module_file) in zip(module_names, module_files)
]
if build_with_cython:
import Cython.Build
return Cython.Build.cythonize(extensions)
else:
return extensions
_CYTHON_EXTENSION_MODULES = cython_extensions(
list(_CYTHON_EXTENSION_PACKAGE_NAMES), list(_CYTHON_EXTENSION_MODULE_NAMES),
list(_EXTENSION_INCLUDE_DIRECTORIES), list(_EXTENSION_LIBRARIES),
bool(_BUILD_WITH_CYTHON))
# TODO(atash): We shouldn't need to gate any C code based on the python version
# from the distutils build system. Remove this hackery once we're on Cython and
# 3.x C API compliant.
_EXTENSION_MODULES = list(_CYTHON_EXTENSION_MODULES)
if sys.version_info[0:2] <= (2, 7):
_EXTENSION_MODULES += _C_EXTENSION_MODULES
_PACKAGES = (
'grpc',
'grpc._adapter',
'grpc._cython',
'grpc._cython._cygrpc',
'grpc._junkdrawer',
'grpc.early_adopter',
'grpc.framework',
@ -79,6 +136,7 @@ _PACKAGES = (
_PACKAGE_DIRECTORIES = {
'grpc': 'grpc',
'grpc._adapter': 'grpc/_adapter',
'grpc._cython': 'grpc/_cython',
'grpc._junkdrawer': 'grpc/_junkdrawer',
'grpc.early_adopter': 'grpc/early_adopter',
'grpc.framework': 'grpc/framework',
@ -87,7 +145,7 @@ _PACKAGE_DIRECTORIES = {
setuptools.setup(
name='grpcio',
version='0.9.0a1',
ext_modules=[_EXTENSION_MODULE],
ext_modules=_EXTENSION_MODULES,
packages=list(_PACKAGES),
package_dir=_PACKAGE_DIRECTORIES,
install_requires=[

@ -54,44 +54,55 @@ LIB_DIRS = [
LIBDIR
]
# Check to see if GRPC_ROOT is defined or available
grpc_root = ENV['GRPC_ROOT']
if grpc_root.nil?
r = File.expand_path(File.join(File.dirname(__FILE__), '../../../..'))
grpc_root = r if File.exist?(File.join(r, 'include/grpc/grpc.h'))
end
# When grpc_root is available attempt to build the grpc core.
unless grpc_root.nil?
grpc_config = ENV['GRPC_CONFIG'] || 'opt'
if ENV.key?('GRPC_LIB_DIR')
grpc_lib_dir = File.join(grpc_root, ENV['GRPC_LIB_DIR'])
else
grpc_lib_dir = File.join(File.join(grpc_root, 'libs'), grpc_config)
end
unless File.exist?(File.join(grpc_lib_dir, 'libgrpc.a'))
system("make -C #{grpc_root} static_c CONFIG=#{grpc_config}")
def check_grpc_root
grpc_root = ENV['GRPC_ROOT']
if grpc_root.nil?
r = File.expand_path(File.join(File.dirname(__FILE__), '../../../..'))
grpc_root = r if File.exist?(File.join(r, 'include/grpc/grpc.h'))
end
HEADER_DIRS.unshift File.join(grpc_root, 'include')
LIB_DIRS.unshift grpc_lib_dir
grpc_root
end
def crash(msg)
print(" extconf failure: #{msg}\n")
exit 1
end
grpc_pkg_config = system('pkg-config --exists grpc')
if grpc_pkg_config
$CFLAGS << ' ' + `pkg-config --static --cflags grpc`.strip + ' '
$LDFLAGS << ' ' + `pkg-config --static --libs grpc`.strip + ' '
else
dir_config('grpc', HEADER_DIRS, LIB_DIRS)
fail 'libdl not found' unless have_library('dl', 'dlopen')
fail 'zlib not found' unless have_library('z', 'inflate')
begin
fail 'Fail' unless have_library('gpr', 'gpr_now')
fail 'Fail' unless have_library('grpc', 'grpc_channel_destroy')
rescue
# Check to see if GRPC_ROOT is defined or available
grpc_root = check_grpc_root
dir_config('grpc', HEADER_DIRS, LIB_DIRS)
# Stop if there is still no grpc_root
exit 1 if grpc_root.nil?
$CFLAGS << ' -Wno-implicit-function-declaration '
$CFLAGS << ' -Wno-pointer-sign '
$CFLAGS << ' -Wno-return-type '
grpc_config = ENV['GRPC_CONFIG'] || 'opt'
if ENV.key?('GRPC_LIB_DIR')
grpc_lib_dir = File.join(grpc_root, ENV['GRPC_LIB_DIR'])
else
grpc_lib_dir = File.join(File.join(grpc_root, 'libs'), grpc_config)
end
unless File.exist?(File.join(grpc_lib_dir, 'libgrpc.a'))
print "Building internal gRPC\n"
system("make -C #{grpc_root} static_c CONFIG=#{grpc_config}")
end
$CFLAGS << ' -I' + File.join(grpc_root, 'include')
$LDFLAGS << ' -L' + grpc_lib_dir
raise 'gpr not found' unless have_library('gpr', 'gpr_now')
raise 'grpc not found' unless have_library('grpc', 'grpc_channel_destroy')
end
end
$CFLAGS << ' -std=c99 '
$CFLAGS << ' -Wall '
$CFLAGS << ' -Wextra '
$CFLAGS << ' -pedantic '
$CFLAGS << ' -Werror '
$LDFLAGS << ' -lgrpc -lgpr -lz -ldl'
crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
have_library('grpc', 'grpc_channel_destroy')
crash('need gpr lib') unless have_library('gpr', 'gpr_now')
create_makefile('grpc/grpc')

@ -68,8 +68,12 @@ static void grpc_rb_server_free(void *p) {
/* Deletes the wrapped object if the mark object is Qnil, which indicates
that no other object is the actual owner. */
/* grpc_server_shutdown does not exist. Change this to something that does
or delete it */
if (svr->wrapped != NULL && svr->mark == Qnil) {
grpc_server_shutdown(svr->wrapped);
// grpc_server_shutdown(svr->wrapped);
// Aborting to indicate a bug
abort();
grpc_server_destroy(svr->wrapped);
}

@ -29,5 +29,5 @@
# GRPC contains the General RPC module.
module GRPC
VERSION = '0.9.4'
VERSION = '0.10.0'
end

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save