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

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