heroic-sns 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ZGJmMTYxNjFmNmJlZGFkM2E4ZDMzN2I0ODY1ODJmYzc5ZWMxNTAxZA==
5
- data.tar.gz: !binary |-
6
- ODU4NTFjMTM2MTBjZTY0MWMzNDNjZGZhYTQ1YjdhZTE0OWEwODAzOQ==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- ZTkxZjczZTVjODdjMjBlMTM0OWU2YjFmYzI4NzM1YzZmMWYzZWFlZjQ2YzMx
10
- ODM1ODk3ZmNlMDRmYjE4NmI3MWE4ZTVmM2RmNTgwZTc2YjMwMjEzZjRhNjEy
11
- NTVhMDZkZjlmN2IwYjkxNTdjM2M1Mzc2ODFjMzU3ZjhhZGRjZTg=
12
- data.tar.gz: !binary |-
13
- ZTY3YzJmZWRkZTE3OGE4ODRkNjgxNjMwMmYwYzRmZGQ4ODQ1MzE1YjMzZTcw
14
- ZmYzMTJkYzhhM2Y5MmQ2YTQ5ZGRlYmUwMWMwYTc5YWUwM2RhMGFiOGJkZmVh
15
- OWUyODNjYWE3Njg3Y2MyMzQ1ODI2N2Q0MGE4MmU5MTU0ODIzMDU=
2
+ SHA1:
3
+ metadata.gz: 11d6f41c3874ec698df32ac516c3d103f74bfd7b
4
+ data.tar.gz: 9dbf1f1690e3ec3888a52c661c4755c6d5c1a05e
5
+ SHA512:
6
+ metadata.gz: b0e8fe315fd65c499060616c8780586c427b1ed3ff3a829df29437fe17128d764cdc502f1eed9f450c7e09c068b80e65bf9fddce08253ab2ba1d6b4dc3d1062f
7
+ data.tar.gz: 20d5a1e66117f7c30e15d4107d93d6f84d8439dad5fd58919f46eb2e7bf0209067e47c048e3e7c040dbe365dfe8e18ddb4e33f16e6eb1898bb211b20b9028eac
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /capture
2
+ /html
2
3
  /pkg
3
4
  /tmp
4
5
  /Gemfile.lock
