firehose 0.2.alpha.7 → 0.2.alpha.8

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.
@@ -6,7 +6,7 @@ class Firehose.LongPoll extends Firehose.Transport
6
6
  # access "simple request" response headers. This means we don't yet have a
7
7
  # plan to support IE<10 (when it gets a real XHR2 implementation). Sucks...
8
8
  @ieSupported: ->
9
- $.browser.msie and parseInt($.browser.version) >= 10
9
+ $.browser.msie and parseInt($.browser.version) >= 8
10
10
 
11
11
  @supported: ->
12
12
  # IE 8+, FF 3.5+, Chrome 4+, Safari 4+, Opera 12+, iOS 3.2+, Android 2.1+
@@ -43,21 +43,13 @@ class Firehose.LongPoll extends Firehose.Transport
43
43
  data.last_message_sequence = @_lastMessageSequence
44
44
  # TODO: Some of these options will be deprecated in jQuery 1.8
45
45
  # See: http://api.jquery.com/jQuery.ajax/#jqXHR
46
- opts =
46
+ $.ajax @config.longPoll.url,
47
47
  crossDomain: true
48
48
  data: data
49
49
  timeout: @_timeout
50
50
  success: @_success
51
51
  error: @_error
52
- complete: @_xhrComplete
53
- opts.xhr = hackAroundFirefoxXhrHeadersBug if $.browser.mozilla
54
- $.ajax @config.longPoll.url, opts
55
-
56
- _xhrComplete: (jqXhr) =>
57
- # Get the last sequence from the server if specified.
58
- if jqXhr.status is 200
59
- val = jqXhr.getResponseHeader @messageSequenceHeader
60
- @_lastMessageSequence = val if val?
52
+ cache: false
61
53
 
62
54
  stop: =>
63
55
  @_stopRequestLoop = true
@@ -65,17 +57,14 @@ class Firehose.LongPoll extends Firehose.Transport
65
57
  _success: (data, status, jqXhr) =>
66
58
  @_open data unless @_succeeded
67
59
  return if @_stopRequestLoop
68
- if jqXhr.status == 204
69
- # If we get a 204 back, that means the server timed-out and sent back a 204 with a
70
- # X-Http-Next-Request header
71
- #
72
- # Why did we use a 204 and not a 408? Because Firefox is really stupid about 400 level error
73
- # codes and would claims its a 0 error code, which we use for something else. Firefox is IE
74
- # in this case
75
- @connect(@_okInterval)
76
- else
77
- @config.message(@config.parse(jqXhr.responseText))
78
- @connect(@_okInterval)
60
+ if jqXhr.status is 200
61
+ # Of course, IE's XDomainRequest doesn't support non-200 success codes.
62
+ try
63
+ {message, last_sequence} = JSON.parse jqXhr.responseText
64
+ @_lastMessageSequence = last_sequence
65
+ @config.message @config.parse message
66
+ catch e
67
+ @connect @_okInterval
79
68
 
80
69
  _ping: =>
81
70
  # Ping long poll server to verify internet connectivity
@@ -98,59 +87,34 @@ class Firehose.LongPoll extends Firehose.Transport
98
87
  # Reconnect with delay
99
88
  setTimeout @_request, @_retryDelay
100
89
 
