rack-request_replication 0.0.3 → 0.0.5
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 +4 -4
- data/README.md +49 -4
- data/lib/rack/request_replication.rb +0 -1
- data/lib/rack/request_replication/forwarder.rb +61 -1
- data/lib/rack/request_replication/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6ae7943af00005747ede7913cb2a8ec935ac80e
|
4
|
+
data.tar.gz: 469f4eda7bd661a49dd74ac05f57a7085c0e871e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 513b0e735a59e0acade09e128779bd4a132613e809fb7a1d5db9decb2d51f0e964b70776e05489dc77d24a20a004c4252a56dda915107e002c9a91f2f64d0f87
|
7
|
+
data.tar.gz: 467c2ba93881da20eea3d9567eb21039b59f87d3eacb409c27fc0ad86b8c1af014644ac12a4bec00282d9b6d026d1b44bdade62a08f4ad3f077e04ce32c08c0f
|
data/README.md
CHANGED
@@ -1,17 +1,62 @@
|
|
1
1
|
# Rack::RequestReplication - Replicate Rack app HTTP requests
|
2
2
|
|
3
3
|
Replicate requests from one app instance to another. At
|
4
|
-
[Springest](http://www.springest.com) we
|
5
|
-
|
6
|
-
|
4
|
+
[Springest](http://www.springest.com) we used
|
5
|
+
[Gor](https://github.com/buger/gor) once to test our new Postgres stack
|
6
|
+
vs our at that time current MySQL stack.
|
7
|
+
We replicated all live requests to our staging environment to test new
|
8
|
+
code before it went live. With real traffic!
|
7
9
|
|
8
|
-
|
10
|
+
Unfortunately, we could not test everything we wanted with the
|
11
|
+
[setup with Gor](http://devblog.springest.com/testing-big-infrastructure-changes-at-springest/).
|
12
|
+
Stuff that relied on sessions (like
|
13
|
+
[MySpringest](https://www.springest.com/my-springest), and course
|
14
|
+
management through our [Admin panel](http://providers.springest.com/))
|
15
|
+
could not be tested properly because the staging environment did not
|
16
|
+
share sessions with the production stack.
|
17
|
+
|
18
|
+
Recently, @foxycoder was asked to give a talk about these adventures at
|
19
|
+
[Amsterdam.rb](http://www.meetup.com/Amsterdam-rb/events/206133762/).
|
20
|
+
And while we were thinking about all the stuff that we needed to do to
|
21
|
+
get it right with Gor, we came up with the concept of this gem.
|
22
|
+
|
23
|
+
## Full control over the requests through Rack
|
24
|
+
|
25
|
+
This is Rack MiddleWare. And thanks to that, we have all the information
|
26
|
+
and handy tools available to parse and alter request data before we
|
27
|
+
forward it to another stack.
|
28
|
+
|
29
|
+
|
30
|
+
## Features
|
31
|
+
|
32
|
+
At the moment, it just forwards the request with only a couple of
|
33
|
+
changes:
|
34
|
+
|
35
|
+
- The host and/or port to match the stack to forward to.
|
36
|
+
- The session cookie – it stores a persistent link between the source
|
37
|
+
app's session and the destination app's session in Redis.
|
38
|
+
- The CSRF token – same as the session, the destination app's
|
39
|
+
`authenticity_token` is persistent and used in consecutive requests.
|
40
|
+
|
41
|
+
### Session support
|
9
42
|
|
10
43
|
It has support for sessions. To make use of it, you need to have Redis
|
11
44
|
running. Redis serves as a key-value store where sessions from the
|
12
45
|
Source App are linked to sessions from the Forward App. This way both
|
13
46
|
apps can have their own session management.
|
14
47
|
|
48
|
+
### Rails's CSRF tokens
|
49
|
+
|
50
|
+
Rails uses a cross site scripting defense mechanism in the form of an
|
51
|
+
`authenticity_token` parameter. Absense of it, or modifying it between
|
52
|
+
requests results in XSS errors. Therefore we needed to make sure these
|
53
|
+
were properly captured and replaced by the Forwarder before sending the
|
54
|
+
request to the other application.
|
55
|
+
|
56
|
+
CSRF tokens are also persisted in Redis and used in consecutive requests, by
|
57
|
+
updating `params["authenticity_token"]` before handing off the request
|
58
|
+
to the replica app.
|
59
|
+
|
15
60
|
## API Docs
|
16
61
|
|
17
62
|
Check out the official [API docs](http://rubydoc.info/gems/rack-request_replication)
|
@@ -67,13 +67,68 @@ module Rack
|
|
67
67
|
Thread.new do
|
68
68
|
begin
|
69
69
|
forward_request.add_field("Cookie", cookies( request ))
|
70
|
-
|
70
|
+
update_csrf_token_and_cookies( request, http.request(forward_request) )
|
71
71
|
rescue => e
|
72
72
|
logger.debug "Replicating request failed with: #{e.message}"
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
+
##
|
78
|
+
# Update CSRF token and cookies.
|
79
|
+
#
|
80
|
+
# @param [Rack::Request] request
|
81
|
+
# @param [Net::HTTP::Response] response
|
82
|
+
#
|
83
|
+
def update_csrf_token_and_cookies( request, response )
|
84
|
+
update_csrf_token( request, response )
|
85
|
+
update_cookies( request, response )
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# The CSRF-token to use.
|
90
|
+
#
|
91
|
+
# @param [Rack::Request] request
|
92
|
+
# @returns [String]
|
93
|
+
#
|
94
|
+
def csrf_token( request )
|
95
|
+
token = request.params["authenticity_token"]
|
96
|
+
return if token.nil?
|
97
|
+
|
98
|
+
redis.get( "csrf-#{token}" ) || token
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Update CSRF token to bypass XSS errors in Rails.
|
103
|
+
#
|
104
|
+
# @param [Rack::Request] request
|
105
|
+
#
|
106
|
+
def update_csrf_token( request, response )
|
107
|
+
token = request.params["authenticity_token"]
|
108
|
+
return if token.nil?
|
109
|
+
|
110
|
+
response_token = csrf_token_from response
|
111
|
+
return token if response_token.nil?
|
112
|
+
|
113
|
+
redis.set "csrf-#{token}", response_token
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# Pull CSRF token from the HTML document's header.
|
118
|
+
#
|
119
|
+
# @param [Net::HTTP::Response] response
|
120
|
+
# @returns [String]
|
121
|
+
#
|
122
|
+
def csrf_token_from( response )
|
123
|
+
response.split("\n").
|
124
|
+
select{|l| l.match(/csrf-token/) }.
|
125
|
+
first.split(" ").
|
126
|
+
select{|t| t.match(/^content=/)}.first.
|
127
|
+
match(/content="(.*)"/)[1]
|
128
|
+
rescue
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
|
77
132
|
##
|
78
133
|
# Update cookies from the forwarded request using the session id
|
79
134
|
# from the cookie of the source app as a key. The cookie is stored
|
@@ -234,6 +289,11 @@ module Rack
|
|
234
289
|
value = request.send( m )
|
235
290
|
replicated_options[m] = value unless value.nil?
|
236
291
|
end
|
292
|
+
|
293
|
+
if replicated_options[:params]["authenticity_token"]
|
294
|
+
replicated_options[:params]["authenticity_token"] = csrf_token
|
295
|
+
end
|
296
|
+
|
237
297
|
replicated_options
|
238
298
|
end
|
239
299
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-request_replication
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter de Vos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|