scout 5.7.1 → 5.7.2.pre

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.
Files changed (251) hide show
  1. data/CHANGELOG.markdown +5 -0
  2. data/lib/scout/command/run.rb +1 -1
  3. data/lib/scout/server.rb +2 -1
  4. data/lib/scout/streamer.rb +3 -2
  5. data/lib/scout/streamer_daemon.rb +4 -4
  6. data/lib/scout/version.rb +1 -1
  7. data/test/scout_test.rb +7 -8
  8. data/vendor/httpclient/README.txt +759 -0
  9. data/vendor/httpclient/bin/httpclient +65 -0
  10. data/vendor/httpclient/lib/hexdump.rb +50 -0
  11. data/vendor/httpclient/lib/http-access2.rb +55 -0
  12. data/vendor/httpclient/lib/http-access2/cookie.rb +1 -0
  13. data/vendor/httpclient/lib/http-access2/http.rb +1 -0
  14. data/vendor/httpclient/lib/httpclient.rb +1156 -0
  15. data/vendor/httpclient/lib/httpclient/auth.rb +899 -0
  16. data/vendor/httpclient/lib/httpclient/cacert.p7s +1912 -0
  17. data/vendor/httpclient/lib/httpclient/connection.rb +88 -0
  18. data/vendor/httpclient/lib/httpclient/cookie.rb +438 -0
  19. data/vendor/httpclient/lib/httpclient/http.rb +1046 -0
  20. data/vendor/httpclient/lib/httpclient/include_client.rb +83 -0
  21. data/vendor/httpclient/lib/httpclient/session.rb +1025 -0
  22. data/vendor/httpclient/lib/httpclient/ssl_config.rb +403 -0
  23. data/vendor/httpclient/lib/httpclient/timeout.rb +140 -0
  24. data/vendor/httpclient/lib/httpclient/util.rb +178 -0
  25. data/vendor/httpclient/lib/httpclient/version.rb +3 -0
  26. data/vendor/httpclient/lib/oauthclient.rb +110 -0
  27. data/vendor/httpclient/sample/async.rb +8 -0
  28. data/vendor/httpclient/sample/auth.rb +11 -0
  29. data/vendor/httpclient/sample/cookie.rb +18 -0
  30. data/vendor/httpclient/sample/dav.rb +103 -0
  31. data/vendor/httpclient/sample/howto.rb +49 -0
  32. data/vendor/httpclient/sample/oauth_buzz.rb +57 -0
  33. data/vendor/httpclient/sample/oauth_friendfeed.rb +59 -0
  34. data/vendor/httpclient/sample/oauth_salesforce_10.rb +63 -0
  35. data/vendor/httpclient/sample/oauth_twitter.rb +61 -0
  36. data/vendor/httpclient/sample/ssl/0cert.pem +22 -0
  37. data/vendor/httpclient/sample/ssl/0key.pem +30 -0
  38. data/vendor/httpclient/sample/ssl/1000cert.pem +19 -0
  39. data/vendor/httpclient/sample/ssl/1000key.pem +18 -0
  40. data/vendor/httpclient/sample/ssl/htdocs/index.html +10 -0
  41. data/vendor/httpclient/sample/ssl/ssl_client.rb +22 -0
  42. data/vendor/httpclient/sample/ssl/webrick_httpsd.rb +29 -0
  43. data/vendor/httpclient/sample/stream.rb +21 -0
  44. data/vendor/httpclient/sample/thread.rb +27 -0
  45. data/vendor/httpclient/sample/wcat.rb +21 -0
  46. data/vendor/httpclient/test/ca-chain.cert +44 -0
  47. data/vendor/httpclient/test/ca.cert +23 -0
  48. data/vendor/httpclient/test/client.cert +19 -0
  49. data/vendor/httpclient/test/client.key +15 -0
  50. data/vendor/httpclient/test/helper.rb +129 -0
  51. data/vendor/httpclient/test/htdigest +1 -0
  52. data/vendor/httpclient/test/htpasswd +2 -0
  53. data/vendor/httpclient/test/runner.rb +2 -0
  54. data/vendor/httpclient/test/server.cert +19 -0
  55. data/vendor/httpclient/test/server.key +15 -0
  56. data/vendor/httpclient/test/sslsvr.rb +65 -0
  57. data/vendor/httpclient/test/subca.cert +21 -0
  58. data/vendor/httpclient/test/test_auth.rb +321 -0
  59. data/vendor/httpclient/test/test_cookie.rb +391 -0
  60. data/vendor/httpclient/test/test_hexdump.rb +14 -0
  61. data/vendor/httpclient/test/test_http-access2.rb +507 -0
  62. data/vendor/httpclient/test/test_httpclient.rb +1783 -0
  63. data/vendor/httpclient/test/test_include_client.rb +52 -0
  64. data/vendor/httpclient/test/test_ssl.rb +235 -0
  65. data/vendor/multi_json/.document +5 -0
  66. data/vendor/multi_json/.rspec +3 -0
  67. data/vendor/multi_json/.travis.yml +11 -0
  68. data/vendor/multi_json/.yardopts +6 -0
  69. data/vendor/multi_json/CHANGELOG.md +169 -0
  70. data/vendor/multi_json/CONTRIBUTING.md +46 -0
  71. data/vendor/multi_json/Gemfile +31 -0
  72. data/vendor/multi_json/LICENSE.md +20 -0
  73. data/vendor/multi_json/README.md +109 -0
  74. data/vendor/multi_json/Rakefile +12 -0
  75. data/vendor/multi_json/lib/multi_json.rb +157 -0
  76. data/vendor/multi_json/lib/multi_json/adapter.rb +48 -0
  77. data/vendor/multi_json/lib/multi_json/adapters/gson.rb +19 -0
  78. data/vendor/multi_json/lib/multi_json/adapters/jr_jackson.rb +19 -0
  79. data/vendor/multi_json/lib/multi_json/adapters/json_common.rb +25 -0
  80. data/vendor/multi_json/lib/multi_json/adapters/json_gem.rb +11 -0
  81. data/vendor/multi_json/lib/multi_json/adapters/json_pure.rb +11 -0
  82. data/vendor/multi_json/lib/multi_json/adapters/nsjsonserialization.rb +34 -0
  83. data/vendor/multi_json/lib/multi_json/adapters/oj.rb +24 -0
  84. data/vendor/multi_json/lib/multi_json/adapters/ok_json.rb +22 -0
  85. data/vendor/multi_json/lib/multi_json/adapters/yajl.rb +19 -0
  86. data/vendor/multi_json/lib/multi_json/convertible_hash_keys.rb +43 -0
  87. data/vendor/multi_json/lib/multi_json/load_error.rb +11 -0
  88. data/vendor/multi_json/lib/multi_json/options.rb +48 -0
  89. data/vendor/multi_json/lib/multi_json/vendor/okjson.rb +606 -0
  90. data/vendor/multi_json/lib/multi_json/version.rb +20 -0
  91. data/vendor/multi_json/multi_json.gemspec +22 -0
  92. data/vendor/multi_json/spec/adapter_shared_example.rb +235 -0
  93. data/vendor/multi_json/spec/has_options.rb +119 -0
  94. data/vendor/multi_json/spec/helper.rb +35 -0
  95. data/vendor/multi_json/spec/json_common_shared_example.rb +30 -0
  96. data/vendor/multi_json/spec/multi_json_spec.rb +226 -0
  97. data/vendor/{signature → pusher}/.document +0 -0
  98. data/vendor/{json_pure/diagrams/.keep → pusher/.gemtest} +0 -0
  99. data/vendor/pusher/.gitignore +23 -0
  100. data/vendor/pusher/.travis.yml +15 -0
  101. data/vendor/pusher/Gemfile +2 -0
  102. data/vendor/{pusher-gem → pusher}/LICENSE +1 -1
  103. data/vendor/pusher/README.md +186 -0
  104. data/vendor/{pusher-gem → pusher}/Rakefile +0 -0
  105. data/vendor/{pusher-gem → pusher}/examples/async_message.rb +0 -0
  106. data/vendor/pusher/lib/pusher.rb +60 -0
  107. data/vendor/{pusher-gem → pusher}/lib/pusher/channel.rb +47 -54
  108. data/vendor/pusher/lib/pusher/client.rb +306 -0
  109. data/vendor/pusher/lib/pusher/request.rb +107 -0
  110. data/vendor/pusher/lib/pusher/resource.rb +36 -0
  111. data/vendor/pusher/lib/pusher/webhook.rb +110 -0
  112. data/vendor/{pusher-gem → pusher}/pusher.gemspec +6 -5
  113. data/vendor/pusher/spec/channel_spec.rb +127 -0
  114. data/vendor/pusher/spec/client_spec.rb +464 -0
  115. data/vendor/{pusher-gem → pusher}/spec/spec_helper.rb +12 -0
  116. data/vendor/pusher/spec/web_hook_spec.rb +117 -0
  117. data/vendor/signature/.travis.yml +15 -0
  118. data/vendor/signature/Gemfile +1 -1
  119. data/vendor/signature/README.md +38 -28
  120. data/vendor/signature/lib/signature.rb +97 -15
  121. data/vendor/signature/lib/signature/query_encoder.rb +47 -0
  122. data/vendor/signature/lib/signature/version.rb +1 -1
  123. data/vendor/signature/signature.gemspec +3 -2
  124. data/vendor/signature/spec/signature_spec.rb +164 -55
  125. data/vendor/signature/spec/spec_helper.rb +2 -3
  126. metadata +120 -145
  127. data/vendor/json_pure/.gitignore +0 -12
  128. data/vendor/json_pure/.travis.yml +0 -20
  129. data/vendor/json_pure/CHANGES +0 -282
  130. data/vendor/json_pure/COPYING +0 -58
  131. data/vendor/json_pure/COPYING-json-jruby +0 -57
  132. data/vendor/json_pure/GPL +0 -340
  133. data/vendor/json_pure/Gemfile +0 -11
  134. data/vendor/json_pure/README-json-jruby.markdown +0 -33
  135. data/vendor/json_pure/README.rdoc +0 -358
  136. data/vendor/json_pure/Rakefile +0 -412
  137. data/vendor/json_pure/TODO +0 -1
  138. data/vendor/json_pure/VERSION +0 -1
  139. data/vendor/json_pure/data/example.json +0 -1
  140. data/vendor/json_pure/data/index.html +0 -38
  141. data/vendor/json_pure/data/prototype.js +0 -4184
  142. data/vendor/json_pure/ext/json/ext/fbuffer/fbuffer.h +0 -181
  143. data/vendor/json_pure/ext/json/ext/generator/depend +0 -1
  144. data/vendor/json_pure/ext/json/ext/generator/extconf.rb +0 -14
  145. data/vendor/json_pure/ext/json/ext/generator/generator.c +0 -1435
  146. data/vendor/json_pure/ext/json/ext/generator/generator.h +0 -148
  147. data/vendor/json_pure/ext/json/ext/parser/depend +0 -1
  148. data/vendor/json_pure/ext/json/ext/parser/extconf.rb +0 -13
  149. data/vendor/json_pure/ext/json/ext/parser/parser.c +0 -2204
  150. data/vendor/json_pure/ext/json/ext/parser/parser.h +0 -77
  151. data/vendor/json_pure/ext/json/ext/parser/parser.rl +0 -927
  152. data/vendor/json_pure/install.rb +0 -23
  153. data/vendor/json_pure/java/src/json/ext/ByteListTranscoder.java +0 -167
  154. data/vendor/json_pure/java/src/json/ext/Generator.java +0 -444
  155. data/vendor/json_pure/java/src/json/ext/GeneratorMethods.java +0 -232
  156. data/vendor/json_pure/java/src/json/ext/GeneratorService.java +0 -43
  157. data/vendor/json_pure/java/src/json/ext/GeneratorState.java +0 -543
  158. data/vendor/json_pure/java/src/json/ext/OptionsReader.java +0 -114
  159. data/vendor/json_pure/java/src/json/ext/Parser.java +0 -2644
  160. data/vendor/json_pure/java/src/json/ext/Parser.rl +0 -968
  161. data/vendor/json_pure/java/src/json/ext/ParserService.java +0 -35
  162. data/vendor/json_pure/java/src/json/ext/RuntimeInfo.java +0 -121
  163. data/vendor/json_pure/java/src/json/ext/StringDecoder.java +0 -167
  164. data/vendor/json_pure/java/src/json/ext/StringEncoder.java +0 -106
  165. data/vendor/json_pure/java/src/json/ext/Utils.java +0 -89
  166. data/vendor/json_pure/json-java.gemspec +0 -23
  167. data/vendor/json_pure/json.gemspec +0 -37
  168. data/vendor/json_pure/json_pure.gemspec +0 -39
  169. data/vendor/json_pure/lib/json.rb +0 -62
  170. data/vendor/json_pure/lib/json/add/bigdecimal.rb +0 -28
  171. data/vendor/json_pure/lib/json/add/complex.rb +0 -22
  172. data/vendor/json_pure/lib/json/add/core.rb +0 -11
  173. data/vendor/json_pure/lib/json/add/date.rb +0 -34
  174. data/vendor/json_pure/lib/json/add/date_time.rb +0 -50
  175. data/vendor/json_pure/lib/json/add/exception.rb +0 -31
  176. data/vendor/json_pure/lib/json/add/ostruct.rb +0 -31
  177. data/vendor/json_pure/lib/json/add/range.rb +0 -29
  178. data/vendor/json_pure/lib/json/add/rational.rb +0 -22
  179. data/vendor/json_pure/lib/json/add/regexp.rb +0 -30
  180. data/vendor/json_pure/lib/json/add/struct.rb +0 -30
  181. data/vendor/json_pure/lib/json/add/symbol.rb +0 -25
  182. data/vendor/json_pure/lib/json/add/time.rb +0 -38
  183. data/vendor/json_pure/lib/json/common.rb +0 -487
  184. data/vendor/json_pure/lib/json/ext.rb +0 -21
  185. data/vendor/json_pure/lib/json/ext/.keep +0 -0
  186. data/vendor/json_pure/lib/json/generic_object.rb +0 -70
  187. data/vendor/json_pure/lib/json/pure.rb +0 -21
  188. data/vendor/json_pure/lib/json/pure/generator.rb +0 -522
  189. data/vendor/json_pure/lib/json/pure/parser.rb +0 -359
  190. data/vendor/json_pure/lib/json/version.rb +0 -8
  191. data/vendor/json_pure/tests/fixtures/fail1.json +0 -1
  192. data/vendor/json_pure/tests/fixtures/fail10.json +0 -1
  193. data/vendor/json_pure/tests/fixtures/fail11.json +0 -1
  194. data/vendor/json_pure/tests/fixtures/fail12.json +0 -1
  195. data/vendor/json_pure/tests/fixtures/fail13.json +0 -1
  196. data/vendor/json_pure/tests/fixtures/fail14.json +0 -1
  197. data/vendor/json_pure/tests/fixtures/fail18.json +0 -1
  198. data/vendor/json_pure/tests/fixtures/fail19.json +0 -1
  199. data/vendor/json_pure/tests/fixtures/fail2.json +0 -1
  200. data/vendor/json_pure/tests/fixtures/fail20.json +0 -1
  201. data/vendor/json_pure/tests/fixtures/fail21.json +0 -1
  202. data/vendor/json_pure/tests/fixtures/fail22.json +0 -1
  203. data/vendor/json_pure/tests/fixtures/fail23.json +0 -1
  204. data/vendor/json_pure/tests/fixtures/fail24.json +0 -1
  205. data/vendor/json_pure/tests/fixtures/fail25.json +0 -1
  206. data/vendor/json_pure/tests/fixtures/fail27.json +0 -2
  207. data/vendor/json_pure/tests/fixtures/fail28.json +0 -2
  208. data/vendor/json_pure/tests/fixtures/fail3.json +0 -1
  209. data/vendor/json_pure/tests/fixtures/fail4.json +0 -1
  210. data/vendor/json_pure/tests/fixtures/fail5.json +0 -1
  211. data/vendor/json_pure/tests/fixtures/fail6.json +0 -1
  212. data/vendor/json_pure/tests/fixtures/fail7.json +0 -1
  213. data/vendor/json_pure/tests/fixtures/fail8.json +0 -1
  214. data/vendor/json_pure/tests/fixtures/fail9.json +0 -1
  215. data/vendor/json_pure/tests/fixtures/pass1.json +0 -56
  216. data/vendor/json_pure/tests/fixtures/pass15.json +0 -1
  217. data/vendor/json_pure/tests/fixtures/pass16.json +0 -1
  218. data/vendor/json_pure/tests/fixtures/pass17.json +0 -1
  219. data/vendor/json_pure/tests/fixtures/pass2.json +0 -1
  220. data/vendor/json_pure/tests/fixtures/pass26.json +0 -1
  221. data/vendor/json_pure/tests/fixtures/pass3.json +0 -6
  222. data/vendor/json_pure/tests/setup_variant.rb +0 -11
  223. data/vendor/json_pure/tests/test_json.rb +0 -545
  224. data/vendor/json_pure/tests/test_json_addition.rb +0 -196
  225. data/vendor/json_pure/tests/test_json_encoding.rb +0 -65
  226. data/vendor/json_pure/tests/test_json_fixtures.rb +0 -35
  227. data/vendor/json_pure/tests/test_json_generate.rb +0 -322
  228. data/vendor/json_pure/tests/test_json_generic_object.rb +0 -75
  229. data/vendor/json_pure/tests/test_json_string_matching.rb +0 -39
  230. data/vendor/json_pure/tests/test_json_unicode.rb +0 -72
  231. data/vendor/json_pure/tools/fuzz.rb +0 -139
  232. data/vendor/json_pure/tools/server.rb +0 -62
  233. data/vendor/pusher-gem/Gemfile +0 -2
  234. data/vendor/pusher-gem/README.md +0 -80
  235. data/vendor/pusher-gem/lib/pusher.rb +0 -107
  236. data/vendor/pusher-gem/lib/pusher/request.rb +0 -107
  237. data/vendor/pusher-gem/spec/channel_spec.rb +0 -274
  238. data/vendor/pusher-gem/spec/pusher_spec.rb +0 -87
  239. data/vendor/ruby-hmac/History.txt +0 -15
  240. data/vendor/ruby-hmac/Manifest.txt +0 -11
  241. data/vendor/ruby-hmac/README.md +0 -41
  242. data/vendor/ruby-hmac/Rakefile +0 -23
  243. data/vendor/ruby-hmac/lib/hmac-md5.rb +0 -11
  244. data/vendor/ruby-hmac/lib/hmac-rmd160.rb +0 -11
  245. data/vendor/ruby-hmac/lib/hmac-sha1.rb +0 -11
  246. data/vendor/ruby-hmac/lib/hmac-sha2.rb +0 -25
  247. data/vendor/ruby-hmac/lib/hmac.rb +0 -118
  248. data/vendor/ruby-hmac/lib/ruby_hmac.rb +0 -2
  249. data/vendor/ruby-hmac/ruby-hmac.gemspec +0 -33
  250. data/vendor/ruby-hmac/test/test_hmac.rb +0 -89
  251. data/vendor/signature/VERSION +0 -1
@@ -0,0 +1,107 @@
1
+ require 'signature'
2
+ require 'digest/md5'
3
+ require 'multi_json'
4
+
5
+ module Pusher
6
+ class Request
7
+ attr_reader :body, :params
8
+
9
+ def initialize(client, verb, uri, params, body = nil)
10
+ @client, @verb, @uri = client, verb, uri
11
+ @head = {}
12
+
13
+ @body = body
14
+ if body
15
+ params[:body_md5] = Digest::MD5.hexdigest(body)
16
+ @head['Content-Type'] = 'application/json'
17
+ end
18
+
19
+ request = Signature::Request.new(verb.to_s.upcase, uri.path, params)
20
+ request.sign(client.authentication_token)
21
+ @params = request.signed_params
22
+ end
23
+
24
+ def send_sync
25
+ http = @client.sync_http_client
26
+
27
+ begin
28
+ response = http.request(@verb, @uri, @params, @body, @head)
29
+ rescue HTTPClient::BadResponseError, HTTPClient::TimeoutError,
30
+ SocketError, Errno::ECONNREFUSED => e
31
+ error = Pusher::HTTPError.new("#{e.message} (#{e.class})")
32
+ error.original_error = e
33
+ raise error
34
+ end
35
+
36
+ body = response.body ? response.body.chomp : nil
37
+
38
+ return handle_response(response.code.to_i, body)
39
+ end
40
+
41
+ def send_async
42
+ if defined?(EventMachine) && EventMachine.reactor_running?
43
+ http_client = @client.em_http_client(@uri)
44
+ df = EM::DefaultDeferrable.new
45
+
46
+ http = case @verb
47
+ when :post
48
+ http_client.post({
49
+ :query => @params, :body => @body, :head => @head
50
+ })
51
+ when :get
52
+ http_client.get({
53
+ :query => @params, :head => @head
54
+ })
55
+ else
56
+ raise "Unsuported verb"
57
+ end
58
+ http.callback {
59
+ begin
60
+ df.succeed(handle_response(http.response_header.status, http.response.chomp))
61
+ rescue => e
62
+ df.fail(e)
63
+ end
64
+ }
65
+ http.errback { |e|
66
+ message = "Network error connecting to pusher (#{http.error})"
67
+ Pusher.logger.debug(message)
68
+ df.fail(Error.new(message))
69
+ }
70
+
71
+ return df
72
+ else
73
+ http = @client.sync_http_client
74
+
75
+ return http.request_async(@verb, @uri, @params, @body, @head)
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def handle_response(status_code, body)
82
+ case status_code
83
+ when 200
84
+ return symbolize_first_level(MultiJson.decode(body))
85
+ when 202
86
+ return true
87
+ when 400
88
+ raise Error, "Bad request: #{body}"
89
+ when 401
90
+ raise AuthenticationError, body
91
+ when 404
92
+ raise Error, "404 Not found (#{@uri.path})"
93
+ when 407
94
+ raise Error, "Proxy Authentication Required"
95
+ else
96
+ raise Error, "Unknown error (status code #{status_code}): #{body}"
97
+ end
98
+ end
99
+
100
+ def symbolize_first_level(hash)
101
+ hash.inject({}) do |result, (key, value)|
102
+ result[key.to_sym] = value
103
+ result
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,36 @@
1
+ module Pusher
2
+ class Resource
3
+ def initialize(client, path)
4
+ @client = client
5
+ @path = path
6
+ end
7
+
8
+ def get(params)
9
+ create_request(:get, params).send_sync
10
+ end
11
+
12
+ def get_async(params)
13
+ create_request(:get, params).send_async
14
+ end
15
+
16
+ def post(params)
17
+ body = MultiJson.encode(params)
18
+ create_request(:post, {}, body).send_sync
19
+ end
20
+
21
+ def post_async(params)
22
+ body = MultiJson.encode(params)
23
+ create_request(:post, {}, body).send_async
24
+ end
25
+
26
+ private
27
+
28
+ def create_request(verb, params, body = nil)
29
+ Request.new(@client, verb, url, params, body)
30
+ end
31
+
32
+ def url
33
+ @_url ||= @client.url(@path)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,110 @@
1
+ require 'multi_json'
2
+ require 'openssl'
3
+
4
+ module Pusher
5
+ # Used to parse and authenticate WebHooks
6
+ #
7
+ # @example Sinatra
8
+ # post '/webhooks' do
9
+ # webhook = Pusher::WebHook.new(request)
10
+ # if webhook.valid?
11
+ # webhook.events.each do |event|
12
+ # case event["name"]
13
+ # when 'channel_occupied'
14
+ # puts "Channel occupied: #{event["channel"]}"
15
+ # when 'channel_vacated'
16
+ # puts "Channel vacated: #{event["channel"]}"
17
+ # end
18
+ # end
19
+ # else
20
+ # status 401
21
+ # end
22
+ # return
23
+ # end
24
+ #
25
+ class WebHook
26
+ attr_reader :key, :signature
27
+
28
+ # Provide either a Rack::Request or a Hash containing :key, :signature,
29
+ # :body, and :content_type (optional)
30
+ #
31
+ def initialize(request, client = Pusher)
32
+ @client = client
33
+ # Should work without Rack
34
+ if defined?(Rack::Request) && request.kind_of?(Rack::Request)
35
+ @key = request.env['HTTP_X_PUSHER_KEY']
36
+ @signature = request.env["HTTP_X_PUSHER_SIGNATURE"]
37
+ @content_type = request.content_type
38
+
39
+ request.body.rewind
40
+ @body = request.body.read
41
+ request.body.rewind
42
+ else
43
+ @key, @signature, @body = request.values_at(:key, :signature, :body)
44
+ @content_type = request[:content_type] || 'application/json'
45
+ end
46
+ end
47
+
48
+ # Returns whether the WebHook is valid by checking that the signature
49
+ # matches the configured key & secret. In the case that the webhook is
50
+ # invalid, the reason is logged
51
+ #
52
+ # @param extra_tokens [Hash] If you have extra tokens for your Pusher app, you can specify them so that they're used to attempt validation.
53
+ #
54
+ def valid?(extra_tokens = nil)
55
+ extra_tokens = [extra_tokens] if extra_tokens.kind_of?(Hash)
56
+ if @key == @client.key
57
+ return check_signature(@client.secret)
58
+ elsif extra_tokens
59
+ extra_tokens.each do |token|
60
+ return check_signature(token[:secret]) if @key == token[:key]
61
+ end
62
+ end
63
+ Pusher.logger.warn "Received webhook with unknown key: #{key}"
64
+ return false
65
+ end
66
+
67
+ # Array of events (as Hashes) contained inside the webhook
68
+ #
69
+ def events
70
+ data["events"]
71
+ end
72
+
73
+ # The time at which the WebHook was initially triggered by Pusher, i.e.
74
+ # when the event occurred
75
+ #
76
+ # @return [Time]
77
+ #
78
+ def time
79
+ Time.at(data["time_ms"].to_f/1000)
80
+ end
81
+
82
+ # Access the parsed WebHook body
83
+ #
84
+ def data
85
+ @data ||= begin
86
+ case @content_type
87
+ when 'application/json'
88
+ MultiJson.decode(@body)
89
+ else
90
+ raise "Unknown Content-Type (#{@content_type})"
91
+ end
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ # Checks signature against secret and returns boolean
98
+ #
99
+ def check_signature(secret)
100
+ digest = OpenSSL::Digest::SHA256.new
101
+ expected = OpenSSL::HMAC.hexdigest(digest, secret, @body)
102
+ if @signature == expected
103
+ return true
104
+ else
105
+ Pusher.logger.warn "Received WebHook with invalid signature: got #{@signature}, expected #{expected}"
106
+ return false
107
+ end
108
+ end
109
+ end
110
+ end
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "pusher"
6
- s.version = "0.8.4"
6
+ s.version = "0.12.0"
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Pusher"]
9
9
  s.email = ["support@pusher.com"]