101
- # NB: This is a stupid hack to deal with CORS short-comings in jQuery in
102
- # Firefox. There is a ticket for this: http://bugs.jquery.com/ticket/10338
103
- # Once jQuery is upgraded to this version we can probably remove this, but be
104
- # sure you test the crap out of Firefox!
105
- # Its also worth noting that I had to localize this monkey-patch to the
106
- # Firehose.LongPoll consumer because a previous global patch on
107
- # jQuery.ajaxSettings.xhr was breaking regular IE7 loading. Better to localize
108
- # this anyway to solve that problem and loading order issues.
109
- hackAroundFirefoxXhrHeadersBug = ->
110
- XHR_HEADERS = [
111
- "Cache-Control", "Content-Language", "Content-Type"
112
- "Expires", "Last-Modified", "Pragma"
113
- ]
114
- xhr = jQuery.ajaxSettings.xhr()
115
- originalFun = xhr.getAllResponseHeaders
116
- xhr.getAllResponseHeaders = ->
117
- return allHeaders if allHeaders = originalFun.call xhr
118
- lines = for name in XHR_HEADERS when xhr.getResponseHeader(name)?
119
- "#{name}: #{xhr.getResponseHeader name}"
120
- lines.join '\n'
121
- xhr
122
-
123
- # NB: Leaving this here for now. It does work, but won't help us since the XDR
124
- # object cannot access any response headers, which Firehose relies on.
125
-
126
- # # Let's try to hack in support for IE8+ via the XDomainRequest object!
127
- # # This was adapted from code shamelessly stolen from:
128
- # # https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js
129
- # if $.browser.msie and parseInt($.browser.version, 10) in [8, 9]
130
- # jQuery.ajaxTransport (s) ->
131
- # if s.crossDomain and s.async
132
- # if s.timeout
133
- # s.xdrTimeout = s.timeout
134
- # delete s.timeout
135
- # xdr = undefined
136
- # return {
137
- # send: (_, complete) ->
138
- # callback = (status, statusText, responses, responseHeaders) ->
139
- # xdr.onload = xdr.onerror = xdr.ontimeout = jQuery.noop
140
- # xdr = undefined
141
- # complete status, statusText, responses, responseHeaders
142
- # xdr = new XDomainRequest()
143
- # xdr.open s.type, s.url
144
- # xdr.onload = ->
145
- # headers = "Content-Type: #{xdr.contentType}"
146
- # callback 200, "OK", {text: xdr.responseText}, headers
147
- # xdr.onerror = -> callback 404, "Not Found"
148
- # if s.xdrTimeout?
149
- # xdr.ontimeout = -> callback 0, "timeout"
150
- # xdr.timeout = s.xdrTimeout
151
- # xdr.send (s.hasContent and s.data) or null
152
- # abort: ->
153
- # if xdr?
154
- # xdr.onerror = jQuery.noop()
155
- # xdr.abort()
156
- # }
90
+ # Let's try to hack in support for IE8-9 via the XDomainRequest object!
91
+ # This was adapted from code shamelessly stolen from:
92
+ # https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js
93
+ if $.browser.msie and parseInt($.browser.version, 10) in [8, 9]
94
+ jQuery.ajaxTransport (s) ->
95
+ if s.crossDomain and s.async
96
+ if s.timeout
97
+ s.xdrTimeout = s.timeout
98
+ delete s.timeout
99
+ xdr = undefined
100
+ return {
101
+ send: (_, complete) ->
102
+ callback = (status, statusText, responses, responseHeaders) ->
103
+ xdr.onload = xdr.onerror = xdr.ontimeout = jQuery.noop
104
+ xdr = undefined
105
+ complete status, statusText, responses, responseHeaders
106
+ xdr = new XDomainRequest()
107
+ xdr.open s.type, s.url
108
+ xdr.onload = ->
109
+ headers = "Content-Type: #{xdr.contentType}"
110
+ callback 200, "OK", {text: xdr.responseText}, headers
111
+ xdr.onerror = -> callback 404, "Not Found"
112
+ if s.xdrTimeout?
113
+ xdr.ontimeout = -> callback 0, "timeout"
114
+ xdr.timeout = s.xdrTimeout
115
+ xdr.send (s.hasContent and s.data) or null
116
+ abort: ->
117
+ if xdr?
118
+ xdr.onerror = jQuery.noop()
119
+ xdr.abort()
120
+ }
@@ -12,8 +12,23 @@ module Firehose
12
12
  # TODO hi-redis isn't that awesome... we have to setup an errback per even for wrong
13
13
  # commands because of the lack of a method_missing whitelist. Perhaps implement a whitelist in
14
14
  # em-hiredis or us a diff lib?
15
- deferrable = EM::DefaultDeferrable.new
16
- deferrable.errback {|e| EM.next_tick { raise e } }
15
+ if (deferrable = opts[:deferrable]).nil?
16
+ deferrable = EM::DefaultDeferrable.new
17
+ deferrable.errback do |e|
18
+ # Handle missing Lua publishing script in cache
19
+ # (such as Redis restarting or someone executing SCRIPT FLUSH)
20
+ if e.message =~ /NOSCRIPT/
21
+ deferrable.succeed
22
+ EM.next_tick do
23
+ @publish_script_digest = nil
24
+ combined_opts = opts.merge :deferrable => deferrable
25
+ self.publish channel_key, message, combined_opts
26
+ end
27
+ else
28
+ EM.next_tick { raise e }
29
+ end
30
+ end
31
+ end
17
32
 
18
33
  if @publish_script_digest.nil?
19
34
  register_publish_script.errback do |e|
@@ -70,7 +85,6 @@ module Firehose
70
85
  PAYLOAD_DELIMITER,
