├── .travis.yml ├── DESCRIPTION ├── NAMESPACE ├── R ├── RcppExports.R ├── client.R ├── parser.R ├── server.R └── utils.R ├── README.md ├── demo ├── 00Index ├── health-check-client.R ├── health-check-server.R ├── helloclient.R ├── helloserver.R ├── iris-client.R └── iris-server.R ├── inst ├── docker_dev │ └── Dockerfile ├── docker_stable │ └── Dockerfile ├── examples │ ├── health_check.proto │ ├── helloworld.proto │ └── iris_classifier.proto └── tests │ ├── testthat.R │ └── testthat │ ├── helloworld.out │ └── test-helloworld.R ├── install ├── man ├── grpc_client.Rd ├── grpc_default_hooks.Rd ├── grpc_version.Rd ├── hello.Rd ├── newResponse.Rd ├── rcpp_hello.Rd ├── read_services.Rd └── start_server.Rd └── src ├── Makevars ├── RcppExports.cpp ├── client.cpp ├── common.h ├── ext ├── byte_buffer.cc ├── byte_buffer.h ├── call.cc ├── call.h ├── call_credentials.cc ├── call_credentials.h ├── channel.cc ├── channel.h ├── channel_credentials.cc ├── channel_credentials.h ├── completion_queue.cc ├── completion_queue.h ├── node_grpc.cc ├── server.cc ├── server.h ├── server_credentials.cc ├── server_credentials.h ├── slice.cc ├── slice.h ├── timeval.cc └── timeval.h ├── onload.c ├── rcpp_hello.cpp └── server.cpp /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | 5 | before_install: 6 | - docker pull rgrpc/grpc_dev:stable 7 | 8 | script: 9 | - > 10 | docker run -v `pwd`:/grpc rgrpc/grpc_dev:stable /bin/bash -c " 11 | cd /tmp && 12 | R CMD INSTALL /grpc && 13 | Rscript --vanilla -e \"testthat::test_package('grpc')\" 14 | " 15 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: grpc 2 | Type: Package 3 | Title: gRPC Library 4 | Version: 0.2.0 5 | Author: Neal Fultz, Google 6 | Maintainer: Neal Fultz 7 | Description: Client and server scaffolding for Google's Remote Procedure Call framework 8 | License: GPL-3 9 | Encoding: UTF-8 10 | LazyData: true 11 | Imports: 12 | Rcpp (>= 0.12.5), 13 | RProtoBuf, 14 | futile.logger 15 | Suggests: 16 | testthat, 17 | processx 18 | LinkingTo: Rcpp 19 | SystemRequirements: 20 | grpc (>= 4.0.0) 21 | protobuf 22 | RoxygenNote: 6.0.1 23 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(grpc_client) 4 | export(grpc_default_hooks) 5 | export(grpc_version) 6 | export(newResponse) 7 | export(read_services) 8 | export(start_server) 9 | importFrom(RProtoBuf,P) 10 | importFrom(RProtoBuf,new) 11 | importFrom(RProtoBuf,read) 12 | importFrom(RProtoBuf,readProtoFiles) 13 | importFrom(RProtoBuf,serialize) 14 | importFrom(futile.logger,flog.debug) 15 | importFrom(futile.logger,flog.info) 16 | importFrom(futile.logger,flog.trace) 17 | importFrom(methods,selectMethod) 18 | useDynLib(grpc) 19 | -------------------------------------------------------------------------------- /R/RcppExports.R: -------------------------------------------------------------------------------- 1 | # Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | fetch <- function(server, method, requestArg, metadata) { 5 | .Call('_grpc_fetch', PACKAGE = 'grpc', server, method, requestArg, metadata) 6 | } 7 | 8 | #' Check grpc version 9 | #' @return version string and what g stands for 10 | #' @export 11 | grpc_version <- function() { 12 | .Call('_grpc_grpc_version', PACKAGE = 'grpc') 13 | } 14 | 15 | run <- function(target, hoststring, hooks) { 16 | .Call('_grpc_run', PACKAGE = 'grpc', target, hoststring, hooks) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /R/client.R: -------------------------------------------------------------------------------- 1 | #' Build a client handle 2 | #' 3 | #' @param stubs the stub of the service 4 | #' @param channel what to connect to 5 | #' @return client handle 6 | #' @importFrom RProtoBuf P serialize read new 7 | #' @export 8 | grpc_client <- function(impl, channel) { 9 | 10 | 11 | client_functions <- lapply(impl, function(fn) 12 | { 13 | RequestDescriptor <- P(fn[["RequestType"]]$proto) 14 | ResponseDescriptor <- P(fn[["ResponseType"]]$proto) 15 | 16 | list( 17 | call = function(x, metadata=character(0)) read(ResponseDescriptor, fetch(channel, fn$name, serialize(x, NULL), metadata)), 18 | build = function(...) new(RequestDescriptor, ...) 19 | ) 20 | }) 21 | 22 | 23 | 24 | client_functions 25 | } -------------------------------------------------------------------------------- /R/parser.R: -------------------------------------------------------------------------------- 1 | #' Create stub object from protobuf spec 2 | #' 3 | #' @param file the spec file 4 | #' @return a stub data structure 5 | #' @importFrom RProtoBuf readProtoFiles 6 | #' @export 7 | read_services <- function(file){ 8 | SERVICE = "service" 9 | RPC = "rpc" 10 | RETURNS = "returns" 11 | STREAM = "stream" 12 | PACKAGE = "package" 13 | 14 | services <- list() 15 | pkg <- "" 16 | 17 | 18 | doServices <- function(i){ 19 | service_name <- tokens[i+1] 20 | # services[[service_name]] <<- list() 21 | 22 | while(tokens[i] != '}') { 23 | if(tokens[i] == RPC){ 24 | i <- doRPC(i, service_name) 25 | } 26 | i <- i + 1 27 | } 28 | 29 | return(i) 30 | } 31 | 32 | doRPC <- function(i, service_name) { 33 | rpc_name = tokens[i+1] 34 | fn <- list(f=I) 35 | 36 | w <- "RequestType" 37 | 38 | while(tokens[i] != '}' && tokens[i] != ';'){ 39 | 40 | if(tokens[i] == '('){ 41 | i <- i + 1 42 | isStream <- tokens[i] == STREAM 43 | if(isStream){ 44 | i <- i + 1 45 | } 46 | 47 | fn[[w]] <- list(name=tokens[i], stream=isStream, proto=sprintf("%s.%s", pkg, tokens[i])) 48 | w <- "ResponseType" 49 | } 50 | 51 | i <- i + 1 52 | } 53 | fn$name <- sprintf("/%s.%s/%s",pkg, service_name, rpc_name) 54 | services[[rpc_name]] <<- fn 55 | return(i) 56 | } 57 | 58 | readProtoFiles(file) 59 | 60 | lines <- readLines(file) 61 | 62 | tokens <- Filter(f=nchar, unlist(strsplit(lines, '(^//.*$|\\s+|(?=[{}();]))', perl=TRUE))) 63 | 64 | i <- 1 65 | while(i <= length(tokens)){ 66 | if(tokens[i] == PACKAGE) { 67 | pkg <- tokens[i+1]; 68 | } 69 | else if(tokens[i] == SERVICE){ 70 | i <- doServices(i) 71 | } 72 | 73 | i <- i + 1 74 | } 75 | 76 | services 77 | } 78 | -------------------------------------------------------------------------------- /R/server.R: -------------------------------------------------------------------------------- 1 | #' Start a gRPC server 2 | #' 3 | #' @param impl an implementation of a proto service 4 | #' @param channel a channel string in 'host:port' format 5 | #' @param hooks list of R function(s) with \code{params} argument as a list to be run at different parts of the C++ calls. Supported hooks: \code{server_create}, \code{queue_create}, \code{bind} (when \code{params$port} becomes available), \code{server_start}, \code{run}, \code{shutdown}, \code{stopped}, \code{exit} 6 | #' @return none 7 | #' @importFrom methods selectMethod 8 | #' @importFrom RProtoBuf P serialize read 9 | #' @useDynLib grpc 10 | #' @export 11 | #' @seealso \code{\link{grpc_default_hooks}} 12 | start_server <- function(impl, channel, hooks = grpc_default_hooks()) { 13 | 14 | if (!is.null(hooks$exit) & is.function(hooks$exit)) { 15 | on.exit(hooks$exit()) 16 | } 17 | 18 | server_functions <- lapply(impl, function(fn){ 19 | descriptor <- P(fn[["RequestType"]]$proto) 20 | 21 | f <- structure(fn$f, 22 | RequestType = fn[["RequestType"]], 23 | ResponseType = fn[["ResponseType"]]) 24 | 25 | function(x) serialize(f(read(descriptor, x)), NULL) 26 | }) 27 | 28 | names(server_functions) <- vapply(impl, function(x)x$name, NA_character_) 29 | 30 | run(server_functions, channel, hooks) 31 | invisible(NULL) 32 | } 33 | 34 | 35 | #' Construct a new ProtoBuf of ResponseType 36 | #' 37 | #' @param ... threaded through to the ProtoBuf constructor 38 | #' @param WFUN A GRPC service method with RequestType and ResponseType attributes 39 | #' @return 40 | #' 41 | #' @export 42 | #' @importFrom RProtoBuf P new 43 | newResponse <- function(..., WFUN=sys.function(-1)){ 44 | new(P(attr(WFUN, "ResponseType")$proto), ...) 45 | } 46 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' Provides a list of logging functions to be passed to gRPC server 2 | #' 3 | #' You may want to override this with custom functions, eg registering the gRPC service in Hashicorp's consul.io or similar after \code{server_start} etc. 4 | #' @return \code{list} 5 | #' @export 6 | #' @importFrom futile.logger flog.trace flog.debug flog.info 7 | grpc_default_hooks <- function() { 8 | list( 9 | server_create = function(params) { 10 | flog.trace('gRPC server created') 11 | }, 12 | queue_create = function(params) { 13 | flog.trace('Completion queue created and registered') 14 | }, 15 | bind = function(params) { 16 | flog.debug(paste('gRPC service will listen on port', params$port)) 17 | }, 18 | server_start = function(params) { 19 | flog.trace(paste('gRPC service started on port', params$port)) 20 | }, 21 | run = function(params) { 22 | flog.info(paste('gRPC service is now listening on port', params$port)) 23 | }, 24 | event_received = function(params) { 25 | flog.trace(paste('event received', params$event_method)) 26 | }, 27 | event_processed = function(params) { 28 | flog.trace('event processed') 29 | }, 30 | shutdown = function(params) { 31 | flog.info('gRPC service is going to shut down') 32 | }, 33 | stopped = function(params) { 34 | flog.debug('gRPC service stopped') 35 | }, 36 | exit = function(params) { 37 | flog.trace('The gRPC server exited') 38 | } 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grpc 2 | 3 | An **R** library for [**GRPC**](https://grpc.io/) a high-performance, open-source universal RPC framework. 4 | 5 | ## Installation - Debian 6 | 7 | ### Pre-requisites 8 | 9 | The following is copied from [gRPC C++ - Building from source](https://github.com/grpc/grpc/blob/master/BUILDING.md) 10 | ```shell 11 | sudo apt-get install build-essential autoconf libtool pkg-config 12 | ## If you plan to build from source and run tests, install the following as well: 13 | sudo apt-get install libgflags-dev libgtest-dev 14 | sudo apt-get install clang libc++-dev 15 | ``` 16 | 17 | ### Download and Install grpc 18 | ```shell 19 | export GRPC_INSTALL_DIR=$HOME/.local 20 | mkdir -p $GRPC_INSTALL_DIR 21 | export PATH="$GRPC_INSTALL_DIR/bin:$PATH" 22 | 23 | sudo apt install -y cmake 24 | 25 | LATEST_VER=$(curl -L "https://api.github.com/repos/grpc/grpc/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")') 26 | git clone --recurse-submodules -b $LATEST_VER https://github.com/grpc/grpc grpc_base 27 | 28 | cd grpc_base 29 | mkdir -p cmake/build 30 | pushd cmake/build 31 | cmake -DgRPC_INSTALL=ON \ 32 | -DgRPC_BUILD_TESTS=OFF \ 33 | -DCMAKE_INSTALL_PREFIX=$GRPC_INSTALL_DIR \ 34 | ../.. 35 | make -j4 36 | sudo make install 37 | popd 38 | 39 | mkdir -p third_party/abseil-cpp/cmake/build 40 | pushd third_party/abseil-cpp/cmake/build 41 | cmake -DCMAKE_INSTALL_PREFIX=$GRPC_INSTALL_DIR \ 42 | -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \ 43 | ../.. 44 | make -j4 45 | sudo make install 46 | popd 47 | ``` 48 | 49 | # Original 50 | 51 | [![Build Status](https://travis-ci.org/nfultz/grpc.svg)](https://travis-ci.org/nfultz/grpc) 52 | 53 | Easily create [gRPC](https://github.com/grpc/grpc) clients and servers from protobuf descriptions to build distributed services. 54 | 55 | Copyright 2015 Google Inc, 2017 Neal Fultz 56 | 57 | 58 | ## Dependencies 59 | 60 | * grpc 61 | * protobuf 62 | * RProtoBuf 63 | 64 | See `install` for my installation notes... 65 | 66 | 67 | ## Examples 68 | 69 | There are runnable examples in the `demo/` folder. 70 | 71 | ### Hello, World! 72 | 73 | To start a HelloWorld server: 74 | 75 | R -e 'demo("helloserver", "grpc")' 76 | 77 | Or with much more detailed logging: 78 | 79 | R -e 'library(futile.logger); flog.threshold(TRACE); demo("helloserver", "grpc")' 80 | 81 | To run a client against a running HelloWorld server: 82 | 83 | R -e 'demo("helloclient", "grpc")' 84 | 85 | Both are cross compatible with the Node, Python and C++ Greeter examples provided by the grpc library. 86 | 87 | ### Health check 88 | 89 | This server implements the above service along with the standard [GRPC Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md): 90 | 91 | R -e 'demo("health-check-server", "grpc")' 92 | 93 | The client runs a health-check then calls the Hello, World! method once: 94 | 95 | R -e 'demo("health-check-client", "grpc")' 96 | 97 | Please check the sources of the server to see how to bundle services defined in multiple `proto` files. 98 | 99 | ### Live scoring 100 | 101 | There's a simple trained on the `iris` dataset and making that available for scoring via a gRPC service: 102 | 103 | R -e 'demo("iris-server", "grpc")' 104 | 105 | An example client to this service from R: 106 | 107 | R -e 'demo("iris-client", "grpc")' 108 | 109 | ## Todo 110 | 111 | * Streaming services 112 | * Authentication and Encryption 113 | * Error handling 114 | * Docs 115 | 116 | ## Contributing 117 | -------------------------------------------------------------------------------- /demo/00Index: -------------------------------------------------------------------------------- 1 | helloserver The standard Hello, World! server implementation 2 | helloclient The standard Hello, World! client implementation 3 | health-check-server The standard Hello, World! server implementation with standard health check 4 | health-check-client The standard Hello, World! client implementation with standard health check 5 | iris-server Building a basic model and using that for live-scoring 6 | iris-client A basic client to the live-scoring server 7 | -------------------------------------------------------------------------------- /demo/health-check-client.R: -------------------------------------------------------------------------------- 1 | library(grpc) 2 | library(RProtoBuf) 3 | library(futile.logger) 4 | 5 | ## reading the service definitions 6 | impl <- c( 7 | read_services(system.file('examples/helloworld.proto', package = 'grpc')), 8 | read_services(system.file('examples/health_check.proto', package = 'grpc'))) 9 | 10 | ## connection to the gRPC server 11 | port <- readLines('/tmp/health-check-server.port', warn = FALSE) 12 | client <- grpc_client(impl, paste('localhost', port, sep = ':')) 13 | 14 | ## check if service is online 15 | res <- client$Check$call(client$Check$build()) 16 | flog.info(as.character(res)) 17 | 18 | ## actual gRPC call 19 | res <- client$SayHello$call(client$SayHello$build(name = 'Gergely')) 20 | flog.info(as.character(res)) 21 | -------------------------------------------------------------------------------- /demo/health-check-server.R: -------------------------------------------------------------------------------- 1 | ## Example gRPC service + health check 2 | 3 | library(grpc) 4 | library(futile.logger) 5 | library(jsonlite) 6 | library(RProtoBuf) 7 | 8 | ## build model for scoring (saved to global environment) 9 | library(rpart) 10 | names(iris) <- tolower(sub('.', '_', names(iris), fixed = TRUE)) 11 | fit <- rpart(species ~ ., iris) 12 | 13 | ## reading the service definitions 14 | impl <- c( 15 | read_services(system.file('examples/helloworld.proto', package = 'grpc')), 16 | read_services(system.file('examples/health_check.proto', package = 'grpc'))) 17 | 18 | impl$SayHello$f <- function(request){ 19 | newResponse(message = paste('Hello,', request$name)) 20 | } 21 | 22 | impl$Check$f <- function(request) { 23 | new(grpc.health.v1.HealthCheckResponse, status = 1) 24 | } 25 | 26 | ## run the service handlers on an open port 27 | start_server(impl, '0.0.0.0:0', hooks = list( 28 | run = function(params) { 29 | cat(params$port, file = '/tmp/health-check-server.port') 30 | flog.info('Server running on port %s', params$port) 31 | } 32 | )) 33 | -------------------------------------------------------------------------------- /demo/helloclient.R: -------------------------------------------------------------------------------- 1 | #' Example gRPC client 2 | #' 3 | #' Sends a message with a name and returns a message greeting the name. 4 | #' @references \url{https://github.com/grpc/grpc/tree/master/examples/cpp/helloworld} 5 | 6 | library(grpc) 7 | 8 | 9 | spec <- system.file('examples/helloworld.proto', package = 'grpc') 10 | impl <- read_services(spec) 11 | client <- grpc_client(impl, "localhost:50051") 12 | 13 | 14 | for(who in c("Neal", "Gergely", "Jay")){ 15 | hello <- client$SayHello$build(name=who) 16 | message <- client$SayHello$call(hello) 17 | 18 | print(message) 19 | print(as.list(message)) 20 | 21 | thanks <- client$SayThanks$build(name=who) 22 | message <- client$SayThanks$call(thanks, c("key1", "val1")) 23 | 24 | print(message) 25 | print(as.list(message)) 26 | 27 | bye <- client$SayBye$build(name=who) 28 | message <- client$SayBye$call(bye) 29 | 30 | print(message) 31 | print(as.list(message)) 32 | } 33 | -------------------------------------------------------------------------------- /demo/helloserver.R: -------------------------------------------------------------------------------- 1 | #' Example gRPC service 2 | #' 3 | #' Reads a message with a name and returns a message greeting the name. 4 | #' @references \url{https://github.com/grpc/grpc/tree/master/examples/cpp/helloworld} 5 | 6 | library(grpc) 7 | 8 | ## reading the service definitions 9 | spec <- system.file('examples/helloworld.proto', package = 'grpc') 10 | 11 | impl <- read_services(spec) 12 | 13 | impl$SayHello$f <- function(request){ 14 | newResponse(message = paste('Hello,', request$name)) 15 | } 16 | 17 | impl$SayThanks$f <- function(request){ 18 | newResponse(message = paste('Thanks,', request$name)) 19 | } 20 | 21 | impl$SayBye$f <- function(request){ 22 | newResponse(message = paste('Bye,', request$name)) 23 | } 24 | 25 | ## actually running the service handlers 26 | start_server(impl, "0.0.0.0:50051") 27 | 28 | -------------------------------------------------------------------------------- /demo/iris-client.R: -------------------------------------------------------------------------------- 1 | ## Example gRPC client to score features sent in a Proto message 2 | 3 | library(grpc) 4 | library(RProtoBuf) 5 | library(futile.logger) 6 | 7 | ## reading the service definitions 8 | spec <- system.file('examples/iris_classifier.proto', package = 'grpc') 9 | impl <- read_services(spec) 10 | 11 | ## connection to the gRPC server 12 | client <- grpc_client(impl, 'localhost:50051') 13 | 14 | ## define message to be sent to the gRPC server 15 | msg <- client$Classify$build(sepal_length = 5, sepal_width = 2, petal_length = 1, petal_width = 0.3) 16 | 17 | ## score 18 | res <- client$Classify$call(msg) 19 | 20 | ## log results 21 | flog.info('Result: %s with %s probability', res$species, res$probability) 22 | 23 | ## score a number of items 24 | df <- iris[sample(1:150, 25), ] 25 | for (n in seq_len(nrow(df))) { 26 | 27 | msg <- client$Classify$build( 28 | sepal_length = df[n, 'Sepal.Length'], 29 | sepal_width = df[n, 'Sepal.Width'], 30 | petal_length = df[n, 'Petal.Length'], 31 | petal_width = df[n, 'Petal.Width']) 32 | res <- client$Classify$call(msg) 33 | flog.info('Result: %s with %s probability', res$species, res$probability) 34 | 35 | } 36 | 37 | ## fail the classifier 38 | msg <- client$Classify$build(sepal_length = -5) 39 | res <- client$Classify$call(msg) 40 | str(as.list(res)) 41 | str(as.list(res$status)) 42 | -------------------------------------------------------------------------------- /demo/iris-server.R: -------------------------------------------------------------------------------- 1 | ## Example gRPC service to score features sent in a Proto message 2 | 3 | library(grpc) 4 | library(futile.logger) 5 | library(jsonlite) 6 | library(RProtoBuf) 7 | 8 | ## reading the service definitions 9 | spec <- system.file('examples/iris_classifier.proto', package = 'grpc') 10 | 11 | ## build model for scoring (saved to global environment) 12 | library(rpart) 13 | names(iris) <- tolower(sub('.', '_', names(iris), fixed = TRUE)) 14 | fit <- rpart(species ~ ., iris) 15 | 16 | ## define service definitions 17 | impl <- read_services(spec) 18 | 19 | impl$Classify$f <- function(request) { 20 | 21 | request <- as.list(request) 22 | 23 | flog.info('Data received for scoring: %s', toJSON(request, auto_unbox = TRUE)) 24 | 25 | ## try to score 26 | response <- tryCatch({ 27 | 28 | for (v in attr(terms(fit), "term.labels")) { 29 | if (request[[v]] > 0) next else { 30 | stop('Negative ', v, ' provided') 31 | } 32 | } 33 | 34 | scores <- predict(fit, newdata = request) 35 | i <- which.max(scores) 36 | cls <- attr(fit, "ylevels")[i] 37 | p <- scores[, i] 38 | 39 | flog.info('Predicted class: %s (p=%5.4f)', cls, p) 40 | new(iris.Class, species = cls, probability = p) 41 | }, 42 | error = function(e) { 43 | new(iris.Class, status = new(iris.Status, code = 1, message = e$message)) 44 | }) 45 | response 46 | } 47 | 48 | ## run the service handlers 49 | start_server(impl, '0.0.0.0:50051') 50 | -------------------------------------------------------------------------------- /inst/docker_dev/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | MAINTAINER Gergely Daroczi 3 | 4 | ENV GRPC_RELEASE_TAG v1.4.5 5 | ENV PROTOC_RELEASE_TAG v3.2.0 6 | ENV R_BASE_VERSION 3.4.2 7 | 8 | ## install build tools 9 | RUN apt-get update && apt-get install -y \ 10 | build-essential autoconf libtool \ 11 | libgflags-dev libgtest-dev clang libc++-dev \ 12 | unzip git curl wget \ 13 | pkg-config locales && \ 14 | apt-get clean && rm -rf /var/lib/apt/lists/ 15 | 16 | ## build grpc and protobuf 17 | RUN git clone -b ${GRPC_RELEASE_TAG} https://github.com/grpc/grpc /var/local/git/grpc && \ 18 | cd /var/local/git/grpc && git submodule update --init && \ 19 | cd /var/local/git/grpc/third_party/protobuf && \ 20 | git checkout ${PROTOC_RELEASE_TAG} && \ 21 | ./autogen.sh && ./configure && \ 22 | make && make install && make clean && ldconfig && \ 23 | cd /var/local/git/grpc && \ 24 | make && make install && make clean && ldconfig 25 | 26 | ## set locale for R 27 | RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen \ 28 | && locale-gen en_US.utf8 \ 29 | && /usr/sbin/update-locale LANG=en_US.UTF-8 30 | ENV LC_ALL en_US.UTF-8 31 | ENV LANG en_US.UTF-8 32 | 33 | ## install R 34 | RUN echo "deb http://cloud.r-project.org/bin/linux/ubuntu trusty/" >> /etc/apt/sources.list && \ 35 | apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E084DAB9 && \ 36 | apt-get -qq update && apt-get upgrade -y && \ 37 | apt-get install -y --no-install-recommends \ 38 | littler \ 39 | r-base-core=${R_BASE_VERSION}* \ 40 | r-base-dev=${R_BASE_VERSION}* \ 41 | r-recommended=${R_BASE_VERSION}* \ 42 | libssl-dev libcurl4-openssl-dev libxml2-dev libfftw3-dev && \ 43 | apt-get clean && rm -rf /var/lib/apt/lists/ 44 | 45 | ## install R packages 46 | RUN echo 'options(repos = c(CRAN = "https://cran.rstudio.com/"), download.file.method = "libcurl")' >> /etc/R/Rprofile.site && \ 47 | echo 'source("/etc/R/Rprofile.site")' >> /etc/littler.r && \ 48 | Rscript -e "install.packages('docopt')" && \ 49 | ln -s /usr/share/doc/littler/examples/install2.r /usr/local/bin/install2.r && \ 50 | ln -s /usr/share/doc/littler/examples/installGithub.r /usr/local/bin/installGithub.r && \ 51 | install2.r --error remotes devtools testthat processx futile.logger RProtoBuf && \ 52 | installGithub.r nfultz/grpc && \ 53 | rm -rf /tmp/downloaded_packages/ /tmp/*.rds && \ 54 | rm -rf /var/lib/apt/lists/* 55 | -------------------------------------------------------------------------------- /inst/docker_stable/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rocker/r-devel 2 | MAINTAINER Neal Fultz 3 | 4 | ## Install grpc and protobuf from deb 5 | RUN apt-get update && apt-get install -y \ 6 | r-cran-rprotobuf r-cran-testthat r-cran-rcpp \ 7 | libgrpc6 libgrpc-dev libgrpc++-dev && \ 8 | apt-get clean && rm -rf /var/lib/apt/lists/ 9 | 10 | ## install R packages 11 | ## processx - suggested package for unit testing demo(helloclient) 12 | ## remotes - required to installGithub 13 | RUN install.r docopt processx remotes futile.logger 14 | RUN installGithub.r -d FALSE nfultz/grpc && rm -rf /tmp/* 15 | -------------------------------------------------------------------------------- /inst/examples/health_check.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package grpc.health.v1; 4 | 5 | service Health { 6 | rpc Check(HealthCheckRequest) returns (HealthCheckResponse); 7 | } 8 | 9 | message HealthCheckRequest { 10 | string service = 1; 11 | } 12 | 13 | message HealthCheckResponse { 14 | enum ServingStatus { 15 | UNKNOWN = 0; 16 | SERVING = 1; 17 | NOT_SERVING = 2; 18 | } 19 | ServingStatus status = 1; 20 | } 21 | -------------------------------------------------------------------------------- /inst/examples/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | syntax = "proto3"; 31 | 32 | option java_multiple_files = true; 33 | option java_package = "io.grpc.examples.helloworld"; 34 | option java_outer_classname = "HelloWorldProto"; 35 | option objc_class_prefix = "HLW"; 36 | 37 | package helloworld; 38 | 39 | // The greeting service definition. 40 | service Greeter { 41 | // Sends a hello 42 | rpc SayHello (GreetingRequest) returns (GreetingReply) {} 43 | 44 | // Sends a thank you 45 | rpc SayThanks (GreetingRequest) returns (GreetingReply); 46 | 47 | // Sends a farewell 48 | rpc SayBye (GreetingRequest) returns (GreetingReply); 49 | } 50 | 51 | // The request message containing the user's name. 52 | message GreetingRequest { 53 | string name = 1; 54 | } 55 | 56 | // The response message containing the greetings 57 | message GreetingReply { 58 | string message = 1; 59 | } 60 | -------------------------------------------------------------------------------- /inst/examples/iris_classifier.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_multiple_files = true; 4 | option java_package = "io.grpc.examples.iris"; 5 | option java_outer_classname = "IrisProto"; 6 | option objc_class_prefix = "HLW"; 7 | 8 | package iris; 9 | 10 | service Classifier { 11 | rpc Classify (Features) returns (Class) {} 12 | } 13 | 14 | message Features { 15 | float sepal_length = 1; 16 | float sepal_width = 2; 17 | float petal_length = 3; 18 | float petal_width = 4; 19 | } 20 | 21 | message Class { 22 | Status status = 1; 23 | string species = 2; 24 | float probability = 3; 25 | } 26 | 27 | message Status { 28 | enum Code { 29 | OK = 0; 30 | ERROR = 1; 31 | } 32 | Code code = 1; 33 | string message = 2; 34 | } 35 | -------------------------------------------------------------------------------- /inst/tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(grpc) 3 | 4 | test_check('grpc') 5 | -------------------------------------------------------------------------------- /inst/tests/testthat/helloworld.out: -------------------------------------------------------------------------------- 1 | 2 | 3 | demo(helloclient) 4 | ---- ~~~~~~~~~~~ 5 | 6 | > #' Example gRPC client 7 | > #' 8 | > #' Sends a message with a name and returns a message greeting the name. 9 | > #' @references \url{https://github.com/grpc/grpc/tree/master/examples/cpp/helloworld} 10 | > 11 | > library(grpc) 12 | 13 | > spec <- system.file('examples/helloworld.proto', package = 'grpc') 14 | 15 | > impl <- read_services(spec) 16 | 17 | > client <- grpc_client(impl, "localhost:50051") 18 | 19 | > for(who in c("Neal", "Gergely")){ 20 | + who <- client$SayHello$build(name=who) 21 | + 22 | + hello <- client$SayHello$call(who) 23 | + 24 | + print(hello) 25 | + print(as.list(hello)) 26 | + } 27 | message of type 'helloworld.HelloReply' with 1 field set 28 | $message 29 | [1] "Hello, Neal" 30 | 31 | message of type 'helloworld.HelloReply' with 1 field set 32 | $message 33 | [1] "Hello, Gergely" 34 | 35 | -------------------------------------------------------------------------------- /inst/tests/testthat/test-helloworld.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(methods) 3 | library(grpc) 4 | library(processx) 5 | 6 | context('helloworld example') 7 | 8 | ## start a server in the background 9 | p <- process$new(commandline = "R -e \"demo('helloserver', 'grpc', ask = FALSE)\"", stdout = "|", stderr = "|") 10 | 11 | test_that('server started', { 12 | expect_true(p$is_alive()) 13 | }) 14 | 15 | ## wait for the server to start 16 | for (i in 1:11) { 17 | Sys.sleep(1) 18 | if (any(grepl('RUNNING', p$read_output_lines(), fixed = TRUE))) { 19 | break() 20 | } 21 | } 22 | 23 | test_that('server running', { 24 | expect_lte(i, 10) 25 | expect_true(p$is_alive()) 26 | }) 27 | 28 | test_that('using client', { 29 | expect_output_file(demo('helloclient', 'grpc', ask = FALSE), 'helloworld.out', update = TRUE) 30 | }) 31 | 32 | ## kill server 33 | p$kill() 34 | -------------------------------------------------------------------------------- /install: -------------------------------------------------------------------------------- 1 | export GRPC_INSTALL_DIR=$HOME/.local 2 | mkdir -p $GRPC_INSTALL_DIR 3 | export PATH="$GRPC_INSTALL_DIR/bin:$PATH" 4 | 5 | sudo apt install -y cmake 6 | sudo apt install -y build-essential autoconf libtool pkg-config 7 | 8 | LATEST_VER=$(curl -L "https://api.github.com/repos/grpc/grpc/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")') 9 | git clone --recurse-submodules -b $LATEST_VER https://github.com/grpc/grpc grpc_base 10 | 11 | cd grpc_base 12 | mkdir -p cmake/build 13 | pushd cmake/build 14 | cmake -DgRPC_INSTALL=ON \ 15 | -DgRPC_BUILD_TESTS=OFF \ 16 | -DCMAKE_INSTALL_PREFIX=$GRPC_INSTALL_DIR \ 17 | ../.. 18 | make -j4 19 | sudo make install 20 | popd 21 | 22 | mkdir -p third_party/abseil-cpp/cmake/build 23 | pushd third_party/abseil-cpp/cmake/build 24 | cmake -DCMAKE_INSTALL_PREFIX=$GRPC_INSTALL_DIR \ 25 | -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \ 26 | ../.. 27 | make -j4 28 | sudo make install 29 | popd 30 | -------------------------------------------------------------------------------- /man/grpc_client.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/client.R 3 | \name{grpc_client} 4 | \alias{grpc_client} 5 | \title{Build a client handle} 6 | \usage{ 7 | grpc_client(impl, channel) 8 | } 9 | \arguments{ 10 | \item{channel}{what to connect to} 11 | 12 | \item{stubs}{the stub of the service} 13 | } 14 | \value{ 15 | client handle 16 | } 17 | \description{ 18 | Build a client handle 19 | } 20 | -------------------------------------------------------------------------------- /man/grpc_default_hooks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{grpc_default_hooks} 4 | \alias{grpc_default_hooks} 5 | \title{Provides a list of logging functions to be passed to gRPC server} 6 | \usage{ 7 | grpc_default_hooks() 8 | } 9 | \value{ 10 | \code{list} 11 | } 12 | \description{ 13 | You may want to override this with custom functions, eg registering the gRPC service in Hashicorp's consul.io or similar after \code{server_start} etc. 14 | } 15 | -------------------------------------------------------------------------------- /man/grpc_version.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R 3 | \name{grpc_version} 4 | \alias{grpc_version} 5 | \title{Check grpc version} 6 | \usage{ 7 | grpc_version() 8 | } 9 | \value{ 10 | version string and what g stands for 11 | } 12 | \description{ 13 | Check grpc version 14 | } 15 | -------------------------------------------------------------------------------- /man/hello.Rd: -------------------------------------------------------------------------------- 1 | \name{hello} 2 | \alias{hello} 3 | \title{Hello, World!} 4 | \usage{ 5 | hello() 6 | } 7 | \description{ 8 | Prints 'Hello, world!'. 9 | } 10 | \examples{ 11 | hello() 12 | } 13 | -------------------------------------------------------------------------------- /man/newResponse.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/server.R 3 | \name{newResponse} 4 | \alias{newResponse} 5 | \title{Construct a new ProtoBuf of ResponseType} 6 | \usage{ 7 | newResponse(..., WFUN = sys.function(-1)) 8 | } 9 | \arguments{ 10 | \item{...}{threaded through to the ProtoBuf constructor} 11 | 12 | \item{WFUN}{A GRPC service method with RequestType and ResponseType attributes} 13 | } 14 | \value{ 15 | 16 | } 17 | \description{ 18 | Construct a new ProtoBuf of ResponseType 19 | } 20 | -------------------------------------------------------------------------------- /man/rcpp_hello.Rd: -------------------------------------------------------------------------------- 1 | \name{rcpp_hello} 2 | \alias{rcpp_hello} 3 | \title{Hello, Rcpp!} 4 | \usage{ 5 | rcpp_hello() 6 | } 7 | \description{ 8 | Returns an \R \code{list} containing the character vector 9 | \code{c("foo", "bar")} and the numeric vector \code{c(0, 1)}. 10 | } 11 | \examples{ 12 | rcpp_hello() 13 | } 14 | -------------------------------------------------------------------------------- /man/read_services.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/parser.R 3 | \name{read_services} 4 | \alias{read_services} 5 | \title{Create stub object from protobuf spec} 6 | \usage{ 7 | read_services(file) 8 | } 9 | \arguments{ 10 | \item{file}{the spec file} 11 | } 12 | \value{ 13 | a stub data structure 14 | } 15 | \description{ 16 | Create stub object from protobuf spec 17 | } 18 | -------------------------------------------------------------------------------- /man/start_server.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/server.R 3 | \name{start_server} 4 | \alias{start_server} 5 | \title{Start a gRPC server} 6 | \usage{ 7 | start_server(impl, channel, hooks = grpc_default_hooks()) 8 | } 9 | \arguments{ 10 | \item{impl}{an implementation of a proto service} 11 | 12 | \item{channel}{a channel string in 'host:port' format} 13 | 14 | \item{hooks}{list of R function(s) with \code{params} argument as a list to be run at different parts of the C++ calls. Supported hooks: \code{server_create}, \code{queue_create}, \code{bind} (when \code{params$port} becomes available), \code{server_start}, \code{run}, \code{shutdown}, \code{stopped}, \code{exit}} 15 | } 16 | \value{ 17 | none 18 | } 19 | \description{ 20 | Start a gRPC server 21 | } 22 | \seealso{ 23 | \code{\link{grpc_default_hooks}} 24 | } 25 | -------------------------------------------------------------------------------- /src/Makevars: -------------------------------------------------------------------------------- 1 | CXX_STD = CXX11 2 | PKG_LIBS = `pkg-config --libs grpc` 3 | PKG_CXXFLAGS = `pkg-config --cflags grpc` 4 | 5 | -------------------------------------------------------------------------------- /src/RcppExports.cpp: -------------------------------------------------------------------------------- 1 | // Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | #include 5 | 6 | using namespace Rcpp; 7 | 8 | // fetch 9 | RawVector fetch(CharacterVector server, CharacterVector method, RawVector requestArg, CharacterVector metadata); 10 | RcppExport SEXP _grpc_fetch(SEXP serverSEXP, SEXP methodSEXP, SEXP requestArgSEXP, SEXP metadataSEXP) { 11 | BEGIN_RCPP 12 | Rcpp::RObject rcpp_result_gen; 13 | Rcpp::RNGScope rcpp_rngScope_gen; 14 | Rcpp::traits::input_parameter< CharacterVector >::type server(serverSEXP); 15 | Rcpp::traits::input_parameter< CharacterVector >::type method(methodSEXP); 16 | Rcpp::traits::input_parameter< RawVector >::type requestArg(requestArgSEXP); 17 | Rcpp::traits::input_parameter< CharacterVector >::type metadata(metadataSEXP); 18 | rcpp_result_gen = Rcpp::wrap(fetch(server, method, requestArg, metadata)); 19 | return rcpp_result_gen; 20 | END_RCPP 21 | } 22 | // grpc_version 23 | CharacterVector grpc_version(); 24 | RcppExport SEXP _grpc_grpc_version() { 25 | BEGIN_RCPP 26 | Rcpp::RObject rcpp_result_gen; 27 | Rcpp::RNGScope rcpp_rngScope_gen; 28 | rcpp_result_gen = Rcpp::wrap(grpc_version()); 29 | return rcpp_result_gen; 30 | END_RCPP 31 | } 32 | // run 33 | List run(List target, CharacterVector hoststring, List hooks); 34 | RcppExport SEXP _grpc_run(SEXP targetSEXP, SEXP hoststringSEXP, SEXP hooksSEXP) { 35 | BEGIN_RCPP 36 | Rcpp::RObject rcpp_result_gen; 37 | Rcpp::RNGScope rcpp_rngScope_gen; 38 | Rcpp::traits::input_parameter< List >::type target(targetSEXP); 39 | Rcpp::traits::input_parameter< CharacterVector >::type hoststring(hoststringSEXP); 40 | Rcpp::traits::input_parameter< List >::type hooks(hooksSEXP); 41 | rcpp_result_gen = Rcpp::wrap(run(target, hoststring, hooks)); 42 | return rcpp_result_gen; 43 | END_RCPP 44 | } 45 | -------------------------------------------------------------------------------- /src/client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | 8 | using namespace Rcpp; 9 | 10 | static void *tag(intptr_t i) { return (void *)i; } 11 | 12 | RawVector sliceToRaw2(grpc_slice slice){ 13 | 14 | int n = GRPC_SLICE_LENGTH(slice); 15 | 16 | char* data = grpc_slice_to_c_string(slice); 17 | 18 | RGRPC_LOG("Slice2Raw:\nn: " << n << "\nData: " << data); 19 | 20 | RawVector out(n); 21 | for(int i = 0; i < n; i++) 22 | out[i] = (unsigned char) data[i]; 23 | // std::copy(data, data+n, out.begin()); 24 | 25 | return out; 26 | } 27 | 28 | 29 | 30 | // [[Rcpp::export]] 31 | RawVector fetch(CharacterVector server, CharacterVector method, RawVector requestArg, CharacterVector metadata) { 32 | 33 | // gpr_timespec deadline = five_seconds_from_now(); 34 | 35 | 36 | //setup 37 | grpc_call *c; 38 | 39 | grpc_completion_queue *cq = grpc_completion_queue_create_for_next(NULL); 40 | 41 | const grpc_slice server_slice = grpc_slice_from_copied_string(server[0]); 42 | const grpc_slice method_slice = grpc_slice_from_copied_string(method[0]); 43 | 44 | SEXP raw_ = requestArg; 45 | int len = requestArg.length(); 46 | grpc_slice request_payload_slice = grpc_slice_from_copied_buffer((char*) RAW(raw_), len); 47 | 48 | 49 | const grpc_slice *sp = &server_slice; 50 | 51 | grpc_channel *channel = grpc_insecure_channel_create(server[0], NULL, RESERVED); 52 | 53 | gpr_timespec deadline = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(5000, GPR_TIMESPAN)); 54 | 55 | RGRPC_LOG("Create Call"); 56 | c = grpc_channel_create_call( 57 | channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, 58 | method_slice, //grpc_slice_from_static_string("/foo"), 59 | sp, 60 | deadline, 61 | NULL); 62 | 63 | 64 | RGRPC_LOG("Making ops"); 65 | // perform request 66 | grpc_op ops[6]; 67 | grpc_op *op; 68 | 69 | int metadata_length = metadata.length() / 2; 70 | grpc_metadata meta_c[metadata_length]; 71 | 72 | for(int i = 0; i < metadata_length; i++) { 73 | meta_c[i] = {grpc_slice_from_static_string(metadata[i * 2]), 74 | grpc_slice_from_static_string(metadata[i * 2 + 1]), 75 | 0, 76 | {{nullptr, nullptr, nullptr, nullptr}}}; 77 | } 78 | 79 | grpc_metadata_array initial_metadata_recv; 80 | grpc_metadata_array trailing_metadata_recv; 81 | grpc_metadata_array request_metadata_recv; 82 | grpc_byte_buffer *response_payload_recv; 83 | grpc_call_details call_details; 84 | grpc_status_code status; 85 | grpc_call_error error; 86 | grpc_event event; 87 | grpc_slice details; 88 | int was_cancelled = 2; 89 | 90 | 91 | 92 | grpc_metadata_array_init(&initial_metadata_recv); 93 | grpc_metadata_array_init(&trailing_metadata_recv); 94 | grpc_metadata_array_init(&request_metadata_recv); 95 | grpc_call_details_init(&call_details); 96 | 97 | 98 | grpc_byte_buffer *request_payload = 99 | grpc_raw_byte_buffer_create(&request_payload_slice, 1); 100 | 101 | memset(ops, 0, sizeof(ops)); 102 | op = ops; 103 | op->op = GRPC_OP_SEND_INITIAL_METADATA; 104 | op->data.send_initial_metadata.count = metadata_length; 105 | if (metadata_length > 0) { 106 | op->data.send_initial_metadata.metadata = meta_c; 107 | } 108 | else { 109 | op->data.send_initial_metadata.maybe_compression_level.is_set = false; 110 | } 111 | op->flags = 0; 112 | op->reserved = NULL; 113 | op++; 114 | op->op = GRPC_OP_SEND_MESSAGE; 115 | op->data.send_message.send_message = request_payload; 116 | op->flags = 0; 117 | op->reserved = NULL; 118 | op++; 119 | op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; 120 | op->flags = 0; 121 | op->reserved = NULL; 122 | op++; 123 | op->op = GRPC_OP_RECV_INITIAL_METADATA; 124 | op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; 125 | op->flags = 0; 126 | op->reserved = NULL; 127 | op++; 128 | op->op = GRPC_OP_RECV_MESSAGE; 129 | op->data.recv_message.recv_message = &response_payload_recv; 130 | op->flags = 0; 131 | op->reserved = NULL; 132 | op++; 133 | op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; 134 | op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; 135 | op->data.recv_status_on_client.status = &status; 136 | op->data.recv_status_on_client.status_details = &details; 137 | op->flags = 0; 138 | op->reserved = NULL; 139 | op++; 140 | 141 | RGRPC_LOG("Starting batch..."); 142 | error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL); 143 | if (error != GRPC_CALL_OK) { // 0 144 | stop("gRPC c++ call start failed"); 145 | } 146 | event = grpc_completion_queue_next(cq, deadline, RESERVED); //actually does the work 147 | if (event.type == GRPC_QUEUE_TIMEOUT) { // 1 148 | stop("gRPC c++ call timeout"); 149 | } 150 | if (event.success == 0) { 151 | stop("gRPC c++ call error"); 152 | } 153 | if (!response_payload_recv) { 154 | stop("No response from the gRPC server"); 155 | } 156 | 157 | // client_batch[grpc.opType.SEND_INITIAL_METADATA] = 158 | // metadata._getCoreRepresentation(); 159 | // client_batch[grpc.opType.SEND_MESSAGE] = message; 160 | // client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; 161 | // client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; 162 | // client_batch[grpc.opType.RECV_MESSAGE] = true; 163 | // client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; 164 | 165 | 166 | 167 | 168 | // Call Header, followed by optional Initial-Metadata, followed by zero or more Payload Messages 169 | RGRPC_LOG("Read response Slice"); 170 | grpc_byte_buffer_reader bbr; 171 | 172 | try { 173 | grpc_byte_buffer_reader_init(&bbr, response_payload_recv); 174 | } 175 | catch (std::exception& e) { 176 | Rcout << "Segfault" << e.what() << std::endl; 177 | stop("Segfault in C++"); 178 | } 179 | 180 | grpc_slice response_payload_slice = grpc_byte_buffer_reader_readall(&bbr); 181 | RawVector response_payload_raw = sliceToRaw2(response_payload_slice); 182 | 183 | RGRPC_LOG("Cleanup"); 184 | 185 | // teardown 186 | grpc_completion_queue_shutdown(cq); 187 | //drain_cq(cq); 188 | grpc_completion_queue_destroy(cq); 189 | cq = NULL; 190 | grpc_channel_destroy(channel); 191 | channel = NULL; 192 | //gpr_free(cf->server_uri); 193 | 194 | 195 | grpc_slice_unref(details); 196 | grpc_metadata_array_destroy(&initial_metadata_recv); 197 | grpc_metadata_array_destroy(&trailing_metadata_recv); 198 | grpc_metadata_array_destroy(&request_metadata_recv); 199 | grpc_call_details_destroy(&call_details); 200 | 201 | // grpc_call_unref(c); 202 | // grpc_call_unref(s); 203 | // 204 | // cq_verifier_destroy(cqv); 205 | 206 | grpc_byte_buffer_destroy(request_payload); 207 | grpc_byte_buffer_destroy(response_payload_recv); 208 | 209 | 210 | return response_payload_raw; 211 | 212 | } -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef RGRPC_H 2 | #define RGRPC_H 3 | 4 | /* uncomment for debugging */ 5 | // #define RGRPC_DEBUG 6 | 7 | #ifdef RGRPC_DEBUG 8 | #define RGRPC_LOG(x) Rcout << x << std::endl; 9 | #else 10 | #define RGRPC_LOG(x) 11 | #endif 12 | 13 | #define RESERVED NULL 14 | 15 | #endif 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ext/byte_buffer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include "grpc/byte_buffer_reader.h" 24 | #include "grpc/grpc.h" 25 | #include "grpc/slice.h" 26 | 27 | #include "byte_buffer.h" 28 | #include "slice.h" 29 | 30 | namespace grpc { 31 | namespace node { 32 | 33 | using Nan::Callback; 34 | using Nan::MaybeLocal; 35 | 36 | using v8::Function; 37 | using v8::Local; 38 | using v8::Object; 39 | using v8::Number; 40 | using v8::Value; 41 | 42 | grpc_byte_buffer *BufferToByteBuffer(Local buffer) { 43 | Nan::HandleScope scope; 44 | grpc_slice slice = CreateSliceFromBuffer(buffer); 45 | grpc_byte_buffer *byte_buffer(grpc_raw_byte_buffer_create(&slice, 1)); 46 | grpc_slice_unref(slice); 47 | return byte_buffer; 48 | } 49 | 50 | namespace { 51 | void delete_buffer(char *data, void *hint) { 52 | grpc_slice *slice = static_cast(hint); 53 | grpc_slice_unref(*slice); 54 | delete slice; 55 | } 56 | } 57 | 58 | Local ByteBufferToBuffer(grpc_byte_buffer *buffer) { 59 | Nan::EscapableHandleScope scope; 60 | if (buffer == NULL) { 61 | return scope.Escape(Nan::Null()); 62 | } 63 | grpc_byte_buffer_reader reader; 64 | if (!grpc_byte_buffer_reader_init(&reader, buffer)) { 65 | Nan::ThrowError("Error initializing byte buffer reader."); 66 | return scope.Escape(Nan::Undefined()); 67 | } 68 | grpc_slice *slice = new grpc_slice; 69 | *slice = grpc_byte_buffer_reader_readall(&reader); 70 | grpc_byte_buffer_reader_destroy(&reader); 71 | char *result = reinterpret_cast(GRPC_SLICE_START_PTR(*slice)); 72 | size_t length = GRPC_SLICE_LENGTH(*slice); 73 | Local buf = 74 | Nan::NewBuffer(result, length, delete_buffer, slice).ToLocalChecked(); 75 | return scope.Escape(buf); 76 | } 77 | 78 | } // namespace node 79 | } // namespace grpc 80 | -------------------------------------------------------------------------------- /src/ext/byte_buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef NET_GRPC_NODE_BYTE_BUFFER_H_ 20 | #define NET_GRPC_NODE_BYTE_BUFFER_H_ 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include "grpc/grpc.h" 27 | 28 | namespace grpc { 29 | namespace node { 30 | 31 | /* Convert a Node.js Buffer to grpc_byte_buffer. Requires that 32 | ::node::Buffer::HasInstance(buffer) */ 33 | grpc_byte_buffer *BufferToByteBuffer(v8::Local buffer); 34 | 35 | /* Convert a grpc_byte_buffer to a Node.js Buffer */ 36 | v8::Local ByteBufferToBuffer(grpc_byte_buffer *buffer); 37 | 38 | } // namespace node 39 | } // namespace grpc 40 | 41 | #endif // NET_GRPC_NODE_BYTE_BUFFER_H_ 42 | -------------------------------------------------------------------------------- /src/ext/call.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include "byte_buffer.h" 26 | #include "call.h" 27 | #include "call_credentials.h" 28 | #include "channel.h" 29 | #include "completion_queue.h" 30 | #include "grpc/grpc.h" 31 | #include "grpc/grpc_security.h" 32 | #include "grpc/support/alloc.h" 33 | #include "grpc/support/log.h" 34 | #include "grpc/support/time.h" 35 | #include "slice.h" 36 | #include "timeval.h" 37 | 38 | using std::unique_ptr; 39 | using std::shared_ptr; 40 | using std::vector; 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::Array; 55 | using v8::Boolean; 56 | using v8::Exception; 57 | using v8::External; 58 | using v8::Function; 59 | using v8::FunctionTemplate; 60 | using v8::Integer; 61 | using v8::Local; 62 | using v8::Number; 63 | using v8::Object; 64 | using v8::ObjectTemplate; 65 | using v8::Uint32; 66 | using v8::String; 67 | using v8::Value; 68 | 69 | Callback *Call::constructor; 70 | Persistent Call::fun_tpl; 71 | 72 | /** 73 | * Helper function for throwing errors with a grpc_call_error value. 74 | * Modified from the answer by Gus Goose to 75 | * http://stackoverflow.com/questions/31794200. 76 | */ 77 | Local nanErrorWithCode(const char *msg, grpc_call_error code) { 78 | EscapableHandleScope scope; 79 | Local err = Nan::Error(msg).As(); 80 | Nan::Set(err, Nan::New("code").ToLocalChecked(), Nan::New(code)); 81 | return scope.Escape(err); 82 | } 83 | 84 | bool CreateMetadataArray(Local metadata, grpc_metadata_array *array) { 85 | HandleScope scope; 86 | Local keys = Nan::GetOwnPropertyNames(metadata).ToLocalChecked(); 87 | for (unsigned int i = 0; i < keys->Length(); i++) { 88 | Local current_key = 89 | Nan::To(Nan::Get(keys, i).ToLocalChecked()).ToLocalChecked(); 90 | Local value_array = Nan::Get(metadata, current_key).ToLocalChecked(); 91 | if (!value_array->IsArray()) { 92 | return false; 93 | } 94 | array->capacity += Local::Cast(value_array)->Length(); 95 | } 96 | array->metadata = reinterpret_cast( 97 | gpr_zalloc(array->capacity * sizeof(grpc_metadata))); 98 | for (unsigned int i = 0; i < keys->Length(); i++) { 99 | Local current_key(Nan::To(keys->Get(i)).ToLocalChecked()); 100 | Local values = 101 | Local::Cast(Nan::Get(metadata, current_key).ToLocalChecked()); 102 | grpc_slice key_slice = CreateSliceFromString(current_key); 103 | grpc_slice key_intern_slice = grpc_slice_intern(key_slice); 104 | grpc_slice_unref(key_slice); 105 | for (unsigned int j = 0; j < values->Length(); j++) { 106 | Local value = Nan::Get(values, j).ToLocalChecked(); 107 | grpc_metadata *current = &array->metadata[array->count]; 108 | current->key = key_intern_slice; 109 | // Only allow binary headers for "-bin" keys 110 | if (grpc_is_binary_header(key_intern_slice)) { 111 | if (::node::Buffer::HasInstance(value)) { 112 | current->value = CreateSliceFromBuffer(value); 113 | } else { 114 | return false; 115 | } 116 | } else { 117 | if (value->IsString()) { 118 | Local string_value = Nan::To(value).ToLocalChecked(); 119 | current->value = CreateSliceFromString(string_value); 120 | } else { 121 | return false; 122 | } 123 | } 124 | array->count += 1; 125 | } 126 | } 127 | return true; 128 | } 129 | 130 | void DestroyMetadataArray(grpc_metadata_array *array) { 131 | for (size_t i = 0; i < array->count; i++) { 132 | // Don't unref keys because they are interned 133 | grpc_slice_unref(array->metadata[i].value); 134 | } 135 | grpc_metadata_array_destroy(array); 136 | } 137 | 138 | Local ParseMetadata(const grpc_metadata_array *metadata_array) { 139 | EscapableHandleScope scope; 140 | grpc_metadata *metadata_elements = metadata_array->metadata; 141 | size_t length = metadata_array->count; 142 | Local metadata_object = Nan::New(); 143 | for (unsigned int i = 0; i < length; i++) { 144 | grpc_metadata *elem = &metadata_elements[i]; 145 | // TODO(murgatroid99): Use zero-copy string construction instead 146 | Local key_string = CopyStringFromSlice(elem->key); 147 | Local array; 148 | MaybeLocal maybe_array = Nan::Get(metadata_object, key_string); 149 | if (maybe_array.IsEmpty() || !maybe_array.ToLocalChecked()->IsArray()) { 150 | array = Nan::New(0); 151 | Nan::Set(metadata_object, key_string, array); 152 | } else { 153 | array = Local::Cast(maybe_array.ToLocalChecked()); 154 | } 155 | if (grpc_is_binary_header(elem->key)) { 156 | Nan::Set(array, array->Length(), CreateBufferFromSlice(elem->value)); 157 | } else { 158 | // TODO(murgatroid99): Use zero-copy string construction instead 159 | Nan::Set(array, array->Length(), CopyStringFromSlice(elem->value)); 160 | } 161 | } 162 | return scope.Escape(metadata_object); 163 | } 164 | 165 | Local Op::GetOpType() const { 166 | EscapableHandleScope scope; 167 | return scope.Escape(Nan::New(GetTypeString()).ToLocalChecked()); 168 | } 169 | 170 | Op::~Op() {} 171 | 172 | class SendMetadataOp : public Op { 173 | public: 174 | SendMetadataOp() { grpc_metadata_array_init(&send_metadata); } 175 | ~SendMetadataOp() { DestroyMetadataArray(&send_metadata); } 176 | Local GetNodeValue() const { 177 | EscapableHandleScope scope; 178 | return scope.Escape(Nan::True()); 179 | } 180 | bool ParseOp(Local value, grpc_op *out) { 181 | if (!value->IsObject()) { 182 | return false; 183 | } 184 | MaybeLocal maybe_metadata = Nan::To(value); 185 | if (maybe_metadata.IsEmpty()) { 186 | return false; 187 | } 188 | if (!CreateMetadataArray(maybe_metadata.ToLocalChecked(), &send_metadata)) { 189 | return false; 190 | } 191 | out->data.send_initial_metadata.count = send_metadata.count; 192 | out->data.send_initial_metadata.metadata = send_metadata.metadata; 193 | return true; 194 | } 195 | bool IsFinalOp() { return false; } 196 | void OnComplete(bool success) {} 197 | 198 | protected: 199 | std::string GetTypeString() const { return "send_metadata"; } 200 | 201 | private: 202 | grpc_metadata_array send_metadata; 203 | }; 204 | 205 | class SendMessageOp : public Op { 206 | public: 207 | SendMessageOp() { send_message = NULL; } 208 | ~SendMessageOp() { 209 | if (send_message != NULL) { 210 | grpc_byte_buffer_destroy(send_message); 211 | } 212 | } 213 | Local GetNodeValue() const { 214 | EscapableHandleScope scope; 215 | return scope.Escape(Nan::True()); 216 | } 217 | bool ParseOp(Local value, grpc_op *out) { 218 | if (!::node::Buffer::HasInstance(value)) { 219 | return false; 220 | } 221 | Local object_value = Nan::To(value).ToLocalChecked(); 222 | MaybeLocal maybe_flag_value = 223 | Nan::Get(object_value, Nan::New("grpcWriteFlags").ToLocalChecked()); 224 | if (!maybe_flag_value.IsEmpty()) { 225 | Local flag_value = maybe_flag_value.ToLocalChecked(); 226 | if (flag_value->IsUint32()) { 227 | Maybe maybe_flag = Nan::To(flag_value); 228 | out->flags = maybe_flag.FromMaybe(0) & GRPC_WRITE_USED_MASK; 229 | } 230 | } 231 | send_message = BufferToByteBuffer(value); 232 | out->data.send_message.send_message = send_message; 233 | return true; 234 | } 235 | 236 | bool IsFinalOp() { return false; } 237 | void OnComplete(bool success) {} 238 | 239 | protected: 240 | std::string GetTypeString() const { return "send_message"; } 241 | 242 | private: 243 | grpc_byte_buffer *send_message; 244 | }; 245 | 246 | class SendClientCloseOp : public Op { 247 | public: 248 | Local GetNodeValue() const { 249 | EscapableHandleScope scope; 250 | return scope.Escape(Nan::True()); 251 | } 252 | 253 | bool ParseOp(Local value, grpc_op *out) { return true; } 254 | bool IsFinalOp() { return false; } 255 | void OnComplete(bool success) {} 256 | 257 | protected: 258 | std::string GetTypeString() const { return "client_close"; } 259 | }; 260 | 261 | class SendServerStatusOp : public Op { 262 | public: 263 | SendServerStatusOp() { 264 | details = grpc_empty_slice(); 265 | grpc_metadata_array_init(&status_metadata); 266 | } 267 | ~SendServerStatusOp() { 268 | grpc_slice_unref(details); 269 | DestroyMetadataArray(&status_metadata); 270 | } 271 | Local GetNodeValue() const { 272 | EscapableHandleScope scope; 273 | return scope.Escape(Nan::True()); 274 | } 275 | bool ParseOp(Local value, grpc_op *out) { 276 | if (!value->IsObject()) { 277 | return false; 278 | } 279 | Local server_status = Nan::To(value).ToLocalChecked(); 280 | MaybeLocal maybe_metadata = 281 | Nan::Get(server_status, Nan::New("metadata").ToLocalChecked()); 282 | if (maybe_metadata.IsEmpty()) { 283 | return false; 284 | } 285 | if (!maybe_metadata.ToLocalChecked()->IsObject()) { 286 | return false; 287 | } 288 | Local metadata = 289 | Nan::To(maybe_metadata.ToLocalChecked()).ToLocalChecked(); 290 | MaybeLocal maybe_code = 291 | Nan::Get(server_status, Nan::New("code").ToLocalChecked()); 292 | if (maybe_code.IsEmpty()) { 293 | return false; 294 | } 295 | if (!maybe_code.ToLocalChecked()->IsUint32()) { 296 | return false; 297 | } 298 | uint32_t code = Nan::To(maybe_code.ToLocalChecked()).FromJust(); 299 | MaybeLocal maybe_details = 300 | Nan::Get(server_status, Nan::New("details").ToLocalChecked()); 301 | if (maybe_details.IsEmpty()) { 302 | return false; 303 | } 304 | if (!maybe_details.ToLocalChecked()->IsString()) { 305 | return false; 306 | } 307 | Local details = 308 | Nan::To(maybe_details.ToLocalChecked()).ToLocalChecked(); 309 | if (!CreateMetadataArray(metadata, &status_metadata)) { 310 | return false; 311 | } 312 | out->data.send_status_from_server.trailing_metadata_count = 313 | status_metadata.count; 314 | out->data.send_status_from_server.trailing_metadata = 315 | status_metadata.metadata; 316 | out->data.send_status_from_server.status = 317 | static_cast(code); 318 | this->details = CreateSliceFromString(details); 319 | out->data.send_status_from_server.status_details = &this->details; 320 | return true; 321 | } 322 | bool IsFinalOp() { return true; } 323 | void OnComplete(bool success) {} 324 | 325 | protected: 326 | std::string GetTypeString() const { return "send_status"; } 327 | 328 | private: 329 | grpc_slice details; 330 | grpc_metadata_array status_metadata; 331 | }; 332 | 333 | class GetMetadataOp : public Op { 334 | public: 335 | GetMetadataOp() { grpc_metadata_array_init(&recv_metadata); } 336 | 337 | ~GetMetadataOp() { grpc_metadata_array_destroy(&recv_metadata); } 338 | 339 | Local GetNodeValue() const { 340 | EscapableHandleScope scope; 341 | return scope.Escape(ParseMetadata(&recv_metadata)); 342 | } 343 | 344 | bool ParseOp(Local value, grpc_op *out) { 345 | out->data.recv_initial_metadata.recv_initial_metadata = &recv_metadata; 346 | return true; 347 | } 348 | bool IsFinalOp() { return false; } 349 | void OnComplete(bool success) {} 350 | 351 | protected: 352 | std::string GetTypeString() const { return "metadata"; } 353 | 354 | private: 355 | grpc_metadata_array recv_metadata; 356 | }; 357 | 358 | class ReadMessageOp : public Op { 359 | public: 360 | ReadMessageOp() { recv_message = NULL; } 361 | ~ReadMessageOp() { 362 | if (recv_message != NULL) { 363 | grpc_byte_buffer_destroy(recv_message); 364 | } 365 | } 366 | Local GetNodeValue() const { 367 | EscapableHandleScope scope; 368 | return scope.Escape(ByteBufferToBuffer(recv_message)); 369 | } 370 | 371 | bool ParseOp(Local value, grpc_op *out) { 372 | out->data.recv_message.recv_message = &recv_message; 373 | return true; 374 | } 375 | bool IsFinalOp() { return false; } 376 | void OnComplete(bool success) {} 377 | 378 | protected: 379 | std::string GetTypeString() const { return "read"; } 380 | 381 | private: 382 | grpc_byte_buffer *recv_message; 383 | }; 384 | 385 | class ClientStatusOp : public Op { 386 | public: 387 | ClientStatusOp() { 388 | grpc_metadata_array_init(&metadata_array); 389 | status_details = grpc_empty_slice(); 390 | } 391 | 392 | ~ClientStatusOp() { 393 | grpc_metadata_array_destroy(&metadata_array); 394 | grpc_slice_unref(status_details); 395 | } 396 | 397 | bool ParseOp(Local value, grpc_op *out) { 398 | out->data.recv_status_on_client.trailing_metadata = &metadata_array; 399 | out->data.recv_status_on_client.status = &status; 400 | out->data.recv_status_on_client.status_details = &status_details; 401 | return true; 402 | } 403 | 404 | Local GetNodeValue() const { 405 | EscapableHandleScope scope; 406 | Local status_obj = Nan::New(); 407 | Nan::Set(status_obj, Nan::New("code").ToLocalChecked(), 408 | Nan::New(status)); 409 | Nan::Set(status_obj, Nan::New("details").ToLocalChecked(), 410 | CopyStringFromSlice(status_details)); 411 | Nan::Set(status_obj, Nan::New("metadata").ToLocalChecked(), 412 | ParseMetadata(&metadata_array)); 413 | return scope.Escape(status_obj); 414 | } 415 | bool IsFinalOp() { return true; } 416 | void OnComplete(bool success) {} 417 | 418 | protected: 419 | std::string GetTypeString() const { return "status"; } 420 | 421 | private: 422 | grpc_metadata_array metadata_array; 423 | grpc_status_code status; 424 | grpc_slice status_details; 425 | }; 426 | 427 | class ServerCloseResponseOp : public Op { 428 | public: 429 | Local GetNodeValue() const { 430 | EscapableHandleScope scope; 431 | return scope.Escape(Nan::New(cancelled)); 432 | } 433 | 434 | bool ParseOp(Local value, grpc_op *out) { 435 | out->data.recv_close_on_server.cancelled = &cancelled; 436 | return true; 437 | } 438 | bool IsFinalOp() { return false; } 439 | void OnComplete(bool success) {} 440 | 441 | protected: 442 | std::string GetTypeString() const { return "cancelled"; } 443 | 444 | private: 445 | int cancelled; 446 | }; 447 | 448 | tag::tag(Callback *callback, OpVec *ops, Call *call, Local call_value) 449 | : callback(callback), ops(ops), call(call) { 450 | HandleScope scope; 451 | call_persist.Reset(call_value); 452 | } 453 | 454 | tag::~tag() { 455 | delete callback; 456 | delete ops; 457 | } 458 | 459 | void CompleteTag(void *tag, const char *error_message) { 460 | HandleScope scope; 461 | struct tag *tag_struct = reinterpret_cast(tag); 462 | Callback *callback = tag_struct->callback; 463 | if (error_message == NULL) { 464 | Local tag_obj = Nan::New(); 465 | for (vector >::iterator it = tag_struct->ops->begin(); 466 | it != tag_struct->ops->end(); ++it) { 467 | Op *op_ptr = it->get(); 468 | Nan::Set(tag_obj, op_ptr->GetOpType(), op_ptr->GetNodeValue()); 469 | } 470 | Local argv[] = {Nan::Null(), tag_obj}; 471 | callback->Call(2, argv); 472 | } else { 473 | Local argv[] = {Nan::Error(error_message)}; 474 | callback->Call(1, argv); 475 | } 476 | bool success = (error_message == NULL); 477 | bool is_final_op = false; 478 | for (vector >::iterator it = tag_struct->ops->begin(); 479 | it != tag_struct->ops->end(); ++it) { 480 | Op *op_ptr = it->get(); 481 | op_ptr->OnComplete(success); 482 | if (op_ptr->IsFinalOp()) { 483 | is_final_op = true; 484 | } 485 | } 486 | if (tag_struct->call == NULL) { 487 | return; 488 | } 489 | tag_struct->call->CompleteBatch(is_final_op); 490 | } 491 | 492 | void DestroyTag(void *tag) { 493 | struct tag *tag_struct = reinterpret_cast(tag); 494 | delete tag_struct; 495 | } 496 | 497 | void Call::DestroyCall() { 498 | if (this->wrapped_call != NULL) { 499 | grpc_call_unref(this->wrapped_call); 500 | this->wrapped_call = NULL; 501 | } 502 | } 503 | 504 | Call::Call(grpc_call *call) 505 | : wrapped_call(call), pending_batches(0), has_final_op_completed(false) { 506 | peer = grpc_call_get_peer(call); 507 | } 508 | 509 | Call::~Call() { 510 | DestroyCall(); 511 | gpr_free(peer); 512 | } 513 | 514 | void Call::Init(Local exports) { 515 | HandleScope scope; 516 | Local tpl = Nan::New(New); 517 | tpl->SetClassName(Nan::New("Call").ToLocalChecked()); 518 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 519 | Nan::SetPrototypeMethod(tpl, "startBatch", StartBatch); 520 | Nan::SetPrototypeMethod(tpl, "cancel", Cancel); 521 | Nan::SetPrototypeMethod(tpl, "cancelWithStatus", CancelWithStatus); 522 | Nan::SetPrototypeMethod(tpl, "getPeer", GetPeer); 523 | Nan::SetPrototypeMethod(tpl, "setCredentials", SetCredentials); 524 | fun_tpl.Reset(tpl); 525 | Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); 526 | Nan::Set(exports, Nan::New("Call").ToLocalChecked(), ctr); 527 | constructor = new Callback(ctr); 528 | } 529 | 530 | bool Call::HasInstance(Local val) { 531 | HandleScope scope; 532 | return Nan::New(fun_tpl)->HasInstance(val); 533 | } 534 | 535 | Local Call::WrapStruct(grpc_call *call) { 536 | EscapableHandleScope scope; 537 | if (call == NULL) { 538 | return scope.Escape(Nan::Null()); 539 | } 540 | const int argc = 1; 541 | Local argv[argc] = { 542 | Nan::New(reinterpret_cast(call))}; 543 | MaybeLocal maybe_instance = 544 | Nan::NewInstance(constructor->GetFunction(), argc, argv); 545 | if (maybe_instance.IsEmpty()) { 546 | return scope.Escape(Nan::Null()); 547 | } else { 548 | return scope.Escape(maybe_instance.ToLocalChecked()); 549 | } 550 | } 551 | 552 | void Call::CompleteBatch(bool is_final_op) { 553 | if (is_final_op) { 554 | this->has_final_op_completed = true; 555 | } 556 | this->pending_batches--; 557 | if (this->has_final_op_completed && this->pending_batches == 0) { 558 | this->DestroyCall(); 559 | } 560 | } 561 | 562 | NAN_METHOD(Call::New) { 563 | /* Arguments: 564 | * 0: Channel to make the call on 565 | * 1: Method 566 | * 2: Deadline 567 | * 3: host 568 | * 4: parent Call 569 | * 5: propagation flags 570 | */ 571 | if (info.IsConstructCall()) { 572 | Call *call; 573 | if (info[0]->IsExternal()) { 574 | Local ext = info[0].As(); 575 | // This option is used for wrapping an existing call 576 | grpc_call *call_value = reinterpret_cast(ext->Value()); 577 | call = new Call(call_value); 578 | } else { 579 | if (!Channel::HasInstance(info[0])) { 580 | return Nan::ThrowTypeError("Call's first argument must be a Channel"); 581 | } 582 | if (!info[1]->IsString()) { 583 | return Nan::ThrowTypeError("Call's second argument must be a string"); 584 | } 585 | if (!(info[2]->IsNumber() || info[2]->IsDate())) { 586 | return Nan::ThrowTypeError( 587 | "Call's third argument must be a date or a number"); 588 | } 589 | // These arguments are at the end because they are optional 590 | grpc_call *parent_call = NULL; 591 | if (Call::HasInstance(info[4])) { 592 | Call *parent_obj = 593 | ObjectWrap::Unwrap(Nan::To(info[4]).ToLocalChecked()); 594 | parent_call = parent_obj->wrapped_call; 595 | } else if (!(info[4]->IsUndefined() || info[4]->IsNull())) { 596 | return Nan::ThrowTypeError( 597 | "Call's fifth argument must be another call, if provided"); 598 | } 599 | uint32_t propagate_flags = GRPC_PROPAGATE_DEFAULTS; 600 | if (info[5]->IsUint32()) { 601 | propagate_flags = Nan::To(info[5]).FromJust(); 602 | } else if (!(info[5]->IsUndefined() || info[5]->IsNull())) { 603 | return Nan::ThrowTypeError( 604 | "Call's sixth argument must be propagate flags, if provided"); 605 | } 606 | Local channel_object = Nan::To(info[0]).ToLocalChecked(); 607 | Channel *channel = ObjectWrap::Unwrap(channel_object); 608 | if (channel->GetWrappedChannel() == NULL) { 609 | return Nan::ThrowError("Call cannot be created from a closed channel"); 610 | } 611 | double deadline = Nan::To(info[2]).FromJust(); 612 | grpc_channel *wrapped_channel = channel->GetWrappedChannel(); 613 | grpc_call *wrapped_call; 614 | grpc_slice method = 615 | CreateSliceFromString(Nan::To(info[1]).ToLocalChecked()); 616 | if (info[3]->IsString()) { 617 | grpc_slice *host = new grpc_slice; 618 | *host = 619 | CreateSliceFromString(Nan::To(info[3]).ToLocalChecked()); 620 | wrapped_call = grpc_channel_create_call( 621 | wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(), 622 | method, host, MillisecondsToTimespec(deadline), NULL); 623 | delete host; 624 | } else if (info[3]->IsUndefined() || info[3]->IsNull()) { 625 | wrapped_call = grpc_channel_create_call( 626 | wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(), 627 | method, NULL, MillisecondsToTimespec(deadline), NULL); 628 | } else { 629 | return Nan::ThrowTypeError("Call's fourth argument must be a string"); 630 | } 631 | grpc_slice_unref(method); 632 | call = new Call(wrapped_call); 633 | Nan::Set(info.This(), Nan::New("channel_").ToLocalChecked(), 634 | channel_object); 635 | } 636 | call->Wrap(info.This()); 637 | info.GetReturnValue().Set(info.This()); 638 | } else { 639 | const int argc = 4; 640 | Local argv[argc] = {info[0], info[1], info[2], info[3]}; 641 | MaybeLocal maybe_instance = 642 | Nan::NewInstance(constructor->GetFunction(), argc, argv); 643 | if (maybe_instance.IsEmpty()) { 644 | // There's probably a pending exception 645 | return; 646 | } else { 647 | info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); 648 | } 649 | } 650 | } 651 | 652 | NAN_METHOD(Call::StartBatch) { 653 | if (!Call::HasInstance(info.This())) { 654 | return Nan::ThrowTypeError("startBatch can only be called on Call objects"); 655 | } 656 | if (!info[0]->IsObject()) { 657 | return Nan::ThrowError("startBatch's first argument must be an object"); 658 | } 659 | if (!info[1]->IsFunction()) { 660 | return Nan::ThrowError("startBatch's second argument must be a callback"); 661 | } 662 | Local callback_func = info[1].As(); 663 | Call *call = ObjectWrap::Unwrap(info.This()); 664 | if (call->wrapped_call == NULL) { 665 | /* This implies that the call has completed and has been destroyed. To 666 | * emulate 667 | * previous behavior, we should call the callback immediately with an error, 668 | * as though the batch had failed in core */ 669 | Local argv[] = { 670 | Nan::Error("The async function failed because the call has completed")}; 671 | Nan::Call(callback_func, Nan::New(), 1, argv); 672 | return; 673 | } 674 | Local obj = Nan::To(info[0]).ToLocalChecked(); 675 | Local keys = Nan::GetOwnPropertyNames(obj).ToLocalChecked(); 676 | size_t nops = keys->Length(); 677 | vector ops(nops); 678 | unique_ptr op_vector(new OpVec()); 679 | for (unsigned int i = 0; i < nops; i++) { 680 | unique_ptr op; 681 | MaybeLocal maybe_key = Nan::Get(keys, i); 682 | if (maybe_key.IsEmpty() || (!maybe_key.ToLocalChecked()->IsUint32())) { 683 | return Nan::ThrowError( 684 | "startBatch's first argument's keys must be integers"); 685 | } 686 | uint32_t type = Nan::To(maybe_key.ToLocalChecked()).FromJust(); 687 | ops[i].op = static_cast(type); 688 | ops[i].flags = 0; 689 | ops[i].reserved = NULL; 690 | switch (type) { 691 | case GRPC_OP_SEND_INITIAL_METADATA: 692 | op.reset(new SendMetadataOp()); 693 | break; 694 | case GRPC_OP_SEND_MESSAGE: 695 | op.reset(new SendMessageOp()); 696 | break; 697 | case GRPC_OP_SEND_CLOSE_FROM_CLIENT: 698 | op.reset(new SendClientCloseOp()); 699 | break; 700 | case GRPC_OP_SEND_STATUS_FROM_SERVER: 701 | op.reset(new SendServerStatusOp()); 702 | break; 703 | case GRPC_OP_RECV_INITIAL_METADATA: 704 | op.reset(new GetMetadataOp()); 705 | break; 706 | case GRPC_OP_RECV_MESSAGE: 707 | op.reset(new ReadMessageOp()); 708 | break; 709 | case GRPC_OP_RECV_STATUS_ON_CLIENT: 710 | op.reset(new ClientStatusOp()); 711 | break; 712 | case GRPC_OP_RECV_CLOSE_ON_SERVER: 713 | op.reset(new ServerCloseResponseOp()); 714 | break; 715 | default: 716 | return Nan::ThrowError("Argument object had an unrecognized key"); 717 | } 718 | if (!op->ParseOp(obj->Get(type), &ops[i])) { 719 | return Nan::ThrowTypeError("Incorrectly typed arguments to startBatch"); 720 | } 721 | op_vector->push_back(std::move(op)); 722 | } 723 | Callback *callback = new Callback(callback_func); 724 | grpc_call_error error = grpc_call_start_batch( 725 | call->wrapped_call, &ops[0], nops, 726 | new struct tag(callback, op_vector.release(), call, info.This()), NULL); 727 | if (error != GRPC_CALL_OK) { 728 | return Nan::ThrowError(nanErrorWithCode("startBatch failed", error)); 729 | } 730 | call->pending_batches++; 731 | CompletionQueueNext(); 732 | } 733 | 734 | NAN_METHOD(Call::Cancel) { 735 | if (!Call::HasInstance(info.This())) { 736 | return Nan::ThrowTypeError("cancel can only be called on Call objects"); 737 | } 738 | Call *call = ObjectWrap::Unwrap(info.This()); 739 | if (call->wrapped_call == NULL) { 740 | /* Cancel is supposed to be idempotent. If the call has already finished, 741 | * cancel should just complete silently */ 742 | return; 743 | } 744 | grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL); 745 | if (error != GRPC_CALL_OK) { 746 | return Nan::ThrowError(nanErrorWithCode("cancel failed", error)); 747 | } 748 | } 749 | 750 | NAN_METHOD(Call::CancelWithStatus) { 751 | Nan::HandleScope scope; 752 | if (!HasInstance(info.This())) { 753 | return Nan::ThrowTypeError("cancel can only be called on Call objects"); 754 | } 755 | if (!info[0]->IsUint32()) { 756 | return Nan::ThrowTypeError( 757 | "cancelWithStatus's first argument must be a status code"); 758 | } 759 | if (!info[1]->IsString()) { 760 | return Nan::ThrowTypeError( 761 | "cancelWithStatus's second argument must be a string"); 762 | } 763 | Call *call = ObjectWrap::Unwrap(info.This()); 764 | if (call->wrapped_call == NULL) { 765 | /* Cancel is supposed to be idempotent. If the call has already finished, 766 | * cancel should just complete silently */ 767 | return; 768 | } 769 | grpc_status_code code = 770 | static_cast(Nan::To(info[0]).FromJust()); 771 | if (code == GRPC_STATUS_OK) { 772 | return Nan::ThrowRangeError( 773 | "cancelWithStatus cannot be called with OK status"); 774 | } 775 | Utf8String details(info[1]); 776 | grpc_call_cancel_with_status(call->wrapped_call, code, *details, NULL); 777 | } 778 | 779 | NAN_METHOD(Call::GetPeer) { 780 | Nan::HandleScope scope; 781 | if (!HasInstance(info.This())) { 782 | return Nan::ThrowTypeError("getPeer can only be called on Call objects"); 783 | } 784 | Call *call = ObjectWrap::Unwrap(info.This()); 785 | Local peer_value = Nan::New(call->peer).ToLocalChecked(); 786 | info.GetReturnValue().Set(peer_value); 787 | } 788 | 789 | NAN_METHOD(Call::SetCredentials) { 790 | Nan::HandleScope scope; 791 | if (!HasInstance(info.This())) { 792 | return Nan::ThrowTypeError( 793 | "setCredentials can only be called on Call objects"); 794 | } 795 | if (!CallCredentials::HasInstance(info[0])) { 796 | return Nan::ThrowTypeError( 797 | "setCredentials' first argument must be a CallCredentials"); 798 | } 799 | Call *call = ObjectWrap::Unwrap(info.This()); 800 | if (call->wrapped_call == NULL) { 801 | return Nan::ThrowError( 802 | "Cannot set credentials on a call that has already started"); 803 | } 804 | CallCredentials *creds_object = ObjectWrap::Unwrap( 805 | Nan::To(info[0]).ToLocalChecked()); 806 | grpc_call_credentials *creds = creds_object->GetWrappedCredentials(); 807 | grpc_call_error error = GRPC_CALL_ERROR; 808 | if (creds) { 809 | error = grpc_call_set_credentials(call->wrapped_call, creds); 810 | } 811 | info.GetReturnValue().Set(Nan::New(error)); 812 | } 813 | 814 | } // namespace node 815 | } // namespace grpc 816 | -------------------------------------------------------------------------------- /src/ext/call.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef NET_GRPC_NODE_CALL_H_ 20 | #define NET_GRPC_NODE_CALL_H_ 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include "grpc/grpc.h" 28 | #include "grpc/support/log.h" 29 | 30 | #include "channel.h" 31 | 32 | namespace grpc { 33 | namespace node { 34 | 35 | using std::unique_ptr; 36 | using std::shared_ptr; 37 | 38 | v8::Local nanErrorWithCode(const char *msg, grpc_call_error code); 39 | 40 | v8::Local ParseMetadata(const grpc_metadata_array *metadata_array); 41 | 42 | bool CreateMetadataArray(v8::Local metadata, 43 | grpc_metadata_array *array); 44 | 45 | void DestroyMetadataArray(grpc_metadata_array *array); 46 | 47 | /* Wrapper class for grpc_call structs. */ 48 | class Call : public Nan::ObjectWrap { 49 | public: 50 | static void Init(v8::Local exports); 51 | static bool HasInstance(v8::Local val); 52 | /* Wrap a grpc_call struct in a javascript object */ 53 | static v8::Local WrapStruct(grpc_call *call); 54 | 55 | void CompleteBatch(bool is_final_op); 56 | 57 | private: 58 | explicit Call(grpc_call *call); 59 | ~Call(); 60 | 61 | // Prevent copying 62 | Call(const Call &); 63 | Call &operator=(const Call &); 64 | 65 | void DestroyCall(); 66 | 67 | static NAN_METHOD(New); 68 | static NAN_METHOD(StartBatch); 69 | static NAN_METHOD(Cancel); 70 | static NAN_METHOD(CancelWithStatus); 71 | static NAN_METHOD(GetPeer); 72 | static NAN_METHOD(SetCredentials); 73 | static Nan::Callback *constructor; 74 | // Used for typechecking instances of this javascript class 75 | static Nan::Persistent fun_tpl; 76 | 77 | grpc_call *wrapped_call; 78 | // The number of ops that were started but not completed on this call 79 | int pending_batches; 80 | /* Indicates whether the "final" op on a call has completed. For a client 81 | call, this is GRPC_OP_RECV_STATUS_ON_CLIENT and for a server call, this 82 | is GRPC_OP_SEND_STATUS_FROM_SERVER */ 83 | bool has_final_op_completed; 84 | char *peer; 85 | }; 86 | 87 | class Op { 88 | public: 89 | virtual v8::Local GetNodeValue() const = 0; 90 | virtual bool ParseOp(v8::Local value, grpc_op *out) = 0; 91 | virtual ~Op(); 92 | v8::Local GetOpType() const; 93 | virtual bool IsFinalOp() = 0; 94 | virtual void OnComplete(bool success) = 0; 95 | 96 | protected: 97 | virtual std::string GetTypeString() const = 0; 98 | }; 99 | 100 | typedef std::vector> OpVec; 101 | struct tag { 102 | tag(Nan::Callback *callback, OpVec *ops, Call *call, 103 | v8::Local call_value); 104 | ~tag(); 105 | Nan::Callback *callback; 106 | OpVec *ops; 107 | Call *call; 108 | Nan::Persistent> 109 | call_persist; 110 | }; 111 | 112 | void DestroyTag(void *tag); 113 | 114 | void CompleteTag(void *tag, const char *error_message); 115 | 116 | } // namespace node 117 | } // namespace grpc 118 | 119 | #endif // NET_GRPC_NODE_CALL_H_ 120 | -------------------------------------------------------------------------------- /src/ext/call_credentials.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include "call.h" 26 | #include "call_credentials.h" 27 | #include "grpc/grpc.h" 28 | #include "grpc/grpc_security.h" 29 | #include "grpc/support/log.h" 30 | 31 | namespace grpc { 32 | namespace node { 33 | 34 | using Nan::Callback; 35 | using Nan::EscapableHandleScope; 36 | using Nan::HandleScope; 37 | using Nan::Maybe; 38 | using Nan::MaybeLocal; 39 | using Nan::ObjectWrap; 40 | using Nan::Persistent; 41 | using Nan::Utf8String; 42 | 43 | using v8::Exception; 44 | using v8::External; 45 | using v8::Function; 46 | using v8::FunctionTemplate; 47 | using v8::Integer; 48 | using v8::Local; 49 | using v8::Object; 50 | using v8::ObjectTemplate; 51 | using v8::Value; 52 | 53 | Nan::Callback *CallCredentials::constructor; 54 | Persistent CallCredentials::fun_tpl; 55 | 56 | static Callback *plugin_callback; 57 | 58 | CallCredentials::CallCredentials(grpc_call_credentials *credentials) 59 | : wrapped_credentials(credentials) {} 60 | 61 | CallCredentials::~CallCredentials() { 62 | grpc_call_credentials_release(wrapped_credentials); 63 | } 64 | 65 | void CallCredentials::Init(Local exports) { 66 | HandleScope scope; 67 | Local tpl = Nan::New(New); 68 | tpl->SetClassName(Nan::New("CallCredentials").ToLocalChecked()); 69 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 70 | Nan::SetPrototypeMethod(tpl, "compose", Compose); 71 | fun_tpl.Reset(tpl); 72 | Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); 73 | Nan::Set(ctr, Nan::New("createFromPlugin").ToLocalChecked(), 74 | Nan::GetFunction(Nan::New(CreateFromPlugin)) 75 | .ToLocalChecked()); 76 | Nan::Set(exports, Nan::New("CallCredentials").ToLocalChecked(), ctr); 77 | constructor = new Nan::Callback(ctr); 78 | 79 | Local callback_tpl = 80 | Nan::New(PluginCallback); 81 | plugin_callback = 82 | new Callback(Nan::GetFunction(callback_tpl).ToLocalChecked()); 83 | } 84 | 85 | bool CallCredentials::HasInstance(Local val) { 86 | HandleScope scope; 87 | return Nan::New(fun_tpl)->HasInstance(val); 88 | } 89 | 90 | Local CallCredentials::WrapStruct(grpc_call_credentials *credentials) { 91 | EscapableHandleScope scope; 92 | const int argc = 1; 93 | if (credentials == NULL) { 94 | return scope.Escape(Nan::Null()); 95 | } 96 | Local argv[argc] = { 97 | Nan::New(reinterpret_cast(credentials))}; 98 | MaybeLocal maybe_instance = 99 | Nan::NewInstance(constructor->GetFunction(), argc, argv); 100 | if (maybe_instance.IsEmpty()) { 101 | return scope.Escape(Nan::Null()); 102 | } else { 103 | return scope.Escape(maybe_instance.ToLocalChecked()); 104 | } 105 | } 106 | 107 | grpc_call_credentials *CallCredentials::GetWrappedCredentials() { 108 | return wrapped_credentials; 109 | } 110 | 111 | NAN_METHOD(CallCredentials::New) { 112 | if (info.IsConstructCall()) { 113 | if (!info[0]->IsExternal()) { 114 | return Nan::ThrowTypeError( 115 | "CallCredentials can only be created with the provided functions"); 116 | } 117 | Local ext = info[0].As(); 118 | grpc_call_credentials *creds_value = 119 | reinterpret_cast(ext->Value()); 120 | CallCredentials *credentials = new CallCredentials(creds_value); 121 | credentials->Wrap(info.This()); 122 | info.GetReturnValue().Set(info.This()); 123 | return; 124 | } else { 125 | // This should never be called directly 126 | return Nan::ThrowTypeError( 127 | "CallCredentials can only be created with the provided functions"); 128 | } 129 | } 130 | 131 | NAN_METHOD(CallCredentials::Compose) { 132 | if (!CallCredentials::HasInstance(info.This())) { 133 | return Nan::ThrowTypeError( 134 | "compose can only be called on CallCredentials objects"); 135 | } 136 | if (!CallCredentials::HasInstance(info[0])) { 137 | return Nan::ThrowTypeError( 138 | "compose's first argument must be a CallCredentials object"); 139 | } 140 | CallCredentials *self = ObjectWrap::Unwrap(info.This()); 141 | CallCredentials *other = ObjectWrap::Unwrap( 142 | Nan::To(info[0]).ToLocalChecked()); 143 | grpc_call_credentials *creds = grpc_composite_call_credentials_create( 144 | self->wrapped_credentials, other->wrapped_credentials, NULL); 145 | info.GetReturnValue().Set(WrapStruct(creds)); 146 | } 147 | 148 | NAN_METHOD(CallCredentials::CreateFromPlugin) { 149 | if (!info[0]->IsFunction()) { 150 | return Nan::ThrowTypeError( 151 | "createFromPlugin's argument must be a function"); 152 | } 153 | grpc_metadata_credentials_plugin plugin; 154 | plugin_state *state = new plugin_state; 155 | state->callback = new Nan::Callback(info[0].As()); 156 | state->pending_callbacks = new std::queue(); 157 | uv_mutex_init(&state->plugin_mutex); 158 | uv_async_init(uv_default_loop(), &state->plugin_async, SendPluginCallback); 159 | uv_unref((uv_handle_t *)&state->plugin_async); 160 | 161 | state->plugin_async.data = state; 162 | 163 | plugin.get_metadata = plugin_get_metadata; 164 | plugin.destroy = plugin_destroy_state; 165 | plugin.state = reinterpret_cast(state); 166 | plugin.type = ""; 167 | grpc_call_credentials *creds = 168 | grpc_metadata_credentials_create_from_plugin(plugin, NULL); 169 | info.GetReturnValue().Set(WrapStruct(creds)); 170 | } 171 | 172 | NAN_METHOD(PluginCallback) { 173 | // Arguments: status code, error details, metadata 174 | if (!info[0]->IsUint32()) { 175 | return Nan::ThrowTypeError( 176 | "The callback's first argument must be a status code"); 177 | } 178 | if (!info[1]->IsString()) { 179 | return Nan::ThrowTypeError( 180 | "The callback's second argument must be a string"); 181 | } 182 | if (!info[2]->IsObject()) { 183 | return Nan::ThrowTypeError( 184 | "The callback's third argument must be an object"); 185 | } 186 | if (!info[3]->IsObject()) { 187 | return Nan::ThrowTypeError( 188 | "The callback's fourth argument must be an object"); 189 | } 190 | grpc_status_code code = 191 | static_cast(Nan::To(info[0]).FromJust()); 192 | Utf8String details_utf8_str(info[1]); 193 | char *details = *details_utf8_str; 194 | grpc_metadata_array array; 195 | grpc_metadata_array_init(&array); 196 | Local callback_data = Nan::To(info[3]).ToLocalChecked(); 197 | if (!CreateMetadataArray(Nan::To(info[2]).ToLocalChecked(), &array)) { 198 | return Nan::ThrowError("Failed to parse metadata"); 199 | } 200 | grpc_credentials_plugin_metadata_cb cb = 201 | reinterpret_cast( 202 | Nan::Get(callback_data, Nan::New("cb").ToLocalChecked()) 203 | .ToLocalChecked() 204 | .As() 205 | ->Value()); 206 | void *user_data = 207 | Nan::Get(callback_data, Nan::New("user_data").ToLocalChecked()) 208 | .ToLocalChecked() 209 | .As() 210 | ->Value(); 211 | cb(user_data, array.metadata, array.count, code, details); 212 | DestroyMetadataArray(&array); 213 | } 214 | 215 | NAUV_WORK_CB(SendPluginCallback) { 216 | Nan::HandleScope scope; 217 | plugin_state *state = reinterpret_cast(async->data); 218 | std::queue callbacks; 219 | uv_mutex_lock(&state->plugin_mutex); 220 | state->pending_callbacks->swap(callbacks); 221 | uv_mutex_unlock(&state->plugin_mutex); 222 | while (!callbacks.empty()) { 223 | plugin_callback_data *data = callbacks.front(); 224 | callbacks.pop(); 225 | Local callback_data = Nan::New(); 226 | Nan::Set(callback_data, Nan::New("cb").ToLocalChecked(), 227 | Nan::New(reinterpret_cast(data->cb))); 228 | Nan::Set(callback_data, Nan::New("user_data").ToLocalChecked(), 229 | Nan::New(data->user_data)); 230 | const int argc = 3; 231 | v8::Local argv[argc] = { 232 | Nan::New(data->service_url).ToLocalChecked(), callback_data, 233 | // Get Local from Nan::Callback* 234 | **plugin_callback}; 235 | Nan::Callback *callback = state->callback; 236 | callback->Call(argc, argv); 237 | delete data; 238 | } 239 | } 240 | 241 | void plugin_get_metadata(void *state, grpc_auth_metadata_context context, 242 | grpc_credentials_plugin_metadata_cb cb, 243 | void *user_data) { 244 | plugin_state *p_state = reinterpret_cast(state); 245 | plugin_callback_data *data = new plugin_callback_data; 246 | data->service_url = context.service_url; 247 | data->cb = cb; 248 | data->user_data = user_data; 249 | 250 | uv_mutex_lock(&p_state->plugin_mutex); 251 | p_state->pending_callbacks->push(data); 252 | uv_mutex_unlock(&p_state->plugin_mutex); 253 | 254 | uv_async_send(&p_state->plugin_async); 255 | } 256 | 257 | void plugin_uv_close_cb(uv_handle_t *handle) { 258 | uv_async_t *async = reinterpret_cast(handle); 259 | plugin_state *state = reinterpret_cast(async->data); 260 | uv_mutex_destroy(&state->plugin_mutex); 261 | delete state->pending_callbacks; 262 | delete state->callback; 263 | delete state; 264 | } 265 | 266 | void plugin_destroy_state(void *ptr) { 267 | plugin_state *state = reinterpret_cast(ptr); 268 | uv_close((uv_handle_t *)&state->plugin_async, plugin_uv_close_cb); 269 | } 270 | 271 | } // namespace node 272 | } // namespace grpc 273 | -------------------------------------------------------------------------------- /src/ext/call_credentials.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef GRPC_NODE_CALL_CREDENTIALS_H_ 20 | #define GRPC_NODE_CALL_CREDENTIALS_H_ 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include "grpc/grpc_security.h" 28 | 29 | namespace grpc { 30 | namespace node { 31 | 32 | class CallCredentials : public Nan::ObjectWrap { 33 | public: 34 | static void Init(v8::Local exports); 35 | static bool HasInstance(v8::Local val); 36 | /* Wrap a grpc_call_credentials struct in a javascript object */ 37 | static v8::Local WrapStruct(grpc_call_credentials *credentials); 38 | 39 | /* Returns the grpc_call_credentials struct that this object wraps */ 40 | grpc_call_credentials *GetWrappedCredentials(); 41 | 42 | private: 43 | explicit CallCredentials(grpc_call_credentials *credentials); 44 | ~CallCredentials(); 45 | 46 | // Prevent copying 47 | CallCredentials(const CallCredentials &); 48 | CallCredentials &operator=(const CallCredentials &); 49 | 50 | static NAN_METHOD(New); 51 | static NAN_METHOD(CreateSsl); 52 | static NAN_METHOD(CreateFromPlugin); 53 | 54 | static NAN_METHOD(Compose); 55 | static Nan::Callback *constructor; 56 | // Used for typechecking instances of this javascript class 57 | static Nan::Persistent fun_tpl; 58 | 59 | grpc_call_credentials *wrapped_credentials; 60 | }; 61 | 62 | /* Auth metadata plugin functionality */ 63 | 64 | typedef struct plugin_callback_data { 65 | const char *service_url; 66 | grpc_credentials_plugin_metadata_cb cb; 67 | void *user_data; 68 | } plugin_callback_data; 69 | 70 | typedef struct plugin_state { 71 | Nan::Callback *callback; 72 | std::queue *pending_callbacks; 73 | uv_mutex_t plugin_mutex; 74 | // async.data == this 75 | uv_async_t plugin_async; 76 | } plugin_state; 77 | 78 | void plugin_get_metadata(void *state, grpc_auth_metadata_context context, 79 | grpc_credentials_plugin_metadata_cb cb, 80 | void *user_data); 81 | 82 | void plugin_destroy_state(void *state); 83 | 84 | NAN_METHOD(PluginCallback); 85 | 86 | NAUV_WORK_CB(SendPluginCallback); 87 | 88 | } // namespace node 89 | } // namepsace grpc 90 | 91 | #endif // GRPC_NODE_CALL_CREDENTIALS_H_ 92 | -------------------------------------------------------------------------------- /src/ext/channel.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | 21 | #include "grpc/support/log.h" 22 | 23 | #include 24 | #include 25 | #include "call.h" 26 | #include "channel.h" 27 | #include "channel_credentials.h" 28 | #include "completion_queue.h" 29 | #include "grpc/grpc.h" 30 | #include "grpc/grpc_security.h" 31 | #include "timeval.h" 32 | 33 | namespace grpc { 34 | namespace node { 35 | 36 | using Nan::Callback; 37 | using Nan::EscapableHandleScope; 38 | using Nan::HandleScope; 39 | using Nan::Maybe; 40 | using Nan::MaybeLocal; 41 | using Nan::ObjectWrap; 42 | using Nan::Persistent; 43 | using Nan::Utf8String; 44 | 45 | using v8::Array; 46 | using v8::Exception; 47 | using v8::Function; 48 | using v8::FunctionTemplate; 49 | using v8::Integer; 50 | using v8::Local; 51 | using v8::Number; 52 | using v8::Object; 53 | using v8::String; 54 | using v8::Value; 55 | 56 | Callback *Channel::constructor; 57 | Persistent Channel::fun_tpl; 58 | 59 | bool ParseChannelArgs(Local args_val, 60 | grpc_channel_args **channel_args_ptr) { 61 | if (args_val->IsUndefined() || args_val->IsNull()) { 62 | *channel_args_ptr = NULL; 63 | return true; 64 | } 65 | if (!args_val->IsObject()) { 66 | *channel_args_ptr = NULL; 67 | return false; 68 | } 69 | grpc_channel_args *channel_args = 70 | reinterpret_cast(malloc(sizeof(grpc_channel_args))); 71 | *channel_args_ptr = channel_args; 72 | Local args_hash = Nan::To(args_val).ToLocalChecked(); 73 | Local keys = Nan::GetOwnPropertyNames(args_hash).ToLocalChecked(); 74 | channel_args->num_args = keys->Length(); 75 | channel_args->args = reinterpret_cast( 76 | calloc(channel_args->num_args, sizeof(grpc_arg))); 77 | for (unsigned int i = 0; i < channel_args->num_args; i++) { 78 | Local key = Nan::Get(keys, i).ToLocalChecked(); 79 | Utf8String key_str(key); 80 | if (*key_str == NULL) { 81 | // Key string onversion failed 82 | return false; 83 | } 84 | Local value = Nan::Get(args_hash, key).ToLocalChecked(); 85 | if (value->IsInt32()) { 86 | channel_args->args[i].type = GRPC_ARG_INTEGER; 87 | channel_args->args[i].value.integer = Nan::To(value).FromJust(); 88 | } else if (value->IsString()) { 89 | Utf8String val_str(value); 90 | channel_args->args[i].type = GRPC_ARG_STRING; 91 | channel_args->args[i].value.string = 92 | reinterpret_cast(calloc(val_str.length() + 1, sizeof(char))); 93 | memcpy(channel_args->args[i].value.string, *val_str, 94 | val_str.length() + 1); 95 | } else { 96 | // The value does not match either of the accepted types 97 | return false; 98 | } 99 | channel_args->args[i].key = 100 | reinterpret_cast(calloc(key_str.length() + 1, sizeof(char))); 101 | memcpy(channel_args->args[i].key, *key_str, key_str.length() + 1); 102 | } 103 | return true; 104 | } 105 | 106 | void DeallocateChannelArgs(grpc_channel_args *channel_args) { 107 | if (channel_args == NULL) { 108 | return; 109 | } 110 | for (size_t i = 0; i < channel_args->num_args; i++) { 111 | if (channel_args->args[i].key == NULL) { 112 | /* NULL key implies that this argument and all subsequent arguments failed 113 | * to parse */ 114 | break; 115 | } 116 | free(channel_args->args[i].key); 117 | if (channel_args->args[i].type == GRPC_ARG_STRING) { 118 | free(channel_args->args[i].value.string); 119 | } 120 | } 121 | free(channel_args->args); 122 | free(channel_args); 123 | } 124 | 125 | Channel::Channel(grpc_channel *channel) : wrapped_channel(channel) {} 126 | 127 | Channel::~Channel() { 128 | gpr_log(GPR_DEBUG, "Destroying channel"); 129 | if (wrapped_channel != NULL) { 130 | grpc_channel_destroy(wrapped_channel); 131 | } 132 | } 133 | 134 | void Channel::Init(Local exports) { 135 | Nan::HandleScope scope; 136 | Local tpl = Nan::New(New); 137 | tpl->SetClassName(Nan::New("Channel").ToLocalChecked()); 138 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 139 | Nan::SetPrototypeMethod(tpl, "close", Close); 140 | Nan::SetPrototypeMethod(tpl, "getTarget", GetTarget); 141 | Nan::SetPrototypeMethod(tpl, "getConnectivityState", GetConnectivityState); 142 | Nan::SetPrototypeMethod(tpl, "watchConnectivityState", 143 | WatchConnectivityState); 144 | fun_tpl.Reset(tpl); 145 | Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); 146 | Nan::Set(exports, Nan::New("Channel").ToLocalChecked(), ctr); 147 | constructor = new Callback(ctr); 148 | } 149 | 150 | bool Channel::HasInstance(Local val) { 151 | HandleScope scope; 152 | return Nan::New(fun_tpl)->HasInstance(val); 153 | } 154 | 155 | grpc_channel *Channel::GetWrappedChannel() { return this->wrapped_channel; } 156 | 157 | NAN_METHOD(Channel::New) { 158 | if (info.IsConstructCall()) { 159 | if (!info[0]->IsString()) { 160 | return Nan::ThrowTypeError( 161 | "Channel expects a string, a credential and an object"); 162 | } 163 | grpc_channel *wrapped_channel; 164 | // Owned by the Channel object 165 | Utf8String host(info[0]); 166 | grpc_channel_credentials *creds; 167 | if (!ChannelCredentials::HasInstance(info[1])) { 168 | return Nan::ThrowTypeError( 169 | "Channel's second argument must be a ChannelCredentials"); 170 | } 171 | ChannelCredentials *creds_object = ObjectWrap::Unwrap( 172 | Nan::To(info[1]).ToLocalChecked()); 173 | creds = creds_object->GetWrappedCredentials(); 174 | grpc_channel_args *channel_args_ptr = NULL; 175 | if (!ParseChannelArgs(info[2], &channel_args_ptr)) { 176 | DeallocateChannelArgs(channel_args_ptr); 177 | return Nan::ThrowTypeError( 178 | "Channel options must be an object with " 179 | "string keys and integer or string values"); 180 | } 181 | if (creds == NULL) { 182 | wrapped_channel = 183 | grpc_insecure_channel_create(*host, channel_args_ptr, NULL); 184 | } else { 185 | wrapped_channel = 186 | grpc_secure_channel_create(creds, *host, channel_args_ptr, NULL); 187 | } 188 | DeallocateChannelArgs(channel_args_ptr); 189 | Channel *channel = new Channel(wrapped_channel); 190 | channel->Wrap(info.This()); 191 | info.GetReturnValue().Set(info.This()); 192 | return; 193 | } else { 194 | const int argc = 3; 195 | Local argv[argc] = {info[0], info[1], info[2]}; 196 | MaybeLocal maybe_instance = 197 | Nan::NewInstance(constructor->GetFunction(), argc, argv); 198 | if (maybe_instance.IsEmpty()) { 199 | // There's probably a pending exception 200 | return; 201 | } else { 202 | info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); 203 | } 204 | } 205 | } 206 | 207 | NAN_METHOD(Channel::Close) { 208 | if (!HasInstance(info.This())) { 209 | return Nan::ThrowTypeError("close can only be called on Channel objects"); 210 | } 211 | Channel *channel = ObjectWrap::Unwrap(info.This()); 212 | if (channel->wrapped_channel != NULL) { 213 | grpc_channel_destroy(channel->wrapped_channel); 214 | channel->wrapped_channel = NULL; 215 | } 216 | } 217 | 218 | NAN_METHOD(Channel::GetTarget) { 219 | if (!HasInstance(info.This())) { 220 | return Nan::ThrowTypeError( 221 | "getTarget can only be called on Channel objects"); 222 | } 223 | Channel *channel = ObjectWrap::Unwrap(info.This()); 224 | info.GetReturnValue().Set( 225 | Nan::New(grpc_channel_get_target(channel->wrapped_channel)) 226 | .ToLocalChecked()); 227 | } 228 | 229 | NAN_METHOD(Channel::GetConnectivityState) { 230 | if (!HasInstance(info.This())) { 231 | return Nan::ThrowTypeError( 232 | "getConnectivityState can only be called on Channel objects"); 233 | } 234 | Channel *channel = ObjectWrap::Unwrap(info.This()); 235 | int try_to_connect = (int)info[0]->Equals(Nan::True()); 236 | info.GetReturnValue().Set(grpc_channel_check_connectivity_state( 237 | channel->wrapped_channel, try_to_connect)); 238 | } 239 | 240 | NAN_METHOD(Channel::WatchConnectivityState) { 241 | if (!HasInstance(info.This())) { 242 | return Nan::ThrowTypeError( 243 | "watchConnectivityState can only be called on Channel objects"); 244 | } 245 | if (!info[0]->IsUint32()) { 246 | return Nan::ThrowTypeError( 247 | "watchConnectivityState's first argument must be a channel state"); 248 | } 249 | if (!(info[1]->IsNumber() || info[1]->IsDate())) { 250 | return Nan::ThrowTypeError( 251 | "watchConnectivityState's second argument must be a date or a number"); 252 | } 253 | if (!info[2]->IsFunction()) { 254 | return Nan::ThrowTypeError( 255 | "watchConnectivityState's third argument must be a callback"); 256 | } 257 | grpc_connectivity_state last_state = static_cast( 258 | Nan::To(info[0]).FromJust()); 259 | double deadline = Nan::To(info[1]).FromJust(); 260 | Local callback_func = info[2].As(); 261 | Nan::Callback *callback = new Callback(callback_func); 262 | Channel *channel = ObjectWrap::Unwrap(info.This()); 263 | unique_ptr ops(new OpVec()); 264 | grpc_channel_watch_connectivity_state( 265 | channel->wrapped_channel, last_state, MillisecondsToTimespec(deadline), 266 | GetCompletionQueue(), 267 | new struct tag(callback, ops.release(), NULL, Nan::Null())); 268 | CompletionQueueNext(); 269 | } 270 | 271 | } // namespace node 272 | } // namespace grpc 273 | -------------------------------------------------------------------------------- /src/ext/channel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef NET_GRPC_NODE_CHANNEL_H_ 20 | #define NET_GRPC_NODE_CHANNEL_H_ 21 | 22 | #include 23 | #include 24 | #include "grpc/grpc.h" 25 | 26 | namespace grpc { 27 | namespace node { 28 | 29 | bool ParseChannelArgs(v8::Local args_val, 30 | grpc_channel_args **channel_args_ptr); 31 | 32 | void DeallocateChannelArgs(grpc_channel_args *channel_args); 33 | 34 | /* Wrapper class for grpc_channel structs */ 35 | class Channel : public Nan::ObjectWrap { 36 | public: 37 | static void Init(v8::Local exports); 38 | static bool HasInstance(v8::Local val); 39 | /* This is used to typecheck javascript objects before converting them to 40 | this type */ 41 | static v8::Persistent prototype; 42 | 43 | /* Returns the grpc_channel struct that this object wraps */ 44 | grpc_channel *GetWrappedChannel(); 45 | 46 | private: 47 | explicit Channel(grpc_channel *channel); 48 | ~Channel(); 49 | 50 | // Prevent copying 51 | Channel(const Channel &); 52 | Channel &operator=(const Channel &); 53 | 54 | static NAN_METHOD(New); 55 | static NAN_METHOD(Close); 56 | static NAN_METHOD(GetTarget); 57 | static NAN_METHOD(GetConnectivityState); 58 | static NAN_METHOD(WatchConnectivityState); 59 | static Nan::Callback *constructor; 60 | static Nan::Persistent fun_tpl; 61 | 62 | grpc_channel *wrapped_channel; 63 | }; 64 | 65 | } // namespace node 66 | } // namespace grpc 67 | 68 | #endif // NET_GRPC_NODE_CHANNEL_H_ 69 | -------------------------------------------------------------------------------- /src/ext/channel_credentials.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | 21 | #include "call.h" 22 | #include "call_credentials.h" 23 | #include "channel_credentials.h" 24 | #include "grpc/grpc.h" 25 | #include "grpc/grpc_security.h" 26 | #include "grpc/support/log.h" 27 | 28 | namespace grpc { 29 | namespace node { 30 | 31 | using Nan::Callback; 32 | using Nan::EscapableHandleScope; 33 | using Nan::HandleScope; 34 | using Nan::Maybe; 35 | using Nan::MaybeLocal; 36 | using Nan::ObjectWrap; 37 | using Nan::Persistent; 38 | using Nan::Utf8String; 39 | 40 | using v8::Exception; 41 | using v8::External; 42 | using v8::Function; 43 | using v8::FunctionTemplate; 44 | using v8::Integer; 45 | using v8::Local; 46 | using v8::Object; 47 | using v8::ObjectTemplate; 48 | using v8::Value; 49 | 50 | Nan::Callback *ChannelCredentials::constructor; 51 | Persistent ChannelCredentials::fun_tpl; 52 | 53 | ChannelCredentials::ChannelCredentials(grpc_channel_credentials *credentials) 54 | : wrapped_credentials(credentials) {} 55 | 56 | ChannelCredentials::~ChannelCredentials() { 57 | grpc_channel_credentials_release(wrapped_credentials); 58 | } 59 | 60 | void ChannelCredentials::Init(Local exports) { 61 | HandleScope scope; 62 | Local tpl = Nan::New(New); 63 | tpl->SetClassName(Nan::New("ChannelCredentials").ToLocalChecked()); 64 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 65 | Nan::SetPrototypeMethod(tpl, "compose", Compose); 66 | fun_tpl.Reset(tpl); 67 | Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); 68 | Nan::Set( 69 | ctr, Nan::New("createSsl").ToLocalChecked(), 70 | Nan::GetFunction(Nan::New(CreateSsl)).ToLocalChecked()); 71 | Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(), 72 | Nan::GetFunction(Nan::New(CreateInsecure)) 73 | .ToLocalChecked()); 74 | Nan::Set(exports, Nan::New("ChannelCredentials").ToLocalChecked(), ctr); 75 | constructor = new Nan::Callback(ctr); 76 | } 77 | 78 | bool ChannelCredentials::HasInstance(Local val) { 79 | HandleScope scope; 80 | return Nan::New(fun_tpl)->HasInstance(val); 81 | } 82 | 83 | Local ChannelCredentials::WrapStruct( 84 | grpc_channel_credentials *credentials) { 85 | EscapableHandleScope scope; 86 | const int argc = 1; 87 | Local argv[argc] = { 88 | Nan::New(reinterpret_cast(credentials))}; 89 | MaybeLocal maybe_instance = 90 | Nan::NewInstance(constructor->GetFunction(), argc, argv); 91 | if (maybe_instance.IsEmpty()) { 92 | return scope.Escape(Nan::Null()); 93 | } else { 94 | return scope.Escape(maybe_instance.ToLocalChecked()); 95 | } 96 | } 97 | 98 | grpc_channel_credentials *ChannelCredentials::GetWrappedCredentials() { 99 | return wrapped_credentials; 100 | } 101 | 102 | NAN_METHOD(ChannelCredentials::New) { 103 | if (info.IsConstructCall()) { 104 | if (!info[0]->IsExternal()) { 105 | return Nan::ThrowTypeError( 106 | "ChannelCredentials can only be created with the provided functions"); 107 | } 108 | Local ext = info[0].As(); 109 | grpc_channel_credentials *creds_value = 110 | reinterpret_cast(ext->Value()); 111 | ChannelCredentials *credentials = new ChannelCredentials(creds_value); 112 | credentials->Wrap(info.This()); 113 | info.GetReturnValue().Set(info.This()); 114 | return; 115 | } else { 116 | // This should never be called directly 117 | return Nan::ThrowTypeError( 118 | "ChannelCredentials can only be created with the provided functions"); 119 | } 120 | } 121 | 122 | NAN_METHOD(ChannelCredentials::CreateSsl) { 123 | char *root_certs = NULL; 124 | grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL}; 125 | if (::node::Buffer::HasInstance(info[0])) { 126 | root_certs = ::node::Buffer::Data(info[0]); 127 | } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) { 128 | return Nan::ThrowTypeError("createSsl's first argument must be a Buffer"); 129 | } 130 | if (::node::Buffer::HasInstance(info[1])) { 131 | key_cert_pair.private_key = ::node::Buffer::Data(info[1]); 132 | } else if (!(info[1]->IsNull() || info[1]->IsUndefined())) { 133 | return Nan::ThrowTypeError( 134 | "createSSl's second argument must be a Buffer if provided"); 135 | } 136 | if (::node::Buffer::HasInstance(info[2])) { 137 | key_cert_pair.cert_chain = ::node::Buffer::Data(info[2]); 138 | } else if (!(info[2]->IsNull() || info[2]->IsUndefined())) { 139 | return Nan::ThrowTypeError( 140 | "createSSl's third argument must be a Buffer if provided"); 141 | } 142 | if ((key_cert_pair.private_key == NULL) != 143 | (key_cert_pair.cert_chain == NULL)) { 144 | return Nan::ThrowError( 145 | "createSsl's second and third arguments must be" 146 | " provided or omitted together"); 147 | } 148 | grpc_channel_credentials *creds = grpc_ssl_credentials_create( 149 | root_certs, key_cert_pair.private_key == NULL ? NULL : &key_cert_pair, 150 | NULL); 151 | if (creds == NULL) { 152 | info.GetReturnValue().SetNull(); 153 | } else { 154 | info.GetReturnValue().Set(WrapStruct(creds)); 155 | } 156 | } 157 | 158 | NAN_METHOD(ChannelCredentials::Compose) { 159 | if (!ChannelCredentials::HasInstance(info.This())) { 160 | return Nan::ThrowTypeError( 161 | "compose can only be called on ChannelCredentials objects"); 162 | } 163 | if (!CallCredentials::HasInstance(info[0])) { 164 | return Nan::ThrowTypeError( 165 | "compose's first argument must be a CallCredentials object"); 166 | } 167 | ChannelCredentials *self = 168 | ObjectWrap::Unwrap(info.This()); 169 | if (self->wrapped_credentials == NULL) { 170 | return Nan::ThrowTypeError("Cannot compose insecure credential"); 171 | } 172 | CallCredentials *other = ObjectWrap::Unwrap( 173 | Nan::To(info[0]).ToLocalChecked()); 174 | grpc_channel_credentials *creds = grpc_composite_channel_credentials_create( 175 | self->wrapped_credentials, other->GetWrappedCredentials(), NULL); 176 | if (creds == NULL) { 177 | info.GetReturnValue().SetNull(); 178 | } else { 179 | info.GetReturnValue().Set(WrapStruct(creds)); 180 | } 181 | } 182 | 183 | NAN_METHOD(ChannelCredentials::CreateInsecure) { 184 | info.GetReturnValue().Set(WrapStruct(NULL)); 185 | } 186 | 187 | } // namespace node 188 | } // namespace grpc 189 | -------------------------------------------------------------------------------- /src/ext/channel_credentials.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_ 20 | #define NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_ 21 | 22 | #include 23 | #include 24 | #include "grpc/grpc.h" 25 | #include "grpc/grpc_security.h" 26 | 27 | namespace grpc { 28 | namespace node { 29 | 30 | /* Wrapper class for grpc_channel_credentials structs */ 31 | class ChannelCredentials : public Nan::ObjectWrap { 32 | public: 33 | static void Init(v8::Local exports); 34 | static bool HasInstance(v8::Local val); 35 | /* Wrap a grpc_channel_credentials struct in a javascript object */ 36 | static v8::Local WrapStruct(grpc_channel_credentials *credentials); 37 | 38 | /* Returns the grpc_channel_credentials struct that this object wraps */ 39 | grpc_channel_credentials *GetWrappedCredentials(); 40 | 41 | private: 42 | explicit ChannelCredentials(grpc_channel_credentials *credentials); 43 | ~ChannelCredentials(); 44 | 45 | // Prevent copying 46 | ChannelCredentials(const ChannelCredentials &); 47 | ChannelCredentials &operator=(const ChannelCredentials &); 48 | 49 | static NAN_METHOD(New); 50 | static NAN_METHOD(CreateSsl); 51 | static NAN_METHOD(CreateInsecure); 52 | 53 | static NAN_METHOD(Compose); 54 | static Nan::Callback *constructor; 55 | // Used for typechecking instances of this javascript class 56 | static Nan::Persistent fun_tpl; 57 | 58 | grpc_channel_credentials *wrapped_credentials; 59 | }; 60 | 61 | } // namespace node 62 | } // namespace grpc 63 | 64 | #endif // NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_ 65 | -------------------------------------------------------------------------------- /src/ext/completion_queue.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2016 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "call.h" 25 | #include "completion_queue.h" 26 | 27 | namespace grpc { 28 | namespace node { 29 | 30 | using v8::Local; 31 | using v8::Object; 32 | using v8::Value; 33 | 34 | grpc_completion_queue *queue; 35 | uv_prepare_t prepare; 36 | int pending_batches; 37 | 38 | void drain_completion_queue(uv_prepare_t *handle) { 39 | Nan::HandleScope scope; 40 | grpc_event event; 41 | (void)handle; 42 | do { 43 | event = grpc_completion_queue_next(queue, gpr_inf_past(GPR_CLOCK_MONOTONIC), 44 | NULL); 45 | 46 | if (event.type == GRPC_OP_COMPLETE) { 47 | const char *error_message; 48 | if (event.success) { 49 | error_message = NULL; 50 | } else { 51 | error_message = "The async function encountered an error"; 52 | } 53 | CompleteTag(event.tag, error_message); 54 | grpc::node::DestroyTag(event.tag); 55 | pending_batches--; 56 | if (pending_batches == 0) { 57 | uv_prepare_stop(&prepare); 58 | } 59 | } 60 | } while (event.type != GRPC_QUEUE_TIMEOUT); 61 | } 62 | 63 | grpc_completion_queue *GetCompletionQueue() { return queue; } 64 | 65 | void CompletionQueueNext() { 66 | if (pending_batches == 0) { 67 | GPR_ASSERT(!uv_is_active((uv_handle_t *)&prepare)); 68 | uv_prepare_start(&prepare, drain_completion_queue); 69 | } 70 | pending_batches++; 71 | } 72 | 73 | void CompletionQueueInit(Local exports) { 74 | queue = grpc_completion_queue_create_for_next(NULL); 75 | uv_prepare_init(uv_default_loop(), &prepare); 76 | pending_batches = 0; 77 | } 78 | 79 | } // namespace node 80 | } // namespace grpc 81 | -------------------------------------------------------------------------------- /src/ext/completion_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2016 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | namespace grpc { 23 | namespace node { 24 | 25 | grpc_completion_queue *GetCompletionQueue(); 26 | 27 | void CompletionQueueNext(); 28 | 29 | void CompletionQueueInit(v8::Local exports); 30 | 31 | } // namespace node 32 | } // namespace grpc 33 | -------------------------------------------------------------------------------- /src/ext/node_grpc.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include "grpc/grpc.h" 25 | #include "grpc/grpc_security.h" 26 | #include "grpc/support/alloc.h" 27 | #include "grpc/support/log.h" 28 | #include "grpc/support/time.h" 29 | 30 | // TODO(murgatroid99): Remove this when the endpoint API becomes public 31 | extern "C" { 32 | #include "src/core/lib/iomgr/pollset_uv.h" 33 | } 34 | 35 | #include "call.h" 36 | #include "call_credentials.h" 37 | #include "channel.h" 38 | #include "channel_credentials.h" 39 | #include "completion_queue.h" 40 | #include "server.h" 41 | #include "server_credentials.h" 42 | #include "slice.h" 43 | #include "timeval.h" 44 | 45 | using grpc::node::CreateSliceFromString; 46 | 47 | using v8::FunctionTemplate; 48 | using v8::Local; 49 | using v8::Value; 50 | using v8::Number; 51 | using v8::Object; 52 | using v8::Uint32; 53 | using v8::String; 54 | 55 | typedef struct log_args { 56 | gpr_log_func_args core_args; 57 | gpr_timespec timestamp; 58 | } log_args; 59 | 60 | typedef struct logger_state { 61 | Nan::Callback *callback; 62 | std::queue *pending_args; 63 | uv_mutex_t mutex; 64 | uv_async_t async; 65 | // Indicates that a logger has been set 66 | bool logger_set; 67 | } logger_state; 68 | 69 | logger_state grpc_logger_state; 70 | 71 | static char *pem_root_certs = NULL; 72 | 73 | void InitOpTypeConstants(Local exports) { 74 | Nan::HandleScope scope; 75 | Local op_type = Nan::New(); 76 | Nan::Set(exports, Nan::New("opType").ToLocalChecked(), op_type); 77 | Local SEND_INITIAL_METADATA( 78 | Nan::New(GRPC_OP_SEND_INITIAL_METADATA)); 79 | Nan::Set(op_type, Nan::New("SEND_INITIAL_METADATA").ToLocalChecked(), 80 | SEND_INITIAL_METADATA); 81 | Local SEND_MESSAGE(Nan::New(GRPC_OP_SEND_MESSAGE)); 82 | Nan::Set(op_type, Nan::New("SEND_MESSAGE").ToLocalChecked(), SEND_MESSAGE); 83 | Local SEND_CLOSE_FROM_CLIENT( 84 | Nan::New(GRPC_OP_SEND_CLOSE_FROM_CLIENT)); 85 | Nan::Set(op_type, Nan::New("SEND_CLOSE_FROM_CLIENT").ToLocalChecked(), 86 | SEND_CLOSE_FROM_CLIENT); 87 | Local SEND_STATUS_FROM_SERVER( 88 | Nan::New(GRPC_OP_SEND_STATUS_FROM_SERVER)); 89 | Nan::Set(op_type, Nan::New("SEND_STATUS_FROM_SERVER").ToLocalChecked(), 90 | SEND_STATUS_FROM_SERVER); 91 | Local RECV_INITIAL_METADATA( 92 | Nan::New(GRPC_OP_RECV_INITIAL_METADATA)); 93 | Nan::Set(op_type, Nan::New("RECV_INITIAL_METADATA").ToLocalChecked(), 94 | RECV_INITIAL_METADATA); 95 | Local RECV_MESSAGE(Nan::New(GRPC_OP_RECV_MESSAGE)); 96 | Nan::Set(op_type, Nan::New("RECV_MESSAGE").ToLocalChecked(), RECV_MESSAGE); 97 | Local RECV_STATUS_ON_CLIENT( 98 | Nan::New(GRPC_OP_RECV_STATUS_ON_CLIENT)); 99 | Nan::Set(op_type, Nan::New("RECV_STATUS_ON_CLIENT").ToLocalChecked(), 100 | RECV_STATUS_ON_CLIENT); 101 | Local RECV_CLOSE_ON_SERVER( 102 | Nan::New(GRPC_OP_RECV_CLOSE_ON_SERVER)); 103 | Nan::Set(op_type, Nan::New("RECV_CLOSE_ON_SERVER").ToLocalChecked(), 104 | RECV_CLOSE_ON_SERVER); 105 | } 106 | 107 | void InitConnectivityStateConstants(Local exports) { 108 | Nan::HandleScope scope; 109 | Local channel_state = Nan::New(); 110 | Nan::Set(exports, Nan::New("connectivityState").ToLocalChecked(), 111 | channel_state); 112 | Local IDLE(Nan::New(GRPC_CHANNEL_IDLE)); 113 | Nan::Set(channel_state, Nan::New("IDLE").ToLocalChecked(), IDLE); 114 | Local CONNECTING(Nan::New(GRPC_CHANNEL_CONNECTING)); 115 | Nan::Set(channel_state, Nan::New("CONNECTING").ToLocalChecked(), CONNECTING); 116 | Local READY(Nan::New(GRPC_CHANNEL_READY)); 117 | Nan::Set(channel_state, Nan::New("READY").ToLocalChecked(), READY); 118 | Local TRANSIENT_FAILURE( 119 | Nan::New(GRPC_CHANNEL_TRANSIENT_FAILURE)); 120 | Nan::Set(channel_state, Nan::New("TRANSIENT_FAILURE").ToLocalChecked(), 121 | TRANSIENT_FAILURE); 122 | Local FATAL_FAILURE(Nan::New(GRPC_CHANNEL_SHUTDOWN)); 123 | Nan::Set(channel_state, Nan::New("FATAL_FAILURE").ToLocalChecked(), 124 | FATAL_FAILURE); 125 | } 126 | 127 | NAN_METHOD(MetadataKeyIsLegal) { 128 | if (!info[0]->IsString()) { 129 | return Nan::ThrowTypeError("headerKeyIsLegal's argument must be a string"); 130 | } 131 | Local key = Nan::To(info[0]).ToLocalChecked(); 132 | grpc_slice slice = CreateSliceFromString(key); 133 | info.GetReturnValue().Set(static_cast(grpc_header_key_is_legal(slice))); 134 | grpc_slice_unref(slice); 135 | } 136 | 137 | NAN_METHOD(MetadataNonbinValueIsLegal) { 138 | if (!info[0]->IsString()) { 139 | return Nan::ThrowTypeError( 140 | "metadataNonbinValueIsLegal's argument must be a string"); 141 | } 142 | Local value = Nan::To(info[0]).ToLocalChecked(); 143 | grpc_slice slice = CreateSliceFromString(value); 144 | info.GetReturnValue().Set( 145 | static_cast(grpc_header_nonbin_value_is_legal(slice))); 146 | grpc_slice_unref(slice); 147 | } 148 | 149 | NAN_METHOD(MetadataKeyIsBinary) { 150 | if (!info[0]->IsString()) { 151 | return Nan::ThrowTypeError( 152 | "metadataKeyIsLegal's argument must be a string"); 153 | } 154 | Local key = Nan::To(info[0]).ToLocalChecked(); 155 | grpc_slice slice = CreateSliceFromString(key); 156 | info.GetReturnValue().Set(static_cast(grpc_is_binary_header(slice))); 157 | grpc_slice_unref(slice); 158 | } 159 | 160 | static grpc_ssl_roots_override_result get_ssl_roots_override( 161 | char **pem_root_certs_ptr) { 162 | *pem_root_certs_ptr = pem_root_certs; 163 | if (pem_root_certs == NULL) { 164 | return GRPC_SSL_ROOTS_OVERRIDE_FAIL; 165 | } else { 166 | return GRPC_SSL_ROOTS_OVERRIDE_OK; 167 | } 168 | } 169 | 170 | /* This should only be called once, and only before creating any 171 | *ServerCredentials */ 172 | NAN_METHOD(SetDefaultRootsPem) { 173 | if (!info[0]->IsString()) { 174 | return Nan::ThrowTypeError( 175 | "setDefaultRootsPem's argument must be a string"); 176 | } 177 | Nan::Utf8String utf8_roots(info[0]); 178 | size_t length = static_cast(utf8_roots.length()); 179 | if (length > 0) { 180 | const char *data = *utf8_roots; 181 | pem_root_certs = (char *)gpr_malloc((length + 1) * sizeof(char)); 182 | memcpy(pem_root_certs, data, length + 1); 183 | } 184 | } 185 | 186 | NAUV_WORK_CB(LogMessagesCallback) { 187 | Nan::HandleScope scope; 188 | std::queue args; 189 | uv_mutex_lock(&grpc_logger_state.mutex); 190 | grpc_logger_state.pending_args->swap(args); 191 | uv_mutex_unlock(&grpc_logger_state.mutex); 192 | /* Call the callback with each log message */ 193 | while (!args.empty()) { 194 | log_args *arg = args.front(); 195 | args.pop(); 196 | Local file = Nan::New(arg->core_args.file).ToLocalChecked(); 197 | Local line = Nan::New(arg->core_args.line); 198 | Local severity = 199 | Nan::New(gpr_log_severity_string(arg->core_args.severity)) 200 | .ToLocalChecked(); 201 | Local message = Nan::New(arg->core_args.message).ToLocalChecked(); 202 | Local timestamp = 203 | Nan::New(grpc::node::TimespecToMilliseconds(arg->timestamp)) 204 | .ToLocalChecked(); 205 | const int argc = 5; 206 | Local argv[argc] = {file, line, severity, message, timestamp}; 207 | grpc_logger_state.callback->Call(argc, argv); 208 | delete[] arg->core_args.message; 209 | delete arg; 210 | } 211 | } 212 | 213 | void node_log_func(gpr_log_func_args *args) { 214 | // TODO(mlumish): Use the core's log formatter when it becomes available 215 | log_args *args_copy = new log_args; 216 | size_t message_len = strlen(args->message) + 1; 217 | char *message = new char[message_len]; 218 | memcpy(message, args->message, message_len); 219 | memcpy(&args_copy->core_args, args, sizeof(gpr_log_func_args)); 220 | args_copy->core_args.message = message; 221 | args_copy->timestamp = gpr_now(GPR_CLOCK_REALTIME); 222 | 223 | uv_mutex_lock(&grpc_logger_state.mutex); 224 | grpc_logger_state.pending_args->push(args_copy); 225 | uv_mutex_unlock(&grpc_logger_state.mutex); 226 | 227 | uv_async_send(&grpc_logger_state.async); 228 | } 229 | 230 | void init_logger() { 231 | memset(&grpc_logger_state, 0, sizeof(logger_state)); 232 | grpc_logger_state.pending_args = new std::queue(); 233 | uv_mutex_init(&grpc_logger_state.mutex); 234 | uv_async_init(uv_default_loop(), &grpc_logger_state.async, 235 | LogMessagesCallback); 236 | uv_unref((uv_handle_t *)&grpc_logger_state.async); 237 | grpc_logger_state.logger_set = false; 238 | 239 | gpr_log_verbosity_init(); 240 | } 241 | 242 | /* This registers a JavaScript logger for messages from the gRPC core. Because 243 | that handler has to be run in the context of the JavaScript event loop, it 244 | will be run asynchronously. To minimize the problems that could cause for 245 | debugging, we leave core to do its default synchronous logging until a 246 | JavaScript logger is set */ 247 | NAN_METHOD(SetDefaultLoggerCallback) { 248 | if (!info[0]->IsFunction()) { 249 | return Nan::ThrowTypeError( 250 | "setDefaultLoggerCallback's argument must be a function"); 251 | } 252 | if (!grpc_logger_state.logger_set) { 253 | gpr_set_log_function(node_log_func); 254 | grpc_logger_state.logger_set = true; 255 | } 256 | grpc_logger_state.callback = new Nan::Callback(info[0].As()); 257 | } 258 | 259 | NAN_METHOD(SetLogVerbosity) { 260 | if (!info[0]->IsUint32()) { 261 | return Nan::ThrowTypeError("setLogVerbosity's argument must be a number"); 262 | } 263 | gpr_log_severity severity = 264 | static_cast(Nan::To(info[0]).FromJust()); 265 | gpr_set_log_verbosity(severity); 266 | } 267 | 268 | void init(Local exports) { 269 | Nan::HandleScope scope; 270 | grpc_init(); 271 | grpc_set_ssl_roots_override_callback(get_ssl_roots_override); 272 | init_logger(); 273 | 274 | InitOpTypeConstants(exports); 275 | InitConnectivityStateConstants(exports); 276 | 277 | grpc_pollset_work_run_loop = 0; 278 | 279 | grpc::node::Call::Init(exports); 280 | grpc::node::CallCredentials::Init(exports); 281 | grpc::node::Channel::Init(exports); 282 | grpc::node::ChannelCredentials::Init(exports); 283 | grpc::node::Server::Init(exports); 284 | grpc::node::ServerCredentials::Init(exports); 285 | 286 | grpc::node::CompletionQueueInit(exports); 287 | 288 | // Attach a few utility functions directly to the module 289 | Nan::Set(exports, Nan::New("metadataKeyIsLegal").ToLocalChecked(), 290 | Nan::GetFunction(Nan::New(MetadataKeyIsLegal)) 291 | .ToLocalChecked()); 292 | Nan::Set( 293 | exports, Nan::New("metadataNonbinValueIsLegal").ToLocalChecked(), 294 | Nan::GetFunction(Nan::New(MetadataNonbinValueIsLegal)) 295 | .ToLocalChecked()); 296 | Nan::Set(exports, Nan::New("metadataKeyIsBinary").ToLocalChecked(), 297 | Nan::GetFunction(Nan::New(MetadataKeyIsBinary)) 298 | .ToLocalChecked()); 299 | Nan::Set(exports, Nan::New("setDefaultRootsPem").ToLocalChecked(), 300 | Nan::GetFunction(Nan::New(SetDefaultRootsPem)) 301 | .ToLocalChecked()); 302 | Nan::Set( 303 | exports, Nan::New("setDefaultLoggerCallback").ToLocalChecked(), 304 | Nan::GetFunction(Nan::New(SetDefaultLoggerCallback)) 305 | .ToLocalChecked()); 306 | Nan::Set(exports, Nan::New("setLogVerbosity").ToLocalChecked(), 307 | Nan::GetFunction(Nan::New(SetLogVerbosity)) 308 | .ToLocalChecked()); 309 | } 310 | 311 | NODE_MODULE(grpc_node, init) 312 | -------------------------------------------------------------------------------- /src/ext/server.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | 21 | #include "server.h" 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include "call.h" 28 | #include "completion_queue.h" 29 | #include "grpc/grpc.h" 30 | #include "grpc/grpc_security.h" 31 | #include "grpc/support/log.h" 32 | #include "server_credentials.h" 33 | #include "slice.h" 34 | #include "timeval.h" 35 | 36 | namespace grpc { 37 | namespace node { 38 | 39 | using Nan::Callback; 40 | using Nan::EscapableHandleScope; 41 | using Nan::HandleScope; 42 | using Nan::Maybe; 43 | using Nan::MaybeLocal; 44 | using Nan::ObjectWrap; 45 | using Nan::Persistent; 46 | using Nan::Utf8String; 47 | 48 | using std::unique_ptr; 49 | using v8::Array; 50 | using v8::Boolean; 51 | using v8::Date; 52 | using v8::Exception; 53 | using v8::External; 54 | using v8::Function; 55 | using v8::FunctionTemplate; 56 | using v8::Local; 57 | using v8::Number; 58 | using v8::Object; 59 | using v8::String; 60 | using v8::Value; 61 | 62 | Nan::Callback *Server::constructor; 63 | Persistent Server::fun_tpl; 64 | 65 | static Callback *shutdown_callback = NULL; 66 | 67 | class ServerShutdownOp : public Op { 68 | public: 69 | ServerShutdownOp(grpc_server *server) : server(server) {} 70 | 71 | ~ServerShutdownOp() {} 72 | 73 | Local GetNodeValue() const { return Nan::Null(); } 74 | 75 | bool ParseOp(Local value, grpc_op *out) { return true; } 76 | bool IsFinalOp() { return false; } 77 | void OnComplete(bool success) { 78 | /* Because cancel_all_calls was called, we assume that shutdown_and_notify 79 | completes successfully */ 80 | grpc_server_destroy(server); 81 | } 82 | 83 | grpc_server *server; 84 | 85 | protected: 86 | std::string GetTypeString() const { return "shutdown"; } 87 | }; 88 | 89 | class NewCallOp : public Op { 90 | public: 91 | NewCallOp() { 92 | call = NULL; 93 | grpc_call_details_init(&details); 94 | grpc_metadata_array_init(&request_metadata); 95 | } 96 | 97 | ~NewCallOp() { 98 | grpc_call_details_destroy(&details); 99 | grpc_metadata_array_destroy(&request_metadata); 100 | } 101 | 102 | Local GetNodeValue() const { 103 | Nan::EscapableHandleScope scope; 104 | if (call == NULL) { 105 | return scope.Escape(Nan::Null()); 106 | } 107 | Local obj = Nan::New(); 108 | Nan::Set(obj, Nan::New("call").ToLocalChecked(), Call::WrapStruct(call)); 109 | // TODO(murgatroid99): Use zero-copy string construction instead 110 | Nan::Set(obj, Nan::New("method").ToLocalChecked(), 111 | CopyStringFromSlice(details.method)); 112 | Nan::Set(obj, Nan::New("host").ToLocalChecked(), 113 | CopyStringFromSlice(details.host)); 114 | Nan::Set(obj, Nan::New("deadline").ToLocalChecked(), 115 | Nan::New(TimespecToMilliseconds(details.deadline)) 116 | .ToLocalChecked()); 117 | Nan::Set(obj, Nan::New("metadata").ToLocalChecked(), 118 | ParseMetadata(&request_metadata)); 119 | return scope.Escape(obj); 120 | } 121 | 122 | bool ParseOp(Local value, grpc_op *out) { return true; } 123 | bool IsFinalOp() { return false; } 124 | void OnComplete(bool success) {} 125 | 126 | grpc_call *call; 127 | grpc_call_details details; 128 | grpc_metadata_array request_metadata; 129 | 130 | protected: 131 | std::string GetTypeString() const { return "new_call"; } 132 | }; 133 | 134 | class TryShutdownOp : public Op { 135 | public: 136 | TryShutdownOp(Server *server, Local server_value) : server(server) { 137 | server_persist.Reset(server_value); 138 | } 139 | Local GetNodeValue() const { 140 | EscapableHandleScope scope; 141 | return scope.Escape(Nan::New(server_persist)); 142 | } 143 | bool ParseOp(Local value, grpc_op *out) { return true; } 144 | bool IsFinalOp() { return false; } 145 | void OnComplete(bool success) { 146 | if (success) { 147 | server->DestroyWrappedServer(); 148 | } 149 | } 150 | 151 | protected: 152 | std::string GetTypeString() const { return "try_shutdown"; } 153 | 154 | private: 155 | Server *server; 156 | Nan::Persistent> 157 | server_persist; 158 | }; 159 | 160 | Server::Server(grpc_server *server) : wrapped_server(server) {} 161 | 162 | Server::~Server() { this->ShutdownServer(); } 163 | 164 | void Server::Init(Local exports) { 165 | HandleScope scope; 166 | Local tpl = Nan::New(New); 167 | tpl->SetClassName(Nan::New("Server").ToLocalChecked()); 168 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 169 | Nan::SetPrototypeMethod(tpl, "requestCall", RequestCall); 170 | Nan::SetPrototypeMethod(tpl, "addHttp2Port", AddHttp2Port); 171 | Nan::SetPrototypeMethod(tpl, "start", Start); 172 | Nan::SetPrototypeMethod(tpl, "tryShutdown", TryShutdown); 173 | Nan::SetPrototypeMethod(tpl, "forceShutdown", ForceShutdown); 174 | fun_tpl.Reset(tpl); 175 | Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); 176 | Nan::Set(exports, Nan::New("Server").ToLocalChecked(), ctr); 177 | constructor = new Callback(ctr); 178 | } 179 | 180 | bool Server::HasInstance(Local val) { 181 | HandleScope scope; 182 | return Nan::New(fun_tpl)->HasInstance(val); 183 | } 184 | 185 | void Server::DestroyWrappedServer() { 186 | if (this->wrapped_server != NULL) { 187 | grpc_server_destroy(this->wrapped_server); 188 | this->wrapped_server = NULL; 189 | } 190 | } 191 | 192 | NAN_METHOD(ServerShutdownCallback) { 193 | if (!info[0]->IsNull()) { 194 | return Nan::ThrowError("forceShutdown failed somehow"); 195 | } 196 | } 197 | 198 | void Server::ShutdownServer() { 199 | Nan::HandleScope scope; 200 | if (this->wrapped_server != NULL) { 201 | if (shutdown_callback == NULL) { 202 | Local callback_tpl = 203 | Nan::New(ServerShutdownCallback); 204 | shutdown_callback = 205 | new Callback(Nan::GetFunction(callback_tpl).ToLocalChecked()); 206 | } 207 | 208 | ServerShutdownOp *op = new ServerShutdownOp(this->wrapped_server); 209 | unique_ptr ops(new OpVec()); 210 | ops->push_back(unique_ptr(op)); 211 | 212 | grpc_server_shutdown_and_notify( 213 | this->wrapped_server, GetCompletionQueue(), 214 | new struct tag(new Callback(**shutdown_callback), ops.release(), NULL, 215 | Nan::Null())); 216 | grpc_server_cancel_all_calls(this->wrapped_server); 217 | CompletionQueueNext(); 218 | this->wrapped_server = NULL; 219 | } 220 | } 221 | 222 | NAN_METHOD(Server::New) { 223 | /* If this is not a constructor call, make a constructor call and return 224 | the result */ 225 | if (!info.IsConstructCall()) { 226 | const int argc = 1; 227 | Local argv[argc] = {info[0]}; 228 | MaybeLocal maybe_instance = 229 | Nan::NewInstance(constructor->GetFunction(), argc, argv); 230 | if (maybe_instance.IsEmpty()) { 231 | // There's probably a pending exception 232 | return; 233 | } else { 234 | info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); 235 | return; 236 | } 237 | } 238 | grpc_server *wrapped_server; 239 | grpc_completion_queue *queue = GetCompletionQueue(); 240 | grpc_channel_args *channel_args; 241 | if (!ParseChannelArgs(info[0], &channel_args)) { 242 | DeallocateChannelArgs(channel_args); 243 | return Nan::ThrowTypeError( 244 | "Server options must be an object with " 245 | "string keys and integer or string values"); 246 | } 247 | wrapped_server = grpc_server_create(channel_args, NULL); 248 | DeallocateChannelArgs(channel_args); 249 | grpc_server_register_completion_queue(wrapped_server, queue, NULL); 250 | Server *server = new Server(wrapped_server); 251 | server->Wrap(info.This()); 252 | info.GetReturnValue().Set(info.This()); 253 | } 254 | 255 | NAN_METHOD(Server::RequestCall) { 256 | if (!HasInstance(info.This())) { 257 | return Nan::ThrowTypeError("requestCall can only be called on a Server"); 258 | } 259 | Server *server = ObjectWrap::Unwrap(info.This()); 260 | NewCallOp *op = new NewCallOp(); 261 | unique_ptr ops(new OpVec()); 262 | ops->push_back(unique_ptr(op)); 263 | grpc_call_error error = grpc_server_request_call( 264 | server->wrapped_server, &op->call, &op->details, &op->request_metadata, 265 | GetCompletionQueue(), GetCompletionQueue(), 266 | new struct tag(new Callback(info[0].As()), ops.release(), NULL, 267 | Nan::Null())); 268 | if (error != GRPC_CALL_OK) { 269 | return Nan::ThrowError(nanErrorWithCode("requestCall failed", error)); 270 | } 271 | CompletionQueueNext(); 272 | } 273 | 274 | NAN_METHOD(Server::AddHttp2Port) { 275 | if (!HasInstance(info.This())) { 276 | return Nan::ThrowTypeError("addHttp2Port can only be called on a Server"); 277 | } 278 | if (!info[0]->IsString()) { 279 | return Nan::ThrowTypeError( 280 | "addHttp2Port's first argument must be a String"); 281 | } 282 | if (!ServerCredentials::HasInstance(info[1])) { 283 | return Nan::ThrowTypeError( 284 | "addHttp2Port's second argument must be ServerCredentials"); 285 | } 286 | Server *server = ObjectWrap::Unwrap(info.This()); 287 | ServerCredentials *creds_object = ObjectWrap::Unwrap( 288 | Nan::To(info[1]).ToLocalChecked()); 289 | grpc_server_credentials *creds = creds_object->GetWrappedServerCredentials(); 290 | int port; 291 | if (creds == NULL) { 292 | port = grpc_server_add_insecure_http2_port(server->wrapped_server, 293 | *Utf8String(info[0])); 294 | } else { 295 | port = grpc_server_add_secure_http2_port(server->wrapped_server, 296 | *Utf8String(info[0]), creds); 297 | } 298 | info.GetReturnValue().Set(Nan::New(port)); 299 | } 300 | 301 | NAN_METHOD(Server::Start) { 302 | Nan::HandleScope scope; 303 | if (!HasInstance(info.This())) { 304 | return Nan::ThrowTypeError("start can only be called on a Server"); 305 | } 306 | Server *server = ObjectWrap::Unwrap(info.This()); 307 | grpc_server_start(server->wrapped_server); 308 | } 309 | 310 | NAN_METHOD(Server::TryShutdown) { 311 | Nan::HandleScope scope; 312 | if (!HasInstance(info.This())) { 313 | return Nan::ThrowTypeError("tryShutdown can only be called on a Server"); 314 | } 315 | Server *server = ObjectWrap::Unwrap(info.This()); 316 | if (server->wrapped_server == NULL) { 317 | // Server is already shut down. Call callback immediately. 318 | Nan::Callback callback(info[0].As()); 319 | callback.Call(0, {}); 320 | return; 321 | } 322 | TryShutdownOp *op = new TryShutdownOp(server, info.This()); 323 | unique_ptr ops(new OpVec()); 324 | ops->push_back(unique_ptr(op)); 325 | grpc_server_shutdown_and_notify( 326 | server->wrapped_server, GetCompletionQueue(), 327 | new struct tag(new Nan::Callback(info[0].As()), ops.release(), 328 | NULL, Nan::Null())); 329 | CompletionQueueNext(); 330 | } 331 | 332 | NAN_METHOD(Server::ForceShutdown) { 333 | Nan::HandleScope scope; 334 | if (!HasInstance(info.This())) { 335 | return Nan::ThrowTypeError("forceShutdown can only be called on a Server"); 336 | } 337 | Server *server = ObjectWrap::Unwrap(info.This()); 338 | server->ShutdownServer(); 339 | } 340 | 341 | } // namespace node 342 | } // namespace grpc 343 | -------------------------------------------------------------------------------- /src/ext/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef NET_GRPC_NODE_SERVER_H_ 20 | #define NET_GRPC_NODE_SERVER_H_ 21 | 22 | #include 23 | #include 24 | #include "grpc/grpc.h" 25 | 26 | namespace grpc { 27 | namespace node { 28 | 29 | /* Wraps grpc_server as a JavaScript object. Provides a constructor 30 | and wrapper methods for grpc_server_create, grpc_server_request_call, 31 | grpc_server_add_http2_port, and grpc_server_start. */ 32 | class Server : public Nan::ObjectWrap { 33 | public: 34 | /* Initializes the Server class and exposes the constructor and 35 | wrapper methods to JavaScript */ 36 | static void Init(v8::Local exports); 37 | /* Tests whether the given value was constructed by this class's 38 | JavaScript constructor */ 39 | static bool HasInstance(v8::Local val); 40 | 41 | void DestroyWrappedServer(); 42 | 43 | private: 44 | explicit Server(grpc_server *server); 45 | ~Server(); 46 | 47 | // Prevent copying 48 | Server(const Server &); 49 | Server &operator=(const Server &); 50 | 51 | void ShutdownServer(); 52 | 53 | static NAN_METHOD(New); 54 | static NAN_METHOD(RequestCall); 55 | static NAN_METHOD(AddHttp2Port); 56 | static NAN_METHOD(Start); 57 | static NAN_METHOD(TryShutdown); 58 | static NAN_METHOD(ForceShutdown); 59 | static Nan::Callback *constructor; 60 | static Nan::Persistent fun_tpl; 61 | 62 | grpc_server *wrapped_server; 63 | grpc_completion_queue *shutdown_queue; 64 | }; 65 | 66 | } // namespace node 67 | } // namespace grpc 68 | 69 | #endif // NET_GRPC_NODE_SERVER_H_ 70 | -------------------------------------------------------------------------------- /src/ext/server_credentials.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | 21 | #include "grpc/grpc.h" 22 | #include "grpc/grpc_security.h" 23 | #include "grpc/support/log.h" 24 | #include "server_credentials.h" 25 | 26 | namespace grpc { 27 | namespace node { 28 | 29 | using Nan::Callback; 30 | using Nan::EscapableHandleScope; 31 | using Nan::HandleScope; 32 | using Nan::Maybe; 33 | using Nan::MaybeLocal; 34 | using Nan::ObjectWrap; 35 | using Nan::Persistent; 36 | using Nan::Utf8String; 37 | 38 | using v8::Array; 39 | using v8::Exception; 40 | using v8::External; 41 | using v8::Function; 42 | using v8::FunctionTemplate; 43 | using v8::Integer; 44 | using v8::Local; 45 | using v8::Object; 46 | using v8::ObjectTemplate; 47 | using v8::String; 48 | using v8::Value; 49 | 50 | Nan::Callback *ServerCredentials::constructor; 51 | Persistent ServerCredentials::fun_tpl; 52 | 53 | ServerCredentials::ServerCredentials(grpc_server_credentials *credentials) 54 | : wrapped_credentials(credentials) {} 55 | 56 | ServerCredentials::~ServerCredentials() { 57 | grpc_server_credentials_release(wrapped_credentials); 58 | } 59 | 60 | void ServerCredentials::Init(Local exports) { 61 | Nan::HandleScope scope; 62 | Local tpl = Nan::New(New); 63 | tpl->SetClassName(Nan::New("ServerCredentials").ToLocalChecked()); 64 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 65 | Local ctr = tpl->GetFunction(); 66 | Nan::Set( 67 | ctr, Nan::New("createSsl").ToLocalChecked(), 68 | Nan::GetFunction(Nan::New(CreateSsl)).ToLocalChecked()); 69 | Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(), 70 | Nan::GetFunction(Nan::New(CreateInsecure)) 71 | .ToLocalChecked()); 72 | fun_tpl.Reset(tpl); 73 | constructor = new Nan::Callback(ctr); 74 | Nan::Set(exports, Nan::New("ServerCredentials").ToLocalChecked(), ctr); 75 | } 76 | 77 | bool ServerCredentials::HasInstance(Local val) { 78 | Nan::HandleScope scope; 79 | return Nan::New(fun_tpl)->HasInstance(val); 80 | } 81 | 82 | Local ServerCredentials::WrapStruct( 83 | grpc_server_credentials *credentials) { 84 | Nan::EscapableHandleScope scope; 85 | const int argc = 1; 86 | Local argv[argc] = { 87 | Nan::New(reinterpret_cast(credentials))}; 88 | MaybeLocal maybe_instance = 89 | Nan::NewInstance(constructor->GetFunction(), argc, argv); 90 | if (maybe_instance.IsEmpty()) { 91 | return scope.Escape(Nan::Null()); 92 | } else { 93 | return scope.Escape(maybe_instance.ToLocalChecked()); 94 | } 95 | } 96 | 97 | grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() { 98 | return wrapped_credentials; 99 | } 100 | 101 | NAN_METHOD(ServerCredentials::New) { 102 | if (info.IsConstructCall()) { 103 | if (!info[0]->IsExternal()) { 104 | return Nan::ThrowTypeError( 105 | "ServerCredentials can only be created with the provided functions"); 106 | } 107 | Local ext = info[0].As(); 108 | grpc_server_credentials *creds_value = 109 | reinterpret_cast(ext->Value()); 110 | ServerCredentials *credentials = new ServerCredentials(creds_value); 111 | credentials->Wrap(info.This()); 112 | info.GetReturnValue().Set(info.This()); 113 | } else { 114 | // This should never be called directly 115 | return Nan::ThrowTypeError( 116 | "ServerCredentials can only be created with the provided functions"); 117 | } 118 | } 119 | 120 | NAN_METHOD(ServerCredentials::CreateSsl) { 121 | Nan::HandleScope scope; 122 | char *root_certs = NULL; 123 | if (::node::Buffer::HasInstance(info[0])) { 124 | root_certs = ::node::Buffer::Data(info[0]); 125 | } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) { 126 | return Nan::ThrowTypeError( 127 | "createSSl's first argument must be a Buffer if provided"); 128 | } 129 | if (!info[1]->IsArray()) { 130 | return Nan::ThrowTypeError( 131 | "createSsl's second argument must be a list of objects"); 132 | } 133 | 134 | // Default to not requesting the client certificate 135 | grpc_ssl_client_certificate_request_type client_certificate_request = 136 | GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE; 137 | if (info[2]->IsBoolean()) { 138 | client_certificate_request = 139 | Nan::To(info[2]).FromJust() 140 | ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY 141 | : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE; 142 | } else if (!(info[2]->IsUndefined() || info[2]->IsNull())) { 143 | return Nan::ThrowTypeError( 144 | "createSsl's third argument must be a boolean if provided"); 145 | } 146 | Local pair_list = Local::Cast(info[1]); 147 | uint32_t key_cert_pair_count = pair_list->Length(); 148 | grpc_ssl_pem_key_cert_pair *key_cert_pairs = 149 | new grpc_ssl_pem_key_cert_pair[key_cert_pair_count]; 150 | 151 | Local key_key = Nan::New("private_key").ToLocalChecked(); 152 | Local cert_key = Nan::New("cert_chain").ToLocalChecked(); 153 | 154 | for (uint32_t i = 0; i < key_cert_pair_count; i++) { 155 | Local pair_val = Nan::Get(pair_list, i).ToLocalChecked(); 156 | if (!pair_val->IsObject()) { 157 | delete[] key_cert_pairs; 158 | return Nan::ThrowTypeError("Key/cert pairs must be objects"); 159 | } 160 | Local pair_obj = Nan::To(pair_val).ToLocalChecked(); 161 | Local maybe_key = Nan::Get(pair_obj, key_key).ToLocalChecked(); 162 | Local maybe_cert = Nan::Get(pair_obj, cert_key).ToLocalChecked(); 163 | if (!::node::Buffer::HasInstance(maybe_key)) { 164 | delete[] key_cert_pairs; 165 | return Nan::ThrowTypeError("private_key must be a Buffer"); 166 | } 167 | if (!::node::Buffer::HasInstance(maybe_cert)) { 168 | delete[] key_cert_pairs; 169 | return Nan::ThrowTypeError("cert_chain must be a Buffer"); 170 | } 171 | key_cert_pairs[i].private_key = ::node::Buffer::Data(maybe_key); 172 | key_cert_pairs[i].cert_chain = ::node::Buffer::Data(maybe_cert); 173 | } 174 | grpc_server_credentials *creds = grpc_ssl_server_credentials_create_ex( 175 | root_certs, key_cert_pairs, key_cert_pair_count, 176 | client_certificate_request, NULL); 177 | delete[] key_cert_pairs; 178 | if (creds == NULL) { 179 | info.GetReturnValue().SetNull(); 180 | } else { 181 | info.GetReturnValue().Set(WrapStruct(creds)); 182 | } 183 | } 184 | 185 | NAN_METHOD(ServerCredentials::CreateInsecure) { 186 | info.GetReturnValue().Set(WrapStruct(NULL)); 187 | } 188 | 189 | } // namespace node 190 | } // namespace grpc 191 | -------------------------------------------------------------------------------- /src/ext/server_credentials.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef NET_GRPC_NODE_SERVER_CREDENTIALS_H_ 20 | #define NET_GRPC_NODE_SERVER_CREDENTIALS_H_ 21 | 22 | #include 23 | #include 24 | #include "grpc/grpc.h" 25 | #include "grpc/grpc_security.h" 26 | 27 | namespace grpc { 28 | namespace node { 29 | 30 | /* Wrapper class for grpc_server_credentials structs */ 31 | class ServerCredentials : public Nan::ObjectWrap { 32 | public: 33 | static void Init(v8::Local exports); 34 | static bool HasInstance(v8::Local val); 35 | /* Wrap a grpc_server_credentials struct in a javascript object */ 36 | static v8::Local WrapStruct(grpc_server_credentials *credentials); 37 | 38 | /* Returns the grpc_server_credentials struct that this object wraps */ 39 | grpc_server_credentials *GetWrappedServerCredentials(); 40 | 41 | private: 42 | explicit ServerCredentials(grpc_server_credentials *credentials); 43 | ~ServerCredentials(); 44 | 45 | // Prevent copying 46 | ServerCredentials(const ServerCredentials &); 47 | ServerCredentials &operator=(const ServerCredentials &); 48 | 49 | static NAN_METHOD(New); 50 | static NAN_METHOD(CreateSsl); 51 | static NAN_METHOD(CreateInsecure); 52 | static Nan::Callback *constructor; 53 | // Used for typechecking instances of this javascript class 54 | static Nan::Persistent fun_tpl; 55 | 56 | grpc_server_credentials *wrapped_credentials; 57 | }; 58 | 59 | } // namespace node 60 | } // namespace grpc 61 | 62 | #endif // NET_GRPC_NODE_SERVER_CREDENTIALS_H_ 63 | -------------------------------------------------------------------------------- /src/ext/slice.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2016 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "slice.h" 25 | 26 | namespace grpc { 27 | namespace node { 28 | 29 | using Nan::Persistent; 30 | 31 | using v8::Local; 32 | using v8::String; 33 | using v8::Value; 34 | 35 | namespace { 36 | void SliceFreeCallback(char *data, void *hint) { 37 | grpc_slice *slice = reinterpret_cast(hint); 38 | grpc_slice_unref(*slice); 39 | delete slice; 40 | } 41 | 42 | void string_destroy_func(void *user_data) { 43 | delete reinterpret_cast(user_data); 44 | } 45 | 46 | void buffer_destroy_func(void *user_data) { 47 | delete reinterpret_cast(user_data); 48 | } 49 | } // namespace 50 | 51 | grpc_slice CreateSliceFromString(const Local source) { 52 | Nan::HandleScope scope; 53 | Nan::Utf8String *utf8_value = new Nan::Utf8String(source); 54 | return grpc_slice_new_with_user_data(**utf8_value, source->Length(), 55 | string_destroy_func, utf8_value); 56 | } 57 | 58 | grpc_slice CreateSliceFromBuffer(const Local source) { 59 | // Prerequisite: ::node::Buffer::HasInstance(source) 60 | Nan::HandleScope scope; 61 | return grpc_slice_new_with_user_data( 62 | ::node::Buffer::Data(source), ::node::Buffer::Length(source), 63 | buffer_destroy_func, new PersistentValue(source)); 64 | } 65 | Local CopyStringFromSlice(const grpc_slice slice) { 66 | Nan::EscapableHandleScope scope; 67 | if (GRPC_SLICE_LENGTH(slice) == 0) { 68 | return scope.Escape(Nan::EmptyString()); 69 | } 70 | return scope.Escape( 71 | Nan::New(const_cast(reinterpret_cast( 72 | GRPC_SLICE_START_PTR(slice))), 73 | GRPC_SLICE_LENGTH(slice)) 74 | .ToLocalChecked()); 75 | } 76 | 77 | Local CreateBufferFromSlice(const grpc_slice slice) { 78 | Nan::EscapableHandleScope scope; 79 | grpc_slice *slice_ptr = new grpc_slice; 80 | *slice_ptr = grpc_slice_ref(slice); 81 | return scope.Escape( 82 | Nan::NewBuffer( 83 | const_cast( 84 | reinterpret_cast(GRPC_SLICE_START_PTR(*slice_ptr))), 85 | GRPC_SLICE_LENGTH(*slice_ptr), SliceFreeCallback, slice_ptr) 86 | .ToLocalChecked()); 87 | } 88 | 89 | } // namespace node 90 | } // namespace grpc 91 | -------------------------------------------------------------------------------- /src/ext/slice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2016 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace grpc { 24 | namespace node { 25 | 26 | typedef Nan::Persistent> 27 | PersistentValue; 28 | 29 | grpc_slice CreateSliceFromString(const v8::Local source); 30 | 31 | grpc_slice CreateSliceFromBuffer(const v8::Local source); 32 | 33 | v8::Local CopyStringFromSlice(const grpc_slice slice); 34 | 35 | v8::Local CreateBufferFromSlice(const grpc_slice slice); 36 | 37 | } // namespace node 38 | } // namespace grpc 39 | -------------------------------------------------------------------------------- /src/ext/timeval.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "grpc/grpc.h" 23 | #include "grpc/support/time.h" 24 | #include "timeval.h" 25 | 26 | namespace grpc { 27 | namespace node { 28 | 29 | gpr_timespec MillisecondsToTimespec(double millis) { 30 | if (millis == std::numeric_limits::infinity()) { 31 | return gpr_inf_future(GPR_CLOCK_REALTIME); 32 | } else if (millis == -std::numeric_limits::infinity()) { 33 | return gpr_inf_past(GPR_CLOCK_REALTIME); 34 | } else { 35 | return gpr_time_from_micros(static_cast(millis * 1000), 36 | GPR_CLOCK_REALTIME); 37 | } 38 | } 39 | 40 | double TimespecToMilliseconds(gpr_timespec timespec) { 41 | timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); 42 | if (gpr_time_cmp(timespec, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { 43 | return std::numeric_limits::infinity(); 44 | } else if (gpr_time_cmp(timespec, gpr_inf_past(GPR_CLOCK_REALTIME)) == 0) { 45 | return -std::numeric_limits::infinity(); 46 | } else { 47 | return (static_cast(timespec.tv_sec) * 1000 + 48 | static_cast(timespec.tv_nsec) / 1000000); 49 | } 50 | } 51 | 52 | } // namespace node 53 | } // namespace grpc 54 | -------------------------------------------------------------------------------- /src/ext/timeval.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef NET_GRPC_NODE_TIMEVAL_H_ 20 | #define NET_GRPC_NODE_TIMEVAL_H_ 21 | 22 | #include "grpc/support/time.h" 23 | 24 | namespace grpc { 25 | namespace node { 26 | 27 | double TimespecToMilliseconds(gpr_timespec time); 28 | gpr_timespec MillisecondsToTimespec(double millis); 29 | 30 | } // namespace node 31 | } // namespace grpc 32 | 33 | #endif // NET_GRPC_NODE_TIMEVAL_H_ 34 | -------------------------------------------------------------------------------- /src/onload.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void R_init_grpc(DllInfo *info){ 5 | grpc_init(); 6 | } 7 | 8 | 9 | void R_unload_grpc(DllInfo *info){ 10 | 11 | grpc_shutdown(); 12 | } -------------------------------------------------------------------------------- /src/rcpp_hello.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace Rcpp; 5 | 6 | //' Check grpc version 7 | //' @return version string and what g stands for 8 | //' @export 9 | // [[Rcpp::export]] 10 | CharacterVector grpc_version() { 11 | return CharacterVector::create(grpc_version_string(), grpc_g_stands_for()); 12 | } 13 | -------------------------------------------------------------------------------- /src/server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include 6 | #include 7 | 8 | using namespace Rcpp; 9 | 10 | #include 11 | using grpc::Status; 12 | using grpc::StatusCode; 13 | 14 | #define _INTERRUPT_CHECK_PERIOD_MS 1000 15 | 16 | CharacterVector sliceToChar(grpc_slice slice){ 17 | 18 | char* data = grpc_slice_to_c_string(slice); 19 | 20 | CharacterVector out(1); 21 | out[0] = data; 22 | 23 | return out; 24 | } 25 | 26 | RawVector sliceToRaw(grpc_slice slice){ 27 | 28 | int n = GRPC_SLICE_LENGTH(slice); 29 | 30 | char* data = grpc_slice_to_c_string(slice); 31 | 32 | RGRPC_LOG("Slice2Raw:\nn: " << n << "\nData: " << data); 33 | 34 | RawVector out(n); 35 | for(int i = 0; i < n; i++) 36 | out[i] = (unsigned char) data[i]; 37 | // std::copy(data, data+n, out.begin()); 38 | 39 | return out; 40 | } 41 | 42 | void runFunctionIfProvided(List hooks, std::string hook, List params){ 43 | 44 | if (hooks.containsElementNamed(hook.c_str())){ 45 | RGRPC_LOG("[HOOK " << hook << "] found and starting"); 46 | Function hookFunction = hooks[hook]; 47 | hookFunction(params); 48 | RGRPC_LOG("[HOOK " << hook << "] finished"); 49 | } else { 50 | RGRPC_LOG("[HOOK " << hook << "] not found"); 51 | } 52 | 53 | } 54 | 55 | // [[Rcpp::export]] 56 | List run(List target, CharacterVector hoststring, List hooks) { 57 | 58 | // to passed to R hooks 59 | List params = List::create(); 60 | 61 | bool done = false; 62 | // grpc_arg arg = {GRPC_ARG_STRING, "key", "value"}; 63 | // grpc_channel_args channel_args = {1, &arg}; 64 | 65 | RGRPC_LOG("Create Server"); 66 | 67 | grpc_server* server = grpc_server_create(NULL /*&channel_args*/, 0); 68 | runFunctionIfProvided(hooks, "server_create", params); 69 | 70 | // create completion queue 71 | RGRPC_LOG("Creating Queue"); 72 | grpc_completion_queue* queue = grpc_completion_queue_create_for_next(RESERVED); //todo 73 | grpc_server_register_completion_queue(server, queue, RESERVED); 74 | runFunctionIfProvided(hooks, "queue_create", params); 75 | 76 | RGRPC_LOG("Bind"); 77 | 78 | int port = grpc_server_add_insecure_http2_port(server, hoststring[0]); 79 | params["port"] = port; 80 | runFunctionIfProvided(hooks, "bind", params); 81 | 82 | // rock and roll 83 | RGRPC_LOG("Starting Server on port " << port); 84 | grpc_server_start(server); 85 | runFunctionIfProvided(hooks, "server_start", params); 86 | 87 | grpc_call *call; 88 | grpc_call_details details; 89 | grpc_metadata_array request_meta; 90 | grpc_call_error error; 91 | grpc_op ops[6]; 92 | grpc_op *op; 93 | int was_cancelled = 2; 94 | 95 | grpc_byte_buffer *request_payload_recv; 96 | grpc_byte_buffer *response_payload; 97 | 98 | // init crap 99 | grpc_call_details_init(&details); 100 | grpc_metadata_array_init(&request_meta); 101 | runFunctionIfProvided(hooks, "run", params); 102 | 103 | RGRPC_LOG("[RUNNING]"); 104 | 105 | // Copy pasted from node module... 106 | grpc_event event; 107 | do { 108 | memset(ops, 0, sizeof(ops)); 109 | 110 | grpc_server_request_call(server, &call, 111 | &details, &request_meta, queue, queue, NULL); 112 | // event = grpc_completion_queue_next(queue, gpr_inf_past(GPR_CLOCK_MONOTONIC),NULL); 113 | gpr_timespec c_increment = gpr_time_from_millis(_INTERRUPT_CHECK_PERIOD_MS, GPR_TIMESPAN); 114 | gpr_timespec c_timeout = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c_increment); 115 | 116 | event = grpc_completion_queue_next(queue, c_timeout, RESERVED); 117 | 118 | RGRPC_LOG("Event type: " << event.type); 119 | CharacterVector method = sliceToChar(details.method); 120 | RGRPC_LOG("Event method: " << method); 121 | params["event_method"] = method; 122 | 123 | if (event.type == GRPC_OP_COMPLETE) { 124 | const char *error_message; 125 | if (event.success) { 126 | error_message = NULL; 127 | } else { 128 | error_message = "The async function encountered an error"; 129 | RGRPC_LOG(error_message); 130 | continue; 131 | } 132 | 133 | 134 | RGRPC_LOG("Processing event method: " << method ); 135 | runFunctionIfProvided(hooks, "event_received", params); 136 | 137 | // CompleteTag(event.tag, error_message); 138 | // todo distpatch back to R here 139 | 140 | // var batch = {}; 141 | // batch[grpc.opType.RECV_MESSAGE] = true; 142 | // call.startBatch(); 143 | // 144 | 145 | //stolen from grpclb_test.cc 146 | 147 | 148 | // receive request for backends 149 | memset(ops, 0, sizeof(ops)); 150 | op = ops; 151 | 152 | RGRPC_LOG("GRPC_OP_SEND_INITIAL_METADATA"); 153 | op = ops; 154 | op->op = GRPC_OP_SEND_INITIAL_METADATA; 155 | op->data.send_initial_metadata.count = 0; 156 | op->data.send_initial_metadata.maybe_compression_level.is_set = false; 157 | op->flags = 0; 158 | op->reserved = NULL; 159 | op++; 160 | 161 | RGRPC_LOG("GRPC_OP_RECV_MESSAGE"); 162 | op->op = GRPC_OP_RECV_MESSAGE; 163 | op->data.recv_message.recv_message = &request_payload_recv; 164 | op->flags = 0; 165 | op->reserved = NULL; 166 | op++; 167 | 168 | error = grpc_call_start_batch(call, ops, (size_t)(op - ops), NULL, NULL); 169 | // Rcout << (GRPC_CALL_OK == error ? "OK" : "Not OK") << "\n"; 170 | // GPR_ASSERT(GRPC_CALL_OK == error); 171 | // CQ_EXPECT_COMPLETION(cqv, tag(202), 1); 172 | // cq_verify(cqv); 173 | grpc_completion_queue_next(queue, c_timeout, RESERVED); //actually does the work 174 | // gpr_log(GPR_INFO, "LB Server[%s](%s) after RECV_MSG", sf->servers_hostport, 175 | // sf->balancer_name); 176 | 177 | // validate initial request. 178 | // Rcout << "Read Slice\n"; 179 | 180 | grpc_byte_buffer_reader bbr; 181 | grpc_byte_buffer_reader_init(&bbr, request_payload_recv); 182 | grpc_slice request_payload_slice = grpc_byte_buffer_reader_readall(&bbr); 183 | 184 | RawVector request_payload_raw = sliceToRaw(request_payload_slice); 185 | 186 | RGRPC_LOG("...Slice2Raw Cleanup"); 187 | grpc_slice_unref(request_payload_slice); 188 | grpc_byte_buffer_reader_destroy(&bbr); 189 | grpc_byte_buffer_destroy(request_payload_recv); 190 | 191 | 192 | // Done recving 193 | 194 | // op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; 195 | // op->data.recv_close_on_server.cancelled = &was_cancelled; 196 | // op->flags = 0; 197 | // op->reserved = NULL; 198 | // op++; 199 | // Rcout << "Batch\n"; 200 | // error = grpc_call_start_batch(call, ops, (size_t)(op - ops), NULL, NULL); 201 | // grpc_completion_queue_next(queue, c_timeout, RESERVED); //actually does the work 202 | 203 | // default status code 204 | grpc_status_code status_code = GRPC_STATUS_UNKNOWN; 205 | char const *status_details_string = "Unknown error"; 206 | 207 | // Fire callback 208 | if (target.containsElementNamed(method[0])) { 209 | 210 | RGRPC_LOG("Method found: " << method[0]); 211 | Function callback = as(target[as(method[0])]); 212 | 213 | try { 214 | 215 | RawVector response_payload_raw = callback(request_payload_raw); 216 | runFunctionIfProvided(hooks, "event_processed", params); 217 | RGRPC_LOG("callback() success"); 218 | 219 | status_code = GRPC_STATUS_OK; 220 | status_details_string = "OK"; 221 | 222 | int len = response_payload_raw.length(); 223 | SEXP raw_ = response_payload_raw; 224 | grpc_slice response_payload_slice = grpc_slice_from_copied_buffer((char*) RAW(raw_), len); 225 | response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1); 226 | grpc_slice_unref(response_payload_slice); 227 | 228 | } catch(...) { 229 | RGRPC_LOG("callback() failed"); 230 | } 231 | 232 | } else { 233 | 234 | RGRPC_LOG("Method not found: " << method[0]); 235 | status_code = GRPC_STATUS_UNIMPLEMENTED; 236 | status_details_string = "Method not implemented"; 237 | 238 | } 239 | 240 | memset(ops, 0, sizeof(ops)); 241 | 242 | op = ops; 243 | op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; 244 | op->data.recv_close_on_server.cancelled = &was_cancelled; 245 | op->flags = 0; 246 | op->reserved = NULL; 247 | op++; 248 | 249 | if (status_code == GRPC_STATUS_OK) { 250 | 251 | RGRPC_LOG("GRPC_OP_SEND_MESSAGE"); 252 | 253 | op->op = GRPC_OP_SEND_MESSAGE; 254 | op->data.send_message.send_message = response_payload; 255 | op->flags = 0; 256 | op->reserved = NULL; 257 | op++; 258 | 259 | } 260 | 261 | RGRPC_LOG("GRPC_OP_SEND_STATUS_FROM_SERVER"); 262 | // op = ops; 263 | op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; 264 | op->data.send_status_from_server.trailing_metadata_count = 0; 265 | 266 | op->data.send_status_from_server.status = status_code; 267 | grpc_slice status_details = grpc_slice_from_static_string(status_details_string); 268 | 269 | op->data.send_status_from_server.status_details = &status_details; 270 | op->flags = 0; 271 | op->reserved = NULL; 272 | op++; 273 | 274 | RGRPC_LOG("Batch"); 275 | error = grpc_call_start_batch(call, ops, (size_t)(op - ops), NULL, NULL); 276 | RGRPC_LOG("Hangup next"); 277 | grpc_completion_queue_next(queue, c_timeout, RESERVED); //actually does the work 278 | // Rcout << "Hangup done...\n"; 279 | 280 | // Rcout << "response cleanup...\n"; 281 | grpc_byte_buffer_destroy(response_payload); 282 | 283 | // 284 | // 285 | // grpc_byte_buffer* recv_message = result.read; 286 | // RawVector* recv_serialized = new RawVector(recv_message); 287 | // 288 | // void* message = as(recv_serialized)("here"); 289 | // 290 | // 291 | // message.grpcWriteFlags = flags; 292 | // end_batch[grpc.opType.SEND_MESSAGE] = message; 293 | // end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status; 294 | // call.startBatch(end_batch, function (){}); 295 | } 296 | 297 | try{ 298 | Rcpp::checkUserInterrupt(); 299 | } catch (Rcpp::internal::InterruptedException ie){ 300 | RGRPC_LOG("Stopping server..."); 301 | done = true; 302 | } 303 | 304 | } while (!done); 305 | 306 | 307 | 308 | //shutdown 309 | RGRPC_LOG("Shutting down\n"); 310 | runFunctionIfProvided(hooks, "shutdown", params); 311 | grpc_server_shutdown_and_notify(server, queue, 0 /* tag */); 312 | grpc_server_cancel_all_calls(server); 313 | grpc_completion_queue_next(queue, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); 314 | grpc_server_destroy(server); 315 | RGRPC_LOG("[STOPPED]"); 316 | runFunctionIfProvided(hooks, "stopped", params); 317 | 318 | return List::create(); 319 | } 320 | --------------------------------------------------------------------------------