Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added password protection for single-request profiling

  • Loading branch information...
commit e61042432f54987473a971348f886e3d708e7306 1 parent 8334714
@bhb authored
View
17 README.rdoc
@@ -54,11 +54,12 @@ For Rack::Builder, call 'use' inside the Builder constructor block
== Options
-* :default_printer - can be set to 'text', 'gif', or 'pdf'. Default is :text
+* :default_printer - can be set to 'text', 'gif', or 'pdf'. Default is 'text'.
* :mode - can be set to 'cputime', 'methods', 'objects', 'walltime'. Default is :cputime. See the 'Profiling Modes' section below.
-* :frequency - in :cputime mode, the number of times per second the app will be sampled. Default is 100 (times/sec)
-* :bundler - run the profiler binary using 'bundle' if set to true. Default is false
+* :frequency - in :cputime mode, the number of times per second the app will be sampled. Default is 100 (times/sec).
+* :bundler - run the profiler binary using 'bundle' if set to true. Default is false.
* :gemfile_dir - directory with Gemfile. Default is the current directory.
+* :password - password-protect profiling.
== Usage
@@ -139,6 +140,16 @@ When profiling using multiple requests, add the option when visiting \_\_start\_
If the 'mode' option is omitted, the middleware will default to the mode specified at configuration.
+== Profiling in production
+
+It is recommended that you always profile your application in the 'production' environment (using `rails server -e production` or an equivalent), since there can be important differences between 'development' and 'production' that may affect performance.
+
+However, it is recommended that you profile your application on a development or staging machine rather than on a production machine. This is because profiling with multiple requests *will not* work if your app is running in multiple Ruby server processes.
+
+Profiling a single request will work if there are multiple server processes. If your staging machine is publicly accessible, you can password-protect single-request profiling by using the `:password` option and then using the `profile` GET parameter to provide the password:
+
+ curl http://localhost:3000/foobar?profile=PASSWORD
+
== Changing behavior with environment variables
The mode and frequency settings are enabled by setting environment variables. Some of these environment variables must be set before 'perftools' is required. If you only require 'rack/perftools_profiler', it will do the right thing (require 'perftools' after setting the environment variables).
View
1  lib/rack/perftools_profiler.rb
@@ -12,6 +12,7 @@
require 'rack/perftools_profiler/profile_once'
require 'rack/perftools_profiler/return_data'
require 'rack/perftools_profiler/call_app_directly'
+require 'rack/perftools_profiler/return_password_error'
module Rack::PerftoolsProfiler
View
4 lib/rack/perftools_profiler/action.rb
@@ -17,8 +17,8 @@ def act
def self.for_env(env, profiler, middleware)
request = Rack::Request.new(env)
klass =
- if ENV["PROFILE_PASSWORD"] && request.GET['profile'] != ENV["PROFILE_PASSWORD"]
- CallAppDirectly
+ if !profiler.password_valid?(request.GET['profile'])
+ ReturnPasswordError
else
case request.path_info
when %r{/__start__$}
View
9 lib/rack/perftools_profiler/profiler.rb
@@ -32,8 +32,9 @@ def initialize(app, options)
@printer = (options.delete(:default_printer) { DEFAULT_PRINTER }).to_sym
@frequency = (options.delete(:frequency) { UNSET_FREQUENCY }).to_s
@mode = (options.delete(:mode) { DEFAULT_MODE }).to_sym
- @bundler = (options.delete(:bundler) { false })
- @gemfile_dir = (options.delete(:gemfile_dir) { DEFAULT_GEMFILE_DIR })
+ @bundler = options.delete(:bundler) { false }
+ @gemfile_dir = options.delete(:gemfile_dir) { DEFAULT_GEMFILE_DIR }
+ @password = options.delete(:password) { nil }
@mode_for_request = nil
ProfileDataAction.check_printer(@printer)
ensure_mode_is_valid(@mode)
@@ -53,6 +54,10 @@ def profile(mode = nil)
def self.clear_data
::File.delete(PROFILING_DATA_FILE) if ::File.exists?(PROFILING_DATA_FILE)
end
+
+ def password_valid?(password)
+ @password.nil? || password == @password
+ end
def start(mode = nil)
ensure_mode_is_changeable(mode) if mode
View
14 lib/rack/perftools_profiler/return_password_error.rb
@@ -0,0 +1,14 @@
+module Rack::PerftoolsProfiler
+
+ class ReturnPasswordError < Action
+ include Rack::PerftoolsProfiler::Utils
+
+ def response
+ [401,
+ {'Content-Type' => 'text/plain'},
+ ["Profiling is password-protected. Password is incorrect.\nProvide a password using the 'profile' GET param:\nhttp://domain.com/foobar?profile=PASSWORD"]]
+ end
+
+ end
+
+end
View
14 test/single_request_profiling_test.rb
@@ -325,23 +325,19 @@ def setup
end
context "when a profile password is required" do
- should "not profile unless the parameter matches" do
- ENV["PROFILE_PASSWORD"] = 'secret_password'
+ should "error if password does not match" do
app = @app.clone
env = Rack::MockRequest.env_for('/', :params => {'profile' => 'true'})
- status, headers, body = Rack::PerftoolsProfiler.new(app, :default_printer => 'pdf').call(env)
- assert_equal 200, status
+ status, headers, body = Rack::PerftoolsProfiler.new(app, :default_printer => 'pdf', :password => 'secret_password').call(env)
+ assert_equal 401, status
assert_equal 'text/plain', headers['Content-Type']
- assert_equal 'Oh hai der', RackResponseBody.new(body).to_s
- ENV.delete 'PROFILE_PASSWORD'
+ assert_match /Profiling is password-protected\. Password is incorrect\./, RackResponseBody.new(body).to_s
end
should "profile if the parameter matches" do
- ENV["PROFILE_PASSWORD"] = 'secret_password'
env = Rack::MockRequest.env_for('/', :params => 'profile=secret_password&printer=gif')
- _, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf').call(env)
+ _, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf', :password => 'secret_password').call(env)
assert_equal 'image/gif', headers['Content-Type']
- ENV.delete 'PROFILE_PASSWORD'
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.