forest_admin_rpc_agent 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be12ef320a58188c0b00b823de2b976f715923f5a47a8a76054b12d58ab05829
4
- data.tar.gz: 3e2bb9736bd3625cc1e7be35d2136d149d22ea593acfff01265b5f6833240796
3
+ metadata.gz: d79eae397fb216be01160a0bf1807af637c000c736087983e5388d9f4b69b316
4
+ data.tar.gz: 74eb1e5a8e70cc62dc9443e5771a0e5f9abc9381383e176fbd61202454b9faec
5
5
  SHA512:
6
- metadata.gz: d9f76307375f4e415dd4ac8db8fee5f8f9db0e93e0f06b87c157e6b434cf34e6764917b7cfe9e49b204fecc3034dc3ae6ab06c65ed3b91a58c7c8f44b5e3b265
7
- data.tar.gz: 7ecabd777a5e662863145cf7d6b093a9f3fe3ad02537f6ca1ce8c9aefa719ca39b2faf51fe3bcebcf193f9cb653f443bb3e4ac49cd8e97cd24d09b378e335c7d
6
+ metadata.gz: 04ea3178afb1f079e1aa0c1b66a34765b1dc5575650c2519a7719e7f2d6a941f7385008c22383d2d30f8a02a98e9ae5361864d26fb3577bda09c656fd4d3d50b
7
+ data.tar.gz: 8e7697a78a44fb891040f184c1d8d769449a60e537f1ffff071eca38383c11ff507eef1eb573ffa50a489b30ea32edc4da4ae9764b680f05d67d18c47553f0c8
@@ -3,10 +3,13 @@ require 'jsonapi-serializers'
3
3
  module ForestAdminRpcAgent
4
4
  module Routes
5
5
  class Sse
6
- def initialize(url = 'rpc/sse', method = 'get', name = 'rpc_sse')
6
+ DEFAULT_HEARTBEAT_INTERVAL = 1
7
+
8
+ def initialize(url = 'rpc/sse', method = 'get', name = 'rpc_sse', heartbeat_interval: DEFAULT_HEARTBEAT_INTERVAL)
7
9
  @url = url
8
10
  @method = method
9
11
  @name = name
12
+ @heartbeat_interval = heartbeat_interval
10
13
  end
11
14
 
12
15
  def registered(app)
@@ -20,11 +23,58 @@ module ForestAdminRpcAgent
20
23
  end
21
24
  end
22
25
 
23
- def register_sinatra(_app)
24
- # TODO
26
+ def register_sinatra(app)
27
+ route_instance = self
28
+ app.send(@method.to_sym, "/#{@url}") do
29
+ auth_middleware = ForestAdminRpcAgent::Middleware::Authentication.new(->(_env) { [200, {}, ['OK']] })
30
+ status, headers, response = auth_middleware.call(env)
31
+
32
+ halt status, headers, response if status != 200
33
+
34
+ content_type 'text/event-stream'
35
+ headers 'Cache-Control' => 'no-cache',
36
+ 'Connection' => 'keep-alive',
37
+ 'X-Accel-Buffering' => 'no'
38
+
39
+ stream(:keep_open) do |out|
40
+ should_continue = true
41
+ server_stopped = false
42
+ stop_proc = proc do
43
+ should_continue = false
44
+ server_stopped = true
45
+ end
46
+ original_handler = trap('INT', stop_proc)
47
+
48
+ begin
49
+ streamer = SseStreamer.new(out)
50
+
51
+ while should_continue
52
+ streamer.write('', event: 'heartbeat')
53
+ sleep route_instance.instance_variable_get(:@heartbeat_interval)
54
+ end
55
+
56
+ # Send RpcServerStop only if server is stopping (not client disconnect)
57
+ if server_stopped
58
+ begin
59
+ streamer.write({ event: 'RpcServerStop' }, event: 'RpcServerStop')
60
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', '[SSE] RpcServerStop event sent')
61
+ rescue StandardError => e
62
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', "[SSE] Error sending stop event: #{e.message}")
63
+ end
64
+ end
65
+ rescue IOError, Errno::EPIPE => e
66
+ # Client disconnected normally
67
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', "[SSE] Client disconnected: #{e.message}")
68
+ ensure
69
+ trap('INT', original_handler)
70
+ out.close if out.respond_to?(:close)
71
+ end
72
+ end
73
+ end
25
74
  end
26
75
 
27
76
  def register_rails(router)
77
+ route_instance = self
28
78
  handler = proc do |hash|
29
79
  request = ActionDispatch::Request.new(hash)
30
80
  auth_middleware = ForestAdminRpcAgent::Middleware::Authentication.new(->(_env) { [200, {}, ['OK']] })
@@ -34,31 +84,47 @@ module ForestAdminRpcAgent
34
84
  headers = {
35
85
  'Content-Type' => 'text/event-stream',
36
86
  'Cache-Control' => 'no-cache',
37
- 'Connection' => 'keep-alive'
87
+ 'Connection' => 'keep-alive',
88
+ 'X-Accel-Buffering' => 'no'
38
89
  }
39
90
 
40
91
  should_continue = true
41
- # Intercept CTRL+C
42
- stop_proc = proc { should_continue = false }
92
+ server_stopped = false
93
+ stop_proc = proc do
94
+ should_continue = false
95
+ server_stopped = true
96
+ end
43
97
  original_handler = trap('INT', stop_proc)
44
98
 
45
99
  body = Enumerator.new do |yielder|
46
100
  stream = SseStreamer.new(yielder)
47
101
 
48
102
  begin
103
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', '[SSE] Starting stream')
104
+
49
105
  while should_continue
50
- # ForestAdminRpcAgent::Facades::Container.logger.log('Debug', '[SSE] heartbeat')
51
106
  stream.write('', event: 'heartbeat')
52
- sleep 1
107
+ sleep route_instance.instance_variable_get(:@heartbeat_interval)
108
+ end
109
+
110
+ # Send RpcServerStop only if server is stopping (not client disconnect)
111
+ if server_stopped
112
+ begin
113
+ stream.write({ event: 'RpcServerStop' }, event: 'RpcServerStop')
114
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', '[SSE] RpcServerStop event sent')
115
+ rescue StandardError => e
116
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', "[SSE] Error sending stop event: #{e.message}")
117
+ end
53
118
  end
54
- rescue IOError
55
- # Client disconnected
56
- # ForestAdminRpcAgent::Facades::Container.logger.log('Debug', '[SSE] disconnected')
119
+ rescue IOError, Errno::EPIPE => e
120
+ # Client disconnected normally
121
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', "[SSE] Client disconnected: #{e.message}")
122
+ rescue StandardError => e
123
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Error', "[SSE] Unexpected error: #{e.message}")
124
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Error', e.backtrace.join("\n"))
57
125
  ensure
58
126
  trap('INT', original_handler)
59
- stream.write({ event: 'RpcServerStop' }, event: 'RpcServerStop')
60
- # ForestAdminRpcAgent::Facades::Container.logger.log('Debug', '[SSE] stopped streaming')
61
- yielder.close if yielder.respond_to?(:close)
127
+ ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', '[SSE] Stream stopped')
62
128
  end
63
129
  end
64
130
 
@@ -1,3 +1,3 @@
1
1
  module ForestAdminRpcAgent
2
- VERSION = "1.7.1"
2
+ VERSION = "1.8.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_admin_rpc_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.1
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthieu