conditional proxy
This commit is contained in:
parent
0a10320fa7
commit
d73f0761a8
|
@ -0,0 +1,19 @@
|
||||||
|
Layout/SpaceInsideParens:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Metrics/LineLength:
|
||||||
|
Max: 120
|
||||||
|
|
||||||
|
Metrics/ModuleLength:
|
||||||
|
Exclude:
|
||||||
|
- "**/*_spec.rb"
|
||||||
|
|
||||||
|
Metrics/BlockLength:
|
||||||
|
Exclude:
|
||||||
|
- "**/*_spec.rb"
|
||||||
|
|
||||||
|
Layout/TrailingWhitespace:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/Semicolon:
|
||||||
|
Enabled: false
|
8
Gemfile
8
Gemfile
|
@ -1,7 +1,9 @@
|
||||||
source "https://rubygems.org"
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
# Specify your gem's dependencies in roda-proxy.gemspec
|
# Specify your gem's dependencies in roda-proxy.gemspec
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
gem "rake", "~> 12.0"
|
gem 'rake', '~> 12.0'
|
||||||
gem "rspec", "~> 3.0"
|
gem 'rspec', '~> 3.0'
|
||||||
|
|
8
Rakefile
8
Rakefile
|
@ -1,6 +1,8 @@
|
||||||
require "bundler/gem_tasks"
|
# frozen_string_literal: true
|
||||||
require "rspec/core/rake_task"
|
|
||||||
|
require 'bundler/gem_tasks'
|
||||||
|
require 'rspec/core/rake_task'
|
||||||
|
|
||||||
RSpec::Core::RakeTask.new(:spec)
|
RSpec::Core::RakeTask.new(:spec)
|
||||||
|
|
||||||
task :default => :spec
|
task default: :spec
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "bundler/setup"
|
require 'bundler/setup'
|
||||||
require "roda/proxy"
|
require 'roda/proxy'
|
||||||
|
|
||||||
# You can add fixtures and/or initialization code here to make experimenting
|
# You can add fixtures and/or initialization code here to make experimenting
|
||||||
# with your gem easier. You can also use a different console, if you like.
|
# with your gem easier. You can also use a different console, if you like.
|
||||||
|
@ -10,5 +11,5 @@ require "roda/proxy"
|
||||||
# require "pry"
|
# require "pry"
|
||||||
# Pry.start
|
# Pry.start
|
||||||
|
|
||||||
require "irb"
|
require 'irb'
|
||||||
IRB.start(__FILE__)
|
IRB.start(__FILE__)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require './addressing'
|
require './addressing'
|
||||||
|
|
||||||
run App
|
run App
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
require "faraday"
|
# frozen_string_literal: true
|
||||||
require "roda/proxy/version"
|
|
||||||
|
|
||||||
|
require 'faraday'
|
||||||
|
require 'roda/proxy/version'
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
class Roda
|
class Roda
|
||||||
|
# :nodoc:
|
||||||
module RodaPlugins
|
module RodaPlugins
|
||||||
|
# Roda plugin for simple API proxying
|
||||||
module Proxy
|
module Proxy
|
||||||
|
|
||||||
|
# Respond to the configure method to set the destination when proxying
|
||||||
|
# Expects the following options:
|
||||||
|
# [to] Required. The scheme and host of the proxy. Should not end with a slash.
|
||||||
|
# [path] Optional. The path to append to the above for proxying.
|
||||||
|
# Should begin with a +/+. Defaults to +/+.
|
||||||
|
# Example:
|
||||||
|
# plugin :proxy, to: 'https://foo.bar', path: '/my/api'
|
||||||
def self.configure(app, opts = {})
|
def self.configure(app, opts = {})
|
||||||
app.opts[:proxy_to] = opts.fetch(:to, nil)
|
app.opts[:proxy_to] = opts.fetch(:to, nil)
|
||||||
app.opts[:proxy_path] = opts.fetch(:path, '/')
|
app.opts[:proxy_path] = opts.fetch(:path, '/')
|
||||||
|
@ -12,45 +24,67 @@ class Roda
|
||||||
raise 'Proxy host not set, use "plugin :proxy, to: http://example.com"' unless app.opts[:proxy_to]
|
raise 'Proxy host not set, use "plugin :proxy, to: http://example.com"' unless app.opts[:proxy_to]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
module RequestMethods
|
module RequestMethods
|
||||||
|
|
||||||
|
# Proxies the request, forwarding all headers except +Host+ which is
|
||||||
|
# rewritten to be the destination host. The response headers, body and
|
||||||
|
# status are returned to the client.
|
||||||
def proxy
|
def proxy
|
||||||
#pp @_request.env
|
|
||||||
#pp @_request.body.read
|
|
||||||
method = Faraday.method(env['REQUEST_METHOD'].downcase.to_sym)
|
method = Faraday.method(env['REQUEST_METHOD'].downcase.to_sym)
|
||||||
pp _proxy_url
|
|
||||||
pp _proxy_headers
|
|
||||||
pp roda_class
|
|
||||||
f_response = method.call(_proxy_url) { |req| _proxy_request(req) }
|
f_response = method.call(_proxy_url) { |req| _proxy_request(req) }
|
||||||
response.status = f_response.status
|
_respond(f_response)
|
||||||
f_response.headers.each { |k,v| response[k] = v }
|
end
|
||||||
response.write(f_response.body)
|
|
||||||
halt
|
# Conditionally proxies when +condition+ is true and with selective probability.
|
||||||
|
# For instance, to proxy 50% of the time:
|
||||||
|
# r.proxy_when(probability: 0.5)
|
||||||
|
# Condition can be a truthy value or a block / lambda, in which case
|
||||||
|
# the result from the +#call+ is expected to be truthy.
|
||||||
|
# r.proxy_when( r.env['HTTP_PROXY_ME'] == 'true' )
|
||||||
|
# The two parameters can be combined, the probability is evaluated first.
|
||||||
|
# r.proxy_when( r.env['HTTP_PROXY_ME'] == 'true', probability: 0.8 )
|
||||||
|
# If and only if this method choses not to proxy is the block evaluated.
|
||||||
|
# The block is then expected to return a meaningful response to Roda.
|
||||||
|
def proxy_when(condition = true, probability: 1.0)
|
||||||
|
shall_proxy = Random.rand(0.0..1.0) <= probability
|
||||||
|
|
||||||
|
if shall_proxy && ( condition.respond_to?(:call) ? condition.call : condition )
|
||||||
|
yield(self)
|
||||||
|
else
|
||||||
|
proxy
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def _proxy_url
|
def _proxy_url
|
||||||
@_proxy_uri ||= URI(roda_class.opts[:proxy_to])
|
@_proxy_url ||= URI(roda_class.opts[:proxy_to])
|
||||||
.then { |uri| uri.path = roda_class.opts[:proxy_path] ; uri }
|
.then { |uri| uri.path = roda_class.opts[:proxy_path]; uri }
|
||||||
.then { |uri| uri.query = env['QUERY_STRING'] ; uri }
|
.then { |uri| uri.query = env['QUERY_STRING']; uri }
|
||||||
end
|
end
|
||||||
|
|
||||||
def _proxy_headers
|
def _proxy_headers
|
||||||
env
|
env
|
||||||
.select { |k,_v| k.start_with? 'HTTP_'}
|
.select { |k, _v| k.start_with? 'HTTP_' }
|
||||||
.reject { |k,_v| k == 'HTTP_HOST' }
|
.reject { |k, _v| k == 'HTTP_HOST' }
|
||||||
.transform_keys do |k|
|
.transform_keys do |k|
|
||||||
k.sub(/^HTTP_/, '')
|
k.sub(/^HTTP_/, '')
|
||||||
.split('_')
|
.split('_')
|
||||||
.map(&:capitalize)
|
.map(&:capitalize)
|
||||||
.join('-')
|
.join('-')
|
||||||
end.merge({ 'Host' => "#{_proxy_url.host}:#{_proxy_url.port}" })
|
end.merge({ 'Host' => "#{_proxy_url.host}:#{_proxy_url.port}" })
|
||||||
end
|
end
|
||||||
|
|
||||||
def _proxy_request(req)
|
def _proxy_request(req)
|
||||||
req.headers = _proxy_headers
|
req.headers = _proxy_headers
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def _respond(proxied_response)
|
||||||
|
response.status = proxied_response.status
|
||||||
|
proxied_response.headers.each { |k, v| response[k] = v }
|
||||||
|
response.write(proxied_response.body)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Roda
|
class Roda
|
||||||
module Proxy
|
module Proxy
|
||||||
VERSION = "0.1.0"
|
VERSION = '0.1.0'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,34 +1,37 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative 'lib/roda/proxy/version'
|
require_relative 'lib/roda/proxy/version'
|
||||||
|
|
||||||
Gem::Specification.new do |spec|
|
Gem::Specification.new do |spec|
|
||||||
spec.name = "roda-proxy"
|
spec.name = 'roda-proxy'
|
||||||
spec.version = Roda::Proxy::VERSION
|
spec.version = Roda::Proxy::VERSION
|
||||||
spec.authors = ["Nigel Brookes-Thomas"]
|
spec.authors = ['Nigel Brookes-Thomas']
|
||||||
spec.email = ["nigel@brookes-thomas.co.uk"]
|
spec.email = ['nigel@brookes-thomas.co.uk']
|
||||||
|
|
||||||
spec.summary = 'Proxy service for Roda'
|
spec.summary = 'Proxy service for Roda'
|
||||||
spec.description = 'Roda proxy service'
|
spec.description = 'Roda proxy service'
|
||||||
spec.homepage = "http://foo.bar"
|
spec.homepage = 'http://foo.bar'
|
||||||
spec.license = "MIT"
|
spec.license = 'MIT'
|
||||||
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
|
||||||
|
|
||||||
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
||||||
|
|
||||||
spec.metadata["homepage_uri"] = spec.homepage
|
spec.metadata['homepage_uri'] = spec.homepage
|
||||||
spec.metadata["source_code_uri"] = "http://foo.bar"
|
spec.metadata['source_code_uri'] = 'http://foo.bar'
|
||||||
spec.metadata["changelog_uri"] = "http://foo.bar"
|
spec.metadata['changelog_uri'] = 'http://foo.bar'
|
||||||
|
|
||||||
# Specify which files should be added to the gem when it is released.
|
# Specify which files should be added to the gem when it is released.
|
||||||
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
||||||
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
||||||
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
||||||
end
|
end
|
||||||
spec.bindir = "exe"
|
spec.bindir = 'exe'
|
||||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||||
spec.require_paths = ["lib"]
|
spec.require_paths = ['lib']
|
||||||
|
|
||||||
spec.add_dependency 'faraday', '~> 1.0'
|
spec.add_dependency 'faraday', '~> 1.0'
|
||||||
spec.add_dependency 'roda', '~> 3.0'
|
spec.add_dependency 'roda', '~> 3.0'
|
||||||
|
|
||||||
spec.add_development_dependency 'rerun', '~> 0.13'
|
spec.add_development_dependency 'rerun', '~> 0.13'
|
||||||
|
spec.add_development_dependency 'rubocop', '~> 0.80'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Roda::Proxy do
|
RSpec.describe Roda::Proxy do
|
||||||
it "has a version number" do
|
it 'has a version number' do
|
||||||
expect(Roda::Proxy::VERSION).not_to be nil
|
expect(Roda::Proxy::VERSION).not_to be nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does something useful" do
|
it 'does something useful' do
|
||||||
expect(false).to eq(true)
|
expect(false).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
require "bundler/setup"
|
# frozen_string_literal: true
|
||||||
require "roda/proxy"
|
|
||||||
|
require 'bundler/setup'
|
||||||
|
require 'roda/proxy'
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
# Enable flags like --only-failures and --next-failure
|
# Enable flags like --only-failures and --next-failure
|
||||||
config.example_status_persistence_file_path = ".rspec_status"
|
config.example_status_persistence_file_path = '.rspec_status'
|
||||||
|
|
||||||
# Disable RSpec exposing methods globally on `Module` and `main`
|
# Disable RSpec exposing methods globally on `Module` and `main`
|
||||||
config.disable_monkey_patching!
|
config.disable_monkey_patching!
|
||||||
|
|
Loading…
Reference in New Issue