11#[ cfg( test) ]
22mod test;
33
4+ use std:: env;
5+
46use lazy_static:: lazy_static;
57
68use crate :: {
@@ -32,6 +34,7 @@ struct ClientMetadata {
3234 driver : DriverMetadata ,
3335 os : OsMetadata ,
3436 platform : String ,
37+ env : Option < FaasEnvironment > ,
3538}
3639
3740#[ derive( Clone , Debug ) ]
@@ -49,10 +52,28 @@ struct DriverMetadata {
4952struct OsMetadata {
5053 os_type : String ,
5154 name : Option < String > ,
52- architecture : String ,
55+ architecture : Option < String > ,
5356 version : Option < String > ,
5457}
5558
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+
5677impl From < ClientMetadata > for Bson {
5778 fn from ( metadata : ClientMetadata ) -> Self {
5879 let mut metadata_doc = Document :: new ( ) ;
@@ -72,6 +93,10 @@ impl From<ClientMetadata> for Bson {
7293 metadata_doc. insert ( "os" , metadata. os ) ;
7394 metadata_doc. insert ( "platform" , metadata. platform ) ;
7495
96+ if let Some ( env) = metadata. env {
97+ metadata_doc. insert ( "env" , env) ;
98+ }
99+
75100 Bson :: Document ( metadata_doc)
76101 }
77102}
@@ -84,7 +109,9 @@ impl From<OsMetadata> for Bson {
84109 doc. insert ( "name" , name) ;
85110 }
86111
87- doc. insert ( "architecture" , metadata. architecture ) ;
112+ if let Some ( arch) = metadata. architecture {
113+ doc. insert ( "architecture" , arch) ;
114+ }
88115
89116 if let Some ( version) = metadata. version {
90117 doc. insert ( "version" , version) ;
@@ -94,6 +121,141 @@ impl From<OsMetadata> for Bson {
94121 }
95122}
96123
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+
97259lazy_static ! {
98260 /// Contains the basic handshake information that can be statically determined. This document
99261 /// (potentially with additional fields added) can be cloned and put in the `client` field of
@@ -107,15 +269,47 @@ lazy_static! {
107269 } ,
108270 os: OsMetadata {
109271 os_type: std:: env:: consts:: OS . into( ) ,
110- architecture: std:: env:: consts:: ARCH . into( ) ,
272+ architecture: Some ( std:: env:: consts:: ARCH . into( ) ) ,
111273 name: None ,
112274 version: None ,
113275 } ,
114276 platform: format!( "{} with {}" , rustc_version_runtime:: version_meta( ) . short_version_string, RUNTIME_NAME ) ,
277+ env: None ,
115278 }
116279 } ;
117280}
118281
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+
119313/// Contains the logic needed to handshake a connection.
120314#[ derive( Clone , Debug ) ]
121315pub ( crate ) struct Handshaker {
@@ -130,6 +324,8 @@ pub(crate) struct Handshaker {
130324 http_client : HttpClient ,
131325
132326 server_api : Option < ServerApi > ,
327+
328+ metadata : ClientMetadata ,
133329}
134330
135331impl Handshaker {
@@ -164,6 +360,8 @@ impl Handshaker {
164360 }
165361 }
166362
363+ metadata. env = FaasEnvironment :: new ( ) ;
364+
167365 if options. load_balanced {
168366 command. body . insert ( "loadBalanced" , true ) ;
169367 }
@@ -180,13 +378,14 @@ impl Handshaker {
180378 ) ;
181379 }
182380
183- command. body . insert ( "client" , metadata) ;
381+ command. body . insert ( "client" , metadata. clone ( ) ) ;
184382
185383 Self {
186384 http_client,
187385 command,
188386 compressors,
189387 server_api : options. server_api ,
388+ metadata,
190389 }
191390 }
192391
@@ -205,6 +404,16 @@ impl Handshaker {
205404
206405 let client_first = set_speculative_auth_info ( & mut command. body , credential) ?;
207406
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+
208417 let mut hello_reply = run_hello ( conn, command) . await ?;
209418
210419 conn. stream_description = Some ( StreamDescription :: from_hello_reply ( & hello_reply) ) ;
@@ -308,3 +517,11 @@ fn set_speculative_auth_info(
308517
309518 Ok ( Some ( client_first) )
310519}
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