@@ -126,7 +126,87 @@ let address_of_console __context console : address option =
126126    ) ;
127127  address_option
128128
129- let  real_proxy'  vnc_port  s  = 
129+ module  Console_idle_monitor  =  struct 
130+   let  get_idle_timeout_config  ~__context  ~vm   = 
131+     try 
132+       let  idle_timeout = 
133+         if  Db.VM. get_is_control_domain ~__context ~self: vm then 
134+           let  host =  Helpers. get_localhost ~__context in 
135+           Db.Host. get_console_idle_timeout ~__context ~self: host
136+         else 
137+           let  pool =  Helpers. get_pool ~__context in 
138+           Db.Pool. get_vm_console_idle_timeout ~__context ~self: pool
139+       in 
140+       if  idle_timeout >  0L  then  Some  (Int64. to_float idle_timeout) else  None 
141+     with  _  ->  None 
142+ 
143+   let  is_active  messages  = 
144+     List. exists
145+       (function 
146+         |  Rfb_client_msgtype_parser. KeyEvent 
147+         |  Rfb_client_msgtype_parser. PointerEvent 
148+         |  Rfb_client_msgtype_parser. QEMUClientMessage  ->
149+             true 
150+         |  _  ->
151+             false 
152+         )
153+       messages
154+ 
155+   let  timed_out  ~idle_timeout_seconds   ~last_activity   = 
156+     let  elapsed =  Mtime_clock. count last_activity in 
157+     Mtime.Span. to_float_ns elapsed /.  1e9  >  idle_timeout_seconds
158+ 
159+   (*  Create an idle timeout callback for the console,
160+      if idle timeout, then close the proxy *)  
161+   let  create_idle_timeout_callback  ~__context  ~vm   = 
162+     match  get_idle_timeout_config ~__context ~vm  with 
163+     |  Some  idle_timeout_seconds  -> (
164+         let  module  P  = Rfb_client_msgtype_parser  in 
165+         let  state =  ref  (Some  (P. create () , Mtime_clock. counter () )) in 
166+         (*  Return true for idle timeout to close the proxy,
167+             otherwise return false to keep the proxy open *)  
168+         fun  (buf , read_len , offset ) ->
169+           match  ! state with 
170+           |  None  ->
171+               false 
172+           |  Some  (rfb_parser , last_activity ) ->
173+               let  ok  msgs  = 
174+                 if  is_active msgs then  (
175+                   state :=  Some  (rfb_parser, Mtime_clock. counter () ) ;
176+                   false 
177+                 ) else 
178+                   let  timeout_result = 
179+                     timed_out ~idle_timeout_seconds  ~last_activity 
180+                   in 
181+                   if  timeout_result then 
182+                     debug
183+                       " Console connection idle timeout exceeded for VM %s \
184+                        (timeout: %.1fs)"  
185+                       (Ref. string_of vm) idle_timeout_seconds ;
186+                   timeout_result
187+               in 
188+               let  error  msg  = 
189+                 debug " RFB parse error: %s" 
190+                 state :=  None  ;
191+                 false 
192+               in 
193+               Bytes. sub_string buf offset read_len
194+               |>  rfb_parser
195+               |>  Result. fold ~ok  ~error 
196+       )
197+     |  None  ->
198+         Fun. const false 
199+ end
200+ 
201+ let  get_poll_timeout = 
202+   let  poll_period_timeout =  ! Xapi_globs. proxy_poll_period_timeout in 
203+   if  poll_period_timeout <  0.  then 
204+     - 1 
205+   else 
206+     Float. to_int (poll_period_timeout *.  1000. )
207+ (*  convert to milliseconds *) 
208+ 
209+ let  real_proxy'  ~__context  ~vm   vnc_port  s  = 
130210  try 
131211    Http_svr. headers s (Http. http_200_ok () ) ;
132212    let  vnc_sock = 
@@ -141,7 +221,12 @@ let real_proxy' vnc_port s =
141221    debug " Connected; running proxy (between fds: %d and %d)" 
142222      (Unixext. int_of_file_descr vnc_sock)
143223      (Unixext. int_of_file_descr s') ;
144-     Unixext. proxy vnc_sock s' ;
224+ 
225+     let  poll_timeout =  get_poll_timeout in 
226+     let  should_close = 
227+       Console_idle_monitor. create_idle_timeout_callback ~__context ~vm 
228+     in 
229+     Unixext. proxy ~should_close  ~poll_timeout  vnc_sock s' ;
145230    debug " Proxy exited" 
146231  with  exn  ->  debug " error: %s" ExnHelper. string_of_exn exn )
147232
@@ -153,7 +238,7 @@ let real_proxy __context vm _ _ vnc_port s =
153238  in 
154239  if  Connection_limit. try_add vm_id is_limit_enabled then 
155240    finally (*  Ensure we drop the vm connection count if exceptions occur *) 
156-       (fun  ()  -> real_proxy' vnc_port s)
241+       (fun  ()  -> real_proxy' ~__context  ~vm   vnc_port s)
157242      (fun  ()  -> Connection_limit. drop vm_id)
158243  else 
159244    Http_svr. headers s (Http. http_503_service_unavailable () )
0 commit comments