rack-request_replication 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: