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

Add support for non-GET requests #122

Closed
wants to merge 2 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
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# A Log of Changes!

## [1.4.0] - unreleased
- Support non-GET requests via REQUEST_METHOD and setting body via REQUEST_BODY
- Add ability to set Content-Type and Content-Length headers via CONTENT_TYPE and CONTENT_LENGTH env vars (they don't work with HTTP_ variables)


## [1.3.4]

- Allow for "warming up tasks" via WARM_COUNT env var #119
Expand Down Expand Up @@ -56,4 +61,4 @@

## [0.0.0] - 2014-08-15

- Initial release
- Initial release
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,22 @@ $ HTTP_AUTHORIZATION="Basic YWRtaW46c2VjcmV0\n" \
PATH_TO_HIT=/foo_secret bundle exec derailed exec perf:ips
```

The `Content-Type` and `Content-Length` headers are a bit different. To set those, ignore the HTTP_ prefix, use the `CONTENT_TYPE` and `CONTENT_LENGTH` variables.

### Performing non-GET requests

If the endpoint being tested is not a GET request, you can set the `REQUEST_METHOD` variable with the HTTP method you want (e.g. POST, PUT, PATCH, DELETE).

To set the request body, you can use the `REQUEST_BODY`.

```
$ REQUEST_METHOD=POST \
REQUEST_BODY="{\"user\":{\"email\":\"foo@bar.com\",\"password\":\"123456\",\"password_confirmation\":\"123456\"}}" \
CONTENT_TYPE="application/json" \
PATH_TO_HIT=/users \
bundle exec derailed exec perf:test
```

### Using a real web server with `USE_SERVER`

All tests are run without a webserver (directly using `Rack::Mock` by default), if you want to use a webserver set `USE_SERVER` to a Rack::Server compliant server, such as `webrick`.
Expand Down
38 changes: 32 additions & 6 deletions lib/derailed_benchmarks/tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

DERAILED_APP = Rails.application

# Disables CSRF protection because of non-GET requests
DERAILED_APP.config.action_controller.allow_forgery_protection = false

if DERAILED_APP.respond_to?(:initialized?)
DERAILED_APP.initialize! unless DERAILED_APP.initialized?
else
Expand Down Expand Up @@ -65,20 +68,43 @@
WARM_COUNT = (ENV['WARM_COUNT'] || 0).to_i
TEST_COUNT = (ENV['TEST_COUNT'] || ENV['CNT'] || 1_000).to_i
PATH_TO_HIT = ENV["PATH_TO_HIT"] || ENV['ENDPOINT'] || "/"
REQUEST_METHOD = ENV["REQUEST_METHOD"] || "GET"
REQUEST_BODY = ENV["REQUEST_BODY"]

puts "Method: #{REQUEST_METHOD}"
puts "Endpoint: #{ PATH_TO_HIT.inspect }"

# See https://www.rubydoc.info/github/rack/rack/file/SPEC#The_Environment
# All HTTP_ variables are accepted in the Rack environment hash, except HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH.
# For those, the HTTP_ prefix has to be removed.
HTTP_HEADER_PREFIX = "HTTP_".freeze
RACK_HTTP_HEADERS = ENV.select { |key| key.starts_with?(HTTP_HEADER_PREFIX) }
HTTP_HEADER_REGEXP = /^#{HTTP_HEADER_PREFIX}.+|CONTENT_(TYPE|LENGTH)$/
RACK_ENV_HASH = ENV.select { |key| key =~ HTTP_HEADER_REGEXP }

HTTP_HEADERS = RACK_HTTP_HEADERS.keys.inject({}) do |hash, rack_header_name|
HTTP_HEADERS = RACK_ENV_HASH.keys.inject({}) do |hash, rack_header_name|
# e.g. "HTTP_ACCEPT_CHARSET" -> "Accept-Charset"
header_name = rack_header_name[HTTP_HEADER_PREFIX.size..-1].split("_").map(&:downcase).map(&:capitalize).join("-")
hash[header_name] = RACK_HTTP_HEADERS[rack_header_name]
upper_case_header_name =
if rack_header_name.start_with?(HTTP_HEADER_PREFIX)
rack_header_name[HTTP_HEADER_PREFIX.size..-1]
else
rack_header_name
end

header_name = upper_case_header_name.split("_").map(&:downcase).map(&:capitalize).join("-")

hash[header_name] = RACK_ENV_HASH[rack_header_name]
hash
end
puts "HTTP headers: #{HTTP_HEADERS}" unless HTTP_HEADERS.empty?

CURL_HTTP_HEADER_ARGS = HTTP_HEADERS.map { |http_header_name, value| "-H \"#{http_header_name}: #{value}\"" }.join(" ")
CURL_BODY_ARG = REQUEST_BODY ? "-d '#{REQUEST_BODY}'" : nil

if REQUEST_METHOD != "GET" && REQUEST_BODY
RACK_ENV_HASH["GATEWAY_INTERFACE"] = "CGI/1.1"
RACK_ENV_HASH[:input] = REQUEST_BODY.dup
puts "Body: #{REQUEST_BODY}"
end

require 'rack/test'
require 'rack/file'
Expand All @@ -94,15 +120,15 @@
sleep 1

def call_app(path = File.join("/", PATH_TO_HIT))
cmd = "curl #{CURL_HTTP_HEADER_ARGS} 'http://localhost:#{@port}#{path}' -s --fail 2>&1"
cmd = "curl -X #{REQUEST_METHOD} #{CURL_HTTP_HEADER_ARGS} #{CURL_BODY_ARG} -s --fail 'http://localhost:#{@port}#{path}' 2>&1"
response = `#{cmd}`
raise "Bad request to #{cmd.inspect} Response:\n#{ response.inspect }" unless $?.success?
end
else
@app = Rack::MockRequest.new(DERAILED_APP)

def call_app
response = @app.get(PATH_TO_HIT, RACK_HTTP_HEADERS)
response = @app.request(REQUEST_METHOD, PATH_TO_HIT, RACK_ENV_HASH)
raise "Bad request: #{ response.body }" unless response.status == 200
response
end
Expand Down
38 changes: 38 additions & 0 deletions test/integration/tasks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,44 @@ def rake(cmd, options = {})
assert_match 'HTTP headers: {"Authorization"=>"Basic YWRtaW46c2VjcmV0\n", "Cache-Control"=>"no-cache"}', result
end

test 'CONTENT_TYPE' do
env = {
"REQUEST_METHOD" => "POST",
"PATH_TO_HIT" => "users",
"CONTENT_TYPE" => "application/json",
"REQUEST_BODY" => '{"user":{"email":"foo@bar.com","password":"123456","password_confirmation":"123456"}}',
"TEST_COUNT" => "2"
}

result = rake "perf:test", env: env
assert_match 'Body: {"user":{"email":"foo@bar.com","password":"123456","password_confirmation":"123456"}}', result
assert_match 'HTTP headers: {"Content-Type"=>"application/json"}', result

env["USE_SERVER"] = "webrick"
result = rake "perf:test", env: env
assert_match 'Body: {"user":{"email":"foo@bar.com","password":"123456","password_confirmation":"123456"}}', result
assert_match 'HTTP headers: {"Content-Type"=>"application/json"}', result
end

test 'REQUEST_METHOD and REQUEST_BODY' do
env = {
"REQUEST_METHOD" => "POST",
"PATH_TO_HIT" => "users",
"REQUEST_BODY" => "user%5Bemail%5D=foo%40bar.com&user%5Bpassword%5D=123456&user%5Bpassword_confirmation%5D=123456",
"TEST_COUNT" => "2"
}

result = rake "perf:test", env: env
assert_match 'Endpoint: "users"', result
assert_match 'Method: POST', result
assert_match 'Body: user%5Bemail%5D=foo%40bar.com&user%5Bpassword%5D=123456&user%5Bpassword_confirmation%5D=123456', result

env["USE_SERVER"] = "webrick"
result = rake "perf:test", env: env
assert_match 'Method: POST', result
assert_match 'Body: user%5Bemail%5D=foo%40bar.com&user%5Bpassword%5D=123456&user%5Bpassword_confirmation%5D=123456', result
end

test 'USE_SERVER' do
result = rake "perf:test", env: { "USE_SERVER" => 'webrick', "TEST_COUNT" => "2" }
assert_match 'Server: "webrick"', result
Expand Down
13 changes: 13 additions & 0 deletions test/rails_app/app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class UsersController < ApplicationController
def create
User.create!(user_params)

head :created
end

private

def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
1 change: 1 addition & 0 deletions test/rails_app/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@

get "foo", to: "pages#index"
get "foo_secret", to: "pages#secret"
post "users", to: "users#create"

get "authenticated", to: "authenticated#index"

Expand Down