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
|
||||
gemspec
|
||||
|
||||
gem "rake", "~> 12.0"
|
||||
gem "rspec", "~> 3.0"
|
||||
gem 'rake', '~> 12.0'
|
||||
gem 'rspec', '~> 3.0'
|
||||
|
|
8
Rakefile
8
Rakefile
|
@ -1,6 +1,8 @@
|
|||
require "bundler/gem_tasks"
|
||||
require "rspec/core/rake_task"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/gem_tasks'
|
||||
require 'rspec/core/rake_task'
|
||||
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
|
||||
task :default => :spec
|
||||
task default: :spec
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "bundler/setup"
|
||||
require "roda/proxy"
|
||||
require 'bundler/setup'
|
||||
require 'roda/proxy'
|
||||
|
||||
# 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.
|
||||
|
@ -10,5 +11,5 @@ require "roda/proxy"
|
|||
# require "pry"
|
||||
# Pry.start
|
||||
|
||||
require "irb"
|
||||
require 'irb'
|
||||
IRB.start(__FILE__)
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
require "faraday"
|
||||
require "roda/proxy/version"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'faraday'
|
||||
require 'roda/proxy/version'
|
||||
|
||||
# :nodoc:
|
||||
class Roda
|
||||
# :nodoc:
|
||||
module RodaPlugins
|
||||
# Roda plugin for simple API proxying
|
||||
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 = {})
|
||||
app.opts[:proxy_to] = opts.fetch(:to, nil)
|
||||
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]
|
||||
end
|
||||
|
||||
|
||||
# :nodoc:
|
||||
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
|
||||
#pp @_request.env
|
||||
#pp @_request.body.read
|
||||
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) }
|
||||
response.status = f_response.status
|
||||
f_response.headers.each { |k,v| response[k] = v }
|
||||
response.write(f_response.body)
|
||||
halt
|
||||
_respond(f_response)
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
private
|
||||
|
||||
def _proxy_url
|
||||
@_proxy_uri ||= URI(roda_class.opts[:proxy_to])
|
||||
.then { |uri| uri.path = roda_class.opts[:proxy_path] ; uri }
|
||||
.then { |uri| uri.query = env['QUERY_STRING'] ; uri }
|
||||
@_proxy_url ||= URI(roda_class.opts[:proxy_to])
|
||||
.then { |uri| uri.path = roda_class.opts[:proxy_path]; uri }
|
||||
.then { |uri| uri.query = env['QUERY_STRING']; uri }
|
||||
end
|
||||
|
||||
def _proxy_headers
|
||||
env
|
||||
.select { |k,_v| k.start_with? 'HTTP_'}
|
||||
.reject { |k,_v| k == 'HTTP_HOST' }
|
||||
.select { |k, _v| k.start_with? 'HTTP_' }
|
||||
.reject { |k, _v| k == 'HTTP_HOST' }
|
||||
.transform_keys do |k|
|
||||
k.sub(/^HTTP_/, '')
|
||||
.split('_')
|
||||
.map(&:capitalize)
|
||||
.join('-')
|
||||
.split('_')
|
||||
.map(&:capitalize)
|
||||
.join('-')
|
||||
end.merge({ 'Host' => "#{_proxy_url.host}:#{_proxy_url.port}" })
|
||||
end
|
||||
|
||||
def _proxy_request(req)
|
||||
req.headers = _proxy_headers
|
||||
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
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Roda
|
||||
module Proxy
|
||||
VERSION = "0.1.0"
|
||||
VERSION = '0.1.0'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,34 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'lib/roda/proxy/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "roda-proxy"
|
||||
spec.name = 'roda-proxy'
|
||||
spec.version = Roda::Proxy::VERSION
|
||||
spec.authors = ["Nigel Brookes-Thomas"]
|
||||
spec.email = ["nigel@brookes-thomas.co.uk"]
|
||||
spec.authors = ['Nigel Brookes-Thomas']
|
||||
spec.email = ['nigel@brookes-thomas.co.uk']
|
||||
|
||||
spec.summary = 'Proxy service for Roda'
|
||||
spec.description = 'Roda proxy service'
|
||||
spec.homepage = "http://foo.bar"
|
||||
spec.license = "MIT"
|
||||
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
||||
spec.homepage = 'http://foo.bar'
|
||||
spec.license = 'MIT'
|
||||
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["source_code_uri"] = "http://foo.bar"
|
||||
spec.metadata["changelog_uri"] = "http://foo.bar"
|
||||
spec.metadata['homepage_uri'] = spec.homepage
|
||||
spec.metadata['source_code_uri'] = 'http://foo.bar'
|
||||
spec.metadata['changelog_uri'] = 'http://foo.bar'
|
||||
|
||||
# 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.
|
||||
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)/}) }
|
||||
end
|
||||
spec.bindir = "exe"
|
||||
spec.bindir = 'exe'
|
||||
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 'roda', '~> 3.0'
|
||||
|
||||
spec.add_development_dependency 'rerun', '~> 0.13'
|
||||
spec.add_development_dependency 'rubocop', '~> 0.80'
|
||||
end
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
it "does something useful" do
|
||||
it 'does something useful' do
|
||||
expect(false).to eq(true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
require "bundler/setup"
|
||||
require "roda/proxy"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/setup'
|
||||
require 'roda/proxy'
|
||||
|
||||
RSpec.configure do |config|
|
||||
# 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`
|
||||
config.disable_monkey_patching!
|
||||
|
|
Loading…
Reference in New Issue