3 * Copyright 2015 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 var clone = require('lodash.clone');
23 var grpc = require('./grpc_extension');
25 const common = require('./common');
26 const logVerbosity = require('./constants').logVerbosity;
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;
35 * Class for storing metadata. Keys are normalized to lowercase ASCII.
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
41 * @param {boolean=} [options.idempotentRequest=false] Signal that the request
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
50 * var metadata = new metadata_module.Metadata();
51 * metadata.set('key1', 'value1');
52 * metadata.add('key1', 'value2');
53 * metadata.get('key1') // returns ['value1', 'value2']
55 function Metadata(options) {
56 this._internal_repr = {};
57 this.setOptions(options);
60 function normalizeKey(key) {
61 key = key.toLowerCase();
62 if (grpc.metadataKeyIsLegal(key)) {
65 throw new Error('Metadata key"' + key + '" contains illegal characters');
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');
75 if (typeof value !== 'string') {
77 'keys that don\'t end with \'-bin\' must have String values');
79 if (!grpc.metadataNonbinValueIsLegal(value)) {
80 throw new Error('Metadata string value "' + value +
81 '" contains illegal characters');
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'
93 Metadata.prototype.set = function(key, value) {
94 key = normalizeKey(key);
96 this._internal_repr[key] = [value];
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'
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] = [];
111 this._internal_repr[key].push(value);
115 * Remove the given key and any associated values. Normalizes the key.
116 * @param {String} key The key to remove
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];
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
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];
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
144 Metadata.prototype.getMap = function() {
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];
156 * Clone the metadata object.
157 * @return {grpc.Metadata} The new cloned object
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);
165 copy.flags = this.flags;
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
174 * @param {boolean=} [options.idempotentRequest=false] Signal that the request
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
183 Metadata.prototype.setOptions = function(options) {
186 if (options.idempotentRequest) {
187 flags |= IDEMPOTENT_REQUEST_FLAG;
189 if (options.hasOwnProperty('waitForReady')) {
190 flags |= WAIT_FOR_READY_EXPLICITLY_SET_FLAG;
191 if (options.waitForReady) {
192 flags |= WAIT_FOR_READY_FLAG;
195 if (options.cacheableRequest) {
196 flags |= CACHEABLE_REQUEST_FLAG;
198 if (options.corked) {
199 flags |= CORKED_FLAG;
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
213 * Gets the metadata in the format used by interal code. Intended for internal
214 * use only. API stability is not guaranteed.
216 * @return {grpc~CoreMetadata} The metadata
218 Metadata.prototype._getCoreRepresentation = function() {
220 metadata: this._internal_repr,
226 * Creates a Metadata object from a metadata map in the internal format.
227 * Intended for internal use only. API stability is not guaranteed.
229 * @param {grpc~CoreMetadata} metadata The metadata object from core
230 * @return {Metadata} The new Metadata object
232 Metadata._fromCoreRepresentation = function(metadata) {
233 var newMetadata = new 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: " +
241 ". Please report this at https://github.com/grpc/grpc-node/issues/1173.");
243 newMetadata._internal_repr[key] = clone(value);
246 newMetadata.flags = metadata.flags;
250 module.exports = Metadata;