@@ -0,0 +1,21 @@
1
+ ### 1.1.1 (August 9, 2013)
2
+
3
+ * Ease up json gem dependency - [@speedmanly]
4
+ * Documentation cleaned up and tests improved.
5
+
6
+ ### 1.1.0 (June 20, 2013)
7
+
8
+ * Rework Cert logic - [@sbeckeriv]
9
+
10
+ ### 1.0.1 (May 17, 2013)
11
+
12
+ * Gem housekeeping, based on [advice by @dblock](http://code.dblock.org/your-first-ruby-gem)
13
+
14
+ ### 1.0.0 (May 5, 2013)
15
+
16
+ * Initial public release - [@benzado]
17
+
18
+ [@benzado]: http://github.com/benzado
19
+ [@sbeckeriv]: http://github.com/sbeckeriv
20
+ [@dblock]: http://github.com/dblock
21
+ [@speedmanly]: http://github.com/speedmanly
data/README.md CHANGED
@@ -3,21 +3,26 @@
3
3
  Heroic::SNS provides secure, lightweight Rack middleware for AWS Simple
4
4
  Notification Service (SNS) endpoints.
5
5
 
6
- Any SNS messages POSTed by Amazon to your web application are intercepted,
7
- parsed, verified, and then passed along via the `sns.message` environment key.
6
+ Any SNS messages POSTed by Amazon to your web application are
7
+ intercepted, parsed, verified, and then passed along via the
8
+ `sns.message` environment key.
8
9
 
9
- If something goes wrong, the error will be passed along via the `sns.error`
10
- environment key. `Heroic::SNS::Endpoint` does not log any messages itself.
10
+ If something goes wrong, the error will be passed along via the
11
+ `sns.error` environment key. `Heroic::SNS::Endpoint` does not log any
12
+ messages itself.
11
13
 
12
- **Heroic::SNS aims to be secure.** All message signatures are verified (to avoid
13
- forgeries) and stale messages are rejected (to avoid replay attacks).
14
+ **Heroic::SNS aims to be secure.** All message signatures are verified
15
+ (to avoid forgeries) and stale messages are rejected (to avoid replay
16
+ attacks).
14
17
 
15
- **Heroic::SNS aims to be lightweight.** Beside Ruby standard libraries there are
16
- no dependencies beside [rack][]. Specifically, Heroic::SNS *does not* depend on [aws-sdk][]. They will be friendly to each other, however, if you include both
17
- in a project.
18
+ **Heroic::SNS aims to be lightweight.** Beside Ruby standard libraries
19
+ there are no dependencies besides [json][] and [rack][]. Specifically,
20
+ Heroic::SNS *does not* depend on [aws-sdk][]. They will be friendly to
21
+ each other, however, if you include both in a project.
18
22
 
19
- [aws-sdk]: https://github.com/aws/aws-sdk-ruby
23
+ [json]: https://rubygems.org/gems/json
20
24
  [rack]: http://rack.github.io/
25
+ [aws-sdk]: https://github.com/aws/aws-sdk-ruby
21
26
 
22
27
  ## Overview
23
28
 
@@ -29,8 +34,8 @@ in a project.
29
34
 
30
35
  ## How to use it
31
36
 
32
- Once you have installed the gem, simply add the following to your `config.ru`
33
- file:
37
+ Once you have installed the gem, simply add the following to your
38
+ `config.ru` file:
34
39
 
35
40
  use Heroic::SNS::Endpoint, :topics => /:aws-ses-bounces$/
36
41
 
@@ -38,52 +43,64 @@ On Rails, you could also install it in `/config/initializers/sns_endpoint.rb`:
38
43
 
39
44
  Rails.application.config.middleware.use Heroic::SNS::Endpoint, :topic => ...
40
45
 
41
- The Endpoint class takes an options hash as an argument, and understands these
42
- options:
46
+ The Endpoint class takes an options hash as an argument, and understands
47
+ these options:
48
+
49
+ ### :topic
43
50
 
44
- `:topic` is required, and provides a filter that defines what SNS topics are
45
- handled by this endpoint. **A message is considered either "on-topic" or
46
- "off-topic".** You can supply any of the following:
51
+ `:topic` is required, and provides a filter that defines what SNS topics
52
+ are handled by this endpoint. **A message is considered either
53
+ "on-topic" or "off-topic".** You can supply any of the following:
47
54
 
48
55
  - a `String` containing a single topic ARN
56
+
49
57
  - an `Array` of `String` representing a list of topic ARNs
58
+
50
59
  - a `RegExp` which matches on-topic ARNs
51
- - a `Proc` which accepts an ARN as an argument and returns `true` or `false` for
52
- on-topic and off-topic ARNs, respectively.
60
+
61
+ - a `Proc` which accepts an ARN as an argument and returns `true` or
62
+ `false` for on-topic and off-topic ARNs, respectively.
53
63
 
54
64
  The key `:topics` is also supported.
55
65
 
66
+ ### :auto_confirm
67
+
56
68
  `:auto_confirm` affects how on-topic subscription confirmations are handled.
57
69
 
58
- - If `true`, they are confirmed by retrieving the URL in the `SubscribeURL`
59
- field of the SNS message, and your app is not notified.
60
- - If `false`, they are ignored; your app is also not notified.
61
- - If `nil`, there is no special handling and the message is passed along to your
62
- app.
63
-
64
- The default is `true`.
70
+ - If `true`, they are confirmed by retrieving the URL in the
71
+ `SubscribeURL` field of the SNS message, and your app is NOT notified.
72
+ This is the default.
73
+
74
+ - If `false`, they are ignored; your app is NOT notified.
75
+
76
+ - If `nil`, there is no special handling and the message is passed along
77
+ to your app.
78
+
79
+ ### :auto_resubscribe
65
80
 
66
81
  `:auto_resubscribe` affects how on-topic unsubscribe confirmations are handled.
67
82
 
68
- - If `true`, they topic is automatically re-subscribed by retrieving the URL in
69
- the `SubscribeURL` field of the SNS message, and your app is not notified.
70
- - If `false`, they are ignored and your app is also not notified.
71
- - If `nil`, there is no special handling and the message is passed along to your
72
- app.
83
+ - If `false`, they are ignored and your app is NOT notified. This is the
84
+ default.
85
+
86
+ - If `true`, they topic is automatically re-subscribed by retrieving the
87
+ URL in the `SubscribeURL` field of the SNS message, and your app is
88
+ NOT notified.
73
89
 
74
- The default is `false`.
90
+ - If `nil`, there is no special handling and the message is passed along
91
+ to your app.
75
92
 
76
- If you are a control-freak and want no special handling whatsoever, use these
77
- options:
93
+ If you are a control-freak and want no special handling whatsoever, use
94
+ these options:
78
95
 
79
96
  use Heroic::SNS::Endpoint, :topics => Proc.new { true }, :auto_confirm => nil, :auto_resubscribe => nil
80
97
 
81
- Then the object will simply parse and verify SNS messages it finds and pass them
82
- along to your app, taking no action.
98
+ Then the object will simply parse and verify SNS messages it finds and
99
+ pass them along to your app, taking no action.
83
100
 
84
- Once the middleware is set up, any notifications will be made available in your
85
- Rack environment under the `sns.message` key. If you are using Rails, your
86
- controller would have a method like this:
101
+ Once the middleware is set up, any notifications will be made available
102
+ in your Rack environment under the `sns.message` key. If you are using
103
+ Rails, your controller would have a method like this:
87
104
 
88
105
  skip_before_filter :verify_authenticity_token, :only => [:handle_notification]
89
106
 
@@ -98,43 +115,45 @@ controller would have a method like this:
98
115
  head :ok
99
116
  end
100
117
 
101
- You must skip the authenticity token verification to allow Amazon to POST to the
102
- controller action. Be careful not to disable it for more actions than you need.
103
- Be sure to disable any authentication checks for that action, too.
118
+ You must skip the authenticity token verification to allow Amazon to
119
+ POST to the controller action. Be careful not to disable it for more
120
+ actions than you need. Be sure to disable any authentication checks for
121
+ that action, too.
104
122
 
105
123
  ## Multiple endpoint URLs
106
124
 
107
- If you are receiving multiple notifications at multiple endpoint URLs, you
108
- should only include one instance of the Endpoint in your middleware stack, and
109
- ensure that its topic filter allows all the notifications you are interested in
110
- to pass through.
125
+ If you are receiving multiple notifications at multiple endpoint URLs,
126
+ you should only include one instance of the Endpoint in your middleware
127
+ stack, and ensure that its topic filter allows all the notifications you
128
+ are interested in to pass through.
111
129
 
112
130
  `Endpoint` does not interact with the URL path at all; if you want your
113
131
  subscriptions to go to different URLs, simply set them up that way.
114
132
 
115
133
  ## Off-topic notifications
116
134
 
117
- As a security measure, `Endpoint` requires you to set up a topic filter. Any
118
- notifications that do not match this filter are not passed along to your
119
- application.
135
+ As a security measure, `Endpoint` requires you to set up a topic filter.
136
+ Any notifications that do not match this filter are not passed along to
137
+ your application.
120
138
 
121
- All off-topic messages are ignored with one exception: if the message is a
122
- regular notification (meaning your app has an active subscription) *and* the
123
- message can be verified as authentic (by checking its signature), `Endpoint`
124
- will cancel the subscription by visiting the URL in the `UnsubscribeURL` field
125
- of the message.
139
+ All off-topic messages are ignored with one exception: if the message is
140
+ a regular notification (meaning your app has an active subscription)
141
+ *and* the message can be verified as authentic (by checking its
142
+ signature), `Endpoint` will cancel the subscription by visiting the URL
143
+ in the `UnsubscribeURL` field of the message.
126
144
 
127
- If you would rather make decision about on-topic and off-topic notifications in
128
- your own code, simply pass `Proc.new { true }` as the topic filter, and all
129
- messages will be treated as on topic. Be aware that it is dangerous to leave
130
- `:auto_confirm` enabled with a permissive topic filter, as this will allow
131
- anyone to subscribe your web app to any SNS notification.
145
+ If you would rather make decision about on-topic and off-topic
146
+ notifications in your own code, simply pass `Proc.new { true }` as the
147
+ topic filter, and all messages will be treated as on topic. Be aware
148
+ that it is dangerous to leave `:auto_confirm` enabled with a permissive
149
+ topic filter, as this will allow anyone to subscribe your web app to any
150
+ SNS notification.
132
151
 
133
152
  ## Contributing
134
153
 
135
154
  * Fork the project.
136
155
  * Make your feature addition or bug fix and include tests.
137
- * Update `CHANGELOG`.
156
+ * Update `CHANGELOG.md`.
138
157
  * Send a pull request.
139
158
 
140
159
  ## Copyright and License
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/gem_tasks'
3
3
  require 'rake/testtask'
4
+ require 'rdoc/task'
4
5
 
5
6
  Bundler.setup(:default, :development)
6
7
 
@@ -8,6 +9,11 @@ Rake::TestTask.new do |t|
8
9
  t.libs << 'test'
9
10
  end
10
11
 
12
+ RDoc::Task.new do |rdoc|
13
+ rdoc.main = "README.md"
14
+ rdoc.rdoc_files.include("README.md", "CHANGELOG.md", "lib")
15
+ end
16
+
11
17
  task :demo do
12
18
  sh 'rackup -Ilib demo/config.ru'
13
19
  end
@@ -26,6 +26,7 @@ EOD
26
26
 
27
27
  s.platform = Gem::Platform::RUBY
28
28
  s.required_ruby_version = '>= 1.8.7'
29
+ s.add_development_dependency 'rdoc', '~> 4.0'
29
30
  s.add_runtime_dependency 'rack', '~> 1.4'
30
- s.add_runtime_dependency 'json', '~> 1.7.7'
31
+ s.add_runtime_dependency 'json', '~> 1.7'
31
32
  end
@@ -3,12 +3,12 @@ module Heroic
3
3
  # This LRU Cache is a generic key-value store that is designed to be safe for
4
4
  # concurrent access. It uses a doubly-linked-list to identify which item was
5
5
  # least recently retrieved, and a Hash for fast retrieval by key.
6
-
6
+ #
7
7
  # To support concurrent access, it uses two levels of locks: a cache-level lock
8
8
  # is used to locate or create the desired node and move it to the front of the
9
9
  # LRU list. A node-level lock is used to synchronize access to the node's
10
10
  # value.
11
-
11
+ #
12
12
  # If a thread is busy generating a value to be stored in the cache, other
13
13
  # threads will still be able to read and write to other keys with no conflict.
14
14
  # However, if a second thread tries to read the value that the first thread is
@@ -16,24 +16,6 @@ module Heroic
16
16
 
17
17
  class LRUCache
18
18
 
19
- class Node
20
- attr_reader :key
21
- attr_accessor :left, :right
22
- def initialize(key)
23
- @key = key
24
- @lock = Mutex.new
25
- end
26
- def read
27
- @lock.synchronize { @value ||= yield(@key) }
28
- end
29
- def write(value)
30
- @lock.synchronize { @value = value }
31
- end
32
- def to_s
33
- sprintf '<Node:%x(%s)>', self.object_id, @key.inspect
34
- end
35
- end
36
-
37
19
  # If you yield a block to the constructor, it will be called on every cache
38
20
  # miss to generate the needed value. This is optional but recommended, as
39
21
  # the block will run while holding a lock on the cache node associated with
@@ -53,25 +35,34 @@ module Heroic
53
35
  @rightmost = nil
54
36
  end
55
37
 
38
+ # Retrieve a value from the cache for a given key. If the value is in the
39
+ # cache, the method will return immediately. If the value is not in the
40
+ # cache and a block was provided to the constructor, it will be invoked to
41
+ # generate the value and insert it into the cache. If another thread is in
42
+ # the process of generating the same value, the current thread will wait for
43
+ # it to complete.
44
+ #
45
+ # If the cache is full, this method may cause the least recently used item
46
+ # to be evicted from the cache.
47
+
56
48
  def get(key)
57
49
  node = node_for_key(key)
58
50
  node.read(&@block)
59
51
  end
60
52
 
53
+ # Inserts a value into the cache, assigning it to the given key. (Instead of
54
+ # directly inserting values into the cache, it is recommended that you supply
55
+ # a block to the constructor to generate values on demand.)
56
+
61
57
  def put(key, value)
62
58
  node = node_for_key(key)
63
59
  node.write(value)
64
60
  end
65
61
 
66
- def empty!
67
- @lock.synchronize do
68
- @store.empty!
69
- @leftmost = nil
70
- @rightmost = nil
71
- end
72
- end
62
+ # Verify the data structures used to maintain the cache. If a problem is
63
+ # detected, an exception is raised. This method is intended for testing and
64
+ # debugging only.
73
65
 
74
- # Verify the list structure. Intended for testing and debugging only.
75
66
  def verify!
76
67
  @lock.synchronize do
77
68
  left_to_right = Array.new
@@ -111,6 +102,24 @@ module Heroic
111
102
 
112
103
  private
113
104
 
105
+ class Node # :nodoc:
106
+ attr_reader :key
107
+ attr_accessor :left, :right
108
+ def initialize(key)
109
+ @key = key
110
+ @lock = Mutex.new
111
+ end
112
+ def read
113
+ @lock.synchronize { @value ||= yield(@key) }
114
+ end
115
+ def write(value)
116
+ @lock.synchronize { @value = value }
117
+ end
118
+ def to_s
119
+ sprintf '<Node:%x(%s)>', self.object_id, @key.inspect
120
+ end
121
+ end
122
+
114
123
  def node_for_key(key)
115
124
  @lock.synchronize do
116
125
  node = @store[key]
@@ -1,38 +1,54 @@
1
1
  require 'openssl'
2
2
  require 'open-uri'
3
3
 
4
- # Heroic::SNS::Endpoint is Rack middleware which intercepts messages from
5
- # Amazon's Simple Notification Service (SNS). It makes the parsed and verified
6
- # message available to your application in the Rack environment under the
7
- # 'sns.message' key. If an error occurred during message handling, the error
8
- # is available in the Rack environment in the 'sns.error' key.
9
-
10
- # Endpoint is to be initialized with a hash of options. It understands three
11
- # different options:
12
- # - :topic (or :topics) specifies a filter that defines what SNS topics are
13
- # handled by this endpoint ("on-topic"). You can supply any of the following:
14
- # - a topic ARN as a String
15
- # - a list of topic ARNs as an Array of Strings
16
- # - a regular expression matching on-topic ARNs
17
- # - a Proc which takes a topic ARN as a String and returns true or false.
18
- # You must specify a topic filter. Use Proc.new { true } if you insist on
19
- # indiscriminately accepting all notifications.
20
- # - :auto_confirm determines how SubscriptionConfirmation messages are handled.
21
- # If true, the subscription is confirmed and your app is not notified.
22
- # If false, the subscription is ignored and your app is not notified.
23
- # If nil, the message is passed along to your app.
24
-
25
- # You can install this in your config.ru:
26
- # use Heroic::SNS::Endpoint, :topics => /whatever/
27
-
28
- # For Rails, you can also install it in /config/initializers/sns_endpoint.rb:
29
- # Rails.application.config.middleware.use Heroic::SNS::Endpoint, :topic => ...
30
-
31
4
  module Heroic
32
5
  module SNS
33
6
 
34
7
  SUBSCRIPTION_ARN_HTTP_HEADER = 'HTTP_X_AMZ_SNS_SUBSCRIPTION_ARN'
35
8
 
9
+ =begin rdoc
10
+
11
+ Heroic::SNS::Endpoint is Rack middleware which intercepts messages from
12
+ Amazon's Simple Notification Service (SNS). It makes the parsed and
13
+ verified message available to your application in the Rack environment
14
+ under the 'sns.message' key. If an error occurred during message handling,
15
+ the error is available in the Rack environment in the 'sns.error' key.
16
+
17
+ Endpoint is to be initialized with a hash of options. It understands three
18
+ different options:
19
+
20
+ +:topic+ (or +:topics+) specifies a filter that defines what SNS topics
21
+ are handled by this endpoint ("on-topic"). You can supply any of the
22
+ following:
23
+ - a topic ARN as a String
24
+ - a list of topic ARNs as an Array of Strings
25
+ - a regular expression matching on-topic ARNs
26
+ - a Proc which takes a topic ARN as a String and returns true or false.
27
+ You *must* specify a topic filter. Use <code>Proc.new{true}</code> if
28
+ you insist on indiscriminately accepting all notifications.
29
+
30
+ +:auto_confirm+ determines how SubscriptionConfirmation messages are handled.
31
+ - If true, the subscription is confirmed and your app is not notified.
32
+ This is the default.
33
+ - If false, the subscription is ignored and your app is not notified.
34
+ - If nil, the message is passed along to your app.
35
+
36
+ +:auto_resubscribe+ affects how on-topic UnsubscribeConfirmation messages are handled.
37
+ - If false, they are ignored and your app is also not notified.
38
+ This is the default.
39
+ - If true, they topic is automatically re-subscribed by retrieving the URL in
40
+ the `SubscribeURL` field of the SNS message, and your app is not notified.
41
+ - If nil, there is no special handling and the message is passed along to your
42
+ app.
43
+
44
+ You can install this in your config.ru:
45
+ use Heroic::SNS::Endpoint, :topics => /whatever/
46
+
47
+ For Rails, you can also install it in /config/initializers/sns_endpoint.rb:
48
+ Rails.application.config.middleware.use Heroic::SNS::Endpoint, :topic => ...
49
+
50
+ =end
51
+
36
52
  class Endpoint
37
53
 
38
54
  DEFAULT_OPTIONS = { :auto_confirm => true, :auto_resubscribe => false }
@@ -19,7 +19,10 @@ module Heroic
19
19
  end
20
20
  end
21
21
 
22
- # Encapsulates an SNS message.
22
+ # Encapsulates an SNS message. Since Endpoint takes care of authenticating
23
+ # the message, most of the time you will simply be interested in retrieving
24
+ # the +subject+ and +body+ from the message and acting on it.
25
+ #
23
26
  # See: http://docs.aws.amazon.com/sns/latest/gsg/json-formats.html
24
27
  class Message
25
28
 
@@ -29,6 +32,8 @@ module Heroic
29
32
  raise Error.new("failed to parse message as JSON: #{e.message}")
30
33
  end
31
34
 
35
+ # The message type will be one of +SubscriptionConfirmation+,
36
+ # +UnsubscribeConfirmation+, and +Notification+.
32
37
  def type
33
38
  @msg['Type']
34
39
  end
@@ -37,11 +42,14 @@ module Heroic
37
42
  @msg['TopicArn']
38
43
  end
39
44
 
45
+ # A Universally Unique Identifier, unique for each message published. For
46
+ # a notification that Amazon SNS resends during a retry, the message ID of
47
+ # the original message is used.
40
48
  def id
41
49
  @msg['MessageId']
42
50
  end
43
51
 
44
- # The timestamp as a Time object.
52
+ # The timestamp when the message was published, as a Time object.
45
53
  def timestamp
46
54
  Time.xmlschema(@msg['Timestamp'])
47
55
  end
@@ -64,6 +72,9 @@ module Heroic
64
72
  @msg['Subject']
65
73
  end
66
74
 
75
+ # The message payload. As far as Amazon and this class are concerned, the
76
+ # message payload is just a string of bytes. If you are expecting, for
77
+ # example, a JSON object, you will need to pass this to a JSON parser.
67
78
  def body
68
79
  @msg['Message']
69
80
  end
@@ -77,13 +88,13 @@ module Heroic
77
88
  end
78
89
 
79
90
  # The token is used to confirm subscriptions via the SNS API. If you visit
80
- # the :subscribe_url, you can ignore this field.
91
+ # the +subscribe_url+, you can ignore this field.
81
92
  def token
82
93
  @msg['Token']
83
94
  end
84
95
 
85
- def ==(other_message)
86
- @msg == other_message.instance_variable_get(:@msg)
96
+ def ==(other)
97
+ other.is_a?(Message) && @msg == other.instance_variable_get(:@msg)
87
98
  end
88
99
 
89
100
  def hash
@@ -98,11 +109,14 @@ module Heroic
98
109
  string << ">"
99
110
  end
100
111
 
112
+ # Returns a JSON serialization of the message. Note that it may not be
113
+ # identical to the serialization that was retrieved from the network.
101
114
  def to_json
102
115
  @msg.to_json
103
116
  end
104
117
 
105
- # Verifies the message signature. Raises an exception if it is not valid.
118
+ # Verifies the message signature. Raises Error if it is not valid.
119
+ #
106
120
  # See: http://docs.aws.amazon.com/sns/latest/gsg/SendMessageToHttp.verify.signature.html
107
121
  def verify!
108
122
  age = Time.now - timestamp
@@ -1,5 +1,5 @@
1
1
  module Heroic
2
2
  module SNS
3
- VERSION = '1.1.0'
3
+ VERSION = '1.1.1'
4
4
  end
5
5
  end
@@ -14,12 +14,12 @@ class LRUCacheTest < Test::Unit::TestCase
14
14
 
15
15
  def test_get_put
16
16
  cache = Heroic::LRUCache.new(1)
17
- assert_nil cache.get(:foo)
18
- cache.put(:foo, :bar)
19
- assert_equal :bar, cache.get(:foo)
20
- cache.put(:answer, 42)
21
- assert_equal 42, cache.get(:answer)
22
- assert_nil cache.get(:foo)
17
+ assert_nil cache.get(:casey)
18
+ cache.put(:casey, :jones)
19
+ assert_equal :jones, cache.get(:casey)
20
+ cache.put(:april, :oneil)
21
+ assert_equal :oneil, cache.get(:april)
22
+ assert_nil cache.get(:casey)
23
23
  end
24
24
 
25
25
  def test_exceptions
@@ -32,41 +32,41 @@ class LRUCacheTest < Test::Unit::TestCase
32
32
  end
33
33
  end
34
34
  assert_raises RuntimeError do
35
- cache.get(:foo)
35
+ cache.get(:ooze)
36
36
  end
37
37
  @should_throw = false
38
- assert_equal 4, cache.get(:foo)
38
+ assert_equal 4, cache.get(:ooze)
39
39
  end
40
40
 
41
41
  def test_dynamic
42
42
  @counter = 0
43
- cache = Heroic::LRUCache.new(3) { |k| @counter += 1; "hello, #{k}." }
43
+ cache = Heroic::LRUCache.new(3) { |k| @counter += 1; k.to_s.length }
44
44
  assert_equal 0, @counter
45
45
  cache.verify!
46
- assert_equal "hello, leo.", cache.get(:leo); assert_equal 1, @counter
46
+ assert_equal 3, cache.get(:leo); assert_equal 1, @counter
47
47
  cache.verify!
48
- assert_equal "hello, leo.", cache.get(:leo); assert_equal 1, @counter
48
+ assert_equal 3, cache.get(:leo); assert_equal 1, @counter
49
49
  cache.verify!
50
- assert_equal "hello, donnie.", cache.get(:donnie); assert_equal 2, @counter
50
+ assert_equal 6, cache.get(:donnie); assert_equal 2, @counter
51
51
  cache.verify!
52
- assert_equal "hello, donnie.", cache.get(:donnie); assert_equal 2, @counter
52
+ assert_equal 6, cache.get(:donnie); assert_equal 2, @counter
53
53
  cache.verify!
54
- assert_equal "hello, mikey.", cache.get(:mikey); assert_equal 3, @counter
54
+ assert_equal 5, cache.get(:mikey); assert_equal 3, @counter
55
55
  cache.verify!
56
- assert_equal "hello, mikey.", cache.get(:mikey); assert_equal 3, @counter
56
+ assert_equal 5, cache.get(:mikey); assert_equal 3, @counter
57
57
  cache.verify!
58
58
  # raph will push leo out of cache
59
- assert_equal "hello, raph.", cache.get(:raph); assert_equal 4, @counter
59
+ assert_equal 4, cache.get(:raph); assert_equal 4, @counter
60
60
  cache.verify!
61
- assert_equal "hello, raph.", cache.get(:raph); assert_equal 4, @counter
61
+ assert_equal 4, cache.get(:raph); assert_equal 4, @counter
62
62
  cache.verify!
63
63
  # mikey and donnie remain in cache
64
- assert_equal "hello, mikey.", cache.get(:mikey); assert_equal 4, @counter
64
+ assert_equal 5, cache.get(:mikey); assert_equal 4, @counter
65
65
  cache.verify!
66
- assert_equal "hello, donnie.", cache.get(:donnie); assert_equal 4, @counter
66
+ assert_equal 6, cache.get(:donnie); assert_equal 4, @counter
67
67
  cache.verify!
68
68
  # leo will have to be refetched
69
- assert_equal "hello, leo.", cache.get(:leo); assert_equal 5, @counter
69
+ assert_equal 3, cache.get(:leo); assert_equal 5, @counter
70
70
  cache.verify!
71
71
  end
72
72
 
@@ -76,35 +76,34 @@ class LRUCacheTest < Test::Unit::TestCase
76
76
  cache = Heroic::LRUCache.new(100) do |k|
77
77
  sleep 1 # simulate slow generation, such as network I/O
78
78
  @lock.synchronize { @counter += 1 }
79
- k.to_s
79
+ k.to_s.length
80
80
  end
81
81
  # load the cache with things to read
82
- cache.put(:a, 'a')
83
- cache.put(:b, 'b')
84
- cache.put(:c, 'c')
82
+ cache.put(:leo, 0)
83
+ cache.put(:donnie, 0)
85
84
  start_time = Time.now
86
- # start fetch in background
87
- td = Thread.new do
88
- cache.get(:d)
85
+ # Start threads to fetch in background. :leo and :donnie should return
86
+ # immediately, because the values are in the cache; :mikey and :raph should
87
+ # run concurrently; the second :raph should wait on the first :raph to
88
+ # complete.
89
+ threads = [:leo, :donnie, :mikey, :raph, :raph].map do |k|
90
+ Thread.new { cache.get(k) }
89
91
  end
90
- te = Thread.new do
91
- cache.get(:e)
92
- end
93
- # while background threads fetch, reading other keys should not be blocked
94
- assert_equal 'a', cache.get(:a)
95
- assert_equal 'b', cache.get(:b)
96
- assert_equal 'c', cache.get(:c)
92
+ # Fetching values already in the cache should not block.
93
+ assert_equal 0, cache.get(:leo)
94
+ assert_equal 0, cache.get(:donnie)
97
95
  assert_equal 0, @lock.synchronize { @counter }
98
- # now main thread should block until background items are fetched
99
- assert_equal "d", cache.get(:d)
100
- assert_equal "e", cache.get(:e)
96
+ # Fetching values being computed now will block until somebody computes them.
97
+ assert_equal 5, cache.get(:mikey)
98
+ assert_equal 4, cache.get(:raph)
101
99
  assert_equal 2, @lock.synchronize { @counter }
102
- # reading the same values should be fast (they are cached)
103
- assert_equal "d", cache.get(:d)
104
- assert_equal "e", cache.get(:e)
100
+ # Fetching those same values should not trigger a recompute.
101
+ assert_equal 5, cache.get(:mikey)
102
+ assert_equal 4, cache.get(:raph)
105
103
  assert_equal 2, @lock.synchronize { @counter }
106
- # look at time elapsed to make sure we didn't sleep more than once
107
- [td, te].each { |t| t.join }
104
+ # Let's wait for all threads to finish, then check the clock to make sure we
105
+ # only slept for a second.
106
+ threads.each { |t| t.join }
108
107
  time_elapsed = (Time.now - start_time)
109
108
  assert_in_delta time_elapsed, 1.0, 0.01
110
109
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heroic-sns
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Ragheb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-25 00:00:00.000000000 Z
11
+ date: 2013-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rack
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -30,29 +44,21 @@ dependencies:
30
44
  requirements:
31
45
  - - ~>
32
46
  - !ruby/object:Gem::Version
33
- version: 1.7.7
47
+ version: '1.7'
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - ~>
39
53
  - !ruby/object:Gem::Version
40
- version: 1.7.7
41
- description: ! 'Secure, lightweight Rack middleware for Amazon Simple Notification
42
- Service (SNS)
43
-
54
+ version: '1.7'
55
+ description: |
56
+ Secure, lightweight Rack middleware for Amazon Simple Notification Service (SNS)
44
57
  endpoints. SNS messages are intercepted, parsed, verified, and then passed along
45
-
46
- to the web application via the ''sns.message'' environment key. Heroic::SNS has
47
- no
48
-
58
+ to the web application via the 'sns.message' environment key. Heroic::SNS has no
49
59
  dependencies besides Rack (specifically, the aws-sdk gem is not needed).
50
-
51
60
  SNS message signatures are verified in order to reject forgeries and replay
52
-
53
61
  attacks.
54
-
55
- '
56
62
  email: ben@benzado.com
57
63
  executables: []
58
64
  extensions: []
@@ -60,7 +66,7 @@ extra_rdoc_files: []
60
66
  files:
61
67
  - .gitignore
62
68
  - .travis.yml
63
- - CHANGELOG
69
+ - CHANGELOG.md
64
70
  - Gemfile
65
71
  - LICENSE
66
72
  - README.md
@@ -93,12 +99,12 @@ require_paths:
93
99
  - lib
94
100
  required_ruby_version: !ruby/object:Gem::Requirement
95
101
  requirements:
96
- - - ! '>='
102
+ - - '>='
97
103
  - !ruby/object:Gem::Version
98
104
  version: 1.8.7
99
105
  required_rubygems_version: !ruby/object:Gem::Requirement
100
106
  requirements:
101
- - - ! '>='
107
+ - - '>='
102
108
  - !ruby/object:Gem::Version
103
109
  version: '0'
104
110
  requirements: []
data/CHANGELOG DELETED
@@ -1,13 +0,0 @@
1
- ### 1.1.0 (June 20, 2013)
2
-
3
- * Rework Cert logic - [@sbeckeriv]
4
-
5
- ### 1.0.1 (May 17, 2013)
6
-
7
- * Gem housekeeping, based on [advice by dblock](http://code.dblock.org/your-first-ruby-gem)
8
-
9
- ### 1.0.0 (May 5, 2013)
10
-
11
- * Initial public release - [@benzado]
12
-
13
- [@benzado]: http://github.com/benzado