Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proxy Support #153

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
rvm:
- 1.9.3
- 2.0.0
- 2.1.0
- ruby-head
- jruby
- jruby-head
- rbx

matrix:
allow_failures:
- rvm: ruby-head
- rvm: jruby-head

bundler_args: --without guard docs
before_install:
- gem install bundler -v '= 1.5.1'
3 changes: 3 additions & 0 deletions lib/webmachine/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ class Adapter
# @return [Webmachine::Dispatcher] the application's dispatcher.
attr_reader :dispatcher

# @return [Webmachine::Application]
attr_accessor :application

# @param [Webmachine::Configuration] configuration the application's
# configuration.
# @param [Webmachine::Dispatcher] dispatcher the application's dispatcher.
Expand Down
6 changes: 4 additions & 2 deletions lib/webmachine/adapters/webrick.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class WEBrick < Adapter
def run
options = DEFAULT_OPTIONS.merge({
:Port => configuration.port,
:BindAddress => configuration.ip
:BindAddress => configuration.ip,
:application => application
}).merge(configuration.adapter_options)
@server = Server.new(dispatcher, options)
trap("INT") { shutdown }
Expand All @@ -30,7 +31,7 @@ def shutdown
# WEBRick::HTTPServer that is run by the WEBrick adapter.
class Server < ::WEBrick::HTTPServer
def initialize(dispatcher, options)
@dispatcher = dispatcher
@dispatcher, @application = dispatcher, options[:application]
super(options)
end

Expand All @@ -42,6 +43,7 @@ def service(wreq, wres)
wreq.request_uri,
header,
LazyRequestBody.new(wreq))
request.application = @application
response = Webmachine::Response.new
@dispatcher.dispatch(request, response)
wres.status = response.code.to_i
Expand Down
1 change: 1 addition & 0 deletions lib/webmachine/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def initialize(configuration = Configuration.default, dispatcher = Dispatcher.ne

# Starts this Application serving requests
def run
adapter.application = self.dup
adapter.run
end

Expand Down
6 changes: 4 additions & 2 deletions lib/webmachine/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ module Webmachine
# @attr [Fixnum] port the port to bind to, defaults to 8080
# @attr [Symbol] adapter the adapter to use, defaults to :WEBrick
# @attr [Hash] adapter_options adapter-specific options, defaults to {}
Configuration = Struct.new(:ip, :port, :adapter, :adapter_options)
# @attr [Boolean] runs_behind_proxy whether Webmachine runs behind a proxy or not, defaults to false
# @attr [Array] trusted_headers a list of trusted headers when running behind a proxy, defaults to []
Configuration = Struct.new(:ip, :port, :adapter, :adapter_options, :runs_behind_proxy, :trusted_headers)

# @return [Configuration] the default configuration
def Configuration.default
new("0.0.0.0", 8080, :WEBrick, {})
new("0.0.0.0", 8080, :WEBrick, {}, false, [])
end

# Yields the current configuration to the passed block.
Expand Down
38 changes: 37 additions & 1 deletion lib/webmachine/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Webmachine
class Request
extend Forwardable
attr_reader :method, :uri, :headers, :body
attr_accessor :disp_path, :path_info, :path_tokens
attr_accessor :disp_path, :path_info, :path_tokens, :application

GET_METHOD = "GET"
HEAD_METHOD = "HEAD"
Expand All @@ -32,6 +32,11 @@ class Request
# request, if present
def initialize(method, uri, headers, body)
@method, @uri, @headers, @body = method, uri, headers, body

if (application && application.configuration.runs_behind_proxy == true)
filter_headers
modify_request_uri
end
end

def_delegators :headers, :[]
Expand Down Expand Up @@ -164,5 +169,36 @@ def options?
method == OPTIONS_METHOD
end

private
# When running behind a proxy Webmachine removes headers which start with x- which aren't trusted
def filter_headers
headers.each_key do |header|
if header[0..1] == 'x-'
unless application.configuration.trusted_headers.include?(header)
headers.delete(header)
end
end
end
end

# When running behind a proxy updates the request.uri so that redirects work correctly
def modify_request_uri
uri.scheme = scheme
uri.host = headers.fetch('x-forwarded-host') if headers.fetch('x-forwarded-host', nil)
uri.port = headers.fetch('x-forwarded-port').to_i if headers.fetch('x-forwarded-port', nil)
end

def scheme
if headers.fetch('x-forwarded-https', nil) == 'on' || headers.fetch('x-forwarded-ssl', nil) == 'on'
'https'
elsif headers.fetch('x-forwarded-scheme', nil)
headers.fetch('x-forwarded-scheme')
elsif headers.fetch('x-forwarded-proto', nil)
headers.fetch('x-forwarded-proto').split(',').any?{|x| x.strip == 'https' } ? 'https' : 'http'
else
uri.scheme
end
end

end # class Request
end # module Webmachine