UNPKG

6.98 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright 2019 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 */
18Object.defineProperty(exports, "__esModule", { value: true });
19exports.CompressionFilterFactory = exports.CompressionFilter = void 0;
20const zlib = require("zlib");
21const filter_1 = require("./filter");
22class CompressionHandler {
23 /**
24 * @param message Raw uncompressed message bytes
25 * @param compress Indicates whether the message should be compressed
26 * @return Framed message, compressed if applicable
27 */
28 async writeMessage(message, compress) {
29 let messageBuffer = message;
30 if (compress) {
31 messageBuffer = await this.compressMessage(messageBuffer);
32 }
33 const output = Buffer.allocUnsafe(messageBuffer.length + 5);
34 output.writeUInt8(compress ? 1 : 0, 0);
35 output.writeUInt32BE(messageBuffer.length, 1);
36 messageBuffer.copy(output, 5);
37 return output;
38 }
39 /**
40 * @param data Framed message, possibly compressed
41 * @return Uncompressed message
42 */
43 async readMessage(data) {
44 const compressed = data.readUInt8(0) === 1;
45 let messageBuffer = data.slice(5);
46 if (compressed) {
47 messageBuffer = await this.decompressMessage(messageBuffer);
48 }
49 return messageBuffer;
50 }
51}
52class IdentityHandler extends CompressionHandler {
53 async compressMessage(message) {
54 return message;
55 }
56 async writeMessage(message, compress) {
57 const output = Buffer.allocUnsafe(message.length + 5);
58 /* With "identity" compression, messages should always be marked as
59 * uncompressed */
60 output.writeUInt8(0, 0);
61 output.writeUInt32BE(message.length, 1);
62 message.copy(output, 5);
63 return output;
64 }
65 decompressMessage(message) {
66 return Promise.reject(new Error('Received compressed message but "grpc-encoding" header was identity'));
67 }
68}
69class DeflateHandler extends CompressionHandler {
70 compressMessage(message) {
71 return new Promise((resolve, reject) => {
72 zlib.deflate(message, (err, output) => {
73 if (err) {
74 reject(err);
75 }
76 else {
77 resolve(output);
78 }
79 });
80 });
81 }
82 decompressMessage(message) {
83 return new Promise((resolve, reject) => {
84 zlib.inflate(message, (err, output) => {
85 if (err) {
86 reject(err);
87 }
88 else {
89 resolve(output);
90 }
91 });
92 });
93 }
94}
95class GzipHandler extends CompressionHandler {
96 compressMessage(message) {
97 return new Promise((resolve, reject) => {
98 zlib.gzip(message, (err, output) => {
99 if (err) {
100 reject(err);
101 }
102 else {
103 resolve(output);
104 }
105 });
106 });
107 }
108 decompressMessage(message) {
109 return new Promise((resolve, reject) => {
110 zlib.unzip(message, (err, output) => {
111 if (err) {
112 reject(err);
113 }
114 else {
115 resolve(output);
116 }
117 });
118 });
119 }
120}
121class UnknownHandler extends CompressionHandler {
122 constructor(compressionName) {
123 super();
124 this.compressionName = compressionName;
125 }
126 compressMessage(message) {
127 return Promise.reject(new Error(`Received message compressed with unsupported compression method ${this.compressionName}`));
128 }
129 decompressMessage(message) {
130 // This should be unreachable
131 return Promise.reject(new Error(`Compression method not supported: ${this.compressionName}`));
132 }
133}
134function getCompressionHandler(compressionName) {
135 switch (compressionName) {
136 case 'identity':
137 return new IdentityHandler();
138 case 'deflate':
139 return new DeflateHandler();
140 case 'gzip':
141 return new GzipHandler();
142 default:
143 return new UnknownHandler(compressionName);
144 }
145}
146class CompressionFilter extends filter_1.BaseFilter {
147 constructor() {
148 super(...arguments);
149 this.sendCompression = new IdentityHandler();
150 this.receiveCompression = new IdentityHandler();
151 }
152 async sendMetadata(metadata) {
153 const headers = await metadata;
154 headers.set('grpc-accept-encoding', 'identity,deflate,gzip');
155 headers.set('accept-encoding', 'identity');
156 return headers;
157 }
158 receiveMetadata(metadata) {
159 const receiveEncoding = metadata.get('grpc-encoding');
160 if (receiveEncoding.length > 0) {
161 const encoding = receiveEncoding[0];
162 if (typeof encoding === 'string') {
163 this.receiveCompression = getCompressionHandler(encoding);
164 }
165 }
166 metadata.remove('grpc-encoding');
167 metadata.remove('grpc-accept-encoding');
168 return metadata;
169 }
170 async sendMessage(message) {
171 /* This filter is special. The input message is the bare message bytes,
172 * and the output is a framed and possibly compressed message. For this
173 * reason, this filter should be at the bottom of the filter stack */
174 const resolvedMessage = await message;
175 const compress = resolvedMessage.flags === undefined
176 ? false
177 : (resolvedMessage.flags & 2 /* NoCompress */) === 0;
178 return {
179 message: await this.sendCompression.writeMessage(resolvedMessage.message, compress),
180 flags: resolvedMessage.flags,
181 };
182 }
183 async receiveMessage(message) {
184 /* This filter is also special. The input message is framed and possibly
185 * compressed, and the output message is deframed and uncompressed. So
186 * this is another reason that this filter should be at the bottom of the
187 * filter stack. */
188 return this.receiveCompression.readMessage(await message);
189 }
190}
191exports.CompressionFilter = CompressionFilter;
192class CompressionFilterFactory {
193 constructor(channel) {
194 this.channel = channel;
195 }
196 createFilter(callStream) {
197 return new CompressionFilter();
198 }
199}
200exports.CompressionFilterFactory = CompressionFilterFactory;
201//# sourceMappingURL=compression-filter.js.map
\No newline at end of file