wrap_it_ruby 0.2.0 → 0.3.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.
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "async/http/client"
4
+ require "async/http/endpoint"
5
+
6
+ module WrapItRuby
7
+ module Middleware
8
+ # Protocol::HTTP middleware that intercepts WebSocket upgrade requests
9
+ # BEFORE the Rack adapter, proxying them directly at the HTTP protocol
10
+ # level. This preserves the Upgrade/Connection headers that Rack strips.
11
+ #
12
+ # Must be inserted as Falcon middleware, not as Rack middleware.
13
+ # For non-WebSocket requests, passes through to the Rack app.
14
+ #
15
+ class WebSocketProxy < Protocol::HTTP::Middleware
16
+ PROXY_PATTERN = %r{\A/_proxy/(?<host>[^/]+)(?<path>/.*)?\z}
17
+
18
+ HOP_HEADERS = %w[
19
+ connection keep-alive proxy-authenticate proxy-authorization
20
+ te trailers transfer-encoding upgrade
21
+ ].freeze
22
+
23
+ def initialize(app)
24
+ super(app)
25
+ @clients = {}
26
+ end
27
+
28
+ def call(request)
29
+ if websocket?(request) && (match = PROXY_PATTERN.match(request.path))
30
+ host = match[:host].delete_suffix(".")
31
+ proxy_websocket(request, host)
32
+ else
33
+ super(request)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def websocket?(request)
40
+ # HTTP/2: protocol pseudo-header
41
+ if Array(request.protocol).any? { |p| p.casecmp?("websocket") }
42
+ return true
43
+ end
44
+ # HTTP/1.1: Upgrade header
45
+ if upgrade = request.headers["upgrade"]
46
+ return Array(upgrade).any? { |u| u.casecmp?("websocket") }
47
+ end
48
+ false
49
+ end
50
+
51
+ def proxy_websocket(request, host)
52
+ # Strip /_proxy/{host} prefix from the path
53
+ match = PROXY_PATTERN.match(request.path)
54
+ request.path = match[:path] || "/"
55
+
56
+ # Rewrite authority/host to upstream — write_request adds host
57
+ # from authority automatically, so just delete it from headers.
58
+ request.authority = host
59
+ request.headers.delete("host")
60
+
61
+ # Set protocol as string (not array) for write_upgrade_body,
62
+ # and remove upgrade/connection headers to avoid duplicates.
63
+ request.protocol = "websocket"
64
+ request.headers.delete("upgrade")
65
+ request.headers.delete("connection")
66
+
67
+ # Rewrite origin
68
+ if request.headers["origin"]
69
+ request.headers.delete("origin")
70
+ request.headers.add("origin", "https://#{host}")
71
+ end
72
+
73
+ client = client_for(host)
74
+ client.call(request)
75
+ rescue => error
76
+ $stderr.puts "[ws-proxy] upstream error: #{error.class}: #{error.message}"
77
+ Protocol::HTTP::Response[502, {}, ["WebSocket proxy error: #{error.message}"]]
78
+ end
79
+
80
+ def client_for(host)
81
+ # Force HTTP/1.1 — upstream (Traefik/code-server) returns 405
82
+ # for HTTP/2 WebSocket CONNECT but accepts HTTP/1.1 Upgrade.
83
+ @clients[host] ||= Async::HTTP::Client.new(
84
+ Async::HTTP::Endpoint.parse("https://#{host}",
85
+ alpn_protocols: Async::HTTP::Protocol::HTTP11.names
86
+ ),
87
+ retries: 0
88
+ )
89
+ end
90
+ end
91
+ end
92
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WrapItRuby
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.1"
5
5
  end
data/lib/wrap_it_ruby.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "wrap_it_ruby/version"
4
- require "wrap_it_ruby/engine"
3
+ require 'wrap_it_ruby/version'
4
+ require 'wrap_it_ruby/engine'
5
5
 
6
6
  module WrapItRuby
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wrap_it_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Kidd
@@ -105,10 +105,14 @@ files:
105
105
  - Rakefile
106
106
  - app/assets/javascripts/wrap_it_ruby/interception.js
107
107
  - app/assets/stylesheets/wrap_it_ruby/application.css
108
+ - app/controllers/wrap_it_ruby/menu_settings_controller.rb
108
109
  - app/controllers/wrap_it_ruby/proxy_controller.rb
109
110
  - app/helpers/wrap_it_ruby/iframe_helper.rb
110
111
  - app/helpers/wrap_it_ruby/menu_helper.rb
111
112
  - app/javascript/wrap_it_ruby/controllers/iframe_proxy_controller.js
113
+ - app/javascript/wrap_it_ruby/controllers/sortable_tree_controller.js
114
+ - app/views/wrap_it_ruby/menu_settings/_tree.html.erb
115
+ - app/views/wrap_it_ruby/menu_settings/index.html.erb
112
116
  - app/views/wrap_it_ruby/proxy/show.html.ruby
113
117
  - config/importmap.rb
114
118
  - config/routes.rb
@@ -119,6 +123,7 @@ files:
119
123
  - lib/wrap_it_ruby/middleware/proxy_middleware.rb
120
124
  - lib/wrap_it_ruby/middleware/root_relative_proxy_middleware.rb
121
125
  - lib/wrap_it_ruby/middleware/script_injection_middleware.rb
126
+ - lib/wrap_it_ruby/middleware/websocket_proxy.rb
122
127
  - lib/wrap_it_ruby/version.rb
123
128
  - lib/wrap_it_ruby/version.rb.erb
124
129
  homepage: https://github.com/n-at-han-k/wrap-it-ruby