71
86
  channel_key
72
87
  ]
73
- # Firehose.logger.debug "Evaluating Lua publishing script (#{@publish_script_digest}) with arguments: #{script_args.inspect}"
74
88
  redis.evalsha(
75
89
  @publish_script_digest, script_args.length, *script_args
76
90
  ).errback do |e|
@@ -45,11 +45,10 @@ module Firehose
45
45
  EM.next_tick do
46
46
 
47
47
  if last_sequence < 0
48
- env['async.callback'].call response(400, "Header '#{LAST_MESSAGE_SEQUENCE_HEADER}' may not be less than zero", response_headers(env))
48
+ env['async.callback'].call response(400, "The last_message_sequence parameter may not be less than zero", response_headers(env))
49
49
  else
50
50
  Channel.new(path).next_message(last_sequence, :timeout => TIMEOUT).callback do |message, sequence|
51
- combined_headers = response_headers(env).merge(LAST_MESSAGE_SEQUENCE_HEADER => sequence.to_s)
52
- env['async.callback'].call response(200, message, combined_headers)
51
+ env['async.callback'].call response(200, wrap_frame(message, sequence), response_headers(env))
53
52
  end.errback do |e|
54
53
  if e == :timeout
55
54
  env['async.callback'].call response(204, '', response_headers(env))
@@ -74,6 +73,10 @@ module Firehose
74
73
 
75
74
  private
76
75
 
76
+ def wrap_frame(message, last_sequence)
77
+ JSON.generate :message => message, :last_sequence => last_sequence
78
+ end
79
+
77
80
  # If the request is a CORS request, return those headers, otherwise don't worry 'bout it
78
81
  def response_headers(env)
79
82
  cors_origin(env) ? cors_headers(env) : {}
@@ -85,10 +88,7 @@ module Firehose
85
88
 
86
89
  def cors_headers(env)
87
90
  # TODO seperate out CORS logic as an async middleware with a Goliath web server.
88
- {
89
- 'Access-Control-Allow-Origin' => cors_origin(env),
90
- 'Access-Control-Expose-Headers' => LAST_MESSAGE_SEQUENCE_HEADER
91
- }
91
+ {'Access-Control-Allow-Origin' => cors_origin(env)}
92
92
  end
93
93
  end
94
94
 
@@ -1,4 +1,4 @@
1
1
  module Firehose
2
- VERSION = "0.2.alpha.7"
3
- CODENAME = "Look ma', no Redis/Lua memory leak!"
2
+ VERSION = "0.2.alpha.8"
3
+ CODENAME = "IE Included"
4
4
  end
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'integrations/integration_test_helper'
3
+ require 'json'
3
4
 
4
5
  shared_examples_for 'Firehose::Rack::App' do
5
6
  include EM::TestHelper
@@ -53,10 +54,11 @@ shared_examples_for 'Firehose::Rack::App' do
53
54
  http = EM::HttpRequest.new(http_url).get(:query => {'last_message_sequence' => last_sequence})
54
55
  http.errback { em.stop }
55
56
  http.callback do
56
- received[cid] << http.response
57
+ frame = JSON.parse(http.response, :symbolize_names => true)
58
+ received[cid] << frame[:message]
57
59
  if received[cid].size < messages.size
58
60
  # Add some jitter so the clients aren't syncronized
59
- EM::add_timer(rand*0.001) { http_long_poll.call cid, http.response_header['pragma'] }
61
+ EM::add_timer(rand*0.001) { http_long_poll.call cid, frame[:last_sequence] }
60
62
  else
61
63
  succeed.call cid
62
64
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: firehose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.alpha.7
4
+ version: 0.2.alpha.8
5
5
  prerelease: 4
6
6
  platform: ruby
7
7
  authors:
@@ -12,11 +12,11 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2012-09-06 00:00:00.000000000 Z
15
+ date: 2012-09-13 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: eventmachine
19
- requirement: &70165766290180 !ruby/object:Gem::Requirement
19
+ requirement: &70228764765360 !ruby/object:Gem::Requirement
20
20
  none: false
21
21
  requirements:
22
22
  - - ! '>='
@@ -24,10 +24,10 @@ dependencies:
24
24
  version: 1.0.0.rc
25
25
  type: :runtime
26
26
  prerelease: false
27
- version_requirements: *70165766290180
27
+ version_requirements: *70228764765360
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: em-hiredis
30
- requirement: &70165766289740 !ruby/object:Gem::Requirement
30
+ requirement: &70228764764880 !ruby/object:Gem::Requirement
31
31
  none: false
