murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 1 | /* |
| 2 | * |
Craig Tiller | 6169d5f | 2016-03-31 07:46:18 -0700 | [diff] [blame] | 3 | * Copyright 2015, Google Inc. |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 4 | * All rights reserved. |
| 5 | * |
| 6 | * Redistribution and use in source and binary forms, with or without |
| 7 | * modification, are permitted provided that the following conditions are |
| 8 | * met: |
| 9 | * |
| 10 | * * Redistributions of source code must retain the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer. |
| 12 | * * Redistributions in binary form must reproduce the above |
| 13 | * copyright notice, this list of conditions and the following disclaimer |
| 14 | * in the documentation and/or other materials provided with the |
| 15 | * distribution. |
| 16 | * * Neither the name of Google Inc. nor the names of its |
| 17 | * contributors may be used to endorse or promote products derived from |
| 18 | * this software without specific prior written permission. |
| 19 | * |
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 | * |
| 32 | */ |
| 33 | |
murgatroid99 | dca966d | 2015-02-19 14:37:18 -0800 | [diff] [blame] | 34 | 'use strict'; |
| 35 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 36 | var fs = require('fs'); |
| 37 | var path = require('path'); |
murgatroid99 | 55739d5 | 2015-06-03 10:58:21 -0700 | [diff] [blame] | 38 | var _ = require('lodash'); |
murgatroid99 | 221ae63 | 2015-12-11 15:06:32 -0800 | [diff] [blame] | 39 | var AsyncDelayQueue = require('./async_delay_queue'); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 40 | var grpc = require('..'); |
murgatroid99 | 2af89e4 | 2015-10-01 11:54:00 -0700 | [diff] [blame] | 41 | var testProto = grpc.load({ |
| 42 | root: __dirname + '/../../..', |
Craig Tiller | cd92509 | 2016-01-07 17:10:25 -0800 | [diff] [blame] | 43 | file: 'src/proto/grpc/testing/test.proto'}).grpc.testing; |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 44 | |
murgatroid99 | e634f9a | 2015-08-27 10:02:00 -0700 | [diff] [blame] | 45 | var ECHO_INITIAL_KEY = 'x-grpc-test-echo-initial'; |
| 46 | var ECHO_TRAILING_KEY = 'x-grpc-test-echo-trailing-bin'; |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 47 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 48 | /** |
| 49 | * Create a buffer filled with size zeroes |
| 50 | * @param {number} size The length of the buffer |
| 51 | * @return {Buffer} The new buffer |
| 52 | */ |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 53 | function zeroBuffer(size) { |
| 54 | var zeros = new Buffer(size); |
| 55 | zeros.fill(0); |
| 56 | return zeros; |
| 57 | } |
| 58 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 59 | /** |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 60 | * Echos a header metadata item as specified in the interop spec. |
| 61 | * @param {Call} call The call to echo metadata on |
| 62 | */ |
| 63 | function echoHeader(call) { |
| 64 | var echo_initial = call.metadata.get(ECHO_INITIAL_KEY); |
| 65 | if (echo_initial.length > 0) { |
| 66 | var response_metadata = new grpc.Metadata(); |
| 67 | response_metadata.set(ECHO_INITIAL_KEY, echo_initial[0]); |
| 68 | call.sendMetadata(response_metadata); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * Gets the trailer metadata that should be echoed when the call is done, |
| 74 | * as specified in the interop spec. |
| 75 | * @param {Call} call The call to get metadata from |
| 76 | * @return {grpc.Metadata} The metadata to send as a trailer |
| 77 | */ |
| 78 | function getEchoTrailer(call) { |
| 79 | var echo_trailer = call.metadata.get(ECHO_TRAILING_KEY); |
| 80 | var response_trailer = new grpc.Metadata(); |
| 81 | if (echo_trailer.length > 0) { |
| 82 | response_trailer.set(ECHO_TRAILING_KEY, echo_trailer[0]); |
| 83 | } |
| 84 | return response_trailer; |
| 85 | } |
| 86 | |
murgatroid99 | a6a9a6d | 2015-10-07 16:40:04 -0700 | [diff] [blame] | 87 | function getPayload(payload_type, size) { |
David Garcia Quintas | e89aad0 | 2016-06-21 16:43:31 -0700 | [diff] [blame] | 88 | var body = zeroBuffer(size); |
murgatroid99 | a6a9a6d | 2015-10-07 16:40:04 -0700 | [diff] [blame] | 89 | return {type: payload_type, body: body}; |
| 90 | } |
| 91 | |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 92 | /** |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 93 | * Respond to an empty parameter with an empty response. |
| 94 | * NOTE: this currently does not work due to issue #137 |
| 95 | * @param {Call} call Call to handle |
| 96 | * @param {function(Error, Object)} callback Callback to call with result |
| 97 | * or error |
| 98 | */ |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 99 | function handleEmpty(call, callback) { |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 100 | echoHeader(call); |
| 101 | callback(null, {}, getEchoTrailer(call)); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 102 | } |
| 103 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 104 | /** |
| 105 | * Handle a unary request by sending the requested payload |
| 106 | * @param {Call} call Call to handle |
| 107 | * @param {function(Error, Object)} callback Callback to call with result or |
| 108 | * error |
| 109 | */ |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 110 | function handleUnary(call, callback) { |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 111 | echoHeader(call); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 112 | var req = call.request; |
murgatroid99 | a6a9a6d | 2015-10-07 16:40:04 -0700 | [diff] [blame] | 113 | if (req.response_status) { |
| 114 | var status = req.response_status; |
| 115 | status.metadata = getEchoTrailer(call); |
| 116 | callback(status); |
| 117 | return; |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 118 | } |
murgatroid99 | a6a9a6d | 2015-10-07 16:40:04 -0700 | [diff] [blame] | 119 | var payload = getPayload(req.response_type, req.response_size); |
| 120 | callback(null, {payload: payload}, |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 121 | getEchoTrailer(call)); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 122 | } |
| 123 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 124 | /** |
| 125 | * Respond to a streaming call with the total size of all payloads |
| 126 | * @param {Call} call Call to handle |
| 127 | * @param {function(Error, Object)} callback Callback to call with result or |
| 128 | * error |
| 129 | */ |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 130 | function handleStreamingInput(call, callback) { |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 131 | echoHeader(call); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 132 | var aggregate_size = 0; |
| 133 | call.on('data', function(value) { |
murgatroid99 | 20afbb9 | 2015-05-12 10:35:18 -0700 | [diff] [blame] | 134 | aggregate_size += value.payload.body.length; |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 135 | }); |
| 136 | call.on('end', function() { |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 137 | callback(null, {aggregated_payload_size: aggregate_size}, |
| 138 | getEchoTrailer(call)); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 139 | }); |
| 140 | } |
| 141 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 142 | /** |
| 143 | * Respond to a payload request with a stream of the requested payloads |
| 144 | * @param {Call} call Call to handle |
| 145 | */ |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 146 | function handleStreamingOutput(call) { |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 147 | echoHeader(call); |
murgatroid99 | 221ae63 | 2015-12-11 15:06:32 -0800 | [diff] [blame] | 148 | var delay_queue = new AsyncDelayQueue(); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 149 | var req = call.request; |
murgatroid99 | a6a9a6d | 2015-10-07 16:40:04 -0700 | [diff] [blame] | 150 | if (req.response_status) { |
| 151 | var status = req.response_status; |
| 152 | status.metadata = getEchoTrailer(call); |
| 153 | call.emit('error', status); |
| 154 | return; |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 155 | } |
murgatroid99 | 5dbbd91 | 2015-10-09 14:45:55 -0700 | [diff] [blame] | 156 | _.each(req.response_parameters, function(resp_param) { |
murgatroid99 | 221ae63 | 2015-12-11 15:06:32 -0800 | [diff] [blame] | 157 | delay_queue.add(function(next) { |
| 158 | call.write({payload: getPayload(req.response_type, resp_param.size)}); |
| 159 | next(); |
| 160 | }, resp_param.interval_us); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 161 | }); |
murgatroid99 | 221ae63 | 2015-12-11 15:06:32 -0800 | [diff] [blame] | 162 | delay_queue.add(function(next) { |
| 163 | call.end(getEchoTrailer(call)); |
| 164 | next(); |
| 165 | }); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 166 | } |
| 167 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 168 | /** |
| 169 | * Respond to a stream of payload requests with a stream of payload responses as |
| 170 | * they arrive. |
| 171 | * @param {Call} call Call to handle |
| 172 | */ |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 173 | function handleFullDuplex(call) { |
murgatroid99 | 1eb113c | 2015-08-27 09:26:33 -0700 | [diff] [blame] | 174 | echoHeader(call); |
murgatroid99 | 221ae63 | 2015-12-11 15:06:32 -0800 | [diff] [blame] | 175 | var delay_queue = new AsyncDelayQueue(); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 176 | call.on('data', function(value) { |
murgatroid99 | a6a9a6d | 2015-10-07 16:40:04 -0700 | [diff] [blame] | 177 | if (value.response_status) { |
| 178 | var status = value.response_status; |
| 179 | status.metadata = getEchoTrailer(call); |
| 180 | call.emit('error', status); |
| 181 | return; |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 182 | } |
murgatroid99 | 5dbbd91 | 2015-10-09 14:45:55 -0700 | [diff] [blame] | 183 | _.each(value.response_parameters, function(resp_param) { |
murgatroid99 | 221ae63 | 2015-12-11 15:06:32 -0800 | [diff] [blame] | 184 | delay_queue.add(function(next) { |
| 185 | call.write({payload: getPayload(value.response_type, resp_param.size)}); |
| 186 | next(); |
| 187 | }, resp_param.interval_us); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 188 | }); |
| 189 | }); |
| 190 | call.on('end', function() { |
murgatroid99 | 221ae63 | 2015-12-11 15:06:32 -0800 | [diff] [blame] | 191 | delay_queue.add(function(next) { |
| 192 | call.end(getEchoTrailer(call)); |
| 193 | next(); |
| 194 | }); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 195 | }); |
| 196 | } |
| 197 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 198 | /** |
| 199 | * Respond to a stream of payload requests with a stream of payload responses |
| 200 | * after all requests have arrived |
| 201 | * @param {Call} call Call to handle |
| 202 | */ |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 203 | function handleHalfDuplex(call) { |
murgatroid99 | a6a9a6d | 2015-10-07 16:40:04 -0700 | [diff] [blame] | 204 | call.emit('error', Error('HalfDuplexCall not yet implemented')); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 205 | } |
| 206 | |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 207 | /** |
| 208 | * Get a server object bound to the given port |
| 209 | * @param {string} port Port to which to bind |
| 210 | * @param {boolean} tls Indicates that the bound port should use TLS |
murgatroid99 | f034e50 | 2015-01-21 12:12:39 -0800 | [diff] [blame] | 211 | * @return {{server: Server, port: number}} Server object bound to the support, |
| 212 | * and port number that the server is bound to |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 213 | */ |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 214 | function getServer(port, tls) { |
| 215 | // TODO(mlumish): enable TLS functionality |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 216 | var options = {}; |
murgatroid99 | 1a7dcac | 2015-07-27 16:13:28 -0700 | [diff] [blame] | 217 | var server_creds; |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 218 | if (tls) { |
| 219 | var key_path = path.join(__dirname, '../test/data/server1.key'); |
| 220 | var pem_path = path.join(__dirname, '../test/data/server1.pem'); |
| 221 | |
| 222 | var key_data = fs.readFileSync(key_path); |
| 223 | var pem_data = fs.readFileSync(pem_path); |
murgatroid99 | da02a67 | 2015-03-02 17:28:02 -0800 | [diff] [blame] | 224 | server_creds = grpc.ServerCredentials.createSsl(null, |
murgatroid99 | 6b3737d | 2015-08-19 15:02:15 -0700 | [diff] [blame] | 225 | [{private_key: key_data, |
| 226 | cert_chain: pem_data}]); |
murgatroid99 | 1a7dcac | 2015-07-27 16:13:28 -0700 | [diff] [blame] | 227 | } else { |
| 228 | server_creds = grpc.ServerCredentials.createInsecure(); |
murgatroid99 | b6ab1b4 | 2015-01-21 10:30:36 -0800 | [diff] [blame] | 229 | } |
murgatroid99 | 380b90a | 2015-07-16 13:16:14 -0700 | [diff] [blame] | 230 | var server = new grpc.Server(options); |
murgatroid99 | b5b5f02 | 2017-03-16 16:42:10 -0700 | [diff] [blame] | 231 | server.addService(testProto.TestService.service, { |
murgatroid99 | 366e64d | 2015-07-15 17:01:49 -0700 | [diff] [blame] | 232 | emptyCall: handleEmpty, |
| 233 | unaryCall: handleUnary, |
| 234 | streamingOutputCall: handleStreamingOutput, |
| 235 | streamingInputCall: handleStreamingInput, |
| 236 | fullDuplexCall: handleFullDuplex, |
| 237 | halfDuplexCall: handleHalfDuplex |
| 238 | }); |
murgatroid99 | da02a67 | 2015-03-02 17:28:02 -0800 | [diff] [blame] | 239 | var port_num = server.bind('0.0.0.0:' + port, server_creds); |
murgatroid99 | f034e50 | 2015-01-21 12:12:39 -0800 | [diff] [blame] | 240 | return {server: server, port: port_num}; |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 241 | } |
| 242 | |
| 243 | if (require.main === module) { |
| 244 | var parseArgs = require('minimist'); |
| 245 | var argv = parseArgs(process.argv, { |
| 246 | string: ['port', 'use_tls'] |
| 247 | }); |
murgatroid99 | f034e50 | 2015-01-21 12:12:39 -0800 | [diff] [blame] | 248 | var server_obj = getServer(argv.port, argv.use_tls === 'true'); |
murgatroid99 | 34986de | 2015-01-23 13:27:20 -0800 | [diff] [blame] | 249 | console.log('Server attaching to port ' + argv.port); |
murgatroid99 | bc15a78 | 2015-08-14 11:09:04 -0700 | [diff] [blame] | 250 | server_obj.server.start(); |
murgatroid99 | 97d6130 | 2015-01-20 18:06:43 -0800 | [diff] [blame] | 251 | } |
| 252 | |
| 253 | /** |
| 254 | * See docs for getServer |
| 255 | */ |
Craig Tiller | ce5021b | 2015-02-18 09:25:21 -0800 | [diff] [blame] | 256 | exports.getServer = getServer; |