2525//! .service(my_grpc_service);
2626//! ```
2727
28- use http:: HeaderValue ;
28+ use http:: { HeaderMap , HeaderValue } ;
2929use std:: {
3030 future:: Future ,
3131 pin:: Pin ,
@@ -37,8 +37,11 @@ use tonic::{
3737 server:: NamedService ,
3838} ;
3939use tower:: { Layer , Service } ;
40+ use tracing:: { error, info, warn} ;
4041
41- use crate :: { ComponentInfo , SYSTEM_INFO_HEADER , VERSION_HEADER } ;
42+ use crate :: {
43+ ComponentInfo , DefguardComponent , SYSTEM_INFO_HEADER , SystemInfo , VERSION_HEADER , Version ,
44+ } ;
4245
4346/// A tower `Layer` that adds Defguard version and system information headers to gRPC responses.
4447///
@@ -53,6 +56,8 @@ use crate::{ComponentInfo, SYSTEM_INFO_HEADER, VERSION_HEADER};
5356#[ derive( Clone ) ]
5457pub struct DefguardVersionLayer {
5558 component_info : ComponentInfo ,
59+ remote_component : DefguardComponent ,
60+ remote_component_min_version : Version ,
5661}
5762
5863impl DefguardVersionLayer {
@@ -66,9 +71,15 @@ impl DefguardVersionLayer {
6671 ///
6772 /// * `Ok(DefguardVersionLayer)` - A new layer instance
6873 /// * `Err(DefguardVersionError)` - If the version string cannot be parsed
69- pub fn new ( version : crate :: Version ) -> Self {
74+ pub fn new (
75+ version : Version ,
76+ remote_component : DefguardComponent ,
77+ remote_component_min_version : Version ,
78+ ) -> Self {
7079 Self {
7180 component_info : ComponentInfo :: new ( version) ,
81+ remote_component,
82+ remote_component_min_version,
7283 }
7384 }
7485}
@@ -80,6 +91,8 @@ impl<S> Layer<S> for DefguardVersionLayer {
8091 DefguardVersionService {
8192 inner,
8293 component_info : self . component_info . clone ( ) ,
94+ remote_component : self . remote_component . clone ( ) ,
95+ remote_component_min_version : self . remote_component_min_version . clone ( ) ,
8396 }
8497 }
8598}
@@ -102,6 +115,55 @@ impl<S> Layer<S> for DefguardVersionLayer {
102115pub struct DefguardVersionService < S > {
103116 inner : S ,
104117 component_info : ComponentInfo ,
118+ remote_component : DefguardComponent ,
119+ remote_component_min_version : Version ,
120+ }
121+
122+ impl < S > DefguardVersionService < S > {
123+ /// Checks if remote component version meets minimum version requirements.
124+ fn is_version_supported ( & self , version : Option < & Version > ) -> bool {
125+ let Some ( version) = version else {
126+ error ! (
127+ "Missing core component version information. This most likely means that core component uses unsupported version."
128+ ) ;
129+ return false ;
130+ } ;
131+ if version < & self . remote_component_min_version {
132+ error ! (
133+ "{} version {version} is not supported. Minimal supported {} version is {}." ,
134+ self . remote_component, self . remote_component, self . remote_component_min_version
135+ ) ;
136+ return false ;
137+ }
138+
139+ info ! ( "{} version {version} is supported" , self . remote_component) ;
140+ true
141+ }
142+
143+ pub fn parse_headers ( metadata : & HeaderMap ) -> Option < ComponentInfo > {
144+ let Some ( version) = metadata. get ( VERSION_HEADER ) else {
145+ warn ! ( "Missing version header" ) ;
146+ return None ;
147+ } ;
148+ let Some ( system) = metadata. get ( SYSTEM_INFO_HEADER ) else {
149+ warn ! ( "Missing system info header" ) ;
150+ return None ;
151+ } ;
152+ let ( Ok ( version) , Ok ( system) ) = ( version. to_str ( ) , system. to_str ( ) ) else {
153+ warn ! ( "Failed to stringify version or system info header value" ) ;
154+ return None ;
155+ } ;
156+ let Ok ( version) = Version :: parse ( version) else {
157+ warn ! ( "Failed to parse version: {version}" ) ;
158+ return None ;
159+ } ;
160+ let Ok ( system) = SystemInfo :: try_from_header_value ( system) else {
161+ warn ! ( "Failed to parse system info: {system}" ) ;
162+ return None ;
163+ } ;
164+
165+ Some ( ComponentInfo { version, system } )
166+ }
105167}
106168
107169impl < S , B > Service < Request < Body > > for DefguardVersionService < S >
@@ -136,7 +198,15 @@ where
136198 . ok ( ) ,
137199 ) ;
138200
201+ // Check remote component version
202+ let maybe_info = Self :: parse_headers ( request. headers ( ) ) ;
203+ let version = maybe_info. as_ref ( ) . map ( |info| & info. version ) ;
204+ let is_remote_version_supported = self . is_version_supported ( version) ;
139205 Box :: pin ( async move {
206+ if !is_remote_version_supported {
207+ // TODO: return error response
208+ error ! ( "REMOTE VERSION NOT SUPPORTED" ) ;
209+ }
140210 // Process the request with the inner service first
141211 let mut response = inner. call ( request) . await ?;
142212
0 commit comments