Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc / src / metadata.js
1 /**
2  * @license
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 'use strict';
20
21 var clone = require('lodash.clone');
22
23 var grpc = require('./grpc_extension');
24
25 const common = require('./common');
26 const logVerbosity = require('./constants').logVerbosity;
27
28 const IDEMPOTENT_REQUEST_FLAG = 0x10;
29 const WAIT_FOR_READY_FLAG = 0x20;
30 const CACHEABLE_REQUEST_FLAG = 0x40;
31 const WAIT_FOR_READY_EXPLICITLY_SET_FLAG = 0x80;
32 const CORKED_FLAG = 0x100;
33
34 /**
35  * Class for storing metadata. Keys are normalized to lowercase ASCII.
36  * @memberof grpc
37  * @constructor
38  * @param {Object=} options Boolean options for the beginning of the call.
39  *     These options only have any effect when passed at the beginning of
40  *     a client request.
41  * @param {boolean=} [options.idempotentRequest=false] Signal that the request
42  *     is idempotent
43  * @param {boolean=} [options.waitForReady=true] Signal that the call should
44  *     not return UNAVAILABLE before it has started.
45  * @param {boolean=} [options.cacheableRequest=false] Signal that the call is
46  *     cacheable. GRPC is free to use GET verb.
47  * @param {boolean=} [options.corked=false] Signal that the initial metadata
48  *     should be corked.
49  * @example
50  * var metadata = new metadata_module.Metadata();
51  * metadata.set('key1', 'value1');
52  * metadata.add('key1', 'value2');
53  * metadata.get('key1') // returns ['value1', 'value2']
54  */
55 function Metadata(options) {
56   this._internal_repr = {};
57   this.setOptions(options);
58 }
59
60 function normalizeKey(key) {
61   key = key.toLowerCase();
62   if (grpc.metadataKeyIsLegal(key)) {
63     return key;
64   } else {
65     throw new Error('Metadata key"' + key + '" contains illegal characters');
66   }
67 }
68
69 function validate(key, value) {
70   if (grpc.metadataKeyIsBinary(key)) {
71     if (!(value instanceof Buffer)) {
72       throw new Error('keys that end with \'-bin\' must have Buffer values');
73     }
74   } else {
75     if (typeof value !== 'string') {
76       throw new Error(
77           'keys that don\'t end with \'-bin\' must have String values');
78     }
79     if (!grpc.metadataNonbinValueIsLegal(value)) {
80       throw new Error('Metadata string value "' + value +
81                       '" contains illegal characters');
82     }
83   }
84 }
85
86 /**
87  * Sets the given value for the given key, replacing any other values associated
88  * with that key. Normalizes the key.
89  * @param {String} key The key to set
90  * @param {String|Buffer} value The value to set. Must be a buffer if and only
91  *     if the normalized key ends with '-bin'
92  */
93 Metadata.prototype.set = function(key, value) {
94   key = normalizeKey(key);
95   validate(key, value);
96   this._internal_repr[key] = [value];
97 };
98
99 /**
100  * Adds the given value for the given key. Normalizes the key.
101  * @param {String} key The key to add to.
102  * @param {String|Buffer} value The value to add. Must be a buffer if and only
103  *     if the normalized key ends with '-bin'
104  */
105 Metadata.prototype.add = function(key, value) {
106   key = normalizeKey(key);
107   validate(key, value);
108   if (!this._internal_repr[key]) {
109     this._internal_repr[key] = [];
110   }
111   this._internal_repr[key].push(value);
112 };
113
114 /**
115  * Remove the given key and any associated values. Normalizes the key.
116  * @param {String} key The key to remove
117  */
118 Metadata.prototype.remove = function(key) {
119   key = normalizeKey(key);
120   if (Object.prototype.hasOwnProperty.call(this._internal_repr, key)) {
121     delete this._internal_repr[key];
122   }
123 };
124
125 /**
126  * Gets a list of all values associated with the key. Normalizes the key.
127  * @param {String} key The key to get
128  * @return {Array.<String|Buffer>} The values associated with that key
129  */
130 Metadata.prototype.get = function(key) {
131   key = normalizeKey(key);
132   if (Object.prototype.hasOwnProperty.call(this._internal_repr, key)) {
133     return this._internal_repr[key];
134   } else {
135     return [];
136   }
137 };
138
139 /**
140  * Get a map of each key to a single associated value. This reflects the most
141  * common way that people will want to see metadata.
142  * @return {Object.<String,String|Buffer>} A key/value mapping of the metadata
143  */
144 Metadata.prototype.getMap = function() {
145   var result = {};
146   Object.keys(this._internal_repr).forEach(key => {
147     const values = this._internal_repr[key];
148     if(values.length > 0) {
149       result[key] = values[0];
150     }
151   });
152   return result;
153 };
154
155 /**
156  * Clone the metadata object.
157  * @return {grpc.Metadata} The new cloned object
158  */
159 Metadata.prototype.clone = function() {
160   var copy = new Metadata();
161   Object.keys(this._internal_repr).forEach(key => {
162     const value = this._internal_repr[key];
163     copy._internal_repr[key] = clone(value);
164   });
165   copy.flags = this.flags;
166   return copy;
167 };
168
169 /**
170  * Set options on the metadata object
171  * @param {Object} options Boolean options for the beginning of the call.
172  *     These options only have any effect when passed at the beginning of
173  *     a client request.
174  * @param {boolean=} [options.idempotentRequest=false] Signal that the request
175  *     is idempotent
176  * @param {boolean=} [options.waitForReady=true] Signal that the call should
177  *     not return UNAVAILABLE before it has started.
178  * @param {boolean=} [options.cacheableRequest=false] Signal that the call is
179  *     cacheable. GRPC is free to use GET verb.
180  * @param {boolean=} [options.corked=false] Signal that the initial metadata
181  *     should be corked.
182  */
183 Metadata.prototype.setOptions = function(options) {
184   let flags = 0;
185   if (options) {
186     if (options.idempotentRequest) {
187       flags |= IDEMPOTENT_REQUEST_FLAG;
188     }
189     if (options.hasOwnProperty('waitForReady')) {
190       flags |= WAIT_FOR_READY_EXPLICITLY_SET_FLAG;
191       if (options.waitForReady) {
192         flags |= WAIT_FOR_READY_FLAG;
193       }
194     }
195     if (options.cacheableRequest) {
196       flags |= CACHEABLE_REQUEST_FLAG;
197     }
198     if (options.corked) {
199       flags |= CORKED_FLAG;
200     }
201   }
202   this.flags = flags;
203 }
204
205 /**
206  * Metadata representation as passed to and the native addon
207  * @typedef {object} grpc~CoreMetadata
208  * @param {Object.<String, Array.<String|Buffer>>} metadata The metadata
209  * @param {number} flags Metadata flags
210  */
211
212 /**
213  * Gets the metadata in the format used by interal code. Intended for internal
214  * use only. API stability is not guaranteed.
215  * @private
216  * @return {grpc~CoreMetadata} The metadata
217  */
218 Metadata.prototype._getCoreRepresentation = function() {
219   return {
220     metadata: this._internal_repr,
221     flags: this.flags
222   };
223 };
224
225 /**
226  * Creates a Metadata object from a metadata map in the internal format.
227  * Intended for internal use only. API stability is not guaranteed.
228  * @private
229  * @param {grpc~CoreMetadata} metadata The metadata object from core
230  * @return {Metadata} The new Metadata object
231  */
232 Metadata._fromCoreRepresentation = function(metadata) {
233   var newMetadata = new Metadata();
234   if (metadata) {
235     Object.keys(metadata.metadata).forEach(key => {
236       const value = metadata.metadata[key];
237       if (!grpc.metadataKeyIsLegal(key)) {
238         common.log(logVerbosity.ERROR,
239           "Warning: possibly corrupted metadata key received: " +
240           key + ": " + value +
241           ". Please report this at https://github.com/grpc/grpc-node/issues/1173.");
242       }
243       newMetadata._internal_repr[key] = clone(value);
244     });
245   }
246   newMetadata.flags = metadata.flags;
247   return newMetadata;
248 };
249
250 module.exports = Metadata;