react_on_rails_pro 16.5.1 → 16.6.0.rc.1
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 +4 -4
- data/CONTRIBUTING.md +1 -1
- data/Gemfile.lock +7 -7
- data/app/helpers/react_on_rails_pro_helper.rb +4 -1
- data/lib/react_on_rails_pro/configuration.rb +76 -2
- data/lib/react_on_rails_pro/constants.rb +2 -0
- data/lib/react_on_rails_pro/request.rb +4 -3
- data/lib/react_on_rails_pro/server_rendering_pool/node_rendering_pool.rb +3 -2
- data/lib/react_on_rails_pro/stream_request.rb +4 -0
- data/lib/react_on_rails_pro/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3c916f9674e2623f411de371a45f7a4016780b2f0f2b267b2be6adc00b73e256
|
|
4
|
+
data.tar.gz: ea25bb1c946748b59252d44a5e42bdab08b9782d062ac1e03d35480e1d080f2f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 377bbdc3a94e443fa1fda13389603f1593a8fe3457af9ccc66ebfe5b710164cf1b7a225886d44e44edec6a73fd4bb4cd31813ee2c6f57f02c884efb79a1fb14f
|
|
7
|
+
data.tar.gz: c23fcc4de60626c285b8aeb47fb9b334fac21b57950976aa947be27e51831e3ef221a9e482fb3ba4dccaf693bd2aa755ab13f802780b27eca0411ac65dbca83a
|
data/CONTRIBUTING.md
CHANGED
|
@@ -46,7 +46,7 @@ From [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/
|
|
|
46
46
|
|
|
47
47
|
## Doc Changes
|
|
48
48
|
|
|
49
|
-
When making doc changes, we want the change to work on both [the React on Rails docs site](https://reactonrails.com/docs/pro) and when browsing the GitHub repo.
|
|
49
|
+
When making doc changes, we want the change to work on both [the React on Rails docs site](https://reactonrails.com/docs/pro/) and when browsing the GitHub repo.
|
|
50
50
|
For links from docs pages to non-doc files, use full GitHub URLs so links resolve correctly in both contexts.
|
|
51
51
|
|
|
52
52
|
### Links to other docs:
|
data/Gemfile.lock
CHANGED
|
@@ -9,7 +9,7 @@ GIT
|
|
|
9
9
|
PATH
|
|
10
10
|
remote: ..
|
|
11
11
|
specs:
|
|
12
|
-
react_on_rails (16.
|
|
12
|
+
react_on_rails (16.6.0.rc.1)
|
|
13
13
|
addressable
|
|
14
14
|
connection_pool
|
|
15
15
|
execjs (~> 2.5)
|
|
@@ -20,7 +20,7 @@ PATH
|
|
|
20
20
|
PATH
|
|
21
21
|
remote: .
|
|
22
22
|
specs:
|
|
23
|
-
react_on_rails_pro (16.
|
|
23
|
+
react_on_rails_pro (16.6.0.rc.1)
|
|
24
24
|
addressable
|
|
25
25
|
async (>= 2.29)
|
|
26
26
|
connection_pool
|
|
@@ -29,7 +29,7 @@ PATH
|
|
|
29
29
|
httpx (~> 1.5)
|
|
30
30
|
jwt (~> 2.7)
|
|
31
31
|
rainbow
|
|
32
|
-
react_on_rails (= 16.
|
|
32
|
+
react_on_rails (= 16.6.0.rc.1)
|
|
33
33
|
|
|
34
34
|
GEM
|
|
35
35
|
remote: https://rubygems.org/
|
|
@@ -217,7 +217,7 @@ GEM
|
|
|
217
217
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
218
218
|
rb-inotify (~> 0.9, >= 0.9.10)
|
|
219
219
|
logger (1.7.0)
|
|
220
|
-
loofah (2.25.
|
|
220
|
+
loofah (2.25.1)
|
|
221
221
|
crass (~> 1.0.2)
|
|
222
222
|
nokogiri (>= 1.12.0)
|
|
223
223
|
mail (2.9.0)
|
|
@@ -249,11 +249,11 @@ GEM
|
|
|
249
249
|
net-smtp (0.5.1)
|
|
250
250
|
net-protocol
|
|
251
251
|
nio4r (2.7.5)
|
|
252
|
-
nokogiri (1.19.
|
|
252
|
+
nokogiri (1.19.2-arm64-darwin)
|
|
253
253
|
racc (~> 1.4)
|
|
254
|
-
nokogiri (1.19.
|
|
254
|
+
nokogiri (1.19.2-x86_64-darwin)
|
|
255
255
|
racc (~> 1.4)
|
|
256
|
-
nokogiri (1.19.
|
|
256
|
+
nokogiri (1.19.2-x86_64-linux-gnu)
|
|
257
257
|
racc (~> 1.4)
|
|
258
258
|
package_json (0.2.0)
|
|
259
259
|
parallel (1.27.0)
|
|
@@ -128,7 +128,10 @@ module ReactOnRailsProHelper
|
|
|
128
128
|
# stream_react_component doesn't have the prerender option
|
|
129
129
|
# Because setting prerender to false is equivalent to calling react_component with prerender: false
|
|
130
130
|
options[:prerender] = true
|
|
131
|
-
|
|
131
|
+
if options.key?(:immediate_hydration)
|
|
132
|
+
ReactOnRails::Helper.warn_removed_immediate_hydration_option("stream_react_component")
|
|
133
|
+
options.delete(:immediate_hydration)
|
|
134
|
+
end
|
|
132
135
|
|
|
133
136
|
# Extract streaming-specific callback
|
|
134
137
|
on_complete = options.delete(:on_complete)
|
|
@@ -15,6 +15,7 @@ module ReactOnRailsPro
|
|
|
15
15
|
renderer_http_pool_size: Configuration::DEFAULT_RENDERER_HTTP_POOL_SIZE,
|
|
16
16
|
renderer_http_pool_timeout: Configuration::DEFAULT_RENDERER_HTTP_POOL_TIMEOUT,
|
|
17
17
|
renderer_http_pool_warn_timeout: Configuration::DEFAULT_RENDERER_HTTP_POOL_WARN_TIMEOUT,
|
|
18
|
+
renderer_http_keep_alive_timeout: Configuration::DEFAULT_RENDERER_HTTP_KEEP_ALIVE_TIMEOUT,
|
|
18
19
|
renderer_password: nil,
|
|
19
20
|
tracing: Configuration::DEFAULT_TRACING,
|
|
20
21
|
dependency_globs: Configuration::DEFAULT_DEPENDENCY_GLOBS,
|
|
@@ -44,6 +45,7 @@ module ReactOnRailsPro
|
|
|
44
45
|
DEFAULT_RENDERER_HTTP_POOL_SIZE = 10
|
|
45
46
|
DEFAULT_RENDERER_HTTP_POOL_TIMEOUT = 5
|
|
46
47
|
DEFAULT_RENDERER_HTTP_POOL_WARN_TIMEOUT = 0.25
|
|
48
|
+
DEFAULT_RENDERER_HTTP_KEEP_ALIVE_TIMEOUT = 30
|
|
47
49
|
DEFAULT_SSR_TIMEOUT = 5
|
|
48
50
|
DEFAULT_PRERENDER_CACHING = false
|
|
49
51
|
DEFAULT_TRACING = false
|
|
@@ -72,7 +74,7 @@ module ReactOnRailsPro
|
|
|
72
74
|
:rsc_payload_generation_url_path, :rsc_bundle_js_file, :react_client_manifest_file,
|
|
73
75
|
:react_server_client_manifest_file
|
|
74
76
|
|
|
75
|
-
attr_reader :concurrent_component_streaming_buffer_size
|
|
77
|
+
attr_reader :concurrent_component_streaming_buffer_size, :renderer_http_keep_alive_timeout
|
|
76
78
|
|
|
77
79
|
# Sets the buffer size for concurrent component streaming.
|
|
78
80
|
#
|
|
@@ -91,10 +93,23 @@ module ReactOnRailsPro
|
|
|
91
93
|
@concurrent_component_streaming_buffer_size = value
|
|
92
94
|
end
|
|
93
95
|
|
|
96
|
+
# Sets the keep-alive timeout (in seconds) for persistent HTTP connections to the node renderer.
|
|
97
|
+
#
|
|
98
|
+
# @param value [Numeric, nil] A positive number or nil (to use the HTTPX default)
|
|
99
|
+
# @raise [ReactOnRailsPro::Error] if value is not a positive number or nil
|
|
100
|
+
def renderer_http_keep_alive_timeout=(value)
|
|
101
|
+
unless value.nil? || (value.is_a?(Numeric) && value.positive? && value.finite?)
|
|
102
|
+
raise ReactOnRailsPro::Error,
|
|
103
|
+
"config.renderer_http_keep_alive_timeout must be a finite positive number or nil"
|
|
104
|
+
end
|
|
105
|
+
@renderer_http_keep_alive_timeout = value
|
|
106
|
+
end
|
|
107
|
+
|
|
94
108
|
def initialize(renderer_url: nil, renderer_password: nil, server_renderer: nil, # rubocop:disable Metrics/AbcSize
|
|
95
109
|
renderer_use_fallback_exec_js: nil, prerender_caching: nil,
|
|
96
110
|
renderer_http_pool_size: nil, renderer_http_pool_timeout: nil,
|
|
97
|
-
renderer_http_pool_warn_timeout: nil,
|
|
111
|
+
renderer_http_pool_warn_timeout: nil, renderer_http_keep_alive_timeout: nil,
|
|
112
|
+
tracing: nil,
|
|
98
113
|
dependency_globs: nil, excluded_dependency_globs: nil, rendering_returns_promises: nil,
|
|
99
114
|
remote_bundle_cache_adapter: nil, ssr_pre_hook_js: nil, assets_to_copy: nil,
|
|
100
115
|
renderer_request_retry_limit: nil, throw_js_errors: nil, ssr_timeout: nil,
|
|
@@ -111,6 +126,7 @@ module ReactOnRailsPro
|
|
|
111
126
|
self.renderer_http_pool_size = renderer_http_pool_size
|
|
112
127
|
self.renderer_http_pool_timeout = renderer_http_pool_timeout
|
|
113
128
|
self.renderer_http_pool_warn_timeout = renderer_http_pool_warn_timeout
|
|
129
|
+
self.renderer_http_keep_alive_timeout = renderer_http_keep_alive_timeout
|
|
114
130
|
self.tracing = tracing
|
|
115
131
|
self.rendering_returns_promises = server_renderer == "NodeRenderer" ? rendering_returns_promises : false
|
|
116
132
|
self.dependency_globs = dependency_globs
|
|
@@ -229,10 +245,68 @@ module ReactOnRailsPro
|
|
|
229
245
|
end
|
|
230
246
|
|
|
231
247
|
def setup_renderer_password
|
|
248
|
+
# Explicit passwords, including values loaded from ENV in the initializer, skip URL extraction.
|
|
249
|
+
# Blank values (nil or "") fall through so URL extraction and ENV fallback still apply.
|
|
232
250
|
return if renderer_password.present?
|
|
233
251
|
|
|
234
252
|
uri = URI(renderer_url)
|
|
235
253
|
self.renderer_password = uri.password
|
|
254
|
+
|
|
255
|
+
# Mirror Node-side defaults: if Rails config and URL are both missing a password,
|
|
256
|
+
# use RENDERER_PASSWORD from env.
|
|
257
|
+
self.renderer_password = ENV.fetch("RENDERER_PASSWORD", nil) if renderer_password.blank?
|
|
258
|
+
|
|
259
|
+
validate_renderer_password_for_production
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def validate_renderer_password_for_production
|
|
263
|
+
# Defense-in-depth: skip validation when a password is already configured (e.g. extracted
|
|
264
|
+
# from the renderer URL by setup_renderer_password, or set directly in the initializer).
|
|
265
|
+
return if renderer_password.present?
|
|
266
|
+
return unless node_renderer?
|
|
267
|
+
|
|
268
|
+
# Fail closed: only skip validation when RAILS_ENV is explicitly set to development or test.
|
|
269
|
+
# Rails.env defaults to "development" when RAILS_ENV is unset, which would silently skip
|
|
270
|
+
# validation in misconfigured environments. Checking ENV["RAILS_ENV"] directly matches the
|
|
271
|
+
# Node-side behavior where an unset environment is treated as production-like.
|
|
272
|
+
rails_env = ENV["RAILS_ENV"]&.downcase
|
|
273
|
+
return if rails_env.present? && %w[development test].include?(rails_env)
|
|
274
|
+
|
|
275
|
+
raise ReactOnRailsPro::Error, <<~MSG
|
|
276
|
+
RENDERER_PASSWORD must be set in production-like environments (staging, production, etc.)
|
|
277
|
+
when using the NodeRenderer.
|
|
278
|
+
|
|
279
|
+
In development and test environments, the renderer password is optional and no authentication
|
|
280
|
+
is required. In all other environments, you must explicitly configure a password to secure
|
|
281
|
+
communication between Rails and the Node Renderer.
|
|
282
|
+
|
|
283
|
+
To fix this, set the RENDERER_PASSWORD environment variable:
|
|
284
|
+
|
|
285
|
+
export RENDERER_PASSWORD="your-secure-password"
|
|
286
|
+
|
|
287
|
+
Rails reads it automatically. If you prefer to make it explicit in your initializer:
|
|
288
|
+
|
|
289
|
+
# config/initializers/react_on_rails_pro.rb
|
|
290
|
+
ReactOnRailsPro.configure do |config|
|
|
291
|
+
config.renderer_password = ENV.fetch("RENDERER_PASSWORD")
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
Set the same password for the Node Renderer via the RENDERER_PASSWORD environment variable.
|
|
295
|
+
Rails resolves the password in this order:
|
|
296
|
+
1) config.renderer_password (blank values fall through to the next step)
|
|
297
|
+
2) Password embedded in config.renderer_url (for example, https://:password@host:3800)
|
|
298
|
+
3) ENV["RENDERER_PASSWORD"]
|
|
299
|
+
|
|
300
|
+
If Rails and the Node Renderer disagree about startup behavior, verify both RAILS_ENV and NODE_ENV.
|
|
301
|
+
|
|
302
|
+
Environment matrix:
|
|
303
|
+
development — password optional (no authentication)
|
|
304
|
+
test — password optional (no authentication)
|
|
305
|
+
(RAILS_ENV unset) — treated as production-like; RENDERER_PASSWORD required
|
|
306
|
+
staging — RENDERER_PASSWORD required
|
|
307
|
+
production — RENDERER_PASSWORD required
|
|
308
|
+
(any other) — RENDERER_PASSWORD required
|
|
309
|
+
MSG
|
|
236
310
|
end
|
|
237
311
|
end
|
|
238
312
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module ReactOnRailsPro
|
|
4
|
+
# Status code 400 indicates the renderer rejected the request payload or encountered an unhandled render error.
|
|
5
|
+
STATUS_BAD_REQUEST = 400
|
|
4
6
|
# Status code 410 means to resend the request with the updated bundle.
|
|
5
7
|
STATUS_SEND_BUNDLE = 410
|
|
6
8
|
# Status code 412 means protocol versions are incompatible between the server and the renderer.
|
|
@@ -297,11 +297,11 @@ module ReactOnRailsPro
|
|
|
297
297
|
# :write_timeout
|
|
298
298
|
# :request_timeout
|
|
299
299
|
# :operation_timeout
|
|
300
|
-
# :keep_alive_timeout
|
|
301
300
|
timeout: {
|
|
302
301
|
connect_timeout: ReactOnRailsPro.configuration.renderer_http_pool_timeout,
|
|
303
|
-
read_timeout: ReactOnRailsPro.configuration.ssr_timeout
|
|
304
|
-
|
|
302
|
+
read_timeout: ReactOnRailsPro.configuration.ssr_timeout,
|
|
303
|
+
keep_alive_timeout: ReactOnRailsPro.configuration.renderer_http_keep_alive_timeout
|
|
304
|
+
}.compact
|
|
305
305
|
)
|
|
306
306
|
rescue StandardError => e
|
|
307
307
|
message = <<~MSG
|
|
@@ -309,6 +309,7 @@ module ReactOnRailsPro
|
|
|
309
309
|
renderer_http_pool_size = #{ReactOnRailsPro.configuration.renderer_http_pool_size}
|
|
310
310
|
renderer_http_pool_timeout = #{ReactOnRailsPro.configuration.renderer_http_pool_timeout}
|
|
311
311
|
renderer_http_pool_warn_timeout = #{ReactOnRailsPro.configuration.renderer_http_pool_warn_timeout}
|
|
312
|
+
renderer_http_keep_alive_timeout = #{ReactOnRailsPro.configuration.renderer_http_keep_alive_timeout}
|
|
312
313
|
renderer_url = #{url}
|
|
313
314
|
Be sure to use a url that contains the protocol of http or https.
|
|
314
315
|
Original error is
|
|
@@ -74,9 +74,10 @@ module ReactOnRailsPro
|
|
|
74
74
|
ReactOnRailsPro::Error.raise_duplicate_bundle_upload_error if send_bundle
|
|
75
75
|
|
|
76
76
|
eval_js(js_code, render_options, send_bundle: true)
|
|
77
|
-
when
|
|
77
|
+
when ReactOnRailsPro::STATUS_BAD_REQUEST
|
|
78
78
|
raise ReactOnRailsPro::Error,
|
|
79
|
-
"Renderer
|
|
79
|
+
"Renderer rejected malformed request or hit an unhandled VM error: " \
|
|
80
|
+
"#{response.status}:\n#{response.body}"
|
|
80
81
|
else
|
|
81
82
|
raise ReactOnRailsPro::Error,
|
|
82
83
|
"Unexpected response code from renderer: #{response.status}:\n#{response.body}"
|
|
@@ -140,6 +140,10 @@ module ReactOnRailsPro
|
|
|
140
140
|
ReactOnRailsPro::Error.raise_duplicate_bundle_upload_error if send_bundle
|
|
141
141
|
|
|
142
142
|
true
|
|
143
|
+
when ReactOnRailsPro::STATUS_BAD_REQUEST
|
|
144
|
+
raise ReactOnRailsPro::Error,
|
|
145
|
+
"Renderer rejected malformed request or hit an unhandled VM error: " \
|
|
146
|
+
"#{response.status}:\n#{error_body}"
|
|
143
147
|
when ReactOnRailsPro::STATUS_INCOMPATIBLE
|
|
144
148
|
raise ReactOnRailsPro::Error, error_body
|
|
145
149
|
else
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: react_on_rails_pro
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 16.
|
|
4
|
+
version: 16.6.0.rc.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Gordon
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-04-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: addressable
|
|
@@ -128,14 +128,14 @@ dependencies:
|
|
|
128
128
|
requirements:
|
|
129
129
|
- - '='
|
|
130
130
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: 16.
|
|
131
|
+
version: 16.6.0.rc.1
|
|
132
132
|
type: :runtime
|
|
133
133
|
prerelease: false
|
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
|
136
136
|
- - '='
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: 16.
|
|
138
|
+
version: 16.6.0.rc.1
|
|
139
139
|
- !ruby/object:Gem::Dependency
|
|
140
140
|
name: bundler
|
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|