32
32
  requirements:
33
33
  - - ! '>='
@@ -35,10 +35,10 @@ dependencies:
35
35
  version: '0'
36
36
  type: :runtime
37
37
  prerelease: false
38
- version_requirements: *70165766289740
38
+ version_requirements: *70228764764880
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: thor
41
- requirement: &70165766289240 !ruby/object:Gem::Requirement
41
+ requirement: &70228764764420 !ruby/object:Gem::Requirement
42
42
  none: false
43
43
  requirements:
44
44
  - - ! '>='
@@ -46,10 +46,10 @@ dependencies:
46
46
  version: '0'
47
47
  type: :runtime
48
48
  prerelease: false
49
- version_requirements: *70165766289240
49
+ version_requirements: *70228764764420
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: faraday
52
- requirement: &70165766288720 !ruby/object:Gem::Requirement
52
+ requirement: &70228764764000 !ruby/object:Gem::Requirement
53
53
  none: false
54
54
  requirements:
55
55
  - - ! '>='
@@ -57,10 +57,10 @@ dependencies:
57
57
  version: '0'
58
58
  type: :runtime
59
59
  prerelease: false
60
- version_requirements: *70165766288720
60
+ version_requirements: *70228764764000
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: faye-websocket
63
- requirement: &70165766288280 !ruby/object:Gem::Requirement
63
+ requirement: &70228764763580 !ruby/object:Gem::Requirement
64
64
  none: false
65
65
  requirements:
66
66
  - - ! '>='
@@ -68,10 +68,10 @@ dependencies:
68
68
  version: '0'
69
69
  type: :runtime
70
70
  prerelease: false
71
- version_requirements: *70165766288280
71
+ version_requirements: *70228764763580
72
72
  - !ruby/object:Gem::Dependency
73
73
  name: em-http-request
74
- requirement: &70165766287720 !ruby/object:Gem::Requirement
74
+ requirement: &70228764763080 !ruby/object:Gem::Requirement
75
75
  none: false
76
76
  requirements:
77
77
  - - ~>
@@ -79,10 +79,10 @@ dependencies:
79
79
  version: 1.0.0
80
80
  type: :runtime
81
81
  prerelease: false
82
- version_requirements: *70165766287720
82
+ version_requirements: *70228764763080
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: json
85
- requirement: &70165766287300 !ruby/object:Gem::Requirement
85
+ requirement: &70228764762660 !ruby/object:Gem::Requirement
86
86
  none: false
87
87
  requirements:
88
88
  - - ! '>='
@@ -90,10 +90,10 @@ dependencies:
90
90
  version: '0'
91
91
  type: :runtime
92
92
  prerelease: false
93
- version_requirements: *70165766287300
93
+ version_requirements: *70228764762660
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: rspec
96
- requirement: &70165766286840 !ruby/object:Gem::Requirement
96
+ requirement: &70228764762200 !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
99
  - - ! '>='
@@ -101,10 +101,10 @@ dependencies:
101
101
  version: '0'
102
102
  type: :development
103
103
  prerelease: false
104
- version_requirements: *70165766286840
104
+ version_requirements: *70228764762200
105
105
  - !ruby/object:Gem::Dependency
106
106
  name: webmock
107
- requirement: &70165766604500 !ruby/object:Gem::Requirement
107
+ requirement: &70228764761760 !ruby/object:Gem::Requirement
108
108
  none: false
109
109
  requirements:
110
110
  - - ! '>='
@@ -112,10 +112,10 @@ dependencies:
112
112
  version: '0'
113
113
  type: :development
114
114
  prerelease: false
115
- version_requirements: *70165766604500
115
+ version_requirements: *70228764761760
116
116
  - !ruby/object:Gem::Dependency
117
117
  name: guard-rspec
118
- requirement: &70165766603880 !ruby/object:Gem::Requirement
118
+ requirement: &70228764761320 !ruby/object:Gem::Requirement
119
119
  none: false
120
120
  requirements:
121
121
  - - ! '>='
@@ -123,10 +123,10 @@ dependencies:
123
123
  version: '0'
124
124
  type: :development
125
125
  prerelease: false
126
- version_requirements: *70165766603880
126
+ version_requirements: *70228764761320
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: guard-bundler
129
- requirement: &70165766603280 !ruby/object:Gem::Requirement
129
+ requirement: &70228764760880 !ruby/object:Gem::Requirement
130
130
  none: false
131
131
  requirements:
132
132
  - - ! '>='
@@ -134,10 +134,10 @@ dependencies:
134
134
  version: '0'
135
135
  type: :development
136
136
  prerelease: false
137
- version_requirements: *70165766603280
137
+ version_requirements: *70228764760880
138
138
  - !ruby/object:Gem::Dependency
139
139
  name: guard-coffeescript
140
- requirement: &70165766602860 !ruby/object:Gem::Requirement
140
+ requirement: &70228764760460 !ruby/object:Gem::Requirement
141
141
  none: false
142
142
  requirements:
143
143
  - - ! '>='
@@ -145,10 +145,10 @@ dependencies:
145
145
  version: '0'
146
146
  type: :development
147
147
  prerelease: false
148
- version_requirements: *70165766602860
148
+ version_requirements: *70228764760460
149
149
  - !ruby/object:Gem::Dependency
150
150
  name: rainbows
151
- requirement: &70165766602440 !ruby/object:Gem::Requirement
151
+ requirement: &70228764760040 !ruby/object:Gem::Requirement
152
152
  none: false
153
153
  requirements:
154
154
  - - ! '>='
@@ -156,10 +156,10 @@ dependencies:
156
156
  version: '0'
157
157
  type: :development
158
158
  prerelease: false
159
- version_requirements: *70165766602440
159
+ version_requirements: *70228764760040
160
160
  - !ruby/object:Gem::Dependency
161
161
  name: thin
162
- requirement: &70165766602000 !ruby/object:Gem::Requirement
162
+ requirement: &70228764759620 !ruby/object:Gem::Requirement
163
163
  none: false
164
164
  requirements:
165
165
  - - ! '>='
@@ -167,10 +167,10 @@ dependencies:
167
167
  version: '0'
168
168
  type: :development
169
169
  prerelease: false
170
- version_requirements: *70165766602000
170
+ version_requirements: *70228764759620
171
171
  - !ruby/object:Gem::Dependency
172
172
  name: rack-test
173
- requirement: &70165766601580 !ruby/object:Gem::Requirement
173
+ requirement: &70228764759200 !ruby/object:Gem::Requirement
174
174
  none: false
175
175
  requirements:
176
176
  - - ! '>='
@@ -178,10 +178,10 @@ dependencies:
178
178
  version: '0'
179
179
  type: :development
180
180
  prerelease: false
181
- version_requirements: *70165766601580
181
+ version_requirements: *70228764759200
182
182
  - !ruby/object:Gem::Dependency
183
183
  name: async_rack_test
184
- requirement: &70165766601140 !ruby/object:Gem::Requirement
184
+ requirement: &70228764758760 !ruby/object:Gem::Requirement
185
185
  none: false
186
186
  requirements:
187
187
  - - ! '>='
@@ -189,10 +189,10 @@ dependencies:
189
189
  version: '0'
190
190
  type: :development
191
191
  prerelease: false
192
- version_requirements: *70165766601140
192
+ version_requirements: *70228764758760
193
193
  - !ruby/object:Gem::Dependency
194
194
  name: foreman
195
- requirement: &70165766600720 !ruby/object:Gem::Requirement
195
+ requirement: &70228764758240 !ruby/object:Gem::Requirement
196
196
  none: false
197
197
  requirements:
198
198
  - - ! '>='
@@ -200,10 +200,10 @@ dependencies:
200
200
  version: '0'
201
201
  type: :development
202
202
  prerelease: false
203
- version_requirements: *70165766600720
203
+ version_requirements: *70228764758240
204
204
  - !ruby/object:Gem::Dependency
205
205
  name: sprockets
206
- requirement: &70165766600300 !ruby/object:Gem::Requirement
206
+ requirement: &70228764757740 !ruby/object:Gem::Requirement
207
207
  none: false
208
208
  requirements:
209
209
  - - ! '>='
@@ -211,7 +211,7 @@ dependencies:
211
211
  version: '0'
212
212
  type: :development
213
213
  prerelease: false
214
- version_requirements: *70165766600300
214
+ version_requirements: *70228764757740
215
215
  description: Firehose is a realtime web application toolkit for building realtime
216
216
  Ruby web applications.
217
217
  email:
@@ -297,7 +297,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
297
297
  version: '0'
298
298
  segments:
299
299
  - 0
300
- hash: -2464332796124160988
300
+ hash: 966724050365286290
301
301
  required_rubygems_version: !ruby/object:Gem::Requirement
302
302
  none: false
303
303
  requirements: