rack-request_replication 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7c53f6dc67d9b644ff865a0e0e5db200e19756ad
4
+ data.tar.gz: 77e9cd22aeab694556c8d2195e766d56c9df25f9
5
+ SHA512:
6
+ metadata.gz: 6c9e718ca2a6a0688088766e7d7a96e550f32a521e6991fcf5d37937ca905a281f3035c1e6f1532d26eebfd523ba55315ea0b464711f356a64e2e7af1fa46a68
7
+ data.tar.gz: afd0d4ffa1b81cf3cd5b2cdeb39af9a3e7ee70009f2335c4686c5bc0c33474266442b2d1b5a5bdc5d709d36128a99ae1c7cf46d3a682b8ea2b35b8e98eb1d052
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-request_replication.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Wouter de Vos
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,45 @@
1
+ # Rack::RequestReplication
2
+
3
+ Replicate requests from one app instance to another. At
4
+ [Springest](http://www.springest.com) we use this to test new features.
5
+ We replicate all live requests to our staging environment.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'rack-request_replication'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install rack-request_replication
20
+
21
+ ## Usage
22
+
23
+ ### Sinatra Example
24
+
25
+ ```ruby
26
+ require 'sinatra/base'
27
+ require 'rack/request_replication'
28
+
29
+ class TestApp < Sinatra::Base
30
+ # Forward all requests to another app that runs on localhost, port 4568
31
+ use Rack::RequestReplication::Forwarder, host: 'localhost', port: 4568
32
+
33
+ get '/' do
34
+ 'Hello World'
35
+ end
36
+ end
37
+ ```
38
+
39
+ ## Contributing
40
+
41
+ 1. Fork it ( https://github.com/[my-github-username]/rack-request_replication/fork )
42
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
43
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
44
+ 4. Push to the branch (`git push origin my-new-feature`)
45
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,3 @@
1
+ require "rack/request_replication/version"
2
+ require "rack/request_replication/logger"
3
+ require "rack/request_replication/forwarder"
@@ -0,0 +1,159 @@
1
+ require 'logger'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module Rack
7
+ module RequestReplication
8
+ ##
9
+ # This class implements forwarding of requests
10
+ # to another host and/or port.
11
+ #
12
+ class Forwarder
13
+ attr_reader :app
14
+ attr_reader :options
15
+
16
+ ##
17
+ # @param [#call] app
18
+ # @param [Hash{Symbol => Object}] options
19
+ # @option options [String] :host ('localhost')
20
+ # @option options [Integer] :port (8080)
21
+ #
22
+ def initialize( app, options = {} )
23
+ @app = app
24
+ @options = {
25
+ host: 'localhost',
26
+ port: 8080
27
+ }.merge options
28
+ end
29
+
30
+ ##
31
+ # @param [Hash{String => String}] env
32
+ # @return [Array(Integer, Hash, #each)]
33
+ # @see http://rack.rubyforge.org/doc/SPEC.html
34
+ #
35
+ def call( env )
36
+ request = Rack::Request.new env
37
+ replicate request
38
+ app.call env
39
+ end
40
+
41
+ ##
42
+ # Replicates the request and passes it on to the request
43
+ # forwarder.
44
+ #
45
+ # @param [Rack::Request] request
46
+ #
47
+ def replicate( request )
48
+ opts = replicate_options_and_data request
49
+ uri = forward_uri request
50
+
51
+ http = Net::HTTP.new uri.host, uri.port
52
+
53
+ forward_request = send("create_#{opts[:request_method].downcase}_request", uri, opts)
54
+ forward_request.add_field("Accept", opts[:accept])
55
+ forward_request.add_field("Accept-Encoding", opts[:accept_encoding])
56
+ forward_request.add_field("Host", request.host)
57
+ forward_request.add_field("Cookie", opts[:cookies]) # TODO: we need to link the source session to the session on the destination app. Maybe we can use Redis to store this.
58
+ Thread.new do
59
+ begin
60
+ http.request(forward_request)
61
+ rescue
62
+ logger.debug "Request to Forward App failed."
63
+ end
64
+ end
65
+ end
66
+
67
+ def create_get_request( uri, opts = {} )
68
+ Net::HTTP::Get.new uri.request_uri
69
+ end
70
+
71
+ def create_post_request( uri, opts = {} )
72
+ forward_request = Net::HTTP::Post.new uri.request_uri
73
+ forward_request.set_form_data opts[:params]
74
+ forward_request
75
+ end
76
+
77
+ def create_put_request( uri, opts = {} )
78
+ forward_request = Net::HTTP::Put.new uri.request_uri
79
+ forward_request.set_form_data opts[:params]
80
+ forward_request
81
+ end
82
+
83
+ def create_patch_request( uri, opts = {} )
84
+ forward_request = Net::HTTP::Patch.new uri.request_uri
85
+ forward_request.set_form_data opts[:params]
86
+ forward_request
87
+ end
88
+
89
+ def create_delete_request( uri, opts = {} )
90
+ Net::HTTP::Delete.new uri.request_uri
91
+ end
92
+
93
+ def create_options_request( uri, opts = {} )
94
+ Net::HTTP::Options.new uri.request_uri
95
+ end
96
+
97
+ ##
98
+ # Replicates all the options and data that was in
99
+ # the original request and puts them in a Hash.
100
+ #
101
+ # @param [Rack::Request] request
102
+ # @returns [Hash]
103
+ #
104
+ def replicate_options_and_data( request )
105
+ replicated_options ||= {}
106
+ %w(
107
+ accept_encoding
108
+ body
109
+ request_method
110
+ content_charset
111
+ cookies
112
+ media_type
113
+ media_type_params
114
+ params
115
+ referer
116
+ request_method
117
+ user_agent
118
+ url
119
+ ).map(&:to_sym).each do |m|
120
+ value = request.send( m )
121
+ replicated_options[m] = value unless value.nil?
122
+ end
123
+ replicated_options
124
+ end
125
+
126
+ ##
127
+ # Creates a URI based on the request info
128
+ # and the options set.
129
+ #
130
+ # @param [Rack::Request] request
131
+ # @returns [URI]
132
+ #
133
+ def forward_uri( request )
134
+ url = "#{request.scheme}://#{options[:host]}"
135
+ url << ":#{options[:port]}" unless port_matches_scheme? request
136
+ url << request.fullpath
137
+ URI(url)
138
+ end
139
+
140
+ ##
141
+ # Checks if the request scheme matches the destination port.
142
+ #
143
+ # @param [Rack::Request] request
144
+ # @returns [boolean]
145
+ #
146
+ def port_matches_scheme?( request )
147
+ options[:port].to_i == Rack::Request::DEFAULT_PORTS[clean_scheme(request)]
148
+ end
149
+
150
+ def clean_scheme( request )
151
+ request.scheme.match(/^\w+/)[0]
152
+ end
153
+
154
+ def logger
155
+ @logger ||= ::Logger.new(STDOUT)
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,8 @@
1
+ require 'logger'
2
+
3
+ module Rack
4
+ module RequestReplication
5
+ class Logg
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ module RequestReplication
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/request_replication/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rack-request_replication'
8
+ spec.version = Rack::RequestReplication::VERSION
9
+ spec.authors = ['Wouter de Vos']
10
+ spec.email = ['wouter@springest.com']
11
+ spec.summary = %q{Request replication MiddleWare for Rack.}
12
+ spec.description = %q{Replicate or record HTTP requests to your Rack application and replay them elsewhere or at another time.}
13
+ spec.homepage = 'https://github.com/Springest/rack-request_replication'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.6'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rack-test', '>= 0.5.3'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+ spec.add_development_dependency 'yard' , '>= 0.5.5'
26
+ spec.add_development_dependency 'sinatra'
27
+
28
+ spec.add_runtime_dependency 'rack', '>= 1.0.0'
29
+ end
@@ -0,0 +1,72 @@
1
+ require 'sinatra/base'
2
+ require 'rack/request_replication'
3
+
4
+ $destination_responses ||= []
5
+
6
+ class TestApp < Sinatra::Base
7
+ set :port, 4567
8
+
9
+ enable :logging
10
+
11
+ use Rack::RequestReplication::Forwarder, port: 4568
12
+
13
+ get '/' do
14
+ 'GET OK'
15
+ end
16
+
17
+ post '/' do
18
+ 'POST OK'
19
+ end
20
+
21
+ put '/' do
22
+ 'PUT OK'
23
+ end
24
+
25
+ patch '/' do
26
+ 'PATCH OK'
27
+ end
28
+
29
+ delete '/' do
30
+ 'DELETE OK'
31
+ end
32
+
33
+ options '/' do
34
+ 'OPTIONS OK'
35
+ end
36
+ end
37
+
38
+ class DestApp < Sinatra::Base
39
+ set :port, 4568
40
+
41
+ enable :logging
42
+
43
+ get '/' do
44
+ $destination_responses << 'GET OK'
45
+ 'Hello, World!'
46
+ end
47
+
48
+ post '/' do
49
+ $destination_responses << 'POST OK'
50
+ 'Created!'
51
+ end
52
+
53
+ put '/' do
54
+ $destination_responses << 'PUT OK'
55
+ 'Replaced!'
56
+ end
57
+
58
+ patch '/' do
59
+ $destination_responses << 'PATCH OK'
60
+ 'Updated'
61
+ end
62
+
63
+ delete '/' do
64
+ $destination_responses << 'DELETE OK'
65
+ 'Removed!'
66
+ end
67
+
68
+ options '/' do
69
+ $destination_responses << 'OPTIONS OK'
70
+ 'Appeased!'
71
+ end
72
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ def destination_response
4
+ sleep 1
5
+ $destination_responses.last
6
+ end
7
+
8
+ describe Rack::RequestReplication::Forwarder do
9
+ include Rack::Test::Methods
10
+
11
+ let(:app) { TestApp.new }
12
+ let(:forwarder) { Rack::RequestReplication::Forwarder.new(app, options) }
13
+ let(:options) do
14
+ {
15
+ port: destination_port
16
+ }
17
+ end
18
+ let(:request) do
19
+ OpenStruct.new({
20
+ request_method: request_method,
21
+ scheme: scheme,
22
+ port: source_port,
23
+ path: path
24
+ })
25
+ end
26
+
27
+ let(:request_method) { 'GET' }
28
+ let(:scheme) { 'http' }
29
+ let(:source_port) { 80 }
30
+ let(:destination_port) { 3000 }
31
+ let(:host) { 'localhost' }
32
+ let(:path) { '/' }
33
+
34
+ describe 'a GET request' do
35
+ before { get '/' }
36
+
37
+ it { expect(last_response.body).to eq destination_response }
38
+ end
39
+
40
+ describe 'a POST request' do
41
+ before { post '/', foo: 'bar' }
42
+
43
+ it { expect(last_response.body).to eq destination_response }
44
+ end
45
+
46
+ describe 'a PUT request' do
47
+ before { put '/', foo: 'bar' }
48
+
49
+ it { expect(last_response.body).to eq destination_response }
50
+ end
51
+
52
+ describe 'a PATCH request' do
53
+ before { patch '/', foo: 'bar' }
54
+
55
+ it { expect(last_response.body).to eq destination_response }
56
+ end
57
+
58
+ describe 'a DELETE request' do
59
+ before { delete '/' }
60
+
61
+ it { expect(last_response.body).to eq destination_response }
62
+ end
63
+
64
+ describe '#port_matches_scheme?' do
65
+ subject { forwarder.port_matches_scheme? request }
66
+ it { is_expected.to be false }
67
+
68
+ context 'default port' do
69
+ let(:destination_port) { 80 }
70
+ it { is_expected.to be true }
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,21 @@
1
+ require 'rspec'
2
+ require 'rack/test'
3
+ require 'rack/request_replication'
4
+
5
+ ENV['RACK_ENV'] ||= 'test'
6
+
7
+ require File.expand_path('../app/test_app.rb', __FILE__)
8
+
9
+ RSpec.configure do |config|
10
+ config.before :suite do
11
+ puts "Starting example applications on ports 4567 (source) and 4568 (destination)."
12
+ @dpid = Thread.new { DestApp.run! }
13
+ sleep 1
14
+ end
15
+
16
+ config.after :suite do
17
+ puts "Quitting example applications..."
18
+ #@spid.kill
19
+ @dpid.kill
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-request_replication
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Wouter de Vos
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-15 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.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack-test
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.5.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.5.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 0.5.5
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: 0.5.5
83
+ - !ruby/object:Gem::Dependency
84
+ name: sinatra
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.0.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: 1.0.0
111
+ description: Replicate or record HTTP requests to your Rack application and replay
112
+ them elsewhere or at another time.
113
+ email:
114
+ - wouter@springest.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - .gitignore
120
+ - Gemfile
121
+ - LICENSE.txt
122
+ - README.md
123
+ - Rakefile
124
+ - lib/rack/request_replication.rb
125
+ - lib/rack/request_replication/forwarder.rb
126
+ - lib/rack/request_replication/logger.rb
127
+ - lib/rack/request_replication/version.rb
128
+ - rack-request_replication.gemspec
129
+ - spec/app/test_app.rb
130
+ - spec/forwarder_spec.rb
131
+ - spec/spec_helper.rb
132
+ homepage: https://github.com/Springest/rack-request_replication
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.2.2
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Request replication MiddleWare for Rack.
156
+ test_files:
157
+ - spec/app/test_app.rb
158
+ - spec/forwarder_spec.rb
159
+ - spec/spec_helper.rb
160
+ has_rdoc: