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

pull/2135/head
David Garcia Quintas 10 years ago
commit 14ce90ff82
  1. 4
      BUILD
  2. 112
      Makefile
  3. 30
      build.json
  4. 49
      doc/interop-test-descriptions.md
  5. 3
      gRPC.podspec
  6. 6
      include/grpc++/credentials.h
  7. 4
      include/grpc++/server.h
  8. 33
      include/grpc++/server_builder.h
  9. 9
      include/grpc/grpc_security.h
  10. 2
      src/core/channel/census_filter.c
  11. 18
      src/core/channel/http_client_filter.c
  12. 30
      src/core/channel/http_server_filter.c
  13. 4
      src/core/httpcli/httpcli.h
  14. 20
      src/core/security/client_auth_filter.c
  15. 56
      src/core/security/credentials.c
  16. 830
      src/core/security/jwt_verifier.c
  17. 136
      src/core/security/jwt_verifier.h
  18. 20
      src/core/surface/call.c
  19. 34
      src/core/surface/channel.c
  20. 82
      src/core/surface/server.c
  21. 12
      src/core/transport/chttp2/hpack_parser.c
  22. 6
      src/core/transport/chttp2/hpack_table.c
  23. 2
      src/core/transport/chttp2/incoming_metadata.c
  24. 4
      src/core/transport/chttp2/parsing.c
  25. 26
      src/core/transport/chttp2/stream_encoder.c
  26. 2
      src/core/transport/chttp2_transport.c
  27. 107
      src/core/transport/metadata.c
  28. 26
      src/core/transport/metadata.h
  29. 6
      src/core/transport/stream_op.c
  30. 2
      src/core/transport/transport.c
  31. 2
      src/cpp/client/channel.cc
  32. 7
      src/cpp/client/secure_credentials.cc
  33. 9
      src/cpp/server/server.cc
  34. 18
      src/cpp/server/server_builder.cc
  35. 75
      src/ruby/ext/grpc/extconf.rb
  36. 6
      src/ruby/ext/grpc/rb_server.c
  37. 44
      templates/Makefile.template
  38. 22
      test/core/security/credentials_test.c
  39. 565
      test/core/security/jwt_verifier_test.c
  40. 4
      test/core/security/print_google_default_creds_token.c
  41. 119
      test/core/security/verify_jwt.c
  42. 2
      test/core/transport/chttp2/hpack_parser_test.c
  43. 2
      test/core/transport/chttp2/hpack_table_test.c
  44. 2
      test/core/transport/chttp2/stream_encoder_test.c
  45. 38
      test/core/transport/metadata_test.c
  46. 27
      test/cpp/end2end/end2end_test.cc
  47. 1
      test/cpp/util/messages.proto
  48. 27
      tools/dockerfile/grpc_java_android/Dockerfile
  49. 10
      tools/dockerfile/grpc_java_android/README.md
  50. 2
      tools/doxygen/Doxyfile.core.internal
  51. 12
      tools/jenkins/run_jenkins.sh
  52. 2
      tools/run_tests/build_ruby.sh
  53. 31
      tools/run_tests/sources_and_headers.json
  54. 9
      tools/run_tests/tests.json
  55. 16
      vsprojects/Grpc.mak
  56. 3
      vsprojects/grpc/grpc.vcxproj
  57. 6
      vsprojects/grpc/grpc.vcxproj.filters

@ -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",
@ -255,6 +256,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",
@ -950,6 +952,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",
@ -1088,6 +1091,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",

File diff suppressed because one or more lines are too long

@ -453,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",
@ -475,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",
@ -1358,6 +1360,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",
@ -1400,6 +1416,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",

@ -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',
@ -264,6 +265,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',
@ -400,6 +402,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',

@ -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_;

@ -126,13 +126,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

@ -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);
}
}

@ -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,

@ -219,11 +219,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) {
@ -272,10 +272,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 +314,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);
}
}

@ -461,7 +461,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;
@ -635,7 +634,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. -- */
@ -876,6 +875,59 @@ grpc_credentials *grpc_fake_oauth2_credentials_create(
return &c->base;
}
/* -- Oauth2 Access Token credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_md_store *access_token_md;
} grpc_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(

@ -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 */

@ -368,18 +368,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) {
@ -436,7 +436,7 @@ static void set_compression_algorithm(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;
}
@ -615,7 +615,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;
@ -968,7 +968,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 =
@ -1076,7 +1076,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;
@ -1275,7 +1275,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_compression_algorithm_string(call->channel)) {
set_compression_algorithm(call, decode_compression(md));
@ -1311,10 +1311,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);
}

@ -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_algorithm_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_algorithm_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);
@ -268,12 +268,12 @@ grpc_mdstr *grpc_channel_get_compression_algorithm_string(
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));
}
}

@ -182,7 +182,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;
@ -403,19 +407,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);
}
@ -429,7 +437,6 @@ 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 */
@ -440,9 +447,8 @@ static void start_new_rpc(grpc_call_element *elem) {
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) */
@ -453,14 +459,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) {
@ -504,10 +509,10 @@ 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;
@ -541,27 +546,34 @@ 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);
} else {
gpr_mu_unlock(&calld->mu_state);
}
gpr_mu_unlock(&chand->server->mu_call);
break;
}
@ -623,6 +635,7 @@ 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);
@ -634,21 +647,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;
size_t i;
gpr_mu_lock(&chand->server->mu_call);
for (i = 0; i < CALL_LIST_COUNT; i++) {
call_list_remove(elem->call_data, i);
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);
}
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);
}
@ -676,10 +690,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);
@ -691,8 +705,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);
}
}
@ -1043,10 +1057,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);
@ -1336,7 +1336,7 @@ static void on_header_not_set(void *user_data, grpc_mdelem *md) {
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);
}

@ -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,
@ -624,10 +624,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);

@ -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);

@ -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);
}
}

@ -60,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())

@ -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,

@ -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]);

@ -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;
}
}

@ -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);
}

@ -229,6 +229,7 @@ endif
endif
INSTALL = install
RM = rm -f
PKG_CONFIG = pkg-config
ifndef VALID_CONFIG_$(CONFIG)
$(error Invalid CONFIG value '$(CONFIG)')
@ -345,7 +346,7 @@ HOST_LDLIBS = $(LDLIBS)
# These are automatically computed variables.
# There shouldn't be any need to change anything from now on.
HAS_PKG_CONFIG = $(shell command -v pkg-config >/dev/null 2>&1 && echo true || echo false)
HAS_PKG_CONFIG = $(shell command -v $(PKG_CONFIG) >/dev/null 2>&1 && echo true || echo false)
PC_TEMPLATE = prefix=$(prefix)\n\
exec_prefix=${'\$${prefix}'}\n\
@ -357,7 +358,7 @@ Description: $(PC_DESCRIPTION)\n\
Version: $(VERSION)\n\
Cflags: -I${'\$${includedir}'} $(PC_CFLAGS)\n\
Requires.private: $(PC_REQUIRES_PRIVATE)\n\
Libs: -L${'\$${libdir}'}\n\
Libs: -L${'\$${libdir}'} $(PC_LIB)\n\
Libs.private: $(PC_LIBS_PRIVATE)
# gpr .pc file
@ -366,7 +367,8 @@ PC_DESCRIPTION = gRPC Portable Runtime
PC_CFLAGS = -pthread
PC_REQUIRES_PRIVATE =
PC_LIBS_PRIVATE = -lpthread
ifeq ($(SYSTEM),Darwin)
PC_LIB = -lgpr
ifneq ($(SYSTEM),Darwin)
PC_LIBS_PRIVATE += -lrt
endif
GPR_PC_FILE := $(PC_TEMPLATE)
@ -396,11 +398,10 @@ OPENSSL_REQUIRES_DL = true
endif
ifeq ($(HAS_PKG_CONFIG),true)
OPENSSL_ALPN_CHECK_CMD = pkg-config --atleast-version=1.0.2 openssl
OPENSSL_NPN_CHECK_CMD = pkg-config --alteast-version=1.0.1 openssl
ZLIB_CHECK_CMD = pkg-config --exists zlib
PERFTOOLS_CHECK_CMD = pkg-config --exists profiler
PROTOBUF_CHECK_CMD = pkg-config --atleast-version=3.0.0-alpha-3 protobuf
OPENSSL_ALPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.2 openssl
OPENSSL_NPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.1 openssl
ZLIB_CHECK_CMD = $(PKG_CONFIG) --exists zlib
PROTOBUF_CHECK_CMD = $(PKG_CONFIG) --atleast-version=3.0.0-alpha-3 protobuf
else # HAS_PKG_CONFIG
ifeq ($(SYSTEM),MINGW32)
@ -412,7 +413,6 @@ endif
OPENSSL_ALPN_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/openssl-alpn.c $(addprefix -l, $(OPENSSL_LIBS)) $(LDFLAGS)
OPENSSL_NPN_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/openssl-npn.c $(addprefix -l, $(OPENSSL_LIBS)) $(LDFLAGS)
ZLIB_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/zlib.c -lz $(LDFLAGS)
PERFTOOLS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/perftools.c -lprofiler $(LDFLAGS)
PROTOBUF_CHECK_CMD = $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/protobuf.cc -lprotobuf $(LDFLAGS)
ifeq ($(OPENSSL_REQUIRES_DL),true)
@ -422,6 +422,8 @@ endif
endif # HAS_PKG_CONFIG
PERFTOOLS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/perftools.c -lprofiler $(LDFLAGS)
PROTOC_CHECK_CMD = which protoc > /dev/null
PROTOC_CHECK_VERSION_CMD = protoc --version | grep -q libprotoc.3
DTRACE_CHECK_CMD = which dtrace > /dev/null
@ -509,8 +511,8 @@ DEP_MISSING += zlib
endif
else
ifeq ($(HAS_PKG_CONFIG),true)
CPPFLAGS += $(shell pkg-config --cflags zlib)
LDFLAGS += $(shell pkg-config --libs-only-L zlib)
CPPFLAGS += $(shell $(PKG_CONFIG) --cflags zlib)
LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L zlib)
PC_REQUIRES_GRPC += zlib
else
PC_LIBS_GRPC += -lz
@ -526,11 +528,11 @@ ifeq ($(HAS_SYSTEM_OPENSSL_ALPN),true)
ifeq ($(HAS_PKG_CONFIG),true)
OPENSSL_PKG_CONFIG = true
PC_REQUIRES_SECURE = openssl
CPPFLAGS := $(shell pkg-config --cflags openssl) $(CPPFLAGS)
LDFLAGS_OPENSSL_PKG_CONFIG = $(shell pkg-config --libs-only-L openssl)
CPPFLAGS := $(shell $(PKG_CONFIG) --cflags openssl) $(CPPFLAGS)
LDFLAGS_OPENSSL_PKG_CONFIG = $(shell $(PKG_CONFIG) --libs-only-L openssl)
ifeq ($(SYSTEM),Linux)
ifneq ($(LDFLAGS_OPENSSL_PKG_CONFIG),)
LDFLAGS_OPENSSL_PKG_CONFIG += $(shell pkg-config --libs-only-L openssl | sed s/L/Wl,-rpath,/)
LDFLAGS_OPENSSL_PKG_CONFIG += $(shell $(PKG_CONFIG) --libs-only-L openssl | sed s/L/Wl,-rpath,/)
endif
endif
LDFLAGS := $(LDFLAGS_OPENSSL_PKG_CONFIG) $(LDFLAGS)
@ -567,7 +569,7 @@ endif
endif
ifeq ($(OPENSSL_PKG_CONFIG),true)
LDLIBS_SECURE += $(shell pkg-config --libs-only-l openssl)
LDLIBS_SECURE += $(shell $(PKG_CONFIG) --libs-only-l openssl)
else
LDLIBS_SECURE += $(addprefix -l, $(LIBS_SECURE))
endif
@ -578,6 +580,7 @@ PC_DESCRIPTION = high performance general RPC framework
PC_CFLAGS =
PC_REQUIRES_PRIVATE = $(PC_REQUIRES_GRPC) $(PC_REQUIRES_SECURE)
PC_LIBS_PRIVATE = $(PC_LIBS_GRPC) $(PC_LIBS_SECURE)
PC_LIB = -lgrpc
GRPC_PC_FILE := $(PC_TEMPLATE)
# gprc_unsecure .pc file
@ -586,6 +589,7 @@ PC_DESCRIPTION = high performance general RPC framework without SSL
PC_CFLAGS =
PC_REQUIRES_PRIVATE = $(PC_REQUIRES_GRPC)
PC_LIBS_PRIVATE = $(PC_LIBS_GRPC)
PC_LIB = -lgrpc
GRPC_UNSECURE_PC_FILE := $(PC_TEMPLATE)
PROTOBUF_PKG_CONFIG = false
@ -597,11 +601,11 @@ ifeq ($(HAS_SYSTEM_PROTOBUF),true)
ifeq ($(HAS_PKG_CONFIG),true)
PROTOBUF_PKG_CONFIG = true
PC_REQUIRES_GRPCXX = protobuf
CPPFLAGS := $(shell pkg-config --cflags protobuf) $(CPPFLAGS)
LDFLAGS_PROTOBUF_PKG_CONFIG = $(shell pkg-config --libs-only-L protobuf)
CPPFLAGS := $(shell $(PKG_CONFIG) --cflags protobuf) $(CPPFLAGS)
LDFLAGS_PROTOBUF_PKG_CONFIG = $(shell $(PKG_CONFIG) --libs-only-L protobuf)
ifeq ($(SYSTEM),Linux)
ifneq ($(LDFLAGS_PROTOBUF_PKG_CONFIG),)
LDFLAGS_PROTOBUF_PKG_CONFIG += $(shell pkg-config --libs-only-L protobuf | sed s/L/Wl,-rpath,/)
LDFLAGS_PROTOBUF_PKG_CONFIG += $(shell $(PKG_CONFIG) --libs-only-L protobuf | sed s/L/Wl,-rpath,/)
endif
endif
else
@ -624,7 +628,7 @@ LIBS_PROTOC = protoc protobuf
HOST_LDLIBS_PROTOC += $(addprefix -l, $(LIBS_PROTOC))
ifeq ($(PROTOBUF_PKG_CONFIG),true)
LDLIBS_PROTOBUF += $(shell pkg-config --libs-only-l protobuf)
LDLIBS_PROTOBUF += $(shell $(PKG_CONFIG) --libs-only-l protobuf)
else
LDLIBS_PROTOBUF += $(addprefix -l, $(LIBS_PROTOBUF))
endif
@ -635,6 +639,7 @@ PC_DESCRIPTION = C++ wrapper for gRPC
PC_CFLAGS =
PC_REQUIRES_PRIVATE = grpc $(PC_REQUIRES_GRPCXX)
PC_LIBS_PRIVATE = $(PC_LIBS_GRPCXX)
PC_LIB = -lgrpc++
GRPCXX_PC_FILE := $(PC_TEMPLATE)
# grpc++_unsecure .pc file
@ -643,6 +648,7 @@ PC_DESCRIPTION = C++ wrapper for gRPC without SSL
PC_CFLAGS =
PC_REQUIRES_PRIVATE = grpc_unsecure $(PC_REQUIRES_GRPCXX)
PC_LIBS_PRIVATE = $(PC_LIBS_GRPCXX)
PC_LIB = -lgrpc++
GRPCXX_UNSECURE_PC_FILE := $(PC_TEMPLATE)
ifeq ($(MAKECMDGOALS),clean)

@ -331,6 +331,27 @@ static void test_iam_creds(void) {
check_iam_metadata, creds);
}
static void check_access_token_metadata(void *user_data,
grpc_credentials_md *md_elems,
size_t num_md,
grpc_credentials_status status) {
grpc_credentials *c = (grpc_credentials *)user_data;
expected_md emd[] = {{GRPC_AUTHORIZATION_METADATA_KEY, "Bearer blah"}};
GPR_ASSERT(status == GRPC_CREDENTIALS_OK);
GPR_ASSERT(num_md == 1);
check_metadata(emd, md_elems, num_md);
grpc_credentials_unref(c);
}
static void test_access_token_creds(void) {
grpc_credentials *creds = grpc_access_token_credentials_create("blah");
GPR_ASSERT(grpc_credentials_has_request_metadata(creds));
GPR_ASSERT(grpc_credentials_has_request_metadata_only(creds));
GPR_ASSERT(strcmp(creds->type, GRPC_CREDENTIALS_TYPE_OAUTH2) == 0);
grpc_credentials_get_request_metadata(creds, NULL, test_service_url,
check_access_token_metadata, creds);
}
static void check_ssl_oauth2_composite_metadata(
void *user_data, grpc_credentials_md *md_elems, size_t num_md,
grpc_credentials_status status) {
@ -863,6 +884,7 @@ int main(int argc, char **argv) {
test_oauth2_token_fetcher_creds_parsing_missing_token_type();
test_oauth2_token_fetcher_creds_parsing_missing_token_lifetime();
test_iam_creds();
test_access_token_creds();
test_ssl_oauth2_composite_creds();
test_ssl_oauth2_iam_composite_creds();
test_compute_engine_creds_success();

@ -0,0 +1,565 @@
/*
*
* 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/core/security/jwt_verifier.h"
#include <string.h>
#include "src/core/httpcli/httpcli.h"
#include "src/core/security/base64.h"
#include "src/core/security/json_token.h"
#include "test/core/util/test_config.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice.h>
#include <grpc/support/string_util.h>
/* This JSON key was generated with the GCE console and revoked immediately.
The identifiers have been changed as well.
Maximum size for a string literal is 509 chars in C89, yay! */
static const char json_key_str_part1[] =
"{ \"private_key\": \"-----BEGIN PRIVATE KEY-----"
"\\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOEvJsnoHnyHkXcp\\n7mJE"
"qg"
"WGjiw71NfXByguekSKho65FxaGbsnSM9SMQAqVk7Q2rG+I0OpsT0LrWQtZ\\nyjSeg/"
"rWBQvS4hle4LfijkP3J5BG+"
"IXDMP8RfziNRQsenAXDNPkY4kJCvKux2xdD\\nOnVF6N7dL3nTYZg+"
"uQrNsMTz9UxVAgMBAAECgYEAzbLewe1xe9vy+2GoSsfib+28\\nDZgSE6Bu/"
"zuFoPrRc6qL9p2SsnV7txrunTyJkkOnPLND9ABAXybRTlcVKP/sGgza\\n/"
"8HpCqFYM9V8f34SBWfD4fRFT+n/"
"73cfRUtGXdXpseva2lh8RilIQfPhNZAncenU\\ngqXjDvpkypEusgXAykECQQD+";
static const char json_key_str_part2[] =
"53XxNVnxBHsYb+AYEfklR96yVi8HywjVHP34+OQZ\\nCslxoHQM8s+"
"dBnjfScLu22JqkPv04xyxmt0QAKm9+vTdAkEA4ib7YvEAn2jXzcCI\\nEkoy2L/"
"XydR1GCHoacdfdAwiL2npOdnbvi4ZmdYRPY1LSTO058tQHKVXV7NLeCa3\\nAARh2QJBAMKeDA"
"G"
"W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\\n8FZi5c8idxiwC36kbAL6Hz"
"A"
"ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\\n6z8RJm0+"
"6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/"
"5nZ68ECQQDvYuI3\\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZ"
"Y"
"Ap6LI9W\\nIqv4vr6y38N79TTC\\n-----END PRIVATE KEY-----\\n\", ";
static const char json_key_str_part3_for_google_email_issuer[] =
"\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
"\"client_email\": "
"\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount."
"com\", \"client_id\": "
"\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
"com\", \"type\": \"service_account\" }";
/* Trick our JWT library into issuing a JWT with iss=accounts.google.com. */
static const char json_key_str_part3_for_url_issuer[] =
"\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
"\"client_email\": \"accounts.google.com\", "
"\"client_id\": "
"\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
"com\", \"type\": \"service_account\" }";
static const char json_key_str_part3_for_custom_email_issuer[] =
"\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", "
"\"client_email\": "
"\"foo@bar.com\", \"client_id\": "
"\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent."
"com\", \"type\": \"service_account\" }";
static grpc_jwt_verifier_email_domain_key_url_mapping custom_mapping = {
"bar.com", "keys.bar.com/jwk"
};
static const char expected_user_data[] = "user data";
static const char good_jwk_set[] =
"{"
" \"keys\": ["
" {"
" \"kty\": \"RSA\","
" \"alg\": \"RS256\","
" \"use\": \"sig\","
" \"kid\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\","
" \"n\": "
"\"4S8myegefIeRdynuYkSqBYaOLDvU19cHKC56RIqGjrkXFoZuydIz1IxACpWTtDasb4jQ6mxP"
"QutZC1nKNJ6D-tYFC9LiGV7gt-KOQ_cnkEb4hcMw_xF_OI1FCx6cBcM0-"
"RjiQkK8q7HbF0M6dUXo3t0vedNhmD65Cs2wxPP1TFU=\","
" \"e\": \"AQAB\""
" }"
" ]"
"}";
static gpr_timespec expected_lifetime = {3600, 0};
static const char good_google_email_keys_part1[] =
"{\"e6b5137873db8d2ef81e06a47289e6434ec8a165\": \"-----BEGIN "
"CERTIFICATE-----"
"\\nMIICATCCAWoCCQDEywLhxvHjnDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB\\nVTET"
"MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0\\ncyBQdHkgTHR"
"kMB4XDTE1MDYyOTA4Mzk1MFoXDTI1MDYyNjA4Mzk1MFowRTELMAkG\\nA1UEBhMCQVUxEzARBg"
"NVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0\\nIFdpZGdpdHMgUHR5IEx0ZDCBn"
"zANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4S8m\\nyegefIeRdynuYkSqBYaOLDvU19cHKC56"
"RIqGjrkXFoZuydIz1IxACpWTtDasb4jQ\\n6mxPQutZC1nKNJ6D+tYFC9LiGV7gt+KOQ/";
static const char good_google_email_keys_part2[] =
"cnkEb4hcMw/xF/OI1FCx6cBcM0+"
"Rji\\nQkK8q7HbF0M6dUXo3t0vedNhmD65Cs2wxPP1TFUCAwEAATANBgkqhkiG9w0BAQsF\\nA"
"AOBgQBfu69FkPmBknbKNFgurPz78kbs3VNN+k/"
"PUgO5DHKskJmgK2TbtvX2VMpx\\nkftmHGzgzMzUlOtigCaGMgHWjfqjpP9uuDbahXrZBJzB8c"
"Oq7MrQF8r17qVvo3Ue\\nPjTKQMAsU8uxTEMmeuz9L6yExs0rfd6bPOrQkAoVfFfiYB3/"
"pA==\\n-----END CERTIFICATE-----\\n\"}";
static const char expected_audience[] = "https://foo.com";
static const char good_openid_config[] =
"{"
" \"issuer\": \"https://accounts.google.com\","
" \"authorization_endpoint\": "
"\"https://accounts.google.com/o/oauth2/v2/auth\","
" \"token_endpoint\": \"https://www.googleapis.com/oauth2/v4/token\","
" \"userinfo_endpoint\": \"https://www.googleapis.com/oauth2/v3/userinfo\","
" \"revocation_endpoint\": \"https://accounts.google.com/o/oauth2/revoke\","
" \"jwks_uri\": \"https://www.googleapis.com/oauth2/v3/certs\""
"}";
static const char expired_claims[] =
"{ \"aud\": \"https://foo.com\","
" \"iss\": \"blah.foo.com\","
" \"sub\": \"juju@blah.foo.com\","
" \"jti\": \"jwtuniqueid\","
" \"iat\": 100," /* Way back in the past... */
" \"exp\": 120,"
" \"nbf\": 60,"
" \"foo\": \"bar\"}";
static const char claims_without_time_constraint[] =
"{ \"aud\": \"https://foo.com\","
" \"iss\": \"blah.foo.com\","
" \"sub\": \"juju@blah.foo.com\","
" \"jti\": \"jwtuniqueid\","
" \"foo\": \"bar\"}";
static const char invalid_claims[] =
"{ \"aud\": \"https://foo.com\","
" \"iss\": 46," /* Issuer cannot be a number. */
" \"sub\": \"juju@blah.foo.com\","
" \"jti\": \"jwtuniqueid\","
" \"foo\": \"bar\"}";
typedef struct {
grpc_jwt_verifier_status expected_status;
const char *expected_issuer;
const char *expected_subject;
} verifier_test_config;
static void test_claims_success(void) {
grpc_jwt_claims *claims;
gpr_slice s = gpr_slice_from_copied_string(claims_without_time_constraint);
grpc_json *json = grpc_json_parse_string_with_len(
(char *)GPR_SLICE_START_PTR(s), GPR_SLICE_LENGTH(s));
GPR_ASSERT(json != NULL);
claims = grpc_jwt_claims_from_json(json, s);
GPR_ASSERT(claims != NULL);
GPR_ASSERT(grpc_jwt_claims_json(claims) == json);
GPR_ASSERT(strcmp(grpc_jwt_claims_audience(claims), "https://foo.com") == 0);
GPR_ASSERT(strcmp(grpc_jwt_claims_issuer(claims), "blah.foo.com") == 0);
GPR_ASSERT(strcmp(grpc_jwt_claims_subject(claims), "juju@blah.foo.com") == 0);
GPR_ASSERT(strcmp(grpc_jwt_claims_id(claims), "jwtuniqueid") == 0);
GPR_ASSERT(grpc_jwt_claims_check(claims, "https://foo.com") ==
GRPC_JWT_VERIFIER_OK);
grpc_jwt_claims_destroy(claims);
}
static void test_expired_claims_failure(void) {
grpc_jwt_claims *claims;
gpr_slice s = gpr_slice_from_copied_string(expired_claims);
grpc_json *json = grpc_json_parse_string_with_len(
(char *)GPR_SLICE_START_PTR(s), GPR_SLICE_LENGTH(s));
gpr_timespec exp_iat = {100, 0};
gpr_timespec exp_exp = {120, 0};
gpr_timespec exp_nbf = {60, 0};
GPR_ASSERT(json != NULL);
claims = grpc_jwt_claims_from_json(json, s);
GPR_ASSERT(claims != NULL);
GPR_ASSERT(grpc_jwt_claims_json(claims) == json);
GPR_ASSERT(strcmp(grpc_jwt_claims_audience(claims), "https://foo.com") == 0);
GPR_ASSERT(strcmp(grpc_jwt_claims_issuer(claims), "blah.foo.com") == 0);
GPR_ASSERT(strcmp(grpc_jwt_claims_subject(claims), "juju@blah.foo.com") == 0);
GPR_ASSERT(strcmp(grpc_jwt_claims_id(claims), "jwtuniqueid") == 0);
GPR_ASSERT(gpr_time_cmp(grpc_jwt_claims_issued_at(claims), exp_iat) == 0);
GPR_ASSERT(gpr_time_cmp(grpc_jwt_claims_expires_at(claims), exp_exp) == 0);
GPR_ASSERT(gpr_time_cmp(grpc_jwt_claims_not_before(claims), exp_nbf) == 0);
GPR_ASSERT(grpc_jwt_claims_check(claims, "https://foo.com") ==
GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE);
grpc_jwt_claims_destroy(claims);
}
static void test_invalid_claims_failure(void) {
gpr_slice s = gpr_slice_from_copied_string(invalid_claims);
grpc_json *json = grpc_json_parse_string_with_len(
(char *)GPR_SLICE_START_PTR(s), GPR_SLICE_LENGTH(s));
GPR_ASSERT(grpc_jwt_claims_from_json(json, s) == NULL);
}
static void test_bad_audience_claims_failure(void) {
grpc_jwt_claims *claims;
gpr_slice s = gpr_slice_from_copied_string(claims_without_time_constraint);
grpc_json *json = grpc_json_parse_string_with_len(
(char *)GPR_SLICE_START_PTR(s), GPR_SLICE_LENGTH(s));
GPR_ASSERT(json != NULL);
claims = grpc_jwt_claims_from_json(json, s);
GPR_ASSERT(claims != NULL);
GPR_ASSERT(grpc_jwt_claims_check(claims, "https://bar.com") ==
GRPC_JWT_VERIFIER_BAD_AUDIENCE);
grpc_jwt_claims_destroy(claims);
}
static char *json_key_str(const char *last_part) {
size_t result_len = strlen(json_key_str_part1) + strlen(json_key_str_part2) +
strlen(last_part);
char *result = gpr_malloc(result_len + 1);
char *current = result;
strcpy(result, json_key_str_part1);
current += strlen(json_key_str_part1);
strcpy(current, json_key_str_part2);
current += strlen(json_key_str_part2);
strcpy(current, last_part);
return result;
}
static char *good_google_email_keys(void) {
size_t result_len = strlen(good_google_email_keys_part1) +
strlen(good_google_email_keys_part2);
char *result = gpr_malloc(result_len + 1);
char *current = result;
strcpy(result, good_google_email_keys_part1);
current += strlen(good_google_email_keys_part1);
strcpy(current, good_google_email_keys_part2);
return result;
}
static grpc_httpcli_response http_response(int status, char *body) {
grpc_httpcli_response response;
memset(&response, 0, sizeof(grpc_httpcli_response));
response.status = status;
response.body = body;
response.body_length = strlen(body);
return response;
}
static int httpcli_post_should_not_be_called(
const grpc_httpcli_request *request, const char *body_bytes,
size_t body_size, gpr_timespec deadline,
grpc_httpcli_response_cb on_response, void *user_data) {
GPR_ASSERT("HTTP POST should not be called" == NULL);
return 1;
}
static int httpcli_get_google_keys_for_email(
const grpc_httpcli_request *request, gpr_timespec deadline,
grpc_httpcli_response_cb on_response, void *user_data) {
grpc_httpcli_response response = http_response(200, good_google_email_keys());
GPR_ASSERT(request->use_ssl);
GPR_ASSERT(strcmp(request->host, "www.googleapis.com") == 0);
GPR_ASSERT(strcmp(request->path,
"/robot/v1/metadata/x509/"
"777-abaslkan11hlb6nmim3bpspl31ud@developer."
"gserviceaccount.com") == 0);
on_response(user_data, &response);
gpr_free(response.body);
return 1;
}
static void on_verification_success(void *user_data,
grpc_jwt_verifier_status status,
grpc_jwt_claims *claims) {
GPR_ASSERT(status == GRPC_JWT_VERIFIER_OK);
GPR_ASSERT(claims != NULL);
GPR_ASSERT(user_data == (void *)expected_user_data);
GPR_ASSERT(strcmp(grpc_jwt_claims_audience(claims), expected_audience) == 0);
grpc_jwt_claims_destroy(claims);
}
static void test_jwt_verifier_google_email_issuer_success(void) {
grpc_jwt_verifier *verifier = grpc_jwt_verifier_create(NULL, 0);
char *jwt = NULL;
char *key_str = json_key_str(json_key_str_part3_for_google_email_issuer);
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_google_keys_for_email,
httpcli_post_should_not_be_called);
jwt =
grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, NULL);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != NULL);
grpc_jwt_verifier_verify(verifier, NULL, jwt, expected_audience,
on_verification_success, (void *)expected_user_data);
gpr_free(jwt);
grpc_jwt_verifier_destroy(verifier);
grpc_httpcli_set_override(NULL, NULL);
}
static int httpcli_get_custom_keys_for_email(
const grpc_httpcli_request *request, gpr_timespec deadline,
grpc_httpcli_response_cb on_response, void *user_data) {
grpc_httpcli_response response = http_response(200, gpr_strdup(good_jwk_set));
GPR_ASSERT(request->use_ssl);
GPR_ASSERT(strcmp(request->host, "keys.bar.com") == 0);
GPR_ASSERT(strcmp(request->path, "/jwk/foo@bar.com") == 0);
on_response(user_data, &response);
gpr_free(response.body);
return 1;
}
static void test_jwt_verifier_custom_email_issuer_success(void) {
grpc_jwt_verifier *verifier = grpc_jwt_verifier_create(&custom_mapping, 1);
char *jwt = NULL;
char *key_str = json_key_str(json_key_str_part3_for_custom_email_issuer);
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_custom_keys_for_email,
httpcli_post_should_not_be_called);
jwt =
grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, NULL);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != NULL);
grpc_jwt_verifier_verify(verifier, NULL, jwt, expected_audience,
on_verification_success, (void *)expected_user_data);
gpr_free(jwt);
grpc_jwt_verifier_destroy(verifier);
grpc_httpcli_set_override(NULL, NULL);
}
static int httpcli_get_jwk_set(
const grpc_httpcli_request *request, gpr_timespec deadline,
grpc_httpcli_response_cb on_response, void *user_data) {
grpc_httpcli_response response = http_response(200, gpr_strdup(good_jwk_set));
GPR_ASSERT(request->use_ssl);
GPR_ASSERT(strcmp(request->host, "www.googleapis.com") == 0);
GPR_ASSERT(strcmp(request->path, "/oauth2/v3/certs") == 0);
on_response(user_data, &response);
gpr_free(response.body);
return 1;
}
static int httpcli_get_openid_config(const grpc_httpcli_request *request,
gpr_timespec deadline,
grpc_httpcli_response_cb on_response,
void *user_data) {
grpc_httpcli_response response =
http_response(200, gpr_strdup(good_openid_config));
GPR_ASSERT(request->use_ssl);
GPR_ASSERT(strcmp(request->host, "accounts.google.com") == 0);
GPR_ASSERT(strcmp(request->path, GRPC_OPENID_CONFIG_URL_SUFFIX) == 0);
grpc_httpcli_set_override(httpcli_get_jwk_set,
httpcli_post_should_not_be_called);
on_response(user_data, &response);
gpr_free(response.body);
return 1;
}
static void test_jwt_verifier_url_issuer_success(void) {
grpc_jwt_verifier *verifier = grpc_jwt_verifier_create(NULL, 0);
char *jwt = NULL;
char *key_str = json_key_str(json_key_str_part3_for_url_issuer);
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_openid_config,
httpcli_post_should_not_be_called);
jwt =
grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, NULL);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != NULL);
grpc_jwt_verifier_verify(verifier, NULL, jwt, expected_audience,
on_verification_success, (void *)expected_user_data);
gpr_free(jwt);
grpc_jwt_verifier_destroy(verifier);
grpc_httpcli_set_override(NULL, NULL);
}
static void on_verification_key_retrieval_error(void *user_data,
grpc_jwt_verifier_status status,
grpc_jwt_claims *claims) {
GPR_ASSERT(status == GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR);
GPR_ASSERT(claims == NULL);
GPR_ASSERT(user_data == (void *)expected_user_data);
}
static int httpcli_get_bad_json(const grpc_httpcli_request *request,
gpr_timespec deadline,
grpc_httpcli_response_cb on_response,
void *user_data) {
grpc_httpcli_response response =
http_response(200, gpr_strdup("{\"bad\": \"stuff\"}"));
GPR_ASSERT(request->use_ssl);
on_response(user_data, &response);
gpr_free(response.body);
return 1;
}
static void test_jwt_verifier_url_issuer_bad_config(void) {
grpc_jwt_verifier *verifier = grpc_jwt_verifier_create(NULL, 0);
char *jwt = NULL;
char *key_str = json_key_str(json_key_str_part3_for_url_issuer);
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_bad_json,
httpcli_post_should_not_be_called);
jwt =
grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, NULL);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != NULL);
grpc_jwt_verifier_verify(verifier, NULL, jwt, expected_audience,
on_verification_key_retrieval_error,
(void *)expected_user_data);
gpr_free(jwt);
grpc_jwt_verifier_destroy(verifier);
grpc_httpcli_set_override(NULL, NULL);
}
static void test_jwt_verifier_bad_json_key(void) {
grpc_jwt_verifier *verifier = grpc_jwt_verifier_create(NULL, 0);
char *jwt = NULL;
char *key_str = json_key_str(json_key_str_part3_for_google_email_issuer);
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_bad_json,
httpcli_post_should_not_be_called);
jwt =
grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, NULL);
grpc_auth_json_key_destruct(&key);
GPR_ASSERT(jwt != NULL);
grpc_jwt_verifier_verify(verifier, NULL, jwt, expected_audience,
on_verification_key_retrieval_error,
(void *)expected_user_data);
gpr_free(jwt);
grpc_jwt_verifier_destroy(verifier);
grpc_httpcli_set_override(NULL, NULL);
}
static void corrupt_jwt_sig(char *jwt) {
gpr_slice sig;
char *bad_b64_sig;
gpr_uint8 *sig_bytes;
char *last_dot = strrchr(jwt, '.');
GPR_ASSERT(last_dot != NULL);
sig = grpc_base64_decode(last_dot + 1, 1);
GPR_ASSERT(!GPR_SLICE_IS_EMPTY(sig));
sig_bytes = GPR_SLICE_START_PTR(sig);
(*sig_bytes)++; /* Corrupt first byte. */
bad_b64_sig =
grpc_base64_encode(GPR_SLICE_START_PTR(sig), GPR_SLICE_LENGTH(sig), 1, 0);
memcpy(last_dot + 1, bad_b64_sig, strlen(bad_b64_sig));
gpr_free(bad_b64_sig);
gpr_slice_unref(sig);
}
static void on_verification_bad_signature(void *user_data,
grpc_jwt_verifier_status status,
grpc_jwt_claims *claims) {
GPR_ASSERT(status == GRPC_JWT_VERIFIER_BAD_SIGNATURE);
GPR_ASSERT(claims == NULL);
GPR_ASSERT(user_data == (void *)expected_user_data);
}
static void test_jwt_verifier_bad_signature(void) {
grpc_jwt_verifier *verifier = grpc_jwt_verifier_create(NULL, 0);
char *jwt = NULL;
char *key_str = json_key_str(json_key_str_part3_for_url_issuer);
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(key_str);
gpr_free(key_str);
GPR_ASSERT(grpc_auth_json_key_is_valid(&key));
grpc_httpcli_set_override(httpcli_get_openid_config,
httpcli_post_should_not_be_called);
jwt =
grpc_jwt_encode_and_sign(&key, expected_audience, expected_lifetime, NULL);
grpc_auth_json_key_destruct(&key);
corrupt_jwt_sig(jwt);
GPR_ASSERT(jwt != NULL);
grpc_jwt_verifier_verify(verifier, NULL, jwt, expected_audience,
on_verification_bad_signature,
(void *)expected_user_data);
gpr_free(jwt);
grpc_jwt_verifier_destroy(verifier);
grpc_httpcli_set_override(NULL, NULL);
}
static int httpcli_get_should_not_be_called(
const grpc_httpcli_request *request, gpr_timespec deadline,
grpc_httpcli_response_cb on_response, void *user_data) {
GPR_ASSERT(0);
return 1;
}
static void on_verification_bad_format(void *user_data,
grpc_jwt_verifier_status status,
grpc_jwt_claims *claims) {
GPR_ASSERT(status == GRPC_JWT_VERIFIER_BAD_FORMAT);
GPR_ASSERT(claims == NULL);
GPR_ASSERT(user_data == (void *)expected_user_data);
}
static void test_jwt_verifier_bad_format(void) {
grpc_jwt_verifier *verifier = grpc_jwt_verifier_create(NULL, 0);
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
httpcli_post_should_not_be_called);
grpc_jwt_verifier_verify(verifier, NULL, "bad jwt", expected_audience,
on_verification_bad_format,
(void *)expected_user_data);
grpc_jwt_verifier_destroy(verifier);
grpc_httpcli_set_override(NULL, NULL);
}
/* find verification key: bad jks, cannot find key in jks */
/* bad signature custom provided email*/
/* bad key */
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
test_claims_success();
test_expired_claims_failure();
test_invalid_claims_failure();
test_bad_audience_claims_failure();
test_jwt_verifier_google_email_issuer_success();
test_jwt_verifier_custom_email_issuer_success();
test_jwt_verifier_url_issuer_success();
test_jwt_verifier_url_issuer_bad_config();
test_jwt_verifier_bad_json_key();
test_jwt_verifier_bad_signature();
test_jwt_verifier_bad_format();
return 0;
}

