|
"use strict"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.Metadata = void 0; |
|
const logging_1 = require("./logging"); |
|
const constants_1 = require("./constants"); |
|
const error_1 = require("./error"); |
|
const LEGAL_KEY_REGEX = /^[0-9a-z_.-]+$/; |
|
const LEGAL_NON_BINARY_VALUE_REGEX = /^[ -~]*$/; |
|
function isLegalKey(key) { |
|
return LEGAL_KEY_REGEX.test(key); |
|
} |
|
function isLegalNonBinaryValue(value) { |
|
return LEGAL_NON_BINARY_VALUE_REGEX.test(value); |
|
} |
|
function isBinaryKey(key) { |
|
return key.endsWith('-bin'); |
|
} |
|
function isCustomMetadata(key) { |
|
return !key.startsWith('grpc-'); |
|
} |
|
function normalizeKey(key) { |
|
return key.toLowerCase(); |
|
} |
|
function validate(key, value) { |
|
if (!isLegalKey(key)) { |
|
throw new Error('Metadata key "' + key + '" contains illegal characters'); |
|
} |
|
if (value !== null && value !== undefined) { |
|
if (isBinaryKey(key)) { |
|
if (!Buffer.isBuffer(value)) { |
|
throw new Error("keys that end with '-bin' must have Buffer values"); |
|
} |
|
} |
|
else { |
|
if (Buffer.isBuffer(value)) { |
|
throw new Error("keys that don't end with '-bin' must have String values"); |
|
} |
|
if (!isLegalNonBinaryValue(value)) { |
|
throw new Error('Metadata string value "' + value + '" contains illegal characters'); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
class Metadata { |
|
constructor(options = {}) { |
|
this.internalRepr = new Map(); |
|
this.options = options; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set(key, value) { |
|
key = normalizeKey(key); |
|
validate(key, value); |
|
this.internalRepr.set(key, [value]); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
add(key, value) { |
|
key = normalizeKey(key); |
|
validate(key, value); |
|
const existingValue = this.internalRepr.get(key); |
|
if (existingValue === undefined) { |
|
this.internalRepr.set(key, [value]); |
|
} |
|
else { |
|
existingValue.push(value); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
remove(key) { |
|
key = normalizeKey(key); |
|
|
|
this.internalRepr.delete(key); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
get(key) { |
|
key = normalizeKey(key); |
|
|
|
return this.internalRepr.get(key) || []; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
getMap() { |
|
const result = {}; |
|
for (const [key, values] of this.internalRepr) { |
|
if (values.length > 0) { |
|
const v = values[0]; |
|
result[key] = Buffer.isBuffer(v) ? Buffer.from(v) : v; |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
clone() { |
|
const newMetadata = new Metadata(this.options); |
|
const newInternalRepr = newMetadata.internalRepr; |
|
for (const [key, value] of this.internalRepr) { |
|
const clonedValue = value.map(v => { |
|
if (Buffer.isBuffer(v)) { |
|
return Buffer.from(v); |
|
} |
|
else { |
|
return v; |
|
} |
|
}); |
|
newInternalRepr.set(key, clonedValue); |
|
} |
|
return newMetadata; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
merge(other) { |
|
for (const [key, values] of other.internalRepr) { |
|
const mergedValue = (this.internalRepr.get(key) || []).concat(values); |
|
this.internalRepr.set(key, mergedValue); |
|
} |
|
} |
|
setOptions(options) { |
|
this.options = options; |
|
} |
|
getOptions() { |
|
return this.options; |
|
} |
|
|
|
|
|
|
|
toHttp2Headers() { |
|
|
|
const result = {}; |
|
for (const [key, values] of this.internalRepr) { |
|
|
|
|
|
result[key] = values.map(bufToString); |
|
} |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
toJSON() { |
|
const result = {}; |
|
for (const [key, values] of this.internalRepr) { |
|
result[key] = values; |
|
} |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
static fromHttp2Headers(headers) { |
|
const result = new Metadata(); |
|
for (const key of Object.keys(headers)) { |
|
|
|
if (key.charAt(0) === ':') { |
|
continue; |
|
} |
|
const values = headers[key]; |
|
try { |
|
if (isBinaryKey(key)) { |
|
if (Array.isArray(values)) { |
|
values.forEach(value => { |
|
result.add(key, Buffer.from(value, 'base64')); |
|
}); |
|
} |
|
else if (values !== undefined) { |
|
if (isCustomMetadata(key)) { |
|
values.split(',').forEach(v => { |
|
result.add(key, Buffer.from(v.trim(), 'base64')); |
|
}); |
|
} |
|
else { |
|
result.add(key, Buffer.from(values, 'base64')); |
|
} |
|
} |
|
} |
|
else { |
|
if (Array.isArray(values)) { |
|
values.forEach(value => { |
|
result.add(key, value); |
|
}); |
|
} |
|
else if (values !== undefined) { |
|
result.add(key, values); |
|
} |
|
} |
|
} |
|
catch (error) { |
|
const message = `Failed to add metadata entry ${key}: ${values}. ${(0, error_1.getErrorMessage)(error)}. For more information see https://github.com/grpc/grpc-node/issues/1173`; |
|
(0, logging_1.log)(constants_1.LogVerbosity.ERROR, message); |
|
} |
|
} |
|
return result; |
|
} |
|
} |
|
exports.Metadata = Metadata; |
|
const bufToString = (val) => { |
|
return Buffer.isBuffer(val) ? val.toString('base64') : val; |
|
}; |
|
|