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

Adds proxy server support. #626

Merged
merged 5 commits into from
Sep 20, 2017
Merged
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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,24 @@ Rollbar.configure do |config|
end
```

## Web Proxies

If your application is deployed behind a proxy server, you can set the ```https_proxy``` (note the 's') environment variable and it will be honored, including username and password, if any.

```shell
export https_proxy='http://some_user:some_password@some.proxy.com:80'
```

Alternately, you can configure the proxy settings in ```config/initializers/rollbar.rb```. If used, ```host``` is mandatory and must include the URL scheme (e.g. ```http://```), all other fields are optional:

```ruby
config.proxy = {
host: 'http://some.proxy.server',
port: 80,
user: 'username_if_auth_required',
password: 'password_if_auth_required'
}
```

## Using with Zeus

Expand Down
12 changes: 12 additions & 0 deletions lib/generators/rollbar/templates/initializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@
# You can supply custom Sidekiq options:
# config.use_sidekiq 'queue' => 'default'

# If your application runs behind a proxy server, you can set proxy parameters here.
# If https_proxy is set in your environment, that will be used. Settings here have precedence.
# The :host key is mandatory and must include the URL scheme (e.g. 'http://'), all other fields
# are optional.
#
# config.proxy = {
# host: 'http://some.proxy.server',
# port: 80,
# user: 'username_if_auth_required',
# password: 'password_if_auth_required'
# }

# If you run your staging application instance in production environment then
# you'll want to override the environment reported by `Rails.env` with an
# environment variable like this: `ROLLBAR_ENV=staging`. This is a recommended
Expand Down
2 changes: 2 additions & 0 deletions lib/rollbar/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Configuration
attr_accessor :write_to_file
attr_reader :send_extra_frame_data
attr_accessor :use_exception_level_filters_default
attr_accessor :proxy

attr_reader :project_gem_paths

Expand Down Expand Up @@ -118,6 +119,7 @@ def initialize
@send_extra_frame_data = :none
@project_gem_paths = []
@use_exception_level_filters_default = false
@proxy = nil
end

def initialize_copy(orig)
Expand Down
54 changes: 46 additions & 8 deletions lib/rollbar/notifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require 'rollbar/delay/thread'
require 'rollbar/logger_proxy'
require 'rollbar/item'
require 'ostruct'
Copy link

@printercu printercu Nov 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this one redundant?


module Rollbar
# The notifier class. It has the core functionality
Expand Down Expand Up @@ -438,12 +439,13 @@ def build_item(level, message, exception, extra)

## Delivery functions

def send_item_using_eventmachine(item)
def send_item_using_eventmachine(item, uri)
body = item.dump
return unless body

headers = { 'X-Rollbar-Access-Token' => item['access_token'] }
req = EventMachine::HttpRequest.new(configuration.endpoint).post(:body => body, :head => headers)
options = http_proxy_for_em(uri)
req = EventMachine::HttpRequest.new(uri.to_s, options).post(:body => body, :head => headers)

req.callback do
if req.response_header.status == 200
Expand All @@ -463,21 +465,23 @@ def send_item_using_eventmachine(item)
def send_item(item)
log_info '[Rollbar] Sending item'

if configuration.use_eventmachine
send_item_using_eventmachine(item)
return
end

body = item.dump
return unless body

uri = URI.parse(configuration.endpoint)

if configuration.use_eventmachine
send_item_using_eventmachine(item, uri)
return
end

handle_response(do_post(uri, body, item['access_token']))
end

def do_post(uri, body, access_token)
http = Net::HTTP.new(uri.host, uri.port)
proxy = http_proxy(uri)
http = Net::HTTP.new(uri.host, uri.port, proxy.host, proxy.port, proxy.user, proxy.password)

http.open_timeout = configuration.open_timeout
http.read_timeout = configuration.request_timeout

Expand All @@ -496,6 +500,40 @@ def do_post(uri, body, access_token)
handle_net_retries { http.request(request) }
end

def http_proxy_for_em(uri)
proxy = http_proxy(uri)
{
:proxy => {
:host => proxy.host,
:port => proxy.port,
:authorization => [proxy.user, proxy.password]
}
}
end

def http_proxy(uri)
@http_proxy ||= proxy_from_config || proxy_from_env(uri) || null_proxy
end

def proxy_from_config
proxy = configuration.proxy
return nil unless proxy

px = URI.parse(proxy[:host])
px.port = proxy[:port]
px.user = proxy[:user]
px.password = proxy[:password]
px
end

def proxy_from_env(uri)
uri.find_proxy
end

def null_proxy
Struct.new(:host, :port, :user, :password).new
end

def handle_net_retries
return yield if skip_retries?

Expand Down
58 changes: 58 additions & 0 deletions spec/rollbar_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
require 'active_support/json/encoding'

require 'rollbar/item'
require 'ostruct'

begin
require 'rollbar/delay/sidekiq'
require 'rollbar/delay/sucker_punch'
Expand Down Expand Up @@ -1008,6 +1010,62 @@ def backtrace
end
end

context 'using a proxy server' do
before do
allow_any_instance_of(Net::HTTP).to receive(:request).and_return(OpenStruct.new(:code => 200, :body => "Success"))
@env_vars = clear_proxy_env_vars
end

after do
restore_proxy_env_vars(@env_vars)
end

context 'via environment variables' do
it 'honors proxy settings in the environment' do
ENV['http_proxy'] = 'http://user:pass@example.com:80'
ENV['https_proxy'] = 'http://user:pass@example.com:80'

uri = URI.parse(Rollbar::Configuration::DEFAULT_ENDPOINT)
expect(Net::HTTP).to receive(:new).with(uri.host, uri.port, 'example.com', 80, 'user', 'pass').and_call_original
Rollbar.info("proxy this")
end

it 'does not use a proxy if no proxy settings in environemnt' do
uri = URI.parse(Rollbar::Configuration::DEFAULT_ENDPOINT)
expect(Net::HTTP).to receive(:new).with(uri.host, uri.port, nil, nil, nil, nil).and_call_original
Rollbar.info("proxy this")
end
end

context 'set in configuration file' do
before do
Rollbar.configure do |config|
config.proxy = {
:host => 'http://config.com',
:port => 8080,
:user => 'foo',
:password => 'bar'
}
end
end

it 'honors proxy settings in the config file' do
uri = URI.parse(Rollbar::Configuration::DEFAULT_ENDPOINT)
expect(Net::HTTP).to receive(:new).with(uri.host, uri.port, 'config.com', 8080, 'foo', 'bar').and_call_original
Rollbar.info("proxy this")
end

it 'gives the configuration settings precedence over environment' do
ENV['http_proxy'] = 'http://user:pass@example.com:80'
ENV['https_proxy'] = 'http://user:pass@example.com:80'

uri = URI.parse(Rollbar::Configuration::DEFAULT_ENDPOINT)
expect(Net::HTTP).to receive(:new).with(uri.host, uri.port, 'config.com', 8080, 'foo', 'bar').and_call_original
Rollbar.info("proxy this")
end
end
end

context 'asynchronous_handling' do
before do
Rollbar.clear_notifier!
Expand Down
18 changes: 18 additions & 0 deletions spec/support/notifier_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,22 @@ def reset_configuration
Rollbar.reconfigure do |config|
end
end

def clear_proxy_env_vars
env_vars = {}
proxy_env_vars.each do |var|
env_vars[var] = ENV.delete(var)
end
env_vars
end

def restore_proxy_env_vars(env_vars)
proxy_env_vars.each do |var|
ENV[var] = env_vars[var]
end
end

def proxy_env_vars
%w[http_proxy HTTP_PROXY https_proxy HTTPS_PROXY]
end
end