@ -91,13 +91,13 @@ int main(int argc, char **argv) {
grpc_pollset_init(&sync.pollset);
sync.is_done = 0;
grpc_credentials_get_request_metadata(creds, &sync.pollset, "", on_metadata_response, &sync);
grpc_credentials_get_request_metadata(creds, &sync.pollset, service_url,
on_metadata_response, &sync);
gpr_mu_lock(GRPC_POLLSET_MU(&sync.pollset));
while (!sync.is_done) grpc_pollset_work(&sync.pollset, gpr_inf_future);
gpr_mu_unlock(GRPC_POLLSET_MU(&sync.pollset));
grpc_pollset_destroy(&sync.pollset);
grpc_credentials_release(creds);
end:

@ -0,0 +1,119 @@
/*
*
* 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 <stdio.h>
#include <string.h>
#include "src/core/security/jwt_verifier.h"
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
#include <grpc/support/cmdline.h>
#include <grpc/support/log.h>
#include <grpc/support/slice.h>
#include <grpc/support/sync.h>
typedef struct {
grpc_pollset pollset;
int is_done;
int success;
} synchronizer;
static void print_usage_and_exit(gpr_cmdline *cl, const char *argv0) {
char *usage = gpr_cmdline_usage_string(cl, argv0);
fprintf(stderr, "%s", usage);
gpr_free(usage);
gpr_cmdline_destroy(cl);
exit(1);
}
static void on_jwt_verification_done(void *user_data,
grpc_jwt_verifier_status status,
grpc_jwt_claims *claims) {
synchronizer *sync = user_data;
sync->success = (status == GRPC_JWT_VERIFIER_OK);
if (sync->success) {
char *claims_str;
GPR_ASSERT(claims != NULL);
claims_str =
grpc_json_dump_to_string((grpc_json *)grpc_jwt_claims_json(claims), 2);
printf("Claims: \n\n%s\n", claims_str);
gpr_free(claims_str);
grpc_jwt_claims_destroy(claims);
} else {
GPR_ASSERT(claims == NULL);
fprintf(stderr, "Verification failed with error %s\n",
grpc_jwt_verifier_status_to_string(status));
}
gpr_mu_lock(GRPC_POLLSET_MU(&sync->pollset));
sync->is_done = 1;
grpc_pollset_kick(&sync->pollset);
gpr_mu_unlock(GRPC_POLLSET_MU(&sync->pollset));
}
int main(int argc, char **argv) {
synchronizer sync;
grpc_jwt_verifier *verifier;
gpr_cmdline *cl;
char *jwt = NULL;
char *aud = NULL;
cl = gpr_cmdline_create("JWT verifier tool");
gpr_cmdline_add_string(cl, "jwt", "JSON web token to verify", &jwt);
gpr_cmdline_add_string(cl, "aud", "Audience for the JWT", &aud);
gpr_cmdline_parse(cl, argc, argv);
if (jwt == NULL || aud == NULL) {
print_usage_and_exit(cl, argv[0]);
}
verifier = grpc_jwt_verifier_create(NULL, 0);
grpc_init();
grpc_pollset_init(&sync.pollset);
sync.is_done = 0;
grpc_jwt_verifier_verify(verifier, &sync.pollset, jwt, aud,
on_jwt_verification_done, &sync);
gpr_mu_lock(GRPC_POLLSET_MU(&sync.pollset));
while (!sync.is_done) grpc_pollset_work(&sync.pollset, gpr_inf_future);
gpr_mu_unlock(GRPC_POLLSET_MU(&sync.pollset));
grpc_jwt_verifier_destroy(verifier);
gpr_cmdline_destroy(cl);
return !sync.success;
}

@ -53,7 +53,7 @@ static void onhdr(void *ud, grpc_mdelem *md) {
GPR_ASSERT(evalue);
GPR_ASSERT(gpr_slice_str_cmp(md->key->slice, ekey) == 0);
GPR_ASSERT(gpr_slice_str_cmp(md->value->slice, evalue) == 0);
grpc_mdelem_unref(md);
GRPC_MDELEM_UNREF(md);
}
static void test_vector(grpc_chttp2_hpack_parser *parser,

@ -167,7 +167,7 @@ static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
const char *value) {
grpc_mdelem *md = grpc_mdelem_from_strings(tbl->mdctx, key, value);
grpc_chttp2_hptbl_find_result r = grpc_chttp2_hptbl_find(tbl, md);
grpc_mdelem_unref(md);
GRPC_MDELEM_UNREF(md);
return r;
}

@ -262,7 +262,7 @@ static void chk_hdr(void *p, grpc_mdelem *el) {
GPR_ASSERT(0 == gpr_slice_str_cmp(el->key->slice, st->key));
GPR_ASSERT(0 == gpr_slice_str_cmp(el->value->slice, st->value));
st->got_hdr = 1;
grpc_mdelem_unref(el);
GRPC_MDELEM_UNREF(el);
}
static void test_decode_random_headers_inner(int max_len) {

@ -70,10 +70,10 @@ static void test_create_string(void) {
GPR_ASSERT(s3 != s1);
GPR_ASSERT(gpr_slice_str_cmp(s1->slice, "hello") == 0);
GPR_ASSERT(gpr_slice_str_cmp(s3->slice, "very much not hello") == 0);
grpc_mdstr_unref(s1);
grpc_mdstr_unref(s2);
GRPC_MDSTR_UNREF(s1);
GRPC_MDSTR_UNREF(s2);
grpc_mdctx_unref(ctx);
grpc_mdstr_unref(s3);
GRPC_MDSTR_UNREF(s3);
}
static void test_create_metadata(void) {
@ -93,9 +93,9 @@ static void test_create_metadata(void) {
GPR_ASSERT(gpr_slice_str_cmp(m1->key->slice, "a") == 0);
GPR_ASSERT(gpr_slice_str_cmp(m1->value->slice, "b") == 0);
GPR_ASSERT(gpr_slice_str_cmp(m3->value->slice, "c") == 0);
grpc_mdelem_unref(m1);
grpc_mdelem_unref(m2);
grpc_mdelem_unref(m3);
GRPC_MDELEM_UNREF(m1);
GRPC_MDELEM_UNREF(m2);
GRPC_MDELEM_UNREF(m3);
grpc_mdctx_unref(ctx);
}
@ -112,7 +112,7 @@ static void test_create_many_ephemeral_metadata(void) {
/* add, and immediately delete a bunch of different elements */
for (i = 0; i < MANY; i++) {
gpr_ltoa(i, buffer);
grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", buffer));
GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", buffer));
}
/* capacity should not grow */
GPR_ASSERT(mdtab_capacity_before ==
@ -140,11 +140,11 @@ static void test_create_many_persistant_metadata(void) {
gpr_ltoa(i, buffer);
md = grpc_mdelem_from_strings(ctx, "a", buffer);
GPR_ASSERT(md == created[i]);
grpc_mdelem_unref(md);
GRPC_MDELEM_UNREF(md);
}
/* cleanup phase */
for (i = 0; i < MANY; i++) {
grpc_mdelem_unref(created[i]);
GRPC_MDELEM_UNREF(created[i]);
}
grpc_mdctx_unref(ctx);
@ -160,15 +160,15 @@ static void test_spin_creating_the_same_thing(void) {
GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 0);
GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 0);
grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", "b"));
GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", "b"));
GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", "b"));
GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
@ -196,8 +196,8 @@ static void test_things_stick_around(void) {
}
for (i = 0; i < nstrs; i++) {
grpc_mdstr_ref(strs[i]);
grpc_mdstr_unref(strs[i]);
GRPC_MDSTR_REF(strs[i]);
GRPC_MDSTR_UNREF(strs[i]);
}
for (i = 0; i < nstrs; i++) {
@ -209,12 +209,12 @@ static void test_things_stick_around(void) {
}
for (i = 0; i < nstrs; i++) {
grpc_mdstr_unref(strs[shuf[i]]);
GRPC_MDSTR_UNREF(strs[shuf[i]]);
for (j = i + 1; j < nstrs; j++) {
gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", shuf[j]);
test = grpc_mdstr_from_string(ctx, buffer);
GPR_ASSERT(test == strs[shuf[j]]);
grpc_mdstr_unref(test);
GRPC_MDSTR_UNREF(test);
gpr_free(buffer);
}
}
@ -237,14 +237,14 @@ static void test_slices_work(void) {
str = grpc_mdstr_from_string(
ctx, "123456789012345678901234567890123456789012345678901234567890");
slice = gpr_slice_ref(str->slice);
grpc_mdstr_unref(str);
GRPC_MDSTR_UNREF(str);
gpr_slice_unref(slice);
str = grpc_mdstr_from_string(
ctx, "123456789012345678901234567890123456789012345678901234567890");
slice = gpr_slice_ref(str->slice);
gpr_slice_unref(slice);
grpc_mdstr_unref(str);
GRPC_MDSTR_UNREF(str);
grpc_mdctx_unref(ctx);
}
@ -264,7 +264,7 @@ static void test_base64_and_huffman_works(void) {
GPR_ASSERT(0 == gpr_slice_cmp(slice1, slice2));
gpr_slice_unref(slice2);
grpc_mdstr_unref(str);
GRPC_MDSTR_UNREF(str);
grpc_mdctx_unref(ctx);
}

@ -87,12 +87,16 @@ void MaybeEchoDeadline(ServerContext* context, const EchoRequest* request,
class TestServiceImpl : public ::grpc::cpp::test::util::TestService::Service {
public:
TestServiceImpl() : signal_client_(false) {}
TestServiceImpl() : signal_client_(false), host_(nullptr) {}
explicit TestServiceImpl(const grpc::string& host) : signal_client_(false), host_(new grpc::string(host)) {}
Status Echo(ServerContext* context, const EchoRequest* request,
EchoResponse* response) GRPC_OVERRIDE {
response->set_message(request->message());
MaybeEchoDeadline(context, request, response);
if (host_) {
response->mutable_param()->set_host(*host_);
}
if (request->has_param() && request->param().client_cancel_after_us()) {
{
std::unique_lock<std::mutex> lock(mu_);
@ -191,6 +195,7 @@ class TestServiceImpl : public ::grpc::cpp::test::util::TestService::Service {
private:
bool signal_client_;
std::mutex mu_;
std::unique_ptr<grpc::string> host_;
};
class TestServiceImplDupPkg
@ -205,7 +210,7 @@ class TestServiceImplDupPkg
class End2endTest : public ::testing::Test {
protected:
End2endTest() : kMaxMessageSize_(8192), thread_pool_(2) {}
End2endTest() : kMaxMessageSize_(8192), special_service_("special"), thread_pool_(2) {}
void SetUp() GRPC_OVERRIDE {
int port = grpc_pick_unused_port_or_die();
@ -215,6 +220,7 @@ class End2endTest : public ::testing::Test {
builder.AddListeningPort(server_address_.str(),
FakeTransportSecurityServerCredentials());
builder.RegisterService(&service_);
builder.RegisterService("special", &special_service_);
builder.SetMaxMessageSize(
kMaxMessageSize_); // For testing max message size.
builder.RegisterService(&dup_pkg_service_);
@ -236,6 +242,7 @@ class End2endTest : public ::testing::Test {
std::ostringstream server_address_;
const int kMaxMessageSize_;
TestServiceImpl service_;
TestServiceImpl special_service_;
TestServiceImplDupPkg dup_pkg_service_;
ThreadPool thread_pool_;
};
@ -255,6 +262,22 @@ static void SendRpc(grpc::cpp::test::util::TestService::Stub* stub,
}
}
TEST_F(End2endTest, SimpleRpcWithHost) {
ResetStub();
EchoRequest request;
EchoResponse response;
request.set_message("Hello");
ClientContext context;
context.set_authority("special");
Status s = stub_->Echo(&context, request, &response);
EXPECT_EQ(response.message(), request.message());
EXPECT_TRUE(response.has_param());
EXPECT_EQ(response.param().host(), "special");
EXPECT_TRUE(s.ok());
}
TEST_F(End2endTest, SimpleRpc) {
ResetStub();
SendRpc(stub_.get(), 1);

@ -46,6 +46,7 @@ message EchoRequest {
message ResponseParams {
optional int64 request_deadline = 1;
optional string host = 2;
}
message EchoResponse {

@ -43,27 +43,20 @@ ENV PATH $PATH:$ANDROID_HOME/platform-tools
# Some old Docker versions consider '/' as HOME
ENV HOME /root
# Update sdk for android 5.1 (API level 22)
RUN echo y | android update sdk --all --filter platform-tools,build-tools-22.0.1,sys-img-armeabi-v7a-addon-google_apis-google-22,sys-img-armeabi-v7a-addon-google_apis-google-21,sys-img-armeabi-v7a-android-19,addon-google_apis-google-22,addon-google_apis-google-21,addon-google_apis-google-19,extra-android-m2repository,extra-google-m2repository --no-ui --force
# Update sdk for android API level 19 (4.4), 21 (5.0), 22 (5.1).
RUN echo y | android update sdk --all --filter platform-tools,build-tools-22.0.1,sys-img-armeabi-v7a-addon-google_apis-google-22,sys-img-armeabi-v7a-addon-google_apis-google-21,sys-img-armeabi-v7a-android-19,android-22,android-21,android-19,addon-google_apis-google-22,addon-google_apis-google-21,addon-google_apis-google-19,extra-android-m2repository,extra-google-m2repository --no-ui --force
# Create an AVD with API level 22
RUN echo no | android create avd --force -n avd-google-api-22 -t "Google Inc.:Google APIs:22" --abi google_apis/armeabi-v7a
RUN echo no | android create avd --force -n avd-google-api-21 -t "Google Inc.:Google APIs:21" --abi google_apis/armeabi-v7a
RUN echo no | android create avd --force -n avd-google-api-19 -t "Google Inc.:Google APIs:19" --abi default/armeabi-v7a
# Create AVDs with API level 19,21,22
RUN echo no | android create avd --force -n avd-google-api-22 -t "Google Inc.:Google APIs:22" --abi google_apis/armeabi-v7a && \
echo no | android create avd --force -n avd-google-api-21 -t "Google Inc.:Google APIs:21" --abi google_apis/armeabi-v7a && \
echo no | android create avd --force -n avd-google-api-19 -t "Google Inc.:Google APIs:19" --abi default/armeabi-v7a
# Pull gRPC Java and trigger download of needed Maven and Gradle artifacts.
RUN git clone --depth 1 https://github.com/grpc/grpc-java.git /var/local/git/grpc-java && \
cd /var/local/git/grpc-java && \
./gradlew grpc-core:install grpc-stub:install grpc-okhttp:install grpc-protobuf-nano:install && \
rm -r "$(pwd)"
./gradlew grpc-core:install grpc-stub:install grpc-okhttp:install grpc-protobuf-nano:install grpc-compiler:install
# Pull gRPC Android integration test App
RUN git clone --depth 1 https://github.com/madongfly/grpc-android-test.git /var/local/git/grpc-android-test
# Config android sdk for gradle
RUN cd /var/local/git/grpc-android-test && echo "sdk.dir=/usr/local/android-sdk-linux" > local.properties
# Build apks to trigger download of needed Maven and Gradle artifacts.
RUN cd /var/local/git/grpc-android-test && ./gradlew assembleDebug
RUN cd /var/local/git/grpc-android-test && ./gradlew assembleDebugAndroidTest
# Config android sdk for gradle and build apk to trigger download of needed Maven and Gradle artifacts.
RUN cd /var/local/git/grpc-java/android-interop-testing && echo "sdk.dir=/usr/local/android-sdk-linux" > local.properties && \
../gradlew assembleDebug

@ -15,23 +15,23 @@ Usage
Start the emulator in a detached container, the argument is the name of the AVD you want to start:
```
$ sudo docker run --name=grpc_android_test -d grpc/android /var/local/git/grpc-android-test/start-emulator.sh avd-api-22
$ sudo docker run --name=grpc_android_test -d grpc/android /var/local/git/grpc-java/android-interop-testing/start-emulator.sh avd-google-api-22
```
You can use the following cammand to wait until the emulator is ready:
```
$ sudo docker exec grpc_android_test /var/local/git/grpc-android-test/wait-for-emulator.sh
$ sudo docker exec grpc_android_test /var/local/git/grpc-java/android-interop-testing/wait-for-emulator.sh
```
When you want to update the apk, run:
```
$ sudo docker exec grpc_android_test /var/local/git/grpc-android-test/update-apk.sh
$ sudo docker exec grpc_android_test bash -c "cd /var/local/git/grpc-java && git pull origin master && ./gradlew grpc-core:install grpc-stub:install grpc-okhttp:install grpc-protobuf-nano:install grpc-compiler:install && cd android-interop-testing && ../gradlew installDebug"
```
It will pull the fresh code of gRpc Java and our integration test app from github, build and install it to the runing emulator (so you need to make sure there is a runing emulator).
It pulls the fresh code of gRpc Java and our interop test app from github, build and install it to the runing emulator (so you need to make sure there is a runing emulator).
Trigger the integration test:
```
$ sudo docker exec grpc_android_test /var/local/git/grpc-android-test/run-test.sh -e server_host <hostname or ip address> -e server_port 8030 -e server_host_override foo.test.google.fr -e use_tls true -e use_test_ca true
$ sudo docker exec grpc_android_test adb -e shell am instrument -w -e server_host <hostname or ip address> -e server_port 8030 -e server_host_override foo.test.google.fr -e use_tls true -e use_test_ca true -e test_case all io.grpc.android.integrationtest/.TesterInstrumentation
```
You can also use the android/adb cammands to get more info, such as:

@ -775,6 +775,7 @@ src/core/security/auth_filters.h \
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 \
@ -892,6 +893,7 @@ src/core/security/credentials_posix.c \
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 \

@ -51,11 +51,8 @@ then
# Make sure docker image has been built. Should be instantaneous if so.
docker build -t $DOCKER_IMAGE_NAME tools/jenkins/grpc_jenkins_slave
if [ "$ghprbPullId" != "" ]
then
# if we are building a pull request, grab corresponding refs.
FETCH_PULL_REQUEST_CMD="&& git fetch $GIT_URL refs/pull/$ghprbPullId/merge refs/pull/$ghprbPullId/head"
fi
# Create a local branch so the child Docker script won't complain
git branch jenkins-docker
# Make sure the CID file is gone.
rm -f docker.cid
@ -87,6 +84,11 @@ then
/cygdrive/c/nuget/nuget.exe restore src/csharp/Grpc.sln
python tools/run_tests/run_tests.py -t -l $language -x report.xml
elif [ "$platform" == "macos" ]
then
echo "building $language on MacOS"
./tools/run_tests/run_tests.py -t -l $language -c $config -x report.xml
else
echo "Unknown platform $platform"
exit 1

@ -31,7 +31,7 @@
set -ex
export CONFIG=${CONFIG:-opt}
export GRPC_CONFIG=${CONFIG:-opt}
# change to grpc's ruby directory
cd $(dirname $0)/../../src/ruby

@ -529,6 +529,20 @@
"test/core/security/json_token_test.c"
]
},
{
"deps": [
"gpr",
"gpr_test_util",
"grpc",
"grpc_test_util"
],
"headers": [],
"language": "c",
"name": "grpc_jwt_verifier_test",
"src": [
"test/core/security/jwt_verifier_test.c"
]
},
{
"deps": [
"gpr",
@ -571,6 +585,20 @@
"test/core/transport/stream_op_test.c"
]
},
{
"deps": [
"gpr",
"gpr_test_util",
"grpc",
"grpc_test_util"
],
"headers": [],
"language": "c",
"name": "grpc_verify_jwt",
"src": [
"test/core/security/verify_jwt.c"
]
},
{
"deps": [
"gpr",
@ -9863,6 +9891,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",
@ -10066,6 +10095,8 @@
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/json_token.h",
"src/core/security/jwt_verifier.c",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.c",

@ -311,6 +311,15 @@
"posix"
]
},
{
"flaky": false,
"language": "c",
"name": "grpc_jwt_verifier_test",
"platforms": [
"windows",
"posix"
]
},
{
"flaky": false,
"language": "c",

File diff suppressed because one or more lines are too long

@ -164,6 +164,7 @@
<ClInclude Include="..\..\src\core\security\base64.h" />
<ClInclude Include="..\..\src\core\security\credentials.h" />
<ClInclude Include="..\..\src\core\security\json_token.h" />
<ClInclude Include="..\..\src\core\security\jwt_verifier.h" />
<ClInclude Include="..\..\src\core\security\secure_endpoint.h" />
<ClInclude Include="..\..\src\core\security\secure_transport_setup.h" />
<ClInclude Include="..\..\src\core\security\security_connector.h" />
@ -295,6 +296,8 @@
</ClCompile>
<ClCompile Include="..\..\src\core\security\json_token.c">
</ClCompile>
<ClCompile Include="..\..\src\core\security\jwt_verifier.c">
</ClCompile>
<ClCompile Include="..\..\src\core\security\secure_endpoint.c">
</ClCompile>
<ClCompile Include="..\..\src\core\security\secure_transport_setup.c">

@ -37,6 +37,9 @@
<ClCompile Include="..\..\src\core\security\json_token.c">
<Filter>src\core\security</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\security\jwt_verifier.c">
<Filter>src\core\security</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\security\secure_endpoint.c">
<Filter>src\core\security</Filter>
</ClCompile>
@ -449,6 +452,9 @@
<ClInclude Include="..\..\src\core\security\json_token.h">
<Filter>src\core\security</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\security\jwt_verifier.h">
<Filter>src\core\security</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\security\secure_endpoint.h">
<Filter>src\core\security</Filter>
</ClInclude>

Loading…
Cancel
Save