Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : #include <node.h>
35 :
36 : #include "grpc/grpc.h"
37 : #include "grpc/grpc_security.h"
38 : #include "grpc/support/log.h"
39 : #include "call_credentials.h"
40 : #include "call.h"
41 :
42 : namespace grpc {
43 : namespace node {
44 :
45 : using Nan::Callback;
46 : using Nan::EscapableHandleScope;
47 : using Nan::HandleScope;
48 : using Nan::Maybe;
49 : using Nan::MaybeLocal;
50 : using Nan::ObjectWrap;
51 : using Nan::Persistent;
52 : using Nan::Utf8String;
53 :
54 : using v8::Exception;
55 : using v8::External;
56 : using v8::Function;
57 : using v8::FunctionTemplate;
58 : using v8::Integer;
59 : using v8::Local;
60 : using v8::Object;
61 : using v8::ObjectTemplate;
62 : using v8::Value;
63 :
64 : Nan::Callback *CallCredentials::constructor;
65 1 : Persistent<FunctionTemplate> CallCredentials::fun_tpl;
66 :
67 7 : CallCredentials::CallCredentials(grpc_call_credentials *credentials)
68 14 : : wrapped_credentials(credentials) {}
69 :
70 18 : CallCredentials::~CallCredentials() {
71 6 : grpc_call_credentials_release(wrapped_credentials);
72 12 : }
73 :
74 1 : void CallCredentials::Init(Local<Object> exports) {
75 : HandleScope scope;
76 1 : Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
77 1 : tpl->SetClassName(Nan::New("CallCredentials").ToLocalChecked());
78 2 : tpl->InstanceTemplate()->SetInternalFieldCount(1);
79 : Nan::SetPrototypeMethod(tpl, "compose", Compose);
80 : fun_tpl.Reset(tpl);
81 1 : Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
82 1 : Nan::Set(ctr, Nan::New("createFromPlugin").ToLocalChecked(),
83 : Nan::GetFunction(
84 3 : Nan::New<FunctionTemplate>(CreateFromPlugin)).ToLocalChecked());
85 1 : Nan::Set(exports, Nan::New("CallCredentials").ToLocalChecked(), ctr);
86 1 : constructor = new Nan::Callback(ctr);
87 1 : }
88 :
89 11 : bool CallCredentials::HasInstance(Local<Value> val) {
90 : HandleScope scope;
91 33 : return Nan::New(fun_tpl)->HasInstance(val);
92 : }
93 :
94 7 : Local<Value> CallCredentials::WrapStruct(grpc_call_credentials *credentials) {
95 : EscapableHandleScope scope;
96 7 : const int argc = 1;
97 7 : if (credentials == NULL) {
98 0 : return scope.Escape(Nan::Null());
99 : }
100 : Local<Value> argv[argc] = {
101 7 : Nan::New<External>(reinterpret_cast<void *>(credentials))};
102 : MaybeLocal<Object> maybe_instance = Nan::NewInstance(
103 14 : constructor->GetFunction(), argc, argv);
104 7 : if (maybe_instance.IsEmpty()) {
105 0 : return scope.Escape(Nan::Null());
106 : } else {
107 7 : return scope.Escape(maybe_instance.ToLocalChecked());
108 : }
109 : }
110 :
111 9 : grpc_call_credentials *CallCredentials::GetWrappedCredentials() {
112 9 : return wrapped_credentials;
113 : }
114 :
115 7 : NAN_METHOD(CallCredentials::New) {
116 14 : if (info.IsConstructCall()) {
117 14 : if (!info[0]->IsExternal()) {
118 : return Nan::ThrowTypeError(
119 : "CallCredentials can only be created with the provided functions");
120 : }
121 21 : Local<External> ext = info[0].As<External>();
122 : grpc_call_credentials *creds_value =
123 7 : reinterpret_cast<grpc_call_credentials *>(ext->Value());
124 7 : CallCredentials *credentials = new CallCredentials(creds_value);
125 14 : credentials->Wrap(info.This());
126 21 : info.GetReturnValue().Set(info.This());
127 : return;
128 : } else {
129 : // This should never be called directly
130 : return Nan::ThrowTypeError(
131 : "CallCredentials can only be created with the provided functions");
132 : }
133 : }
134 :
135 1 : NAN_METHOD(CallCredentials::Compose) {
136 2 : if (!CallCredentials::HasInstance(info.This())) {
137 : return Nan::ThrowTypeError(
138 : "compose can only be called on CallCredentials objects");
139 : }
140 2 : if (!CallCredentials::HasInstance(info[0])) {
141 : return Nan::ThrowTypeError(
142 : "compose's first argument must be a CallCredentials object");
143 : }
144 2 : CallCredentials *self = ObjectWrap::Unwrap<CallCredentials>(info.This());
145 : CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>(
146 2 : Nan::To<Object>(info[0]).ToLocalChecked());
147 : grpc_call_credentials *creds = grpc_composite_call_credentials_create(
148 1 : self->wrapped_credentials, other->wrapped_credentials, NULL);
149 3 : info.GetReturnValue().Set(WrapStruct(creds));
150 : }
151 :
152 :
153 :
154 6 : NAN_METHOD(CallCredentials::CreateFromPlugin) {
155 12 : if (!info[0]->IsFunction()) {
156 : return Nan::ThrowTypeError(
157 6 : "createFromPlugin's argument must be a function");
158 : }
159 : grpc_metadata_credentials_plugin plugin;
160 6 : plugin_state *state = new plugin_state;
161 18 : state->callback = new Nan::Callback(info[0].As<Function>());
162 6 : plugin.get_metadata = plugin_get_metadata;
163 6 : plugin.destroy = plugin_destroy_state;
164 6 : plugin.state = reinterpret_cast<void*>(state);
165 6 : plugin.type = "";
166 : grpc_call_credentials *creds = grpc_metadata_credentials_create_from_plugin(
167 6 : plugin, NULL);
168 18 : info.GetReturnValue().Set(WrapStruct(creds));
169 : }
170 :
171 10 : NAN_METHOD(PluginCallback) {
172 : // Arguments: status code, error details, metadata
173 20 : if (!info[0]->IsUint32()) {
174 : return Nan::ThrowTypeError(
175 0 : "The callback's first argument must be a status code");
176 : }
177 30 : if (!info[1]->IsString()) {
178 : return Nan::ThrowTypeError(
179 : "The callback's second argument must be a string");
180 : }
181 20 : if (!info[2]->IsObject()) {
182 : return Nan::ThrowTypeError(
183 : "The callback's third argument must be an object");
184 : }
185 10 : shared_ptr<Resources> resources(new Resources);
186 : grpc_status_code code = static_cast<grpc_status_code>(
187 20 : Nan::To<uint32_t>(info[0]).FromJust());
188 30 : char *details = *Utf8String(info[1]);
189 : grpc_metadata_array array;
190 20 : if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(),
191 20 : &array, resources)){
192 : return Nan::ThrowError("Failed to parse metadata");
193 : }
194 : grpc_credentials_plugin_metadata_cb cb =
195 : reinterpret_cast<grpc_credentials_plugin_metadata_cb>(
196 : Nan::Get(info.Callee(),
197 20 : Nan::New("cb").ToLocalChecked()
198 40 : ).ToLocalChecked().As<External>()->Value());
199 : void *user_data =
200 : Nan::Get(info.Callee(),
201 10 : Nan::New("user_data").ToLocalChecked()
202 40 : ).ToLocalChecked().As<External>()->Value();
203 10 : cb(user_data, array.metadata, array.count, code, details);
204 : }
205 :
206 10 : NAUV_WORK_CB(SendPluginCallback) {
207 : Nan::HandleScope scope;
208 : plugin_callback_data *data = reinterpret_cast<plugin_callback_data*>(
209 10 : async->data);
210 : // Attach cb and user_data to plugin_callback so that it can access them later
211 : v8::Local<v8::Function> plugin_callback = Nan::GetFunction(
212 10 : Nan::New<v8::FunctionTemplate>(PluginCallback)).ToLocalChecked();
213 10 : Nan::Set(plugin_callback, Nan::New("cb").ToLocalChecked(),
214 30 : Nan::New<v8::External>(reinterpret_cast<void*>(data->cb)));
215 10 : Nan::Set(plugin_callback, Nan::New("user_data").ToLocalChecked(),
216 30 : Nan::New<v8::External>(data->user_data));
217 10 : const int argc = 2;
218 : v8::Local<v8::Value> argv[argc] = {
219 10 : Nan::New(data->service_url).ToLocalChecked(),
220 : plugin_callback
221 20 : };
222 10 : Nan::Callback *callback = data->state->callback;
223 : callback->Call(argc, argv);
224 10 : delete data;
225 10 : uv_unref((uv_handle_t *)async);
226 10 : uv_close((uv_handle_t *)async, (uv_close_cb)free);
227 10 : }
228 :
229 10 : void plugin_get_metadata(void *state, grpc_auth_metadata_context context,
230 : grpc_credentials_plugin_metadata_cb cb,
231 : void *user_data) {
232 10 : uv_async_t *async = static_cast<uv_async_t*>(malloc(sizeof(uv_async_t)));
233 : uv_async_init(uv_default_loop(),
234 : async,
235 10 : SendPluginCallback);
236 10 : plugin_callback_data *data = new plugin_callback_data;
237 10 : data->state = reinterpret_cast<plugin_state*>(state);
238 10 : data->service_url = context.service_url;
239 10 : data->cb = cb;
240 10 : data->user_data = user_data;
241 10 : async->data = data;
242 : /* libuv says that it will coalesce calls to uv_async_send. If there is ever a
243 : * problem with a callback not getting called, that is probably the reason */
244 10 : uv_async_send(async);
245 10 : }
246 :
247 1 : void plugin_destroy_state(void *ptr) {
248 1 : plugin_state *state = reinterpret_cast<plugin_state *>(ptr);
249 2 : delete state->callback;
250 1 : }
251 :
252 : } // namespace node
253 3 : } // namespace grpc
|