mirror of https://github.com/grpc/grpc-node.git
				
				
				
			Merge pull request #796 from murgatroid99/metadata_options
Add metadata options
This commit is contained in:
		
						commit
						9c274034d0
					
				|  | @ -18,6 +18,7 @@ HTTP Connect Support | :heavy_check_mark: | :x: | |||
| Retries | :heavy_check_mark: | :x: | ||||
| Stats/tracing/monitoring | :heavy_check_mark: | :x: | ||||
| Load Balancing | :heavy_check_mark: | :x: | ||||
| Initial Metadata Options | :heavy_check_mark: | :x: | ||||
| 
 | ||||
| Other Properties | `grpc` | `@grpc/grpc-js` | ||||
| -----------------|--------|---------------- | ||||
|  |  | |||
|  | @ -62,12 +62,27 @@ function validate(key: string, value?: MetadataValue): void { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| interface MetadataOptions { | ||||
|   /* Signal that the request is idempotent. Defaults to false */ | ||||
|   idempotentRequest?: boolean; | ||||
|   /* Signal that the call should not return UNAVAILABLE before it has | ||||
|    * started. Defaults to true. */ | ||||
|   waitForReady?: boolean; | ||||
|   /* Signal that the call is cacheable. GRPC is free to use GET verb. | ||||
|    * Defaults to false */ | ||||
|   cacheableRequest?: boolean; | ||||
|   /* Signal that the initial metadata should be corked. Defaults to false. */ | ||||
|   corked?: boolean; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * A class for storing metadata. Keys are normalized to lowercase ASCII. | ||||
|  */ | ||||
| export class Metadata { | ||||
|   protected internalRepr: MetadataObject = new Map<string, MetadataValue[]>(); | ||||
| 
 | ||||
|   constructor(private options?: MetadataOptions) {} | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the given value for the given key by replacing any other values | ||||
|    * associated with that key. Normalizes the key. | ||||
|  | @ -181,6 +196,10 @@ export class Metadata { | |||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   setOptions(options: MetadataOptions) { | ||||
|     this.options = options; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Creates an OutgoingHttpHeaders object that can be used with the http2 API. | ||||
|    */ | ||||
|  |  | |||
|  | @ -81,8 +81,13 @@ Local<Value> nanErrorWithCode(const char *msg, grpc_call_error code) { | |||
|   return scope.Escape(err); | ||||
| } | ||||
| 
 | ||||
| bool CreateMetadataArray(Local<Object> metadata, grpc_metadata_array *array) { | ||||
| bool CreateMetadataArray(Local<Object> metadata_obj, grpc_metadata_array *array) { | ||||
|   HandleScope scope; | ||||
|   Local<Value> metadata_value = (Nan::Get(metadata_obj, Nan::New("metadata").ToLocalChecked())).ToLocalChecked(); | ||||
|   if (!metadata_value->IsObject()) { | ||||
|     return false; | ||||
|   } | ||||
|   Local<Object> metadata = Nan::To<Object>(metadata_value).ToLocalChecked(); | ||||
|   Local<Array> keys = Nan::GetOwnPropertyNames(metadata).ToLocalChecked(); | ||||
|   for (unsigned int i = 0; i < keys->Length(); i++) { | ||||
|     Local<String> current_key = | ||||
|  | @ -159,7 +164,10 @@ Local<Value> ParseMetadata(const grpc_metadata_array *metadata_array) { | |||
|       Nan::Set(array, array->Length(), CopyStringFromSlice(elem->value)); | ||||
|     } | ||||
|   } | ||||
|   return scope.Escape(metadata_object); | ||||
|   Local<Object> result = Nan::New<Object>(); | ||||
|   Nan::Set(result, Nan::New("metadata").ToLocalChecked(), metadata_object); | ||||
|   Nan::Set(result, Nan::New("flags").ToLocalChecked(), Nan::New<v8::Uint32>(0)); | ||||
|   return scope.Escape(result); | ||||
| } | ||||
| 
 | ||||
| Local<Value> Op::GetOpType() const { | ||||
|  | @ -185,7 +193,17 @@ class SendMetadataOp : public Op { | |||
|     if (maybe_metadata.IsEmpty()) { | ||||
|       return false; | ||||
|     } | ||||
|     if (!CreateMetadataArray(maybe_metadata.ToLocalChecked(), &send_metadata)) { | ||||
|     Local<Object> metadata_object = maybe_metadata.ToLocalChecked(); | ||||
|     MaybeLocal<Value> maybe_flag_value = | ||||
|         Nan::Get(metadata_object, Nan::New("flags").ToLocalChecked()); | ||||
|     if (!maybe_flag_value.IsEmpty()) { | ||||
|       Local<Value> flag_value = maybe_flag_value.ToLocalChecked(); | ||||
|       if (flag_value->IsUint32()) { | ||||
|         Maybe<uint32_t> maybe_flag = Nan::To<uint32_t>(flag_value); | ||||
|         out->flags |= maybe_flag.FromMaybe(0) & GRPC_INITIAL_METADATA_USED_MASK; | ||||
|       } | ||||
|     } | ||||
|     if (!CreateMetadataArray(metadata_object, &send_metadata)) { | ||||
|       return false; | ||||
|     } | ||||
|     out->data.send_initial_metadata.count = send_metadata.count; | ||||
|  | @ -225,7 +243,7 @@ class SendMessageOp : public Op { | |||
|       Local<Value> flag_value = maybe_flag_value.ToLocalChecked(); | ||||
|       if (flag_value->IsUint32()) { | ||||
|         Maybe<uint32_t> maybe_flag = Nan::To<uint32_t>(flag_value); | ||||
|         out->flags = maybe_flag.FromMaybe(0) & GRPC_WRITE_USED_MASK; | ||||
|         out->flags |= maybe_flag.FromMaybe(0) & GRPC_WRITE_USED_MASK; | ||||
|       } | ||||
|     } | ||||
|     send_message = BufferToByteBuffer(value); | ||||
|  |  | |||
|  | @ -506,10 +506,29 @@ declare module "grpc" { | |||
|   type sendUnaryData<ResponseType> = | ||||
|     (error: ServiceError | null, value: ResponseType | null, trailer?: Metadata, flags?: number) => void; | ||||
| 
 | ||||
|   interface MetadataOptions { | ||||
|     /* Signal that the request is idempotent. Defaults to false */ | ||||
|     idempotentRequest?: boolean; | ||||
|     /* Signal that the call should not return UNAVAILABLE before it has | ||||
|      * started. Defaults to true. */ | ||||
|     waitForReady?: boolean; | ||||
|     /* Signal that the call is cacheable. GRPC is free to use GET verb. | ||||
|      * Defaults to false */ | ||||
|     cacheableRequest?: boolean; | ||||
|     /* Signal that the initial metadata should be corked. Defaults to false. */ | ||||
|     corked?: boolean; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * A class for storing metadata. Keys are normalized to lowercase ASCII. | ||||
|    */ | ||||
|   export class Metadata { | ||||
|     /** | ||||
|      * @param options Boolean options for the beginning of the call. | ||||
|      *   These options only have any effect when passed at the beginning of | ||||
|      *   a client request. | ||||
|      */ | ||||
|     constructor(options?: MetadataOptions); | ||||
|     /** | ||||
|      * Sets the given value for the given key by replacing any other values | ||||
|      * associated with that key. Normalizes the key. | ||||
|  | @ -553,6 +572,14 @@ declare module "grpc" { | |||
|      * @return The newly cloned object. | ||||
|      */ | ||||
|     clone(): Metadata; | ||||
| 
 | ||||
|     /** | ||||
|      * Set options on the metadata object | ||||
|      * @param options Boolean options for the beginning of the call. | ||||
|      *   These options only have any effect when passed at the beginning of | ||||
|      *   a client request. | ||||
|      */ | ||||
|     setOptions(options: MetadataOptions); | ||||
|   } | ||||
| 
 | ||||
|   export type MetadataValue = string | Buffer; | ||||
|  |  | |||
|  | @ -22,18 +22,36 @@ var clone = require('lodash.clone'); | |||
| 
 | ||||
| var grpc = require('./grpc_extension'); | ||||
| 
 | ||||
| const IDEMPOTENT_REQUEST_FLAG = 0x10; | ||||
| const WAIT_FOR_READY_FLAG = 0x20; | ||||
| const CACHEABLE_REQUEST_FLAG = 0x40; | ||||
| const WAIT_FOR_READY_EXPLICITLY_SET_FLAG = 0x80; | ||||
| const CORKED_FLAG = 0x100; | ||||
| 
 | ||||
| /** | ||||
|  * Class for storing metadata. Keys are normalized to lowercase ASCII. | ||||
|  * @memberof grpc | ||||
|  * @constructor | ||||
|  * @param {Object=} options Boolean options for the beginning of the call. | ||||
|  *     These options only have any effect when passed at the beginning of | ||||
|  *     a client request. | ||||
|  * @param {boolean=} [options.idempotentRequest=false] Signal that the request | ||||
|  *     is idempotent | ||||
|  * @param {boolean=} [options.waitForReady=true] Signal that the call should | ||||
|  *     not return UNAVAILABLE before it has started. | ||||
|  * @param {boolean=} [options.cacheableRequest=false] Signal that the call is | ||||
|  *     cacheable. GRPC is free to use GET verb. | ||||
|  * @param {boolean=} [options.corked=false] Signal that the initial metadata | ||||
|  *     should be corked. | ||||
|  * @example | ||||
|  * var metadata = new metadata_module.Metadata(); | ||||
|  * metadata.set('key1', 'value1'); | ||||
|  * metadata.add('key1', 'value2'); | ||||
|  * metadata.get('key1') // returns ['value1', 'value2']
 | ||||
|  */ | ||||
| function Metadata() { | ||||
| function Metadata(options) { | ||||
|   this._internal_repr = {}; | ||||
|   this.setOptions(options); | ||||
| } | ||||
| 
 | ||||
| function normalizeKey(key) { | ||||
|  | @ -141,34 +159,82 @@ Metadata.prototype.clone = function() { | |||
|     const value = this._internal_repr[key]; | ||||
|     copy._internal_repr[key] = clone(value); | ||||
|   }); | ||||
|   copy.flags = this.flags; | ||||
|   return copy; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Set options on the metadata object | ||||
|  * @param {Object} options Boolean options for the beginning of the call. | ||||
|  *     These options only have any effect when passed at the beginning of | ||||
|  *     a client request. | ||||
|  * @param {boolean=} [options.idempotentRequest=false] Signal that the request | ||||
|  *     is idempotent | ||||
|  * @param {boolean=} [options.waitForReady=true] Signal that the call should | ||||
|  *     not return UNAVAILABLE before it has started. | ||||
|  * @param {boolean=} [options.cacheableRequest=false] Signal that the call is | ||||
|  *     cacheable. GRPC is free to use GET verb. | ||||
|  * @param {boolean=} [options.corked=false] Signal that the initial metadata | ||||
|  *     should be corked. | ||||
|  */ | ||||
| Metadata.prototype.setOptions = function(options) { | ||||
|   let flags = 0; | ||||
|   if (options) { | ||||
|     if (options.idempotentRequest) { | ||||
|       flags |= IDEMPOTENT_REQUEST_FLAG; | ||||
|     } | ||||
|     if (options.hasOwnProperty('waitForReady')) { | ||||
|       flags |= WAIT_FOR_READY_EXPLICITLY_SET_FLAG; | ||||
|       if (options.waitForReady) { | ||||
|         flags |= WAIT_FOR_READY_FLAG; | ||||
|       } | ||||
|     } | ||||
|     if (options.cacheableRequest) { | ||||
|       flags |= CACHEABLE_REQUEST_FLAG; | ||||
|     } | ||||
|     if (options.corked) { | ||||
|       flags |= CORKED_FLAG; | ||||
|     } | ||||
|   } | ||||
|   this.flags = flags; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Metadata representation as passed to and the native addon | ||||
|  * @typedef {object} grpc~CoreMetadata | ||||
|  * @param {Object.<String, Array.<String|Buffer>>} metadata The metadata | ||||
|  * @param {number} flags Metadata flags | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Gets the metadata in the format used by interal code. Intended for internal | ||||
|  * use only. API stability is not guaranteed. | ||||
|  * @private | ||||
|  * @return {Object.<String, Array.<String|Buffer>>} The metadata | ||||
|  * @return {grpc~CoreMetadata} The metadata | ||||
|  */ | ||||
| Metadata.prototype._getCoreRepresentation = function() { | ||||
|   return this._internal_repr; | ||||
|   return { | ||||
|     metadata: this._internal_repr, | ||||
|     flags: this.flags | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Creates a Metadata object from a metadata map in the internal format. | ||||
|  * Intended for internal use only. API stability is not guaranteed. | ||||
|  * @private | ||||
|  * @param {Object.<String, Array.<String|Buffer>>} The metadata | ||||
|  * @param {grpc~CoreMetadata} metadata The metadata object from core | ||||
|  * @return {Metadata} The new Metadata object | ||||
|  */ | ||||
| Metadata._fromCoreRepresentation = function(metadata) { | ||||
|   var newMetadata = new Metadata(); | ||||
|   if (metadata) { | ||||
|     Object.keys(metadata).forEach(key => { | ||||
|       const value = metadata[key]; | ||||
|     Object.keys(metadata.metadata).forEach(key => { | ||||
|       const value = metadata.metadata[key]; | ||||
|       newMetadata._internal_repr[key] = clone(value); | ||||
|     }); | ||||
|   } | ||||
|   newMetadata.flags = metadata.flags; | ||||
|   return newMetadata; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -772,7 +772,7 @@ Server.prototype.start = function() { | |||
|       batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { | ||||
|         code: constants.status.UNIMPLEMENTED, | ||||
|         details: 'RPC method not implemented ' + method, | ||||
|         metadata: {} | ||||
|         metadata: (new Metadata())._getCoreRepresentation() | ||||
|       }; | ||||
|       batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; | ||||
|       call.startBatch(batch, function() {}); | ||||
|  |  | |||
|  | @ -128,8 +128,9 @@ describe('call', function() { | |||
|       var call = channel.createCall('method', getDeadline(1)); | ||||
|       assert.doesNotThrow(function() { | ||||
|         var batch = {}; | ||||
|         batch[grpc.opType.SEND_INITIAL_METADATA] = {'key1': ['value1'], | ||||
|                                                     'key2': ['value2']}; | ||||
|         batch[grpc.opType.SEND_INITIAL_METADATA] = { | ||||
|           metadata: {'key1': ['value1'], 'key2': ['value2']} | ||||
|         }; | ||||
|         call.startBatch(batch, function(err, resp) { | ||||
|           assert.ifError(err); | ||||
|           assert.deepEqual(resp, {'send_metadata': true}); | ||||
|  | @ -142,8 +143,10 @@ describe('call', function() { | |||
|       assert.doesNotThrow(function() { | ||||
|         var batch = {}; | ||||
|         batch[grpc.opType.SEND_INITIAL_METADATA] = { | ||||
|           'key1-bin': [Buffer.from('value1')], | ||||
|           'key2-bin': [Buffer.from('value2')] | ||||
|           metadata: { | ||||
|             'key1-bin': [Buffer.from('value1')], | ||||
|             'key2-bin': [Buffer.from('value2')] | ||||
|           } | ||||
|         }; | ||||
|         call.startBatch(batch, function(err, resp) { | ||||
|           assert.ifError(err); | ||||
|  | @ -174,6 +177,11 @@ describe('call', function() { | |||
|         batch[grpc.opType.SEND_INITIAL_METADATA] = 5; | ||||
|         call.startBatch(batch, function(){}); | ||||
|       }, TypeError); | ||||
|       assert.throws(function() { | ||||
|         var batch = {}; | ||||
|         batch[grpc.opType.SEND_INITIAL_METADATA] = {}; | ||||
|         call.startBatch(batch, function(){}); | ||||
|       }, TypeError); | ||||
|     }); | ||||
|   }); | ||||
|   describe('startBatch with message', function() { | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ describe('end-to-end', function() { | |||
|                              'dummy_method', | ||||
|                              Infinity); | ||||
|     var client_batch = {}; | ||||
|     client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; | ||||
|     client_batch[grpc.opType.SEND_INITIAL_METADATA] = {metadata: {}}; | ||||
|     client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; | ||||
|     client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; | ||||
|     client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; | ||||
|  | @ -74,11 +74,17 @@ describe('end-to-end', function() { | |||
|       assert.deepEqual(response, { | ||||
|         send_metadata: true, | ||||
|         client_close: true, | ||||
|         metadata: {}, | ||||
|         metadata: { | ||||
|           metadata: {}, | ||||
|           flags: 0 | ||||
|         }, | ||||
|         status: { | ||||
|           code: constants.status.OK, | ||||
|           details: status_text, | ||||
|           metadata: {} | ||||
|           metadata: { | ||||
|             metadata: {}, | ||||
|             flags: 0 | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|       done(); | ||||
|  | @ -90,9 +96,9 @@ describe('end-to-end', function() { | |||
|       var server_call = new_call.call; | ||||
|       assert.notEqual(server_call, null); | ||||
|       var server_batch = {}; | ||||
|       server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; | ||||
|       server_batch[grpc.opType.SEND_INITIAL_METADATA] = {metadata: {}}; | ||||
|       server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { | ||||
|         metadata: {}, | ||||
|         metadata: {metadata: {}}, | ||||
|         code: constants.status.OK, | ||||
|         details: status_text | ||||
|       }; | ||||
|  | @ -116,7 +122,9 @@ describe('end-to-end', function() { | |||
|                              Infinity); | ||||
|     var client_batch = {}; | ||||
|     client_batch[grpc.opType.SEND_INITIAL_METADATA] = { | ||||
|       client_key: ['client_value'] | ||||
|       metadata: { | ||||
|         client_key: ['client_value'] | ||||
|       } | ||||
|     }; | ||||
|     client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; | ||||
|     client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; | ||||
|  | @ -126,10 +134,13 @@ describe('end-to-end', function() { | |||
|       assert.deepEqual(response,{ | ||||
|         send_metadata: true, | ||||
|         client_close: true, | ||||
|         metadata: {server_key: ['server_value']}, | ||||
|         metadata: {metadata: { | ||||
|           server_key: ['server_value']},  | ||||
|           flags: 0 | ||||
|         }, | ||||
|         status: {code: constants.status.OK, | ||||
|                  details: status_text, | ||||
|                  metadata: {}} | ||||
|                  metadata: {metadata: {}, flags: 0}} | ||||
|       }); | ||||
|       done(); | ||||
|     }); | ||||
|  | @ -137,16 +148,18 @@ describe('end-to-end', function() { | |||
|     server.requestCall(function(err, call_details) { | ||||
|       var new_call = call_details.new_call; | ||||
|       assert.notEqual(new_call, null); | ||||
|       assert.strictEqual(new_call.metadata.client_key[0], | ||||
|       assert.strictEqual(new_call.metadata.metadata.client_key[0], | ||||
|                          'client_value'); | ||||
|       var server_call = new_call.call; | ||||
|       assert.notEqual(server_call, null); | ||||
|       var server_batch = {}; | ||||
|       server_batch[grpc.opType.SEND_INITIAL_METADATA] = { | ||||
|         server_key: ['server_value'] | ||||
|         metadata: { | ||||
|           server_key: ['server_value'] | ||||
|         } | ||||
|       }; | ||||
|       server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { | ||||
|         metadata: {}, | ||||
|         metadata: {metadata: {}}, | ||||
|         code: constants.status.OK, | ||||
|         details: status_text | ||||
|       }; | ||||
|  | @ -171,7 +184,7 @@ describe('end-to-end', function() { | |||
|                              'dummy_method', | ||||
|                              Infinity); | ||||
|     var client_batch = {}; | ||||
|     client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; | ||||
|     client_batch[grpc.opType.SEND_INITIAL_METADATA] = {metadata: {}}; | ||||
|     client_batch[grpc.opType.SEND_MESSAGE] = Buffer.from(req_text); | ||||
|     client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; | ||||
|     client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; | ||||
|  | @ -181,12 +194,12 @@ describe('end-to-end', function() { | |||
|       assert.ifError(err); | ||||
|       assert(response.send_metadata); | ||||
|       assert(response.client_close); | ||||
|       assert.deepEqual(response.metadata, {}); | ||||
|       assert.deepEqual(response.metadata, {metadata: {}, flags: 0}); | ||||
|       assert(response.send_message); | ||||
|       assert.strictEqual(response.read.toString(), reply_text); | ||||
|       assert.deepEqual(response.status, {code: constants.status.OK, | ||||
|                                          details: status_text, | ||||
|                                          metadata: {}}); | ||||
|                                          metadata: {metadata: {}, flags: 0}}); | ||||
|       done(); | ||||
|     }); | ||||
| 
 | ||||
|  | @ -196,7 +209,7 @@ describe('end-to-end', function() { | |||
|       var server_call = new_call.call; | ||||
|       assert.notEqual(server_call, null); | ||||
|       var server_batch = {}; | ||||
|       server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; | ||||
|       server_batch[grpc.opType.SEND_INITIAL_METADATA] = {metadata: {}}; | ||||
|       server_batch[grpc.opType.RECV_MESSAGE] = true; | ||||
|       server_call.startBatch(server_batch, function(err, response) { | ||||
|         assert.ifError(err); | ||||
|  | @ -205,7 +218,7 @@ describe('end-to-end', function() { | |||
|         var response_batch = {}; | ||||
|         response_batch[grpc.opType.SEND_MESSAGE] = Buffer.from(reply_text); | ||||
|         response_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { | ||||
|           metadata: {}, | ||||
|           metadata: {metadata: {}}, | ||||
|           code: constants.status.OK, | ||||
|           details: status_text | ||||
|         }; | ||||
|  | @ -226,7 +239,7 @@ describe('end-to-end', function() { | |||
|                              'dummy_method', | ||||
|                              Infinity); | ||||
|     var client_batch = {}; | ||||
|     client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; | ||||
|     client_batch[grpc.opType.SEND_INITIAL_METADATA] = {metadata: {}}; | ||||
|     client_batch[grpc.opType.SEND_MESSAGE] = Buffer.from(requests[0]); | ||||
|     client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; | ||||
|     call.startBatch(client_batch, function(err, response) { | ||||
|  | @ -234,7 +247,7 @@ describe('end-to-end', function() { | |||
|       assert.deepEqual(response, { | ||||
|         send_metadata: true, | ||||
|         send_message: true, | ||||
|         metadata: {} | ||||
|         metadata: {metadata: {}, flags: 0} | ||||
|       }); | ||||
|       var req2_batch = {}; | ||||
|       req2_batch[grpc.opType.SEND_MESSAGE] = Buffer.from(requests[1]); | ||||
|  | @ -248,7 +261,7 @@ describe('end-to-end', function() { | |||
|           status: { | ||||
|             code: constants.status.OK, | ||||
|             details: status_text, | ||||
|             metadata: {} | ||||
|             metadata: {metadata: {}, flags: 0} | ||||
|           } | ||||
|         }); | ||||
|         done(); | ||||
|  | @ -261,7 +274,7 @@ describe('end-to-end', function() { | |||
|       var server_call = new_call.call; | ||||
|       assert.notEqual(server_call, null); | ||||
|       var server_batch = {}; | ||||
|       server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; | ||||
|       server_batch[grpc.opType.SEND_INITIAL_METADATA] = {metadata: {}}; | ||||
|       server_batch[grpc.opType.RECV_MESSAGE] = true; | ||||
|       server_call.startBatch(server_batch, function(err, response) { | ||||
|         assert.ifError(err); | ||||
|  | @ -275,7 +288,7 @@ describe('end-to-end', function() { | |||
|           var end_batch = {}; | ||||
|           end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; | ||||
|           end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { | ||||
|             metadata: {}, | ||||
|             metadata: {metadata: {}}, | ||||
|             code: constants.status.OK, | ||||
|             details: status_text | ||||
|           }; | ||||
|  |  | |||
|  | @ -941,6 +941,22 @@ describe('Other conditions', function() { | |||
|     call.write({}); | ||||
|     call.end(); | ||||
|   }); | ||||
|   it('client should wait for a connection with waitForReady on', function(done) { | ||||
|     /* We have to wait for the client to reach the first connection timeout | ||||
|      * and go to TRANSIENT_FAILURE to confirm that the waitForReady option | ||||
|      * makes it end the call instead of continuing to try. A DNS resolution | ||||
|      * failure makes that transition very fast. */ | ||||
|     this.timeout(15000); | ||||
|     const disconnectedClient = new Client('foo.test.google.com:50051', grpc.credentials.createInsecure()); | ||||
|     const metadata = new grpc.Metadata({waitForReady: true}); | ||||
|     const deadline = new Date(); | ||||
|     deadline.setSeconds(deadline.getSeconds() + 10); | ||||
|     disconnectedClient.unary({}, metadata, {deadline: deadline}, (error, value) =>{ | ||||
|       assert(error); | ||||
|       assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED); | ||||
|       done(); | ||||
|     }); | ||||
|   }); | ||||
|   describe('Server recieving bad input', function() { | ||||
|     var misbehavingClient; | ||||
|     var badArg = Buffer.from([0xFF]); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue