1
1
#[ cfg( test) ]
2
2
mod test;
3
3
4
+ use std:: env;
5
+
4
6
use lazy_static:: lazy_static;
5
7
6
8
use crate :: {
@@ -32,6 +34,7 @@ struct ClientMetadata {
32
34
driver : DriverMetadata ,
33
35
os : OsMetadata ,
34
36
platform : String ,
37
+ env : Option < FaasEnvironment > ,
35
38
}
36
39
37
40
#[ derive( Clone , Debug ) ]
@@ -49,10 +52,28 @@ struct DriverMetadata {
49
52
struct OsMetadata {
50
53
os_type : String ,
51
54
name : Option < String > ,
52
- architecture : String ,
55
+ architecture : Option < String > ,
53
56
version : Option < String > ,
54
57
}
55
58
59
+ #[ derive( Clone , Debug ) ]
60
+ struct FaasEnvironment {
61
+ name : FaasEnvironmentName ,
62
+ runtime : Option < String > ,
63
+ timeout_sec : Option < i32 > ,
64
+ memory_mb : Option < i32 > ,
65
+ region : Option < String > ,
66
+ url : Option < String > ,
67
+ }
68
+
69
+ #[ derive( Copy , Clone , Debug ) ]
70
+ enum FaasEnvironmentName {
71
+ AwsLambda ,
72
+ AzureFunc ,
73
+ GcpFunc ,
74
+ Vercel ,
75
+ }
76
+
56
77
impl From < ClientMetadata > for Bson {
57
78
fn from ( metadata : ClientMetadata ) -> Self {
58
79
let mut metadata_doc = Document :: new ( ) ;
@@ -72,6 +93,10 @@ impl From<ClientMetadata> for Bson {
72
93
metadata_doc. insert ( "os" , metadata. os ) ;
73
94
metadata_doc. insert ( "platform" , metadata. platform ) ;
74
95
96
+ if let Some ( env) = metadata. env {
97
+ metadata_doc. insert ( "env" , env) ;
98
+ }
99
+
75
100
Bson :: Document ( metadata_doc)
76
101
}
77
102
}
@@ -84,7 +109,9 @@ impl From<OsMetadata> for Bson {
84
109
doc. insert ( "name" , name) ;
85
110
}
86
111
87
- doc. insert ( "architecture" , metadata. architecture ) ;
112
+ if let Some ( arch) = metadata. architecture {
113
+ doc. insert ( "architecture" , arch) ;
114
+ }
88
115
89
116
if let Some ( version) = metadata. version {
90
117
doc. insert ( "version" , version) ;
@@ -94,6 +121,141 @@ impl From<OsMetadata> for Bson {
94
121
}
95
122
}
96
123
124
+ impl From < FaasEnvironment > for Bson {
125
+ fn from ( env : FaasEnvironment ) -> Self {
126
+ let FaasEnvironment {
127
+ name,
128
+ runtime,
129
+ timeout_sec,
130
+ memory_mb,
131
+ region,
132
+ url,
133
+ } = env;
134
+ let mut out = doc ! {
135
+ "name" : name. name( ) ,
136
+ } ;
137
+ if let Some ( rt) = runtime {
138
+ out. insert ( "runtime" , rt) ;
139
+ }
140
+ if let Some ( t) = timeout_sec {
141
+ out. insert ( "timeout_sec" , t) ;
142
+ }
143
+ if let Some ( m) = memory_mb {
144
+ out. insert ( "memory_mb" , m) ;
145
+ }
146
+ if let Some ( r) = region {
147
+ out. insert ( "region" , r) ;
148
+ }
149
+ if let Some ( u) = url {
150
+ out. insert ( "url" , u) ;
151
+ }
152
+ Bson :: Document ( out)
153
+ }
154
+ }
155
+
156
+ impl FaasEnvironment {
157
+ const UNSET : Self = FaasEnvironment {
158
+ name : FaasEnvironmentName :: AwsLambda ,
159
+ runtime : None ,
160
+ timeout_sec : None ,
161
+ memory_mb : None ,
162
+ region : None ,
163
+ url : None ,
164
+ } ;
165
+
166
+ fn new ( ) -> Option < Self > {
167
+ let name = FaasEnvironmentName :: new ( ) ?;
168
+ Some ( match name {
169
+ FaasEnvironmentName :: AwsLambda => {
170
+ let runtime = env:: var ( "AWS_EXECUTION_ENV" ) . ok ( ) ;
171
+ let region = env:: var ( "AWS_REGION" ) . ok ( ) ;
172
+ let memory_mb = env:: var ( "AWS_LAMBDA_FUNCTION_MEMORY_SIZE" )
173
+ . ok ( )
174
+ . and_then ( |s| s. parse ( ) . ok ( ) ) ;
175
+ Self {
176
+ name,
177
+ runtime,
178
+ region,
179
+ memory_mb,
180
+ ..Self :: UNSET
181
+ }
182
+ }
183
+ FaasEnvironmentName :: AzureFunc => {
184
+ let runtime = env:: var ( "FUNCTIONS_WORKER_RUNTIME" ) . ok ( ) ;
185
+ Self {
186
+ name,
187
+ runtime,
188
+ ..Self :: UNSET
189
+ }
190
+ }
191
+ FaasEnvironmentName :: GcpFunc => {
192
+ let memory_mb = env:: var ( "FUNCTION_MEMORY_MB" )
193
+ . ok ( )
194
+ . and_then ( |s| s. parse ( ) . ok ( ) ) ;
195
+ let timeout_sec = env:: var ( "FUNCTION_TIMEOUT_SEC" )
196
+ . ok ( )
197
+ . and_then ( |s| s. parse ( ) . ok ( ) ) ;
198
+ let region = env:: var ( "FUNCTION_REGION" ) . ok ( ) ;
199
+ Self {
200
+ name,
201
+ memory_mb,
202
+ timeout_sec,
203
+ region,
204
+ ..Self :: UNSET
205
+ }
206
+ }
207
+ FaasEnvironmentName :: Vercel => {
208
+ let url = env:: var ( "VERCEL_URL" ) . ok ( ) ;
209
+ let region = env:: var ( "VERCEL_REGION" ) . ok ( ) ;
210
+ Self {
211
+ name,
212
+ url,
213
+ region,
214
+ ..Self :: UNSET
215
+ }
216
+ }
217
+ } )
218
+ }
219
+ }
220
+
221
+ fn var_set ( name : & str ) -> bool {
222
+ env:: var_os ( name) . map_or ( false , |v| !v. is_empty ( ) )
223
+ }
224
+
225
+ impl FaasEnvironmentName {
226
+ fn new ( ) -> Option < Self > {
227
+ use FaasEnvironmentName :: * ;
228
+ let mut found = vec ! [ ] ;
229
+ if var_set ( "AWS_EXECUTION_ENV" ) || var_set ( "AWS_LAMBDA_RUNTIME_API" ) {
230
+ found. push ( AwsLambda ) ;
231
+ }
232
+ if var_set ( "FUNCTIONS_WORKER_RUNTIME" ) {
233
+ found. push ( AzureFunc ) ;
234
+ }
235
+ if var_set ( "K_SERVICE" ) || var_set ( "FUNCTION_NAME" ) {
236
+ found. push ( GcpFunc ) ;
237
+ }
238
+ if var_set ( "VERCEL" ) {
239
+ found. push ( Vercel ) ;
240
+ }
241
+ if found. len ( ) != 1 {
242
+ None
243
+ } else {
244
+ Some ( found[ 0 ] )
245
+ }
246
+ }
247
+
248
+ fn name ( & self ) -> & ' static str {
249
+ use FaasEnvironmentName :: * ;
250
+ match self {
251
+ AwsLambda => "aws.lambda" ,
252
+ AzureFunc => "azure.func" ,
253
+ GcpFunc => "gcp.func" ,
254
+ Vercel => "vercel" ,
255
+ }
256
+ }
257
+ }
258
+
97
259
lazy_static ! {
98
260
/// Contains the basic handshake information that can be statically determined. This document
99
261
/// (potentially with additional fields added) can be cloned and put in the `client` field of
@@ -107,15 +269,47 @@ lazy_static! {
107
269
} ,
108
270
os: OsMetadata {
109
271
os_type: std:: env:: consts:: OS . into( ) ,
110
- architecture: std:: env:: consts:: ARCH . into( ) ,
272
+ architecture: Some ( std:: env:: consts:: ARCH . into( ) ) ,
111
273
name: None ,
112
274
version: None ,
113
275
} ,
114
276
platform: format!( "{} with {}" , rustc_version_runtime:: version_meta( ) . short_version_string, RUNTIME_NAME ) ,
277
+ env: None ,
115
278
}
116
279
} ;
117
280
}
118
281
282
+ type Truncation = fn ( & mut ClientMetadata ) ;
283
+
284
+ const METADATA_TRUNCATIONS : & [ Truncation ] = & [
285
+ // truncate `platform`
286
+ |metadata| {
287
+ metadata. platform = rustc_version_runtime:: version_meta ( ) . short_version_string ;
288
+ } ,
289
+ // clear `env.*` except `name`
290
+ |metadata| {
291
+ if let Some ( env) = & mut metadata. env {
292
+ * env = FaasEnvironment {
293
+ name : env. name ,
294
+ ..FaasEnvironment :: UNSET
295
+ }
296
+ }
297
+ } ,
298
+ // clear `os.*` except `type`
299
+ |metadata| {
300
+ metadata. os = OsMetadata {
301
+ os_type : metadata. os . os_type . clone ( ) ,
302
+ architecture : None ,
303
+ name : None ,
304
+ version : None ,
305
+ }
306
+ } ,
307
+ // clear `env`
308
+ |metadata| {
309
+ metadata. env = None ;
310
+ } ,
311
+ ] ;
312
+
119
313
/// Contains the logic needed to handshake a connection.
120
314
#[ derive( Clone , Debug ) ]
121
315
pub ( crate ) struct Handshaker {
@@ -130,6 +324,8 @@ pub(crate) struct Handshaker {
130
324
http_client : HttpClient ,
131
325
132
326
server_api : Option < ServerApi > ,
327
+
328
+ metadata : ClientMetadata ,
133
329
}
134
330
135
331
impl Handshaker {
@@ -164,6 +360,8 @@ impl Handshaker {
164
360
}
165
361
}
166
362
363
+ metadata. env = FaasEnvironment :: new ( ) ;
364
+
167
365
if options. load_balanced {
168
366
command. body . insert ( "loadBalanced" , true ) ;
169
367
}
@@ -180,13 +378,14 @@ impl Handshaker {
180
378
) ;
181
379
}
182
380
183
- command. body . insert ( "client" , metadata) ;
381
+ command. body . insert ( "client" , metadata. clone ( ) ) ;
184
382
185
383
Self {
186
384
http_client,
187
385
command,
188
386
compressors,
189
387
server_api : options. server_api ,
388
+ metadata,
190
389
}
191
390
}
192
391
@@ -205,6 +404,16 @@ impl Handshaker {
205
404
206
405
let client_first = set_speculative_auth_info ( & mut command. body , credential) ?;
207
406
407
+ let body = & mut command. body ;
408
+ let mut trunc_meta = self . metadata . clone ( ) ;
409
+ for trunc_fn in METADATA_TRUNCATIONS {
410
+ if doc_size ( body) ? <= MAX_HELLO_SIZE {
411
+ break ;
412
+ }
413
+ trunc_fn ( & mut trunc_meta) ;
414
+ body. insert ( "client" , trunc_meta. clone ( ) ) ;
415
+ }
416
+
208
417
let mut hello_reply = run_hello ( conn, command) . await ?;
209
418
210
419
conn. stream_description = Some ( StreamDescription :: from_hello_reply ( & hello_reply) ) ;
@@ -308,3 +517,11 @@ fn set_speculative_auth_info(
308
517
309
518
Ok ( Some ( client_first) )
310
519
}
520
+
521
+ fn doc_size ( d : & Document ) -> Result < usize > {
522
+ let mut tmp = vec ! [ ] ;
523
+ d. to_writer ( & mut tmp) ?;
524
+ Ok ( tmp. len ( ) )
525
+ }
526
+
527
+ const MAX_HELLO_SIZE : usize = 512 ;
0 commit comments