Skip to content

Commit 7c93431

Browse files
committed
Add metadata example
1 parent 5d731c4 commit 7c93431

File tree

4 files changed

+479
-0
lines changed

4 files changed

+479
-0
lines changed

examples/metadata/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Metadata example
2+
3+
This example shows how to set and read metadata in RPC headers and trailers.
4+
5+
## Start the server
6+
7+
```
8+
node server.js
9+
```
10+
11+
## Run the client
12+
13+
```
14+
node client.js
15+
```

examples/metadata/client.js

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
/*
2+
*
3+
* Copyright 2023 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+
const grpc = require('@grpc/grpc-js');
20+
const protoLoader = require('@grpc/proto-loader');
21+
const parseArgs = require('minimist');
22+
23+
const PROTO_PATH = __dirname + '/../protos/echo.proto';
24+
25+
const packageDefinition = protoLoader.loadSync(
26+
PROTO_PATH,
27+
{keepCase: true,
28+
longs: String,
29+
enums: String,
30+
defaults: true,
31+
oneofs: true
32+
});
33+
const echoProto = grpc.loadPackageDefinition(packageDefinition).grpc.examples.echo;
34+
35+
const STREAMING_COUNT = 10;
36+
37+
function unaryCallWithMetadata(client, message) {
38+
return new Promise((resolve, reject) => {
39+
console.log('--- unary ---');
40+
const requestMetadata = new grpc.Metadata();
41+
requestMetadata.set('timestamp', new Date().toISOString());
42+
const call = client.unaryEcho({message}, requestMetadata, (error, value) => {
43+
if (error) {
44+
console.log(`Received error ${error}`);
45+
return;
46+
}
47+
console.log('Response:');
48+
console.log(`- ${JSON.stringify(value)}`);
49+
});
50+
call.on('metadata', metadata => {
51+
const timestamps = metadata.get('timestamp');
52+
if (timestamps.length > 0) {
53+
console.log('timestamp from header:');
54+
for (const [index, value] of timestamps.entries()) {
55+
console.log(` ${index}. ${value}`);
56+
}
57+
} else {
58+
console.error("timestamp expected but doesn't exist in header");
59+
}
60+
const locations = metadata.get('location');
61+
if (locations.length > 0) {
62+
console.log('location from header:');
63+
for (const [index, value] of locations.entries()) {
64+
console.log(` ${index}. ${value}`);
65+
}
66+
} else {
67+
console.error("location expected but doesn't exist in header");
68+
}
69+
});
70+
call.on('status', status => {
71+
const timestamps = status.metadata.get('timestamp');
72+
if (timestamps.length > 0) {
73+
console.log('timestamp from trailer:');
74+
for (const [index, value] of timestamps.entries()) {
75+
console.log(` ${index}. ${value}`);
76+
}
77+
} else {
78+
console.error("timestamp expected but doesn't exist in trailer");
79+
}
80+
resolve();
81+
});
82+
});
83+
}
84+
85+
function serverStreamingWithMetadata(client, message) {
86+
return new Promise((resolve, reject) => {
87+
console.log('--- server streaming ---');
88+
const requestMetadata = new grpc.Metadata();
89+
requestMetadata.set('timestamp', new Date().toISOString());
90+
const call = client.serverStreamingEcho({message}, requestMetadata);
91+
call.on('metadata', metadata => {
92+
const timestamps = metadata.get('timestamp');
93+
if (timestamps.length > 0) {
94+
console.log('timestamp from header:');
95+
for (const [index, value] of timestamps.entries()) {
96+
console.log(` ${index}. ${value}`);
97+
}
98+
} else {
99+
console.error("timestamp expected but doesn't exist in header");
100+
}
101+
const locations = metadata.get('location');
102+
if (locations.length > 0) {
103+
console.log('location from header:');
104+
for (const [index, value] of locations.entries()) {
105+
console.log(` ${index}. ${value}`);
106+
}
107+
} else {
108+
console.error("location expected but doesn't exist in header");
109+
}
110+
});
111+
call.on('data', value => {
112+
console.log(`Received response ${JSON.stringify(value)}`);
113+
});
114+
call.on('status', status => {
115+
const timestamps = status.metadata.get('timestamp');
116+
if (timestamps.length > 0) {
117+
console.log('timestamp from trailer:');
118+
for (const [index, value] of timestamps.entries()) {
119+
console.log(` ${index}. ${value}`);
120+
}
121+
} else {
122+
console.error("timestamp expected but doesn't exist in trailer");
123+
}
124+
resolve();
125+
});
126+
call.on('error', error => {
127+
console.log(`Received error ${error}`);
128+
});
129+
});
130+
}
131+
132+
function clientStreamingWithMetadata(client, message) {
133+
return new Promise((resolve, reject) => {
134+
console.log('--- client streaming ---');
135+
const requestMetadata = new grpc.Metadata();
136+
requestMetadata.set('timestamp', new Date().toISOString());
137+
const call = client.clientStreamingEcho(requestMetadata, (error, value) => {
138+
if (error) {
139+
console.log(`Received error ${error}`);
140+
return;
141+
}
142+
console.log('Response:');
143+
console.log(`- ${JSON.stringify(value)}`);
144+
});
145+
call.on('metadata', metadata => {
146+
const timestamps = metadata.get('timestamp');
147+
if (timestamps.length > 0) {
148+
console.log('timestamp from header:');
149+
for (const [index, value] of timestamps.entries()) {
150+
console.log(` ${index}. ${value}`);
151+
}
152+
} else {
153+
console.error("timestamp expected but doesn't exist in header");
154+
}
155+
const locations = metadata.get('location');
156+
if (locations.length > 0) {
157+
console.log('location from header:');
158+
for (const [index, value] of locations.entries()) {
159+
console.log(` ${index}. ${value}`);
160+
}
161+
} else {
162+
console.error("location expected but doesn't exist in header");
163+
}
164+
});
165+
call.on('status', status => {
166+
const timestamps = status.metadata.get('timestamp');
167+
if (timestamps.length > 0) {
168+
console.log('timestamp from trailer:');
169+
for (const [index, value] of timestamps.entries()) {
170+
console.log(` ${index}. ${value}`);
171+
}
172+
} else {
173+
console.error("timestamp expected but doesn't exist in trailer");
174+
}
175+
resolve();
176+
});
177+
for (let i = 0; i < STREAMING_COUNT; i++) {
178+
call.write({message});
179+
}
180+
call.end();
181+
});
182+
}
183+
184+
function bidirectionalWithMetadata(client, message) {
185+
return new Promise((resolve, reject) => {
186+
console.log('--- bidirectional ---');
187+
const requestMetadata = new grpc.Metadata();
188+
requestMetadata.set('timestamp', new Date().toISOString());
189+
const call = client.bidirectionalStreamingEcho(requestMetadata);
190+
call.on('metadata', metadata => {
191+
const timestamps = metadata.get('timestamp');
192+
if (timestamps.length > 0) {
193+
console.log('timestamp from header:');
194+
for (const [index, value] of timestamps.entries()) {
195+
console.log(` ${index}. ${value}`);
196+
}
197+
} else {
198+
console.error("timestamp expected but doesn't exist in header");
199+
}
200+
const locations = metadata.get('location');
201+
if (locations.length > 0) {
202+
console.log('location from header:');
203+
for (const [index, value] of locations.entries()) {
204+
console.log(` ${index}. ${value}`);
205+
}
206+
} else {
207+
console.error("location expected but doesn't exist in header");
208+
}
209+
});
210+
call.on('data', value => {
211+
console.log(`Received response ${JSON.stringify(value)}`);
212+
});
213+
call.on('status', status => {
214+
const timestamps = status.metadata.get('timestamp');
215+
if (timestamps.length > 0) {
216+
console.log('timestamp from trailer:');
217+
for (const [index, value] of timestamps.entries()) {
218+
console.log(` ${index}. ${value}`);
219+
}
220+
} else {
221+
console.error("timestamp expected but doesn't exist in trailer");
222+
}
223+
resolve();
224+
});
225+
call.on('error', error => {
226+
console.log(`Received error ${error}`);
227+
});
228+
for (let i = 0; i < STREAMING_COUNT; i++) {
229+
call.write({message});
230+
}
231+
call.end();
232+
});
233+
}
234+
235+
function asyncWait(ms) {
236+
return new Promise((resolve, reject) => {
237+
setTimeout(resolve, ms);
238+
});
239+
}
240+
241+
const message = 'this is examples/metadata';
242+
243+
async function main() {
244+
let argv = parseArgs(process.argv.slice(2), {
245+
string: 'target'
246+
});
247+
let target;
248+
if (argv.target) {
249+
target = argv.target;
250+
} else {
251+
target = 'localhost:50051';
252+
}
253+
const client = new echoProto.Echo(target, grpc.credentials.createInsecure());
254+
await unaryCallWithMetadata(client, message);
255+
await asyncWait(1000);
256+
257+
await serverStreamingWithMetadata(client, message);
258+
await asyncWait(1000);
259+
260+
await clientStreamingWithMetadata(client, message);
261+
await asyncWait(1000);
262+
263+
await bidirectionalWithMetadata(client, message);
264+
client.close();
265+
}
266+
267+
main();

0 commit comments

Comments
 (0)