rails-threaded-proxy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 97a50696507952bcd8658bcecce9e784ed9f627a10c7add96e0942d0e7806e9a
4
+ data.tar.gz: 75767119d8493f4913b977e27e0d67dadfab57e6ed3cbade742d2ca5db9e4c49
5
+ SHA512:
6
+ metadata.gz: c04a9f03a0b19c139c9de59c00dfa7f49a5e6b3bdaafe1cb1db6f23b0d6ef623c60c30be5e5e08f68517915317f2764bc394bd82da36e65ef52c365641b1dc81
7
+ data.tar.gz: e6368a368ca91d818de566c6884b1840c95ff76665e18d5b8256a88b28c18048f22c52e1f771a8c5348bb9e4d02f9f91022e62c682d3cf7b4f5bd7601ec4891a
data/.bundle/config ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_BIN: "bin"
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.5
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "actionpack"
6
+ gem "addressable"
7
+
8
+ group :development do
9
+ gem "bundler", "~> 2.0"
10
+ gem "jeweler", "~> 2.3.9"
11
+ gem "nokogiri", ">= 1.16.7"
12
+ gem "rdoc", "~> 6.7.0"
13
+ gem "rspec", ">= 0"
14
+ gem "webrick", ">= 0"
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,164 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ actionpack (7.2.1)
5
+ actionview (= 7.2.1)
6
+ activesupport (= 7.2.1)
7
+ nokogiri (>= 1.8.5)
8
+ racc
9
+ rack (>= 2.2.4, < 3.2)
10
+ rack-session (>= 1.0.1)
11
+ rack-test (>= 0.6.3)
12
+ rails-dom-testing (~> 2.2)
13
+ rails-html-sanitizer (~> 1.6)
14
+ useragent (~> 0.16)
15
+ actionview (7.2.1)
16
+ activesupport (= 7.2.1)
17
+ builder (~> 3.1)
18
+ erubi (~> 1.11)
19
+ rails-dom-testing (~> 2.2)
20
+ rails-html-sanitizer (~> 1.6)
21
+ activesupport (7.2.1)
22
+ base64
23
+ bigdecimal
24
+ concurrent-ruby (~> 1.0, >= 1.3.1)
25
+ connection_pool (>= 2.2.5)
26
+ drb
27
+ i18n (>= 1.6, < 2)
28
+ logger (>= 1.4.2)
29
+ minitest (>= 5.1)
30
+ securerandom (>= 0.3)
31
+ tzinfo (~> 2.0, >= 2.0.5)
32
+ addressable (2.4.0)
33
+ base64 (0.2.0)
34
+ bigdecimal (3.1.8)
35
+ builder (3.3.0)
36
+ concurrent-ruby (1.3.4)
37
+ connection_pool (2.4.1)
38
+ crass (1.0.6)
39
+ descendants_tracker (0.0.4)
40
+ thread_safe (~> 0.3, >= 0.3.1)
41
+ diff-lcs (1.5.1)
42
+ drb (2.2.1)
43
+ erubi (1.13.0)
44
+ faraday (0.9.2)
45
+ multipart-post (>= 1.2, < 3)
46
+ git (1.11.0)
47
+ rchardet (~> 1.8)
48
+ github_api (0.16.0)
49
+ addressable (~> 2.4.0)
50
+ descendants_tracker (~> 0.0.4)
51
+ faraday (~> 0.8, < 0.10)
52
+ hashie (>= 3.4)
53
+ mime-types (>= 1.16, < 3.0)
54
+ oauth2 (~> 1.0)
55
+ hashie (5.0.0)
56
+ highline (3.1.1)
57
+ reline
58
+ i18n (1.14.6)
59
+ concurrent-ruby (~> 1.0)
60
+ io-console (0.7.2)
61
+ jeweler (2.3.9)
62
+ builder
63
+ bundler
64
+ git (>= 1.2.5)
65
+ github_api (~> 0.16.0)
66
+ highline (>= 1.6.15)
67
+ nokogiri (>= 1.5.10)
68
+ psych
69
+ rake
70
+ rdoc
71
+ semver2
72
+ jwt (2.9.3)
73
+ base64
74
+ logger (1.6.1)
75
+ loofah (2.22.0)
76
+ crass (~> 1.0.2)
77
+ nokogiri (>= 1.12.0)
78
+ mime-types (2.99.3)
79
+ minitest (5.25.1)
80
+ multi_json (1.15.0)
81
+ multi_xml (0.7.1)
82
+ bigdecimal (~> 3.1)
83
+ multipart-post (2.4.1)
84
+ nokogiri (1.16.7-aarch64-linux)
85
+ racc (~> 1.4)
86
+ nokogiri (1.16.7-arm-linux)
87
+ racc (~> 1.4)
88
+ nokogiri (1.16.7-arm64-darwin)
89
+ racc (~> 1.4)
90
+ nokogiri (1.16.7-x86-linux)
91
+ racc (~> 1.4)
92
+ nokogiri (1.16.7-x86_64-darwin)
93
+ racc (~> 1.4)
94
+ nokogiri (1.16.7-x86_64-linux)
95
+ racc (~> 1.4)
96
+ oauth2 (1.4.8)
97
+ faraday (>= 0.8, < 3.0)
98
+ jwt (>= 1.0, < 3.0)
99
+ multi_json (~> 1.3)
100
+ multi_xml (~> 0.5)
101
+ rack (>= 1.2, < 3)
102
+ psych (5.1.2)
103
+ stringio
104
+ racc (1.8.1)
105
+ rack (2.2.10)
106
+ rack-session (1.0.2)
107
+ rack (< 3)
108
+ rack-test (2.1.0)
109
+ rack (>= 1.3)
110
+ rails-dom-testing (2.2.0)
111
+ activesupport (>= 5.0.0)
112
+ minitest
113
+ nokogiri (>= 1.6)
114
+ rails-html-sanitizer (1.6.0)
115
+ loofah (~> 2.21)
116
+ nokogiri (~> 1.14)
117
+ rake (13.2.1)
118
+ rchardet (1.8.0)
119
+ rdoc (6.7.0)
120
+ psych (>= 4.0.0)
121
+ reline (0.5.10)
122
+ io-console (~> 0.5)
123
+ rspec (3.13.0)
124
+ rspec-core (~> 3.13.0)
125
+ rspec-expectations (~> 3.13.0)
126
+ rspec-mocks (~> 3.13.0)
127
+ rspec-core (3.13.1)
128
+ rspec-support (~> 3.13.0)
129
+ rspec-expectations (3.13.3)
130
+ diff-lcs (>= 1.2.0, < 2.0)
131
+ rspec-support (~> 3.13.0)
132
+ rspec-mocks (3.13.2)
133
+ diff-lcs (>= 1.2.0, < 2.0)
134
+ rspec-support (~> 3.13.0)
135
+ rspec-support (3.13.1)
136
+ securerandom (0.3.1)
137
+ semver2 (3.4.2)
138
+ stringio (3.1.1)
139
+ thread_safe (0.3.6)
140
+ tzinfo (2.0.6)
141
+ concurrent-ruby (~> 1.0)
142
+ useragent (0.16.10)
143
+ webrick (1.8.2)
144
+
145
+ PLATFORMS
146
+ aarch64-linux
147
+ arm-linux
148
+ arm64-darwin
149
+ x86-linux
150
+ x86_64-darwin
151
+ x86_64-linux
152
+
153
+ DEPENDENCIES
154
+ actionpack
155
+ addressable
156
+ bundler (~> 2.0)
157
+ jeweler (~> 2.3.9)
158
+ nokogiri (>= 1.16.7)
159
+ rdoc (~> 6.7.0)
160
+ rspec
161
+ webrick
162
+
163
+ BUNDLED WITH
164
+ 2.5.7
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 James Hu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
17
+ gem.name = "rails-threaded-proxy"
18
+ gem.homepage = "http://github.com/mnutt/rails-threaded-proxy"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Threaded reverse proxy for Ruby on Rails}
21
+ gem.description = %Q{Threaded reverse proxy for Ruby on Rails}
22
+ gem.email = "michael@nuttnet.net"
23
+ gem.authors = ["Michael Nutt"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/bundle ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'bundle' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "rubygems"
12
+
13
+ m = Module.new do
14
+ module_function
15
+
16
+ def invoked_as_script?
17
+ File.expand_path($0) == File.expand_path(__FILE__)
18
+ end
19
+
20
+ def env_var_version
21
+ ENV["BUNDLER_VERSION"]
22
+ end
23
+
24
+ def cli_arg_version
25
+ return unless invoked_as_script? # don't want to hijack other binstubs
26
+ return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27
+ bundler_version = nil
28
+ update_index = nil
29
+ ARGV.each_with_index do |a, i|
30
+ if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN)
31
+ bundler_version = a
32
+ end
33
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34
+ bundler_version = $1
35
+ update_index = i
36
+ end
37
+ bundler_version
38
+ end
39
+
40
+ def gemfile
41
+ gemfile = ENV["BUNDLE_GEMFILE"]
42
+ return gemfile if gemfile && !gemfile.empty?
43
+
44
+ File.expand_path("../Gemfile", __dir__)
45
+ end
46
+
47
+ def lockfile
48
+ lockfile =
49
+ case File.basename(gemfile)
50
+ when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
51
+ else "#{gemfile}.lock"
52
+ end
53
+ File.expand_path(lockfile)
54
+ end
55
+
56
+ def lockfile_version
57
+ return unless File.file?(lockfile)
58
+ lockfile_contents = File.read(lockfile)
59
+ return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60
+ Regexp.last_match(1)
61
+ end
62
+
63
+ def bundler_requirement
64
+ @bundler_requirement ||=
65
+ env_var_version ||
66
+ cli_arg_version ||
67
+ bundler_requirement_for(lockfile_version)
68
+ end
69
+
70
+ def bundler_requirement_for(version)
71
+ return "#{Gem::Requirement.default}.a" unless version
72
+
73
+ bundler_gem_version = Gem::Version.new(version)
74
+
75
+ bundler_gem_version.approximate_recommendation
76
+ end
77
+
78
+ def load_bundler!
79
+ ENV["BUNDLE_GEMFILE"] ||= gemfile
80
+
81
+ activate_bundler
82
+ end
83
+
84
+ def activate_bundler
85
+ gem_error = activation_error_handling do
86
+ gem "bundler", bundler_requirement
87
+ end
88
+ return if gem_error.nil?
89
+ require_error = activation_error_handling do
90
+ require "bundler/version"
91
+ end
92
+ return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
93
+ warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
94
+ exit 42
95
+ end
96
+
97
+ def activation_error_handling
98
+ yield
99
+ nil
100
+ rescue StandardError, LoadError => e
101
+ e
102
+ end
103
+ end
104
+
105
+ m.load_bundler!
106
+
107
+ if m.invoked_as_script?
108
+ load Gem.bin_path("bundler", "bundle")
109
+ end
data/bin/htmldiff ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'htmldiff' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("diff-lcs", "htmldiff")
data/bin/jeweler ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'jeweler' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("jeweler", "jeweler")
data/bin/ldiff ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'ldiff' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("diff-lcs", "ldiff")
data/bin/nokogiri ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'nokogiri' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("nokogiri", "nokogiri")
data/bin/racc ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'racc' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("racc", "racc")
data/bin/rackup ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rackup' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rack", "rackup")
data/bin/rake ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rake' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rake", "rake")
data/bin/rdoc ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rdoc' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rdoc", "rdoc")
data/bin/ri ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'ri' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rdoc", "ri")
data/bin/rspec ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/semver ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'semver' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("semver2", "semver")
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/threaded_proxy'
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/threaded_proxy'
@@ -0,0 +1,89 @@
1
+ require 'addressable/uri'
2
+ require 'net/http'
3
+ require_relative 'http'
4
+
5
+ module ThreadedProxy
6
+ class Client
7
+ DISALLOWED_RESPONSE_HEADERS = %w[keep-alive]
8
+
9
+ METHODS = {
10
+ 'get' => Net::HTTP::Get,
11
+ 'post' => Net::HTTP::Post,
12
+ 'put' => Net::HTTP::Put,
13
+ 'delete' => Net::HTTP::Delete,
14
+ 'head' => Net::HTTP::Head,
15
+ 'options' => Net::HTTP::Options,
16
+ 'trace' => Net::HTTP::Trace
17
+ }
18
+
19
+ DEFAULT_OPTIONS = {
20
+ headers: {},
21
+ debug: false
22
+ }
23
+
24
+ def initialize(origin_url, options={})
25
+ @origin_url = Addressable::URI.parse(origin_url)
26
+ @options = options.merge(DEFAULT_OPTIONS)
27
+ end
28
+
29
+ def log(message)
30
+ $stderr.puts message if @options[:debug]
31
+ end
32
+
33
+ def start(socket)
34
+ request_method = METHODS[(@options[:method] || 'GET').to_s.downcase]
35
+ http_request = request_method.new(@origin_url, (@options[:headers] || {}).merge('Connection' => 'close'))
36
+ if IO === @options[:body]
37
+ http_request.body_stream = @options[:body]
38
+ elsif String === @options[:body]
39
+ http_request.body = @options[:body]
40
+ @options[:headers]['Content-Length'] = @options[:body].bytesize.to_s
41
+ end
42
+
43
+ http = HTTP.new(@origin_url.host, @origin_url.port || default_port(@origin_url))
44
+ http.use_ssl = (@origin_url.scheme == 'https')
45
+ http.set_debug_output($stderr) if @options[:debug]
46
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @options[:ignore_ssl_errors]
47
+
48
+ http.start do
49
+ http.request(http_request) do |client_response|
50
+ # We don't support reusing connections once we have disconnected them from rack
51
+ client_response['connection'] = 'close'
52
+
53
+ yield client_response if block_given?
54
+
55
+ # start writing response
56
+ log("Writing response status and headers")
57
+ socket.write "HTTP/1.1 #{client_response.code} #{client_response.message}\r\n"
58
+
59
+ client_response.each_header do |key, value|
60
+ socket.write "#{key}: #{value}\r\n" unless DISALLOWED_RESPONSE_HEADERS.include?(key.downcase)
61
+ end
62
+
63
+ # Done with headers
64
+ socket.write "\r\n"
65
+
66
+ # There may have been some existing data in client_response's read buffer, flush it out
67
+ # before we manually connect the raw sockets
68
+ log("Flushing existing response buffer to client")
69
+ http.flush_existing_buffer_to(socket)
70
+
71
+ # Copy the rest of the client response to the socket
72
+ log("Copying response body to client")
73
+ http.copy_to(socket)
74
+ end
75
+ end
76
+ end
77
+
78
+ def default_port(uri)
79
+ case uri.scheme
80
+ when 'http'
81
+ 80
82
+ when 'https'
83
+ 443
84
+ else
85
+ nil
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'client'
2
+
3
+ module ThreadedProxy
4
+ module Controller
5
+ def proxy_fetch(origin_url, options={})
6
+ # hijack the response so we can take it outside of the rack request/response cycle
7
+ request.env['rack.hijack'].call
8
+ socket = request.env['rack.hijack_io']
9
+
10
+ Thread.new do
11
+ if options[:body] == :rack
12
+ options[:headers] ||= {}
13
+ options[:body] = request.body_stream
14
+ if request.env['HTTP_TRANSFER_ENCODING'] == 'chunked'
15
+ options[:headers]['Transfer-Encoding'] = 'chunked'
16
+ elsif request.env['CONTENT_LENGTH']
17
+ options[:headers]['Content-Length'] = request.env['CONTENT_LENGTH'].to_s
18
+ else
19
+ raise "Cannot proxy a non-chunked POST request without content-length"
20
+ end
21
+ end
22
+
23
+ client = Client.new(origin_url, options)
24
+ client.start(socket)
25
+ rescue Errno::EPIPE
26
+ # client disconnected before request finished; not an error
27
+ ensure
28
+ socket.close unless socket.closed?
29
+ end
30
+
31
+ head :ok
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'net/http'
2
+
3
+ module ThreadedProxy
4
+ class HTTP < Net::HTTP
5
+ def flush_existing_buffer_to(dest_socket)
6
+ while (data = @socket.send(:rbuf_consume))
7
+ break if data.empty?
8
+ dest_socket.write data
9
+ end
10
+
11
+ dest_socket.flush
12
+ end
13
+
14
+ def copy_to(dest_socket)
15
+ IO.copy_stream(@socket.io, dest_socket)
16
+ end
17
+
18
+ def request(*args)
19
+ if block_given?
20
+ super { |res| yield hijack_response(res) }
21
+ else
22
+ hijack_response(super)
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ # We read the response ourselves; don't need net/http to try to read it again
29
+ def hijack_response(res)
30
+ res.instance_variable_set("@read", true)
31
+ res
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ require 'threaded_proxy/client'
2
+ require 'threaded_proxy/controller'
3
+
4
+ module ThreadedProxy
5
+ def version
6
+ File.open(File.expand_path("../../VERSION", __FILE__)).read.strip
7
+ end
8
+
9
+ extend self
10
+ end
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: rails-threaded-proxy 0.1.0 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "rails-threaded-proxy".freeze
9
+ s.version = "0.1.0".freeze
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Michael Nutt".freeze]
14
+ s.date = "2024-10-14"
15
+ s.description = "Threaded reverse proxy for Ruby on Rails".freeze
16
+ s.email = "michael@nuttnet.net".freeze
17
+ s.executables = ["bundle".freeze, "htmldiff".freeze, "jeweler".freeze, "ldiff".freeze, "nokogiri".freeze, "racc".freeze, "rackup".freeze, "rake".freeze, "rdoc".freeze, "ri".freeze, "rspec".freeze, "semver".freeze]
18
+ s.extra_rdoc_files = [
19
+ "LICENSE"
20
+ ]
21
+ s.files = [
22
+ ".bundle/config",
23
+ ".rspec",
24
+ ".ruby-version",
25
+ "Gemfile",
26
+ "Gemfile.lock",
27
+ "LICENSE",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "bin/bundle",
31
+ "bin/htmldiff",
32
+ "bin/jeweler",
33
+ "bin/ldiff",
34
+ "bin/nokogiri",
35
+ "bin/racc",
36
+ "bin/rackup",
37
+ "bin/rake",
38
+ "bin/rdoc",
39
+ "bin/ri",
40
+ "bin/rspec",
41
+ "bin/semver",
42
+ "lib/rails-threaded-proxy.rb",
43
+ "lib/threaded-proxy.rb",
44
+ "lib/threaded_proxy.rb",
45
+ "lib/threaded_proxy/client.rb",
46
+ "lib/threaded_proxy/controller.rb",
47
+ "lib/threaded_proxy/http.rb",
48
+ "rails-threaded-proxy.gemspec",
49
+ "spec/spec_helper.rb",
50
+ "spec/threaded_proxy/client_spec.rb"
51
+ ]
52
+ s.homepage = "http://github.com/mnutt/rails-threaded-proxy".freeze
53
+ s.licenses = ["MIT".freeze]
54
+ s.rubygems_version = "3.5.16".freeze
55
+ s.summary = "Threaded reverse proxy for Ruby on Rails".freeze
56
+
57
+ s.specification_version = 4
58
+
59
+ s.add_runtime_dependency(%q<actionpack>.freeze, [">= 0".freeze])
60
+ s.add_runtime_dependency(%q<addressable>.freeze, [">= 0".freeze])
61
+ s.add_development_dependency(%q<bundler>.freeze, ["~> 2.0".freeze])
62
+ s.add_development_dependency(%q<jeweler>.freeze, ["~> 2.3.9".freeze])
63
+ s.add_development_dependency(%q<nokogiri>.freeze, [">= 1.16.7".freeze])
64
+ s.add_development_dependency(%q<rdoc>.freeze, ["~> 6.7.0".freeze])
65
+ s.add_development_dependency(%q<rspec>.freeze, [">= 0".freeze])
66
+ s.add_development_dependency(%q<webrick>.freeze, [">= 0".freeze])
67
+ end
68
+
@@ -0,0 +1,13 @@
1
+ require 'webrick'
2
+
3
+ RSpec.configure do |config|
4
+ config.expect_with :rspec do |expectations|
5
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
6
+ end
7
+
8
+ config.mock_with :rspec do |mocks|
9
+ mocks.verify_partial_doubles = true
10
+ end
11
+
12
+ config.shared_context_metadata_behavior = :apply_to_host_groups
13
+ end
@@ -0,0 +1,60 @@
1
+ require 'rails-threaded-proxy'
2
+ require 'json'
3
+
4
+ RSpec.describe ThreadedProxy::Client do
5
+ BACKEND_PORT = 38293
6
+
7
+ before(:all) do
8
+ @backend_server = WEBrick::HTTPServer.new(Port: BACKEND_PORT,
9
+ Logger: WEBrick::Log.new("/dev/null"),
10
+ AccessLog: [])
11
+ @backend_server.mount_proc '/get' do |req, res|
12
+ raise unless req.request_method == 'GET'
13
+ res.body = "Received request: #{req.path}"
14
+ end
15
+
16
+ @backend_server.mount_proc '/post' do |req, res|
17
+ raise unless req.request_method == 'POST'
18
+ res.content_type = 'application/json'
19
+ res.body = JSON.generate(path: req.path,
20
+ headers: req.header,
21
+ body: req.body)
22
+ end
23
+
24
+ @server_thread = Thread.new { @backend_server.start }
25
+ end
26
+
27
+ after(:all) do
28
+ @backend_server.shutdown
29
+ @server_thread.kill
30
+ end
31
+
32
+ it "proxies a GET request" do
33
+ socket = StringIO.new
34
+
35
+ client = ThreadedProxy::Client.new("http://localhost:#{BACKEND_PORT}/get")
36
+ client.start(socket)
37
+
38
+ expect(socket.string).to include("Received request: /get")
39
+ end
40
+
41
+ it "proxies a POST request with content-length" do
42
+ socket = StringIO.new
43
+
44
+ client = ThreadedProxy::Client.new("http://localhost:#{BACKEND_PORT}/post",
45
+ method: 'post',
46
+ body: 'hello world')
47
+ client.start(socket)
48
+
49
+ status, rest = socket.string.split("\r\n", 2)
50
+ headers, body = rest.split("\r\n\r\n", 2)
51
+
52
+ parsed_body = JSON.parse(body)
53
+ parsed_headers = headers.split("\r\n").map { |h| h.split(': ', 2) }.to_h
54
+
55
+ expect(status).to eq('HTTP/1.1 200 OK')
56
+ expect(parsed_headers['content-type']).to eq('application/json')
57
+ expect(parsed_body['path']).to eq('/post')
58
+ expect(parsed_body['headers']['content-length']).to eq(['11'])
59
+ end
60
+ end
metadata ADDED
@@ -0,0 +1,196 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-threaded-proxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Nutt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-10-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: addressable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jeweler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.3.9
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.3.9
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.16.7
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.16.7
83
+ - !ruby/object:Gem::Dependency
84
+ name: rdoc
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 6.7.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 6.7.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webrick
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Threaded reverse proxy for Ruby on Rails
126
+ email: michael@nuttnet.net
127
+ executables:
128
+ - bundle
129
+ - htmldiff
130
+ - jeweler
131
+ - ldiff
132
+ - nokogiri
133
+ - racc
134
+ - rackup
135
+ - rake
136
+ - rdoc
137
+ - ri
138
+ - rspec
139
+ - semver
140
+ extensions: []
141
+ extra_rdoc_files:
142
+ - LICENSE
143
+ files:
144
+ - ".bundle/config"
145
+ - ".rspec"
146
+ - ".ruby-version"
147
+ - Gemfile
148
+ - Gemfile.lock
149
+ - LICENSE
150
+ - Rakefile
151
+ - VERSION
152
+ - bin/bundle
153
+ - bin/htmldiff
154
+ - bin/jeweler
155
+ - bin/ldiff
156
+ - bin/nokogiri
157
+ - bin/racc
158
+ - bin/rackup
159
+ - bin/rake
160
+ - bin/rdoc
161
+ - bin/ri
162
+ - bin/rspec
163
+ - bin/semver
164
+ - lib/rails-threaded-proxy.rb
165
+ - lib/threaded-proxy.rb
166
+ - lib/threaded_proxy.rb
167
+ - lib/threaded_proxy/client.rb
168
+ - lib/threaded_proxy/controller.rb
169
+ - lib/threaded_proxy/http.rb
170
+ - rails-threaded-proxy.gemspec
171
+ - spec/spec_helper.rb
172
+ - spec/threaded_proxy/client_spec.rb
173
+ homepage: http://github.com/mnutt/rails-threaded-proxy
174
+ licenses:
175
+ - MIT
176
+ metadata: {}
177
+ post_install_message:
178
+ rdoc_options: []
179
+ require_paths:
180
+ - lib
181
+ required_ruby_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ required_rubygems_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ requirements: []
192
+ rubygems_version: 3.5.16
193
+ signing_key:
194
+ specification_version: 4
195
+ summary: Threaded reverse proxy for Ruby on Rails
196
+ test_files: []