@@ -12,14 +12,15 @@ Gem::Specification.new do |s|
12
12
  s.description = %q{Wrapper for pusher.com REST api}
13
13
 
14
14
  s.add_dependency "multi_json", "~> 1.0"
15
- s.add_dependency "crack", "~> 0.1.0"
16
- s.add_dependency "ruby-hmac", "~> 0.4.0"
17
- s.add_dependency 'signature', "~> 0.1.2"
15
+ s.add_dependency 'signature', "~> 0.1.6"
16
+ s.add_dependency "httpclient", "~> 2.3.0"
17
+ s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
18
18
 
19
19
  s.add_development_dependency "rspec", "~> 2.0"
20
20
  s.add_development_dependency "webmock"
21
- s.add_development_dependency "em-http-request", "~> 1.0.0"
21
+ s.add_development_dependency "em-http-request", "~> 1.1.0"
22
22
  s.add_development_dependency "rake"
23
+ s.add_development_dependency "rack"
23
24
 
24
25
  s.files = `git ls-files`.split("\n")
25
26
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pusher::Channel do
4
+ before do
5
+ @client = Pusher::Client.new({
6
+ :app_id => '20',
7
+ :key => '12345678900000001',
8
+ :secret => '12345678900000001',
9
+ :host => 'api.pusherapp.com',
10
+ :port => 80,
11
+ })
12
+ @channel = @client['test_channel']
13
+ end
14
+
15
+ let(:pusher_url_regexp) { %r{/apps/20/events} }
16
+
17
+ def stub_post(status, body = nil)
18
+ options = {:status => status}
19
+ options.merge!({:body => body}) if body
20
+
21
+ stub_request(:post, pusher_url_regexp).to_return(options)
22
+ end
23
+
24
+ def stub_post_to_raise(e)
25
+ stub_request(:post, pusher_url_regexp).to_raise(e)
26
+ end
27
+
28
+ describe '#trigger!' do
29
+ it "should use @client.trigger internally" do
30
+ @client.should_receive(:trigger)
31
+ @channel.trigger('new_event', 'Some data')
32
+ end
33
+ end
34
+
35
+ describe '#trigger' do
36
+ it "should log failure if error raised in http call" do
37
+ stub_post_to_raise(HTTPClient::BadResponseError)
38
+
39
+ Pusher.logger.should_receive(:error).with("Exception from WebMock (HTTPClient::BadResponseError) (Pusher::HTTPError)")
40
+ Pusher.logger.should_receive(:debug) #backtrace
41
+ channel = Pusher::Channel.new(@client.url, 'test_channel', @client)
42
+ channel.trigger('new_event', 'Some data')
43
+ end
44
+
45
+ it "should log failure if Pusher returns an error response" do
46
+ stub_post 401, "some signature info"
47
+ Pusher.logger.should_receive(:error).with("some signature info (Pusher::AuthenticationError)")
48
+ Pusher.logger.should_receive(:debug) #backtrace
49
+ channel = Pusher::Channel.new(@client.url, 'test_channel', @client)
50
+ channel.trigger('new_event', 'Some data')
51
+ end
52
+ end
53
+
54
+ describe "#trigger_async" do
55
+ it "should use @client.trigger_async internally" do
56
+ @client.should_receive(:trigger_async)
57
+ @channel.trigger_async('new_event', 'Some data')
58
+ end
59
+ end
60
+
61
+ describe '#info' do
62
+ it "should call the Client#channel_info" do
63
+ @client.should_receive(:get).with("/channels/mychannel", anything)
64
+ @channel = @client['mychannel']
65
+ @channel.info
66
+ end
67
+
68
+ it "should assemble the requested attribes into the info option" do
69
+ @client.should_receive(:get).with(anything, {
70
+ :info => "user_count,connection_count"
71
+ })
72
+ @channel = @client['presence-foo']
73
+ @channel.info(%w{user_count connection_count})
74
+ end
75
+ end
76
+
77
+ describe "#authentication_string" do
78
+ def authentication_string(*data)
79
+ lambda { @channel.authentication_string(*data) }
80
+ end
81
+
82
+ it "should return an authentication string given a socket id" do
83
+ auth = @channel.authentication_string('socketid')
84
+
85
+ auth.should == '12345678900000001:827076f551e22451357939e4c7bb1200de29f921d5bf80b40d71668f9cd61c40'
86
+ end
87
+
88
+ it "should raise error if authentication is invalid" do
89
+ [nil, ''].each do |invalid|
90
+ authentication_string(invalid).should raise_error Pusher::Error
91
+ end
92
+ end
93
+
94
+ describe 'with extra string argument' do
95
+ it 'should be a string or nil' do
96
+ authentication_string('socketid', 123).should raise_error Pusher::Error
97
+ authentication_string('socketid', {}).should raise_error Pusher::Error
98
+
99
+ authentication_string('socketid', 'boom').should_not raise_error
100
+ authentication_string('socketid', nil).should_not raise_error
101
+ end
102
+
103
+ it "should return an authentication string given a socket id and custom args" do
104
+ auth = @channel.authentication_string('socketid', 'foobar')
105
+
106
+ auth.should == "12345678900000001:#{hmac(@client.secret, "socketid:test_channel:foobar")}"
107
+ end
108
+ end
109
+ end
110
+
111
+ describe '#authenticate' do
112
+ before :each do
113
+ @custom_data = {:uid => 123, :info => {:name => 'Foo'}}
114
+ end
115
+
116
+ it 'should return a hash with signature including custom data and data as json string' do
117
+ MultiJson.stub(:encode).with(@custom_data).and_return 'a json string'
118
+
119
+ response = @channel.authenticate('socketid', @custom_data)
120
+
121
+ response.should == {
122
+ :auth => "12345678900000001:#{hmac(@client.secret, "socketid:test_channel:a json string")}",
123
+ :channel_data => 'a json string'
124
+ }
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,464 @@
1
+ require 'spec_helper'
2
+
3
+ require 'em-http'
4
+
5
+ describe Pusher do
6
+ describe 'using multiple Client objects' do
7
+ before :each do
8
+ @client1 = Pusher::Client.new
9
+ @client2 = Pusher::Client.new
10
+
11
+ @client1.scheme = 'ws'
12
+ @client2.scheme = 'wss'
13
+ @client1.host = 'one'
14
+ @client2.host = 'two'
15
+ @client1.port = 81
16
+ @client2.port = 82
17
+ @client1.app_id = '1111'
18
+ @client2.app_id = '2222'
19
+ @client1.key = 'AAAA'
20
+ @client2.key = 'BBBB'
21
+ @client1.secret = 'aaaaaaaa'
22
+ @client2.secret = 'bbbbbbbb'
23
+ end
24
+
25
+ it "should send scheme messages to different objects" do
26
+ @client1.scheme.should_not == @client2.scheme
27
+ end
28
+
29
+ it "should send host messages to different objects" do
30
+ @client1.host.should_not == @client2.host
31
+ end
32
+
33
+ it "should send port messages to different objects" do
34
+ @client1.port.should_not == @client2.port
35
+ end
36
+
37
+ it "should send app_id messages to different objects" do
38
+ @client1.app_id.should_not == @client2.app_id
39
+ end
40
+
41
+ it "should send app_id messages to different objects" do
42
+ @client1.key.should_not == @client2.key
43
+ end
44
+
45
+ it "should send app_id messages to different objects" do
46
+ @client1.secret.should_not == @client2.secret
47
+ end
48
+
49
+ it "should send app_id messages to different objects" do
50
+ @client1.authentication_token.key.should_not == @client2.authentication_token.key
51
+ @client1.authentication_token.secret.should_not == @client2.authentication_token.secret
52
+ end
53
+
54
+ it "should send url messages to different objects" do
55
+ @client1.url.to_s.should_not == @client2.url.to_s
56
+ @client1.url = 'ws://one/apps/111'
57
+ @client2.url = 'wss://two/apps/222'
58
+ @client1.scheme.should_not == @client2.scheme
59
+ @client1.host.should_not == @client2.host
60
+ @client1.app_id.should_not == @client2.app_id
61
+ end
62
+
63
+ it "should send encrypted messages to different objects" do
64
+ @client1.encrypted = false
65
+ @client2.encrypted = true
66
+ @client1.scheme.should_not == @client2.scheme
67
+ @client1.port.should_not == @client2.port
68
+ end
69
+
70
+ it "should send [] messages to different objects" do
71
+ @client1['test'].should_not == @client2['test']
72
+ end
73
+
74
+ it "should send http_proxy messages to different objects" do
75
+ @client1.http_proxy = 'http://oneuser:onepassword@onehost:8080'
76
+ @client2.http_proxy = 'http://twouser:twopassword@twohost:8880'
77
+ @client1.http_proxy.should_not == @client2.http_proxy
78
+ end
79
+ end
80
+
81
+ # The behaviour should be the same when using the Client object, or the
82
+ # 'global' client delegated through the Pusher class
83
+ [lambda { Pusher }, lambda { Pusher::Client.new }].each do |client_gen|
84
+ before :each do
85
+ @client = client_gen.call
86
+ end
87
+
88
+ describe 'default configuration' do
89
+ it 'should be preconfigured for api host' do
90
+ @client.host.should == 'api.pusherapp.com'
91
+ end
92
+
93
+ it 'should be preconfigured for port 80' do
94
+ @client.port.should == 80
95
+ end
96
+
97
+ it 'should use standard logger if no other logger if defined' do
98
+ Pusher.logger.debug('foo')
99
+ Pusher.logger.should be_kind_of(Logger)
100
+ end
101
+ end
102
+
103
+ describe 'logging configuration' do
104
+ it "can be configured to use any logger" do
105
+ logger = double("ALogger")
106
+ logger.should_receive(:debug).with('foo')
107
+ Pusher.logger = logger
108
+ Pusher.logger.debug('foo')
109
+ Pusher.logger = nil
110
+ end
111
+ end
112
+
113
+ describe "configuration using url" do
114
+ it "should be possible to configure everything by setting the url" do
115
+ @client.url = "test://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
116
+
117
+ @client.scheme.should == 'test'
118
+ @client.host.should == 'api.staging.pusherapp.com'
119
+ @client.port.should == 8080
120
+ @client.key.should == 'somekey'
121
+ @client.secret.should == 'somesecret'
122
+ @client.app_id.should == '87'
123
+ end
124
+
125
+ it "should override scheme and port when setting encrypted=true after url" do
126
+ @client.url = "http://somekey:somesecret@api.staging.pusherapp.com:8080/apps/87"
127
+ @client.encrypted = true
128
+
129
+ @client.scheme.should == 'https'
130
+ @client.port.should == 443
131
+ end
132
+ end
133
+
134
+ describe 'configuring a http proxy' do
135
+ it "should be possible to configure everything by setting the http_proxy" do
136
+ @client.http_proxy = 'http://someuser:somepassword@proxy.host.com:8080'
137
+
138
+ @client.proxy.should == {:scheme => 'http', :host => 'proxy.host.com', :port => 8080, :user => 'someuser', :password => 'somepassword'}
139
+ end
140
+ end
141
+
142
+ describe 'when configured' do
143
+ before :each do
144
+ @client.app_id = '20'
145
+ @client.key = '12345678900000001'
146
+ @client.secret = '12345678900000001'
147
+ end
148
+
149
+ describe '#[]' do
150
+ before do
151
+ @channel = @client['test_channel']
152
+ end
153
+
154
+ it 'should return a channel' do
155
+ @channel.should be_kind_of(Pusher::Channel)
156
+ end
157
+
158
+ %w{app_id key secret}.each do |config|
159
+ it "should raise exception if #{config} not configured" do
160
+ @client.send("#{config}=", nil)
161
+ lambda {
162
+ @client['test_channel']
163
+ }.should raise_error(Pusher::ConfigurationError)
164
+ end
165
+ end
166
+ end
167
+
168
+ describe '#channels' do
169
+ it "should call the correct URL and symbolise response correctly" do
170
+ api_path = %r{/apps/20/channels}
171
+ stub_request(:get, api_path).to_return({
172
+ :status => 200,
173
+ :body => MultiJson.encode('channels' => {
174
+ "channel1" => {},
175
+ "channel2" => {}
176
+ })
177
+ })
178
+ @client.channels.should == {
179
+ :channels => {
180
+ "channel1" => {},
181
+ "channel2" => {}
182
+ }
183
+ }
184
+ end
185
+ end
186
+
187
+ describe '#channel_info' do
188
+ it "should call correct URL and symbolise response" do
189
+ api_path = %r{/apps/20/channels/mychannel}
190
+ stub_request(:get, api_path).to_return({
191
+ :status => 200,
192
+ :body => MultiJson.encode({
193
+ 'occupied' => false,
194
+ })
195
+ })
196
+ @client.channel_info('mychannel').should == {
197
+ :occupied => false,
198
+ }
199
+ end
200
+ end
201
+
202
+ describe '#trigger' do
203
+ before :each do
204
+ @api_path = %r{/apps/20/events}
205
+ stub_request(:post, @api_path).to_return({
206
+ :status => 200,
207
+ :body => MultiJson.encode({})
208
+ })
209
+ end
210
+
211
+ it "should call correct URL" do
212
+ @client.trigger(['mychannel'], 'event', {'some' => 'data'}).
213
+ should == {}
214
+ end
215
+
216
+ it "should pass any parameters in the body of the request" do
217
+ @client.trigger(['mychannel', 'c2'], 'event', {'some' => 'data'}, {
218
+ :socket_id => "1234"
219
+ })
220
+ WebMock.should have_requested(:post, @api_path).with { |req|
221
+ parsed = MultiJson.decode(req.body)
222
+ parsed["name"].should == 'event'
223
+ parsed["channels"].should == ["mychannel", "c2"]
224
+ parsed["socket_id"].should == '1234'
225
+ }
226
+ end
227
+
228
+ it "should convert non string data to JSON before posting" do
229
+ @client.trigger(['mychannel'], 'event', {'some' => 'data'})
230
+ WebMock.should have_requested(:post, @api_path).with { |req|
231
+ MultiJson.decode(req.body)["data"].should == '{"some":"data"}'
232
+ }
233
+ end
234
+
235
+ it "should accept a single channel as well as an array" do
236
+ @client.trigger('mychannel', 'event', {'some' => 'data'})
237
+ WebMock.should have_requested(:post, @api_path).with { |req|
238
+ MultiJson.decode(req.body)["channels"].should == ['mychannel']
239
+ }
240
+ end
241
+ end
242
+
243
+ describe '#trigger_async' do
244
+ before :each do
245
+ @api_path = %r{/apps/20/events}
246
+ stub_request(:post, @api_path).to_return({
247
+ :status => 200,
248
+ :body => MultiJson.encode({})
249
+ })
250
+ end
251
+
252
+ it "should call correct URL" do
253
+ EM.run {
254
+ @client.trigger_async('mychannel', 'event', {'some' => 'data'}).callback { |r|
255
+ r.should == {}
256
+ EM.stop
257
+ }
258
+ }
259
+ end
260
+
261
+ it "should pass any parameters in the body of the request" do
262
+ EM.run {
263
+ @client.trigger_async('mychannel', 'event', {'some' => 'data'}, {
264
+ :socket_id => "1234"
265
+ }).callback {
266
+ WebMock.should have_requested(:post, @api_path).with { |req|
267
+ MultiJson.decode(req.body)["socket_id"].should == '1234'
268
+ }
269
+ EM.stop
270
+ }
271
+ }
272
+ end
273
+
274
+ it "should convert non string data to JSON before posting" do
275
+ EM.run {
276
+ @client.trigger_async('mychannel', 'event', {'some' => 'data'}).callback {
277
+ WebMock.should have_requested(:post, @api_path).with { |req|
278
+ MultiJson.decode(req.body)["data"].should == '{"some":"data"}'
279
+ }
280
+ EM.stop
281
+ }
282
+ }
283
+ end
284
+ end
285
+
286
+ [:get, :post].each do |verb|
287
+ describe "##{verb}" do
288
+ before :each do
289
+ @url_regexp = %r{api.pusherapp.com}
290
+ stub_request(verb, @url_regexp).
291
+ to_return(:status => 200, :body => "{}")
292
+ end
293
+
294
+ let(:call_api) { @client.send(verb, '/path') }
295
+
296
+ it "should use http by default" do
297
+ call_api
298
+ WebMock.should have_requested(verb, %r{http://api.pusherapp.com/apps/20/path})
299
+ end
300
+
301
+ it "should use https if configured" do
302
+ @client.encrypted = true
303
+ call_api
304
+ WebMock.should have_requested(verb, %r{https://api.pusherapp.com})
305
+ end
306
+
307
+ it "should format the respose hash with symbols at first level" do
308
+ stub_request(verb, @url_regexp).to_return({
309
+ :status => 200,
310
+ :body => MultiJson.encode({'something' => {'a' => 'hash'}})
311
+ })
312
+ call_api.should == {
313
+ :something => {'a' => 'hash'}
314
+ }
315
+ end
316
+
317
+ it "should catch all http exceptions and raise a Pusher::HTTPError wrapping the original error" do
318
+ stub_request(verb, @url_regexp).to_raise(HTTPClient::TimeoutError)
319
+
320
+ error = nil
321
+ begin
322
+ call_api
323
+ rescue => e
324
+ error = e
325
+ end
326
+
327
+ error.class.should == Pusher::HTTPError
328
+ error.should be_kind_of(Pusher::Error)
329
+ error.message.should == 'Exception from WebMock (HTTPClient::TimeoutError)'
330
+ error.original_error.class.should == HTTPClient::TimeoutError
331
+ end
332
+
333
+ it "should raise Pusher::Error if call returns 400" do
334
+ stub_request(verb, @url_regexp).to_return({:status => 400})
335
+ lambda { call_api }.should raise_error(Pusher::Error)
336
+ end
337
+
338
+ it "should raise AuthenticationError if pusher returns 401" do
339
+ stub_request(verb, @url_regexp).to_return({:status => 401})
340
+ lambda { call_api }.should raise_error(Pusher::AuthenticationError)
341
+ end
342
+
343
+ it "should raise Pusher::Error if pusher returns 404" do
344
+ stub_request(verb, @url_regexp).to_return({:status => 404})
345
+ lambda { call_api }.should raise_error(Pusher::Error, '404 Not found (/apps/20/path)')
346
+ end
347
+
348
+ it "should raise Pusher::Error if pusher returns 407" do
349
+ stub_request(verb, @url_regexp).to_return({:status => 407})
350
+ lambda { call_api }.should raise_error(Pusher::Error, 'Proxy Authentication Required')
351
+ end
352
+
353
+ it "should raise Pusher::Error if pusher returns 500" do
354
+ stub_request(verb, @url_regexp).to_return({:status => 500, :body => "some error"})
355
+ lambda { call_api }.should raise_error(Pusher::Error, 'Unknown error (status code 500): some error')
356
+ end
357
+ end
358
+ end
359
+
360
+ describe "async calling without eventmachine" do
361
+ [[:get, :get_async], [:post, :post_async]].each do |verb, method|
362
+ describe "##{method}" do
363
+ before :each do
364
+ @url_regexp = %r{api.pusherapp.com}
365
+ stub_request(verb, @url_regexp).
366
+ to_return(:status => 200, :body => "{}")
367
+ end
368
+
369
+ let(:call_api) {
370
+ @client.send(method, '/path').tap { |c|
371
+ # Allow the async thread (inside httpclient) to run
372
+ while !c.finished?
373
+ sleep 0.01
374
+ end
375
+ }
376
+ }
377
+
378
+ it "should use http by default" do
379
+ call_api
380
+ WebMock.should have_requested(verb, %r{http://api.pusherapp.com/apps/20/path})
381
+ end
382
+
383
+ it "should use https if configured" do
384
+ @client.encrypted = true
385
+ call_api
386
+ WebMock.should have_requested(verb, %r{https://api.pusherapp.com})
387
+ end
388
+
389
+ # Note that the raw httpclient connection object is returned and
390
+ # the response isn't handled (by handle_response) in the normal way.
391
+ it "should return a httpclient connection object" do
392
+ connection = call_api
393
+ connection.finished?.should be_true
394
+ response = connection.pop
395
+ response.status.should == 200
396
+ response.body.read.should == "{}"
397
+ end
398
+ end
399
+ end
400
+ end
401
+
402
+ describe "async calling with eventmachine" do
403
+ [[:get, :get_async], [:post, :post_async]].each do |verb, method|
404
+ describe "##{method}" do
405
+ before :each do
406
+ @url_regexp = %r{api.pusherapp.com}
407
+ stub_request(verb, @url_regexp).
408
+ to_return(:status => 200, :body => "{}")
409
+ end
410
+
411
+ let(:call_api) { @client.send(method, '/path') }
412
+
413
+ it "should use http by default" do
414
+ EM.run {
415
+ call_api.callback {
416
+ WebMock.should have_requested(verb, %r{http://api.pusherapp.com/apps/20/path})
417
+ EM.stop
418
+ }
419
+ }
420
+ end
421
+
422
+ it "should use https if configured" do
423
+ EM.run {
424
+ @client.encrypted = true
425
+ call_api.callback {
426
+ WebMock.should have_requested(verb, %r{https://api.pusherapp.com})
427
+ EM.stop
428
+ }
429
+ }
430
+ end
431
+
432
+ it "should format the respose hash with symbols at first level" do
433
+ EM.run {
434
+ stub_request(verb, @url_regexp).to_return({
435
+ :status => 200,
436
+ :body => MultiJson.encode({'something' => {'a' => 'hash'}})
437
+ })
438
+ call_api.callback { |response|
439
+ response.should == {
440
+ :something => {'a' => 'hash'}
441
+ }
442
+ EM.stop
443
+ }
444
+ }
445
+ end
446
+
447
+ it "should errback with Pusher::Error on unsuccessful response" do
448
+ EM.run {
449
+ stub_request(verb, @url_regexp).to_return({:status => 400})
450
+
451
+ call_api.errback { |e|
452
+ e.class.should == Pusher::Error
453
+ EM.stop
454
+ }.callback {
455
+ fail
456
+ }
457
+ }
458
+ end
459
+ end
460
+ end
461
+ end
462
+ end
463
+ end
464
+ end