rack-request_police 0.0.1alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +4 -0
- data/lib/rack/request_police/middleware.rb +36 -0
- data/lib/rack/request_police/storage/base.rb +16 -0
- data/lib/rack/request_police/storage/redis.rb +38 -0
- data/lib/rack/request_police/storage/unit.rb +8 -0
- data/lib/rack/request_police/version.rb +5 -0
- data/lib/rack/request_police/web.rb +20 -0
- data/lib/rack/request_police/web_helpers.rb +34 -0
- data/lib/rack/request_police.rb +44 -0
- data/rack-request_police.gemspec +32 -0
- data/spec/bench.rb +125 -0
- data/spec/middleware_spec.rb +207 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/storage/redis_spec.rb +23 -0
- data/spec/web_spec.rb +35 -0
- data/web/views/index.erb +122 -0
- metadata +213 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b0e36c2e22df4b342dbc2b60f234de136cab4e5b
|
4
|
+
data.tar.gz: 534a58f32183ad3319a029efd94e59bf89877558
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 11fd8b55d68d44d1dff25a1062472f39b1b9cfc96fe0646835c578dd49157c2615630c0ebb01d107c3fca361d9b7349bf1c4c2c15854411f593c03bf78e1014d
|
7
|
+
data.tar.gz: 949d05dda8d405ebc2227463a3aadfefd6ba34d1ee95e16d8928488f075b36fc7f369e93048931c0f8bc0f1054457eec73b62dfe7988851360d184b17d41e4a8
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Rafal Wojsznis
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Rack::RequestPolice
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/emq/rack-request_police)
|
4
|
+
[](https://travis-ci.org/emq/rack-request_police)
|
5
|
+
[](https://coveralls.io/r/emq/rack-request_police)
|
6
|
+
[](https://gemnasium.com/emq/rack-request_police)
|
7
|
+
|
8
|
+
Rack middleware for logging selected request for further investigation / analyze.
|
9
|
+
|
10
|
+
Features:
|
11
|
+
|
12
|
+
- filter requests by method (get/post/patch/delete) and/or regular expression
|
13
|
+
- log requests into storage of your choice (at the moment redis supported)
|
14
|
+
|
15
|
+
Work in progress.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'rack-request_police'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install rack-request_police
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
### Rails
|
36
|
+
|
37
|
+
Add do your `application.rb` / environment config:
|
38
|
+
|
39
|
+
``` ruby
|
40
|
+
Application.configure do
|
41
|
+
# ...
|
42
|
+
config.middleware.use Rack::RequestPolice::Middleware
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Configure middleware using initializer (eg. `config/initializers/request_police.rb`).
|
47
|
+
|
48
|
+
``` ruby
|
49
|
+
Rack::RequestPolice.configure do |config|
|
50
|
+
# For the time being only redis storage if provided, but you can hook up
|
51
|
+
# any storage of your choice as long it responds to log_request and page methods
|
52
|
+
# see Storage::Base and Storage::Redis for more references
|
53
|
+
config.storage = Rack::RequestPolice::Storage::Redis.new(host: 'localhost', port: 6379)
|
54
|
+
|
55
|
+
# Regular expression that will be matched against request uri
|
56
|
+
# Nil by default (logs all requests)
|
57
|
+
config.regex = /some-url/
|
58
|
+
|
59
|
+
# array of methods that should be logged (all four by default)
|
60
|
+
# requests not included in this list will be ignored
|
61
|
+
config.method = [:get, :post, :delete, :patch]
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
1. Fork it ( https://github.com/emq/rack-request_police/fork )
|
68
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
69
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
70
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Rack
|
2
|
+
module RequestPolice
|
3
|
+
class Middleware
|
4
|
+
class NoStorageFound < StandardError; end
|
5
|
+
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
if ::Rack::RequestPolice.method.include?(env['REQUEST_METHOD'].downcase.to_sym)
|
12
|
+
full_url = ''
|
13
|
+
full_url << (env['HTTPS'] == 'on' ? 'https://' : 'http://')
|
14
|
+
full_url << env['HTTP_HOST'] << env['PATH_INFO']
|
15
|
+
full_url << '?' << env['QUERY_STRING'] unless env['QUERY_STRING'].empty?
|
16
|
+
|
17
|
+
if !::Rack::RequestPolice.regex || full_url =~ ::Rack::RequestPolice.regex
|
18
|
+
request_params = {
|
19
|
+
'url' => full_url,
|
20
|
+
'ip' => env['REMOTE_ADDR'],
|
21
|
+
'method' => env['REQUEST_METHOD'].downcase,
|
22
|
+
'time' => Time.now.to_i
|
23
|
+
}
|
24
|
+
|
25
|
+
if %w(POST PATCH DELETE).include?(env['REQUEST_METHOD'])
|
26
|
+
request_params.merge!('data' => env['rack.input'].gets)
|
27
|
+
end
|
28
|
+
::Rack::RequestPolice.storage.log_request(request_params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
@app.call(env)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rack
|
2
|
+
module RequestPolice
|
3
|
+
module Storage
|
4
|
+
class Base
|
5
|
+
def log_request(request_params)
|
6
|
+
raise NotImplementedError, "Please implement `log_request` method"
|
7
|
+
end
|
8
|
+
|
9
|
+
def page(pageidx = 1, page_size = 25)
|
10
|
+
raise NotImplementedError, "Please implement `page` method that will return
|
11
|
+
[current_page_number, total_amount_of_logged_requests, array_of_paginated <Unit> objects]"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Rack
|
2
|
+
module RequestPolice
|
3
|
+
module Storage
|
4
|
+
class Redis < Base
|
5
|
+
REDIS_KEY = 'rack:request:police'.freeze
|
6
|
+
|
7
|
+
attr_reader :redis, :parser
|
8
|
+
|
9
|
+
def initialize(hash_of_options, json_parser: JSON)
|
10
|
+
@redis = ::Redis.new(hash_of_options)
|
11
|
+
@parser = json_parser
|
12
|
+
end
|
13
|
+
|
14
|
+
def log_request(request_params)
|
15
|
+
redis.lpush(REDIS_KEY, parser.dump(request_params))
|
16
|
+
end
|
17
|
+
|
18
|
+
def page(pageidx = 1, page_size = 25)
|
19
|
+
current_page = pageidx.to_i < 1 ? 1 : pageidx.to_i
|
20
|
+
pageidx = current_page - 1
|
21
|
+
total_size = 0
|
22
|
+
items = []
|
23
|
+
starting = pageidx * page_size
|
24
|
+
ending = starting + page_size - 1
|
25
|
+
|
26
|
+
total_size = redis.llen(REDIS_KEY)
|
27
|
+
items = redis.lrange(REDIS_KEY, starting, ending).map do |json|
|
28
|
+
hash = parser.load(json)
|
29
|
+
Unit.new(hash['method'], hash['ip'], hash['url'], Time.at(hash['time']), hash['data'])
|
30
|
+
end
|
31
|
+
|
32
|
+
[current_page, total_size, items]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rack/request_police/web_helpers'
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module Rack
|
6
|
+
module RequestPolice
|
7
|
+
class Web < Sinatra::Base
|
8
|
+
helpers WebHelpers
|
9
|
+
|
10
|
+
set :root, ::File.expand_path(::File.dirname(__FILE__) + "/../../../web")
|
11
|
+
|
12
|
+
get '/' do
|
13
|
+
@count = (params[:count] || 25).to_i
|
14
|
+
(@current_page, @total_size, @logs) = Rack::RequestPolice.storage.page(params[:page], @count)
|
15
|
+
|
16
|
+
erb :index
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rack
|
2
|
+
module RequestPolice
|
3
|
+
module WebHelpers
|
4
|
+
def method_class(method)
|
5
|
+
case method
|
6
|
+
when 'get'
|
7
|
+
'primary'
|
8
|
+
when 'post'
|
9
|
+
'info'
|
10
|
+
when 'patch'
|
11
|
+
'warning'
|
12
|
+
else
|
13
|
+
'danger'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def environment_name
|
18
|
+
environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
19
|
+
"[#{environment.upcase}]"
|
20
|
+
end
|
21
|
+
|
22
|
+
def qparams(options)
|
23
|
+
options = options.stringify_keys
|
24
|
+
params.merge(options).map do |key, value|
|
25
|
+
"#{key}=#{value}"
|
26
|
+
end.join("&")
|
27
|
+
end
|
28
|
+
|
29
|
+
def root_path
|
30
|
+
"#{env['SCRIPT_NAME']}/"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "json"
|
2
|
+
require "rack/request_police/version"
|
3
|
+
require "rack/request_police/storage/base"
|
4
|
+
require "rack/request_police/storage/redis"
|
5
|
+
require "rack/request_police/storage/unit"
|
6
|
+
require "rack/request_police/middleware"
|
7
|
+
|
8
|
+
module Rack
|
9
|
+
module RequestPolice
|
10
|
+
class NoStorageFound < StandardError; end
|
11
|
+
|
12
|
+
@@storage = nil
|
13
|
+
@@method = [:get, :post, :delete, :patch]
|
14
|
+
@@regex = nil
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
yield self
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.storage=(obj)
|
21
|
+
@@storage = obj
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.storage
|
25
|
+
@@storage || fail(NoStorageFound)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.method
|
29
|
+
@@method
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.method=(array)
|
33
|
+
@@method = array
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.regex
|
37
|
+
@@regex
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.regex=(regular_expression)
|
41
|
+
@@regex = regular_expression
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rack/request_police/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rack-request_police"
|
8
|
+
spec.version = Rack::RequestPolice::VERSION
|
9
|
+
spec.authors = ["Rafał Wojsznis"]
|
10
|
+
spec.email = ["rafal.wojsznis@gmail.com"]
|
11
|
+
spec.summary = spec.description = "Rack middleware for logging selected request for further investigation / analyze."
|
12
|
+
spec.homepage = "https://github.com/emq/rack-request_police"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.required_ruby_version = '>= 2.0.0'
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.1.0"
|
25
|
+
spec.add_development_dependency 'sinatra', '~> 1.4.5'
|
26
|
+
spec.add_development_dependency 'rack-test', '~> 0.6.3'
|
27
|
+
spec.add_development_dependency 'timecop', '~> 0.7.1'
|
28
|
+
spec.add_development_dependency 'redis', '~> 3.2.0'
|
29
|
+
spec.add_development_dependency 'oj', '~> 2.11.4'
|
30
|
+
spec.add_development_dependency 'coveralls', '~> 0.7.8'
|
31
|
+
spec.add_development_dependency 'rack', '1.5.2' # show useful sinatra errors
|
32
|
+
end
|
data/spec/bench.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
describe "Simple benchmark", type: :request do
|
4
|
+
let(:repeat) { 10_000 }
|
5
|
+
let(:app){
|
6
|
+
Sinatra.new do
|
7
|
+
use(Rack::RequestPolice::Middleware)
|
8
|
+
get '/' do
|
9
|
+
end
|
10
|
+
post '/' do
|
11
|
+
end
|
12
|
+
delete '/' do
|
13
|
+
end
|
14
|
+
patch '/' do
|
15
|
+
end
|
16
|
+
end
|
17
|
+
}
|
18
|
+
|
19
|
+
before do
|
20
|
+
Rack::RequestPolice.configure do |c|
|
21
|
+
c.storage = DummyStorage.new
|
22
|
+
c.regex = nil
|
23
|
+
c.method = [:get, :post, :delete, :patch]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with middleware (defaults)" do
|
28
|
+
it "benchmarks it" do
|
29
|
+
puts "with middleware (defaults)"
|
30
|
+
Benchmark.bm(7) do |x|
|
31
|
+
x.report("get") { repeat.times { get '/' } }
|
32
|
+
x.report("post") { repeat.times { post '/' } }
|
33
|
+
x.report("delete") { repeat.times { delete '/' } }
|
34
|
+
x.report("patch") { repeat.times { patch '/' } }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with middleware (customized)" do
|
40
|
+
Rack::RequestPolice.configure do |c|
|
41
|
+
c.storage = DummyStorage.new
|
42
|
+
c.regex = /.*/
|
43
|
+
c.method = [:get, :post, :delete]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "benchmarks it" do
|
47
|
+
puts "with middleware (customized)"
|
48
|
+
Benchmark.bm(7) do |x|
|
49
|
+
x.report("get") { repeat.times { get '/' } }
|
50
|
+
x.report("post") { repeat.times { post '/' } }
|
51
|
+
x.report("delete") { repeat.times { delete '/' } }
|
52
|
+
x.report("patch") { repeat.times { patch '/' } }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "with middleware (customized, redis storage)" do
|
58
|
+
after { REDIS.flushdb }
|
59
|
+
before do
|
60
|
+
REDIS.flushdb
|
61
|
+
Rack::RequestPolice.configure do |c|
|
62
|
+
c.storage = Rack::RequestPolice::Storage::Redis.new(REDIS_OPTIONS)
|
63
|
+
c.regex = /.*/
|
64
|
+
c.method = [:get, :post, :delete]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "benchmarks it" do
|
69
|
+
puts "with middleware (customized, redis storage)"
|
70
|
+
Benchmark.bm(7) do |x|
|
71
|
+
x.report("get") { repeat.times { get '/' } }
|
72
|
+
x.report("post") { repeat.times { post '/' } }
|
73
|
+
x.report("delete") { repeat.times { delete '/' } }
|
74
|
+
x.report("patch") { repeat.times { patch '/' } }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with middleware (customized, redis storage, OJ json parser)" do
|
80
|
+
after { REDIS.flushdb }
|
81
|
+
before do
|
82
|
+
REDIS.flushdb
|
83
|
+
Rack::RequestPolice.configure do |c|
|
84
|
+
c.storage = Rack::RequestPolice::Storage::Redis.new(REDIS_OPTIONS, json_parser: Oj)
|
85
|
+
c.regex = /.*/
|
86
|
+
c.method = [:get, :post, :delete]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it "benchmarks it" do
|
91
|
+
puts "with middleware (customized, redis storage, OJ parser)"
|
92
|
+
Benchmark.bm(7) do |x|
|
93
|
+
x.report("get") { repeat.times { get '/' } }
|
94
|
+
x.report("post") { repeat.times { post '/' } }
|
95
|
+
x.report("delete") { repeat.times { delete '/' } }
|
96
|
+
x.report("patch") { repeat.times { patch '/' } }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "without middleware" do
|
102
|
+
let(:app){
|
103
|
+
Sinatra.new do
|
104
|
+
get '/' do
|
105
|
+
end
|
106
|
+
post '/' do
|
107
|
+
end
|
108
|
+
delete '/' do
|
109
|
+
end
|
110
|
+
patch '/' do
|
111
|
+
end
|
112
|
+
end
|
113
|
+
}
|
114
|
+
|
115
|
+
it "benchmarks it" do
|
116
|
+
puts "without middleware"
|
117
|
+
Benchmark.bm(7) do |x|
|
118
|
+
x.report("get") { repeat.times { get '/' } }
|
119
|
+
x.report("post") { repeat.times { post '/' } }
|
120
|
+
x.report("delete") { repeat.times { delete '/' } }
|
121
|
+
x.report("patch") { repeat.times { patch '/' } }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "My Middleware", type: :request do
|
4
|
+
before do
|
5
|
+
Timecop.freeze
|
6
|
+
|
7
|
+
Rack::RequestPolice.configure do |c|
|
8
|
+
c.storage = DummyStorage.new
|
9
|
+
c.regex = nil
|
10
|
+
c.method = [:get, :post, :delete, :patch]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
after { Timecop.return }
|
14
|
+
|
15
|
+
context "logging all requests" do
|
16
|
+
let(:app){
|
17
|
+
Sinatra.new do
|
18
|
+
use(Rack::RequestPolice::Middleware)
|
19
|
+
get '/' do
|
20
|
+
end
|
21
|
+
end
|
22
|
+
}
|
23
|
+
|
24
|
+
it "logs request without query params" do
|
25
|
+
expect_any_instance_of(DummyStorage).to receive(:log_request)
|
26
|
+
.with('url' => "http://example.org/", 'ip' => "127.0.0.1", 'method' => "get", 'time' => Time.now.to_i)
|
27
|
+
|
28
|
+
get '/'
|
29
|
+
|
30
|
+
expect(last_response.status).to eq 200
|
31
|
+
end
|
32
|
+
|
33
|
+
it "logs request with query params" do
|
34
|
+
expect_any_instance_of(DummyStorage).to receive(:log_request)
|
35
|
+
.with('url' => "http://example.org/?what-the&hell=", 'ip' => "127.0.0.1", 'method' => "get", 'time' => Time.now.to_i)
|
36
|
+
|
37
|
+
get '/?what-the&hell='
|
38
|
+
|
39
|
+
expect(last_response.status).to eq 200
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "logging only POST requests" do
|
44
|
+
before do
|
45
|
+
Rack::RequestPolice.configure do |c|
|
46
|
+
c.method = [:post]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:app){
|
51
|
+
Sinatra.new do
|
52
|
+
use(Rack::RequestPolice::Middleware)
|
53
|
+
get '/' do
|
54
|
+
end
|
55
|
+
post '/form' do
|
56
|
+
end
|
57
|
+
end
|
58
|
+
}
|
59
|
+
|
60
|
+
it "ignores get requests" do
|
61
|
+
expect_any_instance_of(DummyStorage).not_to receive(:log_request)
|
62
|
+
get '/'
|
63
|
+
expect(last_response.status).to eq 200
|
64
|
+
end
|
65
|
+
|
66
|
+
it "logs post request with request data" do
|
67
|
+
expect_any_instance_of(DummyStorage).to receive(:log_request)
|
68
|
+
.with('url' => "http://example.org/form", 'ip' => "127.0.0.1", 'method' => "post", 'time' => Time.now.to_i, 'data' => 'user[name]=john&user[email]=john%40test.com')
|
69
|
+
|
70
|
+
post '/form', { user: { name: 'john', email: 'john@test.com' } }
|
71
|
+
|
72
|
+
expect(last_response.status).to eq 200
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "logging PATCH requests" do
|
77
|
+
before do
|
78
|
+
Rack::RequestPolice.configure do |c|
|
79
|
+
c.method = [:patch]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:app){
|
84
|
+
Sinatra.new do
|
85
|
+
use(Rack::RequestPolice::Middleware)
|
86
|
+
patch '/update' do
|
87
|
+
end
|
88
|
+
end
|
89
|
+
}
|
90
|
+
|
91
|
+
it "logs patch request with request data" do
|
92
|
+
expect_any_instance_of(DummyStorage).to receive(:log_request)
|
93
|
+
.with('url' => "http://example.org/update", 'ip' => "127.0.0.1", 'method' => "patch", 'time' => Time.now.to_i, 'data' => 'user[name]=john')
|
94
|
+
|
95
|
+
patch '/update', { user: { name: 'john' } }
|
96
|
+
|
97
|
+
expect(last_response.status).to eq 200
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "logging DELETE requests" do
|
102
|
+
before do
|
103
|
+
Rack::RequestPolice.configure do |c|
|
104
|
+
c.method = [:delete]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
let(:app){
|
109
|
+
Sinatra.new do
|
110
|
+
use(Rack::RequestPolice::Middleware)
|
111
|
+
delete '/destroy' do
|
112
|
+
end
|
113
|
+
end
|
114
|
+
}
|
115
|
+
|
116
|
+
it "logs delete request with request data" do
|
117
|
+
expect_any_instance_of(DummyStorage).to receive(:log_request)
|
118
|
+
.with('url' => "http://example.org/destroy", 'ip' => "127.0.0.1", 'method' => "delete", 'time' => Time.now.to_i, 'data' => 'user[id]=1')
|
119
|
+
|
120
|
+
delete '/destroy', { user: { id: 1 } }
|
121
|
+
|
122
|
+
expect(last_response.status).to eq 200
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "logging requests via regex expression" do
|
127
|
+
before do
|
128
|
+
Rack::RequestPolice.configure do |c|
|
129
|
+
c.regex = /user/
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
let(:app){
|
134
|
+
Sinatra.new do
|
135
|
+
use(Rack::RequestPolice::Middleware)
|
136
|
+
get '/user' do
|
137
|
+
end
|
138
|
+
get '/account' do
|
139
|
+
end
|
140
|
+
end
|
141
|
+
}
|
142
|
+
|
143
|
+
it "ignores queries that does not match given regex" do
|
144
|
+
expect_any_instance_of(DummyStorage).not_to receive(:log_request)
|
145
|
+
get '/account'
|
146
|
+
expect(last_response.status).to eq 200
|
147
|
+
end
|
148
|
+
|
149
|
+
it "logs matching queries" do
|
150
|
+
expect_any_instance_of(DummyStorage).to receive(:log_request)
|
151
|
+
.with('url' => "http://example.org/user", 'ip' => "127.0.0.1", 'method' => "get", 'time' => Time.now.to_i)
|
152
|
+
|
153
|
+
get '/user'
|
154
|
+
|
155
|
+
expect(last_response.status).to eq 200
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "logging request via regex expression (with params)" do
|
160
|
+
before do
|
161
|
+
Rack::RequestPolice.configure do |c|
|
162
|
+
c.regex = /user\?id=1/
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
let(:app){
|
167
|
+
Sinatra.new do
|
168
|
+
use(Rack::RequestPolice::Middleware)
|
169
|
+
get '/user' do
|
170
|
+
end
|
171
|
+
end
|
172
|
+
}
|
173
|
+
|
174
|
+
it "ignores queries that does not match given regex" do
|
175
|
+
expect_any_instance_of(DummyStorage).not_to receive(:log_request)
|
176
|
+
get '/user?id=2'
|
177
|
+
expect(last_response.status).to eq 200
|
178
|
+
end
|
179
|
+
|
180
|
+
it "logs matching queries" do
|
181
|
+
expect_any_instance_of(DummyStorage).to receive(:log_request)
|
182
|
+
.with('url' => "http://example.org/user?id=1", 'ip' => "127.0.0.1", 'method' => "get", 'time' => Time.now.to_i)
|
183
|
+
|
184
|
+
get '/user?id=1'
|
185
|
+
|
186
|
+
expect(last_response.status).to eq 200
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context "logging without storage" do
|
191
|
+
before do
|
192
|
+
Rack::RequestPolice.configure do |c|
|
193
|
+
c.storage = nil
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
let(:app){
|
198
|
+
Sinatra.new do
|
199
|
+
use(Rack::RequestPolice::Middleware)
|
200
|
+
end
|
201
|
+
}
|
202
|
+
|
203
|
+
it 'raises an error' do
|
204
|
+
expect { get '/' }.to raise_error(Rack::RequestPolice::NoStorageFound)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
3
|
+
|
4
|
+
require_relative '../lib/rack/request_police'
|
5
|
+
require_relative '../lib/rack/request_police/web'
|
6
|
+
require 'rack/test'
|
7
|
+
require 'timecop'
|
8
|
+
require 'redis'
|
9
|
+
require 'oj'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.include Rack::Test::Methods
|
13
|
+
|
14
|
+
config.expect_with :rspec do |expectations|
|
15
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
16
|
+
end
|
17
|
+
|
18
|
+
config.mock_with :rspec do |mocks|
|
19
|
+
mocks.verify_partial_doubles = true
|
20
|
+
end
|
21
|
+
|
22
|
+
config.warnings = true
|
23
|
+
config.order = :random
|
24
|
+
Kernel.srand config.seed
|
25
|
+
end
|
26
|
+
|
27
|
+
class DummyStorage < Rack::RequestPolice::Storage::Base
|
28
|
+
def log_request(hash); end
|
29
|
+
end
|
30
|
+
|
31
|
+
REDIS_OPTIONS = { url: "redis://localhost/15", namespace: "rack-request-police" }
|
32
|
+
REDIS = Redis.new(REDIS_OPTIONS)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Redis storage' do
|
4
|
+
after { REDIS.flushdb }
|
5
|
+
before { REDIS.flushdb }
|
6
|
+
|
7
|
+
describe '#log_request' do
|
8
|
+
it 'pushes serialized requests params to redis list' do
|
9
|
+
storage = Rack::RequestPolice::Storage::Redis.new(REDIS_OPTIONS)
|
10
|
+
|
11
|
+
expect { storage.log_request({'test' => 'me'}) }
|
12
|
+
.to change{ REDIS.llen('rack:request:police')}.by(1)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can serialize using different JSON library' do
|
16
|
+
storage = Rack::RequestPolice::Storage::Redis.new(REDIS_OPTIONS, json_parser: Oj)
|
17
|
+
|
18
|
+
expect(Oj).to receive(:dump).with({'test' => 'me'})
|
19
|
+
storage.log_request({'test' => 'me'})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/spec/web_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Web interface", type: :request do
|
4
|
+
before { Rack::RequestPolice.storage = Rack::RequestPolice::Storage::Redis.new(REDIS_OPTIONS) }
|
5
|
+
let!(:app){ Rack::RequestPolice::Web }
|
6
|
+
|
7
|
+
context "when there are logged requests" do
|
8
|
+
before do
|
9
|
+
Rack::RequestPolice.storage.log_request(method: 'get', ip: '127.0.0.1', url: 'example.com', time: Time.now.to_i)
|
10
|
+
get '/'
|
11
|
+
end
|
12
|
+
|
13
|
+
after { REDIS.flushdb }
|
14
|
+
|
15
|
+
it "is successful" do
|
16
|
+
expect(last_response.status).to eq 200
|
17
|
+
end
|
18
|
+
|
19
|
+
it "displays logged request" do
|
20
|
+
expect(last_response.body).to match(/127\.0\.0\.1.*example\.com/m)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when there is none logged requests" do
|
25
|
+
before { get '/' }
|
26
|
+
|
27
|
+
it "is successful" do
|
28
|
+
expect(last_response.status).to eq 200
|
29
|
+
end
|
30
|
+
|
31
|
+
it "displays proper information" do
|
32
|
+
expect(last_response.body).to match(/No requests logged/)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/web/views/index.erb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= environment_name %> RequestPolice</title>
|
5
|
+
<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
|
6
|
+
<style type="text/css">
|
7
|
+
tr.details {
|
8
|
+
display: none;
|
9
|
+
}
|
10
|
+
td.time, td.ip {
|
11
|
+
font-size: 80%;
|
12
|
+
}
|
13
|
+
td.method {
|
14
|
+
text-align: center;
|
15
|
+
}
|
16
|
+
|
17
|
+
</style>
|
18
|
+
<script type="text/javascript">
|
19
|
+
function ready(fn){
|
20
|
+
if (document.readyState != 'loading'){
|
21
|
+
fn();
|
22
|
+
} else {
|
23
|
+
document.addEventListener('DOMContentLoaded', fn);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
ready(function(){
|
28
|
+
var elements = document.querySelectorAll('a');
|
29
|
+
Array.prototype.forEach.call(elements, function(el){
|
30
|
+
el.addEventListener('click', function(e){
|
31
|
+
var row = document.getElementById(this.dataset.details);
|
32
|
+
if (row.style.display == 'table-row'){
|
33
|
+
row.style.display = 'none';
|
34
|
+
} else {
|
35
|
+
row.style.display = 'table-row';
|
36
|
+
}
|
37
|
+
e.preventDefault();
|
38
|
+
});
|
39
|
+
});
|
40
|
+
});
|
41
|
+
</script>
|
42
|
+
</head>
|
43
|
+
<body>
|
44
|
+
<nav class="navbar navbar-default">
|
45
|
+
<div class="container">
|
46
|
+
<div class="navbar-header">
|
47
|
+
<a href="/" class="navbar-brand"><%= environment_name %> Rack::RequestPolice v<%= Rack::RequestPolice::VERSION %></a>
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
</nav>
|
51
|
+
<div class="container">
|
52
|
+
<div class="row">
|
53
|
+
<div class="col-md-12">
|
54
|
+
<% if @logs.any? %>
|
55
|
+
<table class="table table-striped table-hover">
|
56
|
+
<thead>
|
57
|
+
<tr>
|
58
|
+
<th class="col-md-1">Method</th>
|
59
|
+
<th class="col-md-2">Time</th>
|
60
|
+
<th class="col-md-1">IP</th>
|
61
|
+
<th class="col-md-5">URL</th>
|
62
|
+
<th class="col-md-4">Data</th>
|
63
|
+
</tr>
|
64
|
+
</thead>
|
65
|
+
<tbody>
|
66
|
+
<% @logs.each_with_index do |log, idx| %>
|
67
|
+
<tr>
|
68
|
+
<td class="method"><span class="label label-<%= method_class log.method %>"><%= log.method %></span></td>
|
69
|
+
<td class="time"><%= log.time %></td>
|
70
|
+
<td class="ip"><%= log.ip %></td>
|
71
|
+
<td><a href="<%= log.url %>" target="_blank"><%= log.url %></a></td>
|
72
|
+
<td>
|
73
|
+
<% if log.data %>
|
74
|
+
<a href='#' data-details="details_<%= idx %>">toggle details</a></td>
|
75
|
+
<tr class="details" id="details_<%= idx %>">
|
76
|
+
<td colspan="5">
|
77
|
+
<pre>
|
78
|
+
<%= log.data.inspect %>
|
79
|
+
</pre>
|
80
|
+
</td>
|
81
|
+
</tr>
|
82
|
+
<% else %>
|
83
|
+
</td>
|
84
|
+
<% end %>
|
85
|
+
</tr>
|
86
|
+
<% end %>
|
87
|
+
</tbody>
|
88
|
+
</table>
|
89
|
+
|
90
|
+
<% if @total_size > @count %>
|
91
|
+
<ul class="pagination pull-right">
|
92
|
+
<li class="<%= 'disabled' if @current_page == 1 %>">
|
93
|
+
<a href="<%= root_path %>?page=1">«</a>
|
94
|
+
</li>
|
95
|
+
<% if @current_page > 1 %>
|
96
|
+
<li>
|
97
|
+
<a href="<%= root_path %>?<%= qparams(page: @current_page - 1) %>"><%= @current_page - 1 %></a>
|
98
|
+
</li>
|
99
|
+
<% end %>
|
100
|
+
<li class="disabled">
|
101
|
+
<a href="<%= root_path %>?<%= qparams(page: @current_page) %>"><%= @current_page %></a>
|
102
|
+
</li>
|
103
|
+
<% if @total_size > @current_page * @count %>
|
104
|
+
<li>
|
105
|
+
<a href="<%= root_path %>?<%= qparams(page: @current_page + 1) %>"><%= @current_page + 1 %></a>
|
106
|
+
</li>
|
107
|
+
<% end %>
|
108
|
+
<li class="<%= 'disabled' if @total_size <= @current_page * @count %>">
|
109
|
+
<a href="<%= root_path %>?<%= qparams(page: (@total_size.to_f / @count).ceil) %>">»</a>
|
110
|
+
</li>
|
111
|
+
</ul>
|
112
|
+
<% end %>
|
113
|
+
<% else %>
|
114
|
+
<div class="alert alert-info">No requests logged</div>
|
115
|
+
<% end %>
|
116
|
+
</div>
|
117
|
+
</div>
|
118
|
+
</div>
|
119
|
+
</body>
|
120
|
+
</html>
|
121
|
+
|
122
|
+
|
metadata
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-request_police
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1alpha
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rafał Wojsznis
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.1.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.1.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sinatra
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.4.5
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.4.5
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.6.3
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.6.3
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: timecop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.7.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.7.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: redis
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 3.2.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 3.2.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: oj
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.11.4
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.11.4
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: coveralls
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.7.8
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.7.8
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rack
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 1.5.2
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 1.5.2
|
153
|
+
description: Rack middleware for logging selected request for further investigation
|
154
|
+
/ analyze.
|
155
|
+
email:
|
156
|
+
- rafal.wojsznis@gmail.com
|
157
|
+
executables: []
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- ".gitignore"
|
162
|
+
- ".rspec"
|
163
|
+
- ".travis.yml"
|
164
|
+
- Gemfile
|
165
|
+
- LICENSE.txt
|
166
|
+
- README.md
|
167
|
+
- Rakefile
|
168
|
+
- lib/rack/request_police.rb
|
169
|
+
- lib/rack/request_police/middleware.rb
|
170
|
+
- lib/rack/request_police/storage/base.rb
|
171
|
+
- lib/rack/request_police/storage/redis.rb
|
172
|
+
- lib/rack/request_police/storage/unit.rb
|
173
|
+
- lib/rack/request_police/version.rb
|
174
|
+
- lib/rack/request_police/web.rb
|
175
|
+
- lib/rack/request_police/web_helpers.rb
|
176
|
+
- rack-request_police.gemspec
|
177
|
+
- spec/bench.rb
|
178
|
+
- spec/middleware_spec.rb
|
179
|
+
- spec/spec_helper.rb
|
180
|
+
- spec/storage/redis_spec.rb
|
181
|
+
- spec/web_spec.rb
|
182
|
+
- web/views/index.erb
|
183
|
+
homepage: https://github.com/emq/rack-request_police
|
184
|
+
licenses:
|
185
|
+
- MIT
|
186
|
+
metadata: {}
|
187
|
+
post_install_message:
|
188
|
+
rdoc_options: []
|
189
|
+
require_paths:
|
190
|
+
- lib
|
191
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: 2.0.0
|
196
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">"
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: 1.3.1
|
201
|
+
requirements: []
|
202
|
+
rubyforge_project:
|
203
|
+
rubygems_version: 2.4.5
|
204
|
+
signing_key:
|
205
|
+
specification_version: 4
|
206
|
+
summary: Rack middleware for logging selected request for further investigation /
|
207
|
+
analyze.
|
208
|
+
test_files:
|
209
|
+
- spec/bench.rb
|
210
|
+
- spec/middleware_spec.rb
|
211
|
+
- spec/spec_helper.rb
|
212
|
+
- spec/storage/redis_spec.rb
|
213
|
+
- spec/web_spec.rb
|