The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
9.0 KiB
255 lines
9.0 KiB
/* |
|
* |
|
* 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 <node.h> |
|
#include <nan.h> |
|
#include <uv.h> |
|
|
|
#include "grpc/grpc.h" |
|
#include "grpc/grpc_security.h" |
|
#include "grpc/support/log.h" |
|
#include "call_credentials.h" |
|
#include "call.h" |
|
|
|
namespace grpc { |
|
namespace node { |
|
|
|
using Nan::Callback; |
|
using Nan::EscapableHandleScope; |
|
using Nan::HandleScope; |
|
using Nan::Maybe; |
|
using Nan::MaybeLocal; |
|
using Nan::ObjectWrap; |
|
using Nan::Persistent; |
|
using Nan::Utf8String; |
|
|
|
using v8::Exception; |
|
using v8::External; |
|
using v8::Function; |
|
using v8::FunctionTemplate; |
|
using v8::Integer; |
|
using v8::Local; |
|
using v8::Object; |
|
using v8::ObjectTemplate; |
|
using v8::Value; |
|
|
|
Nan::Callback *CallCredentials::constructor; |
|
Persistent<FunctionTemplate> CallCredentials::fun_tpl; |
|
|
|
CallCredentials::CallCredentials(grpc_call_credentials *credentials) |
|
: wrapped_credentials(credentials) {} |
|
|
|
CallCredentials::~CallCredentials() { |
|
grpc_call_credentials_release(wrapped_credentials); |
|
} |
|
|
|
void CallCredentials::Init(Local<Object> exports) { |
|
HandleScope scope; |
|
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); |
|
tpl->SetClassName(Nan::New("CallCredentials").ToLocalChecked()); |
|
tpl->InstanceTemplate()->SetInternalFieldCount(1); |
|
Nan::SetPrototypeMethod(tpl, "compose", Compose); |
|
fun_tpl.Reset(tpl); |
|
Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); |
|
Nan::Set(ctr, Nan::New("createFromPlugin").ToLocalChecked(), |
|
Nan::GetFunction( |
|
Nan::New<FunctionTemplate>(CreateFromPlugin)).ToLocalChecked()); |
|
Nan::Set(exports, Nan::New("CallCredentials").ToLocalChecked(), ctr); |
|
constructor = new Nan::Callback(ctr); |
|
} |
|
|
|
bool CallCredentials::HasInstance(Local<Value> val) { |
|
HandleScope scope; |
|
return Nan::New(fun_tpl)->HasInstance(val); |
|
} |
|
|
|
Local<Value> CallCredentials::WrapStruct(grpc_call_credentials *credentials) { |
|
EscapableHandleScope scope; |
|
const int argc = 1; |
|
if (credentials == NULL) { |
|
return scope.Escape(Nan::Null()); |
|
} |
|
Local<Value> argv[argc] = { |
|
Nan::New<External>(reinterpret_cast<void *>(credentials))}; |
|
MaybeLocal<Object> maybe_instance = Nan::NewInstance( |
|
constructor->GetFunction(), argc, argv); |
|
if (maybe_instance.IsEmpty()) { |
|
return scope.Escape(Nan::Null()); |
|
} else { |
|
return scope.Escape(maybe_instance.ToLocalChecked()); |
|
} |
|
} |
|
|
|
grpc_call_credentials *CallCredentials::GetWrappedCredentials() { |
|
return wrapped_credentials; |
|
} |
|
|
|
NAN_METHOD(CallCredentials::New) { |
|
if (info.IsConstructCall()) { |
|
if (!info[0]->IsExternal()) { |
|
return Nan::ThrowTypeError( |
|
"CallCredentials can only be created with the provided functions"); |
|
} |
|
Local<External> ext = info[0].As<External>(); |
|
grpc_call_credentials *creds_value = |
|
reinterpret_cast<grpc_call_credentials *>(ext->Value()); |
|
CallCredentials *credentials = new CallCredentials(creds_value); |
|
credentials->Wrap(info.This()); |
|
info.GetReturnValue().Set(info.This()); |
|
return; |
|
} else { |
|
// This should never be called directly |
|
return Nan::ThrowTypeError( |
|
"CallCredentials can only be created with the provided functions"); |
|
} |
|
} |
|
|
|
NAN_METHOD(CallCredentials::Compose) { |
|
if (!CallCredentials::HasInstance(info.This())) { |
|
return Nan::ThrowTypeError( |
|
"compose can only be called on CallCredentials objects"); |
|
} |
|
if (!CallCredentials::HasInstance(info[0])) { |
|
return Nan::ThrowTypeError( |
|
"compose's first argument must be a CallCredentials object"); |
|
} |
|
CallCredentials *self = ObjectWrap::Unwrap<CallCredentials>(info.This()); |
|
CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>( |
|
Nan::To<Object>(info[0]).ToLocalChecked()); |
|
grpc_call_credentials *creds = grpc_composite_call_credentials_create( |
|
self->wrapped_credentials, other->wrapped_credentials, NULL); |
|
info.GetReturnValue().Set(WrapStruct(creds)); |
|
} |
|
|
|
|
|
|
|
NAN_METHOD(CallCredentials::CreateFromPlugin) { |
|
if (!info[0]->IsFunction()) { |
|
return Nan::ThrowTypeError( |
|
"createFromPlugin's argument must be a function"); |
|
} |
|
grpc_metadata_credentials_plugin plugin; |
|
plugin_state *state = new plugin_state; |
|
state->callback = new Nan::Callback(info[0].As<Function>()); |
|
plugin.get_metadata = plugin_get_metadata; |
|
plugin.destroy = plugin_destroy_state; |
|
plugin.state = reinterpret_cast<void*>(state); |
|
plugin.type = ""; |
|
grpc_call_credentials *creds = grpc_metadata_credentials_create_from_plugin( |
|
plugin, NULL); |
|
info.GetReturnValue().Set(WrapStruct(creds)); |
|
} |
|
|
|
NAN_METHOD(PluginCallback) { |
|
// Arguments: status code, error details, metadata |
|
if (!info[0]->IsUint32()) { |
|
return Nan::ThrowTypeError( |
|
"The callback's first argument must be a status code"); |
|
} |
|
if (!info[1]->IsString()) { |
|
return Nan::ThrowTypeError( |
|
"The callback's second argument must be a string"); |
|
} |
|
if (!info[2]->IsObject()) { |
|
return Nan::ThrowTypeError( |
|
"The callback's third argument must be an object"); |
|
} |
|
shared_ptr<Resources> resources(new Resources); |
|
grpc_status_code code = static_cast<grpc_status_code>( |
|
Nan::To<uint32_t>(info[0]).FromJust()); |
|
char *details = *Utf8String(info[1]); |
|
grpc_metadata_array array; |
|
if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(), |
|
&array, resources)){ |
|
return Nan::ThrowError("Failed to parse metadata"); |
|
} |
|
grpc_credentials_plugin_metadata_cb cb = |
|
reinterpret_cast<grpc_credentials_plugin_metadata_cb>( |
|
Nan::Get(info.Callee(), |
|
Nan::New("cb").ToLocalChecked() |
|
).ToLocalChecked().As<External>()->Value()); |
|
void *user_data = |
|
Nan::Get(info.Callee(), |
|
Nan::New("user_data").ToLocalChecked() |
|
).ToLocalChecked().As<External>()->Value(); |
|
cb(user_data, array.metadata, array.count, code, details); |
|
} |
|
|
|
NAUV_WORK_CB(SendPluginCallback) { |
|
Nan::HandleScope scope; |
|
plugin_callback_data *data = reinterpret_cast<plugin_callback_data*>( |
|
async->data); |
|
// Attach cb and user_data to plugin_callback so that it can access them later |
|
v8::Local<v8::Function> plugin_callback = Nan::GetFunction( |
|
Nan::New<v8::FunctionTemplate>(PluginCallback)).ToLocalChecked(); |
|
Nan::Set(plugin_callback, Nan::New("cb").ToLocalChecked(), |
|
Nan::New<v8::External>(reinterpret_cast<void*>(data->cb))); |
|
Nan::Set(plugin_callback, Nan::New("user_data").ToLocalChecked(), |
|
Nan::New<v8::External>(data->user_data)); |
|
const int argc = 2; |
|
v8::Local<v8::Value> argv[argc] = { |
|
Nan::New(data->service_url).ToLocalChecked(), |
|
plugin_callback |
|
}; |
|
Nan::Callback *callback = data->state->callback; |
|
callback->Call(argc, argv); |
|
delete data; |
|
uv_unref((uv_handle_t *)async); |
|
uv_close((uv_handle_t *)async, (uv_close_cb)free); |
|
} |
|
|
|
void plugin_get_metadata(void *state, grpc_auth_metadata_context context, |
|
grpc_credentials_plugin_metadata_cb cb, |
|
void *user_data) { |
|
uv_async_t *async = static_cast<uv_async_t*>(malloc(sizeof(uv_async_t))); |
|
uv_async_init(uv_default_loop(), |
|
async, |
|
SendPluginCallback); |
|
plugin_callback_data *data = new plugin_callback_data; |
|
data->state = reinterpret_cast<plugin_state*>(state); |
|
data->service_url = context.service_url; |
|
data->cb = cb; |
|
data->user_data = user_data; |
|
async->data = data; |
|
/* libuv says that it will coalesce calls to uv_async_send. If there is ever a |
|
* problem with a callback not getting called, that is probably the reason */ |
|
uv_async_send(async); |
|
} |
|
|
|
void plugin_destroy_state(void *ptr) { |
|
plugin_state *state = reinterpret_cast<plugin_state *>(ptr); |
|
delete state->callback; |
|
} |
|
|
|
} // namespace node |
|
} // namespace grpc
|
|
|