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 <memory>
35 :
36 : #include "server.h"
37 :
38 : #include <node.h>
39 : #include <nan.h>
40 :
41 : #include <vector>
42 : #include "grpc/grpc.h"
43 : #include "grpc/grpc_security.h"
44 : #include "grpc/support/log.h"
45 : #include "call.h"
46 : #include "completion_queue_async_worker.h"
47 : #include "server_credentials.h"
48 : #include "timeval.h"
49 :
50 : namespace grpc {
51 : namespace node {
52 :
53 : using Nan::Callback;
54 : using Nan::EscapableHandleScope;
55 : using Nan::HandleScope;
56 : using Nan::Maybe;
57 : using Nan::MaybeLocal;
58 : using Nan::ObjectWrap;
59 : using Nan::Persistent;
60 : using Nan::Utf8String;
61 :
62 : using std::unique_ptr;
63 : using v8::Array;
64 : using v8::Boolean;
65 : using v8::Date;
66 : using v8::Exception;
67 : using v8::Function;
68 : using v8::FunctionTemplate;
69 : using v8::Local;
70 : using v8::Number;
71 : using v8::Object;
72 : using v8::String;
73 : using v8::Value;
74 :
75 : Nan::Callback *Server::constructor;
76 1 : Persistent<FunctionTemplate> Server::fun_tpl;
77 :
78 : class NewCallOp : public Op {
79 : public:
80 222 : NewCallOp() {
81 111 : call = NULL;
82 111 : grpc_call_details_init(&details);
83 111 : grpc_metadata_array_init(&request_metadata);
84 111 : }
85 :
86 333 : ~NewCallOp() {
87 111 : grpc_call_details_destroy(&details);
88 111 : grpc_metadata_array_destroy(&request_metadata);
89 222 : }
90 :
91 87 : Local<Value> GetNodeValue() const {
92 : Nan::EscapableHandleScope scope;
93 87 : if (call == NULL) {
94 0 : return scope.Escape(Nan::Null());
95 : }
96 87 : Local<Object> obj = Nan::New<Object>();
97 261 : Nan::Set(obj, Nan::New("call").ToLocalChecked(), Call::WrapStruct(call));
98 87 : Nan::Set(obj, Nan::New("method").ToLocalChecked(),
99 261 : Nan::New(details.method).ToLocalChecked());
100 87 : Nan::Set(obj, Nan::New("host").ToLocalChecked(),
101 261 : Nan::New(details.host).ToLocalChecked());
102 87 : Nan::Set(obj, Nan::New("deadline").ToLocalChecked(),
103 : Nan::New<Date>(
104 261 : TimespecToMilliseconds(details.deadline)).ToLocalChecked());
105 87 : Nan::Set(obj, Nan::New("metadata").ToLocalChecked(),
106 261 : ParseMetadata(&request_metadata));
107 87 : return scope.Escape(obj);
108 : }
109 :
110 0 : bool ParseOp(Local<Value> value, grpc_op *out,
111 : shared_ptr<Resources> resources) {
112 0 : return true;
113 : }
114 :
115 : grpc_call *call;
116 : grpc_call_details details;
117 : grpc_metadata_array request_metadata;
118 :
119 : protected:
120 87 : std::string GetTypeString() const {
121 87 : return "new_call";
122 : }
123 : };
124 :
125 82 : Server::Server(grpc_server *server) : wrapped_server(server) {
126 41 : shutdown_queue = grpc_completion_queue_create(NULL);
127 41 : grpc_server_register_completion_queue(server, shutdown_queue, NULL);
128 41 : }
129 :
130 27 : Server::~Server() {
131 9 : this->ShutdownServer();
132 9 : grpc_completion_queue_shutdown(this->shutdown_queue);
133 9 : grpc_server_destroy(this->wrapped_server);
134 9 : grpc_completion_queue_destroy(this->shutdown_queue);
135 18 : }
136 :
137 1 : void Server::Init(Local<Object> exports) {
138 : HandleScope scope;
139 1 : Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
140 1 : tpl->SetClassName(Nan::New("Server").ToLocalChecked());
141 2 : tpl->InstanceTemplate()->SetInternalFieldCount(1);
142 : Nan::SetPrototypeMethod(tpl, "requestCall", RequestCall);
143 : Nan::SetPrototypeMethod(tpl, "addHttp2Port", AddHttp2Port);
144 : Nan::SetPrototypeMethod(tpl, "start", Start);
145 : Nan::SetPrototypeMethod(tpl, "tryShutdown", TryShutdown);
146 : Nan::SetPrototypeMethod(tpl, "forceShutdown", ForceShutdown);
147 : fun_tpl.Reset(tpl);
148 1 : Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
149 1 : Nan::Set(exports, Nan::New("Server").ToLocalChecked(), ctr);
150 1 : constructor = new Callback(ctr);
151 1 : }
152 :
153 217 : bool Server::HasInstance(Local<Value> val) {
154 : HandleScope scope;
155 651 : return Nan::New(fun_tpl)->HasInstance(val);
156 : }
157 :
158 48 : void Server::ShutdownServer() {
159 : grpc_server_shutdown_and_notify(this->wrapped_server,
160 : this->shutdown_queue,
161 48 : NULL);
162 48 : grpc_server_cancel_all_calls(this->wrapped_server);
163 : grpc_completion_queue_pluck(this->shutdown_queue, NULL,
164 48 : gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
165 48 : }
166 :
167 44 : NAN_METHOD(Server::New) {
168 : /* If this is not a constructor call, make a constructor call and return
169 : the result */
170 88 : if (!info.IsConstructCall()) {
171 1 : const int argc = 1;
172 2 : Local<Value> argv[argc] = {info[0]};
173 1 : MaybeLocal<Object> maybe_instance = constructor->GetFunction()->NewInstance(
174 2 : argc, argv);
175 1 : if (maybe_instance.IsEmpty()) {
176 : // There's probably a pending exception
177 : return;
178 : } else {
179 2 : info.GetReturnValue().Set(maybe_instance.ToLocalChecked());
180 : return;
181 : }
182 : }
183 : grpc_server *wrapped_server;
184 43 : grpc_completion_queue *queue = CompletionQueueAsyncWorker::GetQueue();
185 : grpc_channel_args *channel_args;
186 86 : if (!ParseChannelArgs(info[0], &channel_args)) {
187 2 : DeallocateChannelArgs(channel_args);
188 : return Nan::ThrowTypeError("Server options must be an object with "
189 : "string keys and integer or string values");
190 : }
191 41 : wrapped_server = grpc_server_create(channel_args, NULL);
192 41 : DeallocateChannelArgs(channel_args);
193 41 : grpc_server_register_completion_queue(wrapped_server, queue, NULL);
194 41 : Server *server = new Server(wrapped_server);
195 82 : server->Wrap(info.This());
196 123 : info.GetReturnValue().Set(info.This());
197 : }
198 :
199 111 : NAN_METHOD(Server::RequestCall) {
200 222 : if (!HasInstance(info.This())) {
201 : return Nan::ThrowTypeError("requestCall can only be called on a Server");
202 : }
203 222 : Server *server = ObjectWrap::Unwrap<Server>(info.This());
204 111 : NewCallOp *op = new NewCallOp();
205 111 : unique_ptr<OpVec> ops(new OpVec());
206 222 : ops->push_back(unique_ptr<Op>(op));
207 : grpc_call_error error = grpc_server_request_call(
208 : server->wrapped_server, &op->call, &op->details, &op->request_metadata,
209 : CompletionQueueAsyncWorker::GetQueue(),
210 : CompletionQueueAsyncWorker::GetQueue(),
211 333 : new struct tag(new Callback(info[0].As<Function>()), ops.release(),
212 555 : shared_ptr<Resources>(nullptr)));
213 111 : if (error != GRPC_CALL_OK) {
214 0 : return Nan::ThrowError(nanErrorWithCode("requestCall failed", error));
215 : }
216 111 : CompletionQueueAsyncWorker::Next();
217 : }
218 :
219 30 : NAN_METHOD(Server::AddHttp2Port) {
220 60 : if (!HasInstance(info.This())) {
221 : return Nan::ThrowTypeError(
222 : "addHttp2Port can only be called on a Server");
223 : }
224 90 : if (!info[0]->IsString()) {
225 : return Nan::ThrowTypeError(
226 : "addHttp2Port's first argument must be a String");
227 : }
228 60 : if (!ServerCredentials::HasInstance(info[1])) {
229 : return Nan::ThrowTypeError(
230 : "addHttp2Port's second argument must be ServerCredentials");
231 : }
232 60 : Server *server = ObjectWrap::Unwrap<Server>(info.This());
233 : ServerCredentials *creds_object = ObjectWrap::Unwrap<ServerCredentials>(
234 60 : Nan::To<Object>(info[1]).ToLocalChecked());
235 30 : grpc_server_credentials *creds = creds_object->GetWrappedServerCredentials();
236 : int port;
237 30 : if (creds == NULL) {
238 : port = grpc_server_add_insecure_http2_port(server->wrapped_server,
239 81 : *Utf8String(info[0]));
240 : } else {
241 : port = grpc_server_add_secure_http2_port(server->wrapped_server,
242 9 : *Utf8String(info[0]),
243 3 : creds);
244 : }
245 90 : info.GetReturnValue().Set(Nan::New<Number>(port));
246 : }
247 :
248 32 : NAN_METHOD(Server::Start) {
249 : Nan::HandleScope scope;
250 64 : if (!HasInstance(info.This())) {
251 32 : return Nan::ThrowTypeError("start can only be called on a Server");
252 : }
253 64 : Server *server = ObjectWrap::Unwrap<Server>(info.This());
254 32 : grpc_server_start(server->wrapped_server);
255 : }
256 :
257 5 : NAN_METHOD(Server::TryShutdown) {
258 : Nan::HandleScope scope;
259 10 : if (!HasInstance(info.This())) {
260 5 : return Nan::ThrowTypeError("tryShutdown can only be called on a Server");
261 : }
262 10 : Server *server = ObjectWrap::Unwrap<Server>(info.This());
263 5 : unique_ptr<OpVec> ops(new OpVec());
264 : grpc_server_shutdown_and_notify(
265 : server->wrapped_server,
266 : CompletionQueueAsyncWorker::GetQueue(),
267 15 : new struct tag(new Nan::Callback(info[0].As<Function>()), ops.release(),
268 25 : shared_ptr<Resources>(nullptr)));
269 5 : CompletionQueueAsyncWorker::Next();
270 : }
271 :
272 39 : NAN_METHOD(Server::ForceShutdown) {
273 : Nan::HandleScope scope;
274 78 : if (!HasInstance(info.This())) {
275 39 : return Nan::ThrowTypeError("forceShutdown can only be called on a Server");
276 : }
277 78 : Server *server = ObjectWrap::Unwrap<Server>(info.This());
278 39 : server->ShutdownServer();
279 : }
280 :
281 : } // namespace node
282 3 : } // namespace grpc
|