diff --git a/lib/hooks/index.js b/lib/hooks/index.js index d81b9d818..c3898fa4d 100644 --- a/lib/hooks/index.js +++ b/lib/hooks/index.js @@ -39,6 +39,8 @@ var fs = require('fs'); var toInstrument = Object.create(null, { 'express': { enumerable: true, value: { file: './userspace/hook-express.js', patches: {} } }, + 'grpc': { enumerable: true, value: { file: './userspace/hook-grpc.js', + patches: {} } }, 'hapi': { enumerable: true, value: { file: './userspace/hook-hapi.js', patches: {} } }, 'http': { enumerable: true, value: { file: './core/hook-http.js', diff --git a/lib/hooks/userspace/hook-grpc.js b/lib/hooks/userspace/hook-grpc.js new file mode 100644 index 000000000..1885b944c --- /dev/null +++ b/lib/hooks/userspace/hook-grpc.js @@ -0,0 +1,50 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +var cls = require('../../cls.js'); +var shimmer = require('shimmer'); +var semver = require('semver'); +var agent; + +var SUPPORTED_VERSIONS = '0.13.x'; + +function startBatchWrap(startBatch) { + return function startBatchTrace(thing, callback) { + console.log(arguments); + // TODO: maybe we only want to do this if a root context exists. + return startBatch.call(this, thing, cls.getNamespace().bind(callback)); + }; +} + +module.exports = function(version_, agent_) { + if (!semver.satisfies(version_, SUPPORTED_VERSIONS)) { + agent_.logger.info('grpc: unsupported version ' + version_ + ' loaded'); + return {}; + } + return { + 'src/node/src/grpc_extension.js': { + patch: function(extension) { + agent = agent_; + shimmer.wrap(extension.Call.prototype, 'startBatch', startBatchWrap); + }, + unpatch: function(extension) { + shimmer.unwrap(extension.Call.prototype, 'startBatch'); + } + } + }; +}; diff --git a/test/fixtures/test.proto b/test/fixtures/test.proto new file mode 100644 index 000000000..0a2343abf --- /dev/null +++ b/test/fixtures/test.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package nodetest; + +service Tester { + rpc Test (TestRequest) returns (TestReply) {} +} + +message TestRequest { + string message = 1; +} + +message TestReply { + string message = 1; +} \ No newline at end of file diff --git a/test/hooks/fixtures/grpc0.13/index.js b/test/hooks/fixtures/grpc0.13/index.js new file mode 100644 index 000000000..77cac56fd --- /dev/null +++ b/test/hooks/fixtures/grpc0.13/index.js @@ -0,0 +1 @@ +module.exports = require('grpc'); \ No newline at end of file diff --git a/test/hooks/fixtures/grpc0.13/package.json b/test/hooks/fixtures/grpc0.13/package.json new file mode 100644 index 000000000..dfce9ec3d --- /dev/null +++ b/test/hooks/fixtures/grpc0.13/package.json @@ -0,0 +1,8 @@ +{ + "name": "grpc0.13", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "grpc": "^0.13.1" + } +} diff --git a/test/standalone/test-grpc-context.js b/test/standalone/test-grpc-context.js new file mode 100644 index 000000000..8a1453db3 --- /dev/null +++ b/test/standalone/test-grpc-context.js @@ -0,0 +1,69 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +var agent = require('../..').start({ samplingRate: 0 }).private_(); + +var common = require('../hooks/common.js'); + +var assert = require('assert'); +var express = require('../hooks/fixtures/express4'); +var http = require('http'); +var grpc = require('../hooks/fixtures/grpc0.13'); +var test_proto = grpc.load(__dirname + '/../fixtures/test.proto').nodetest; +var grcPort = 50051; +var debugCount = 0; +agent.logger.debug = function(error) { + if (error.indexOf('http') !== -1) { + debugCount++; + } +}; + +describe('express + grpc', function() { + it('grpc should preserve context', function(done) { + var app = express(); + app.get('/', function (req, res) { + var client = new test_proto.Tester('localhost:' + grcPort, + grpc.credentials.createInsecure()); + client.test({message: 'hello'}, function(err, grpcRes) { + http.get('http://www.google.com/', function(httpRes) { + httpRes.on('data', function() {}); + httpRes.on('end', function() { + res.sendStatus(200); + }); + }); + }); + }); + var server = app.listen(common.serverPort, function() { + var grpcServer = new grpc.Server(); + grpcServer.addProtoService(test_proto.Tester.service, { + test: function(call, cb) { + cb(null, {message: 'world'}); + } + }); + grpcServer.bind('localhost:' + grcPort, + grpc.ServerCredentials.createInsecure()); + grpcServer.start(); + http.get({port: common.serverPort}, function(res) { + grpcServer.forceShutdown(); + server.close(); + assert.equal(common.getTraces().length, 1); + assert.equal(debugCount, 1); + done(); + }); + }); + }); +});