firehose 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/Procfile CHANGED
@@ -1 +1 @@
1
- firehose: firehose start
1
+ firehose: firehose server
data/README.md CHANGED
@@ -31,7 +31,7 @@ $ gem install firehose
31
31
  Now fire up the server.
32
32
 
33
33
  ```ruby
34
- $ firehose start
34
+ $ firehose server
35
35
  >> Thin web server (v1.3.1 codename Triple Espresso)
36
36
  >> Maximum connections set to 1024
37
37
  >> Listening on 127.0.0.1:7478, CTRL+C to stop
@@ -72,27 +72,23 @@ Firehose doesn't just stop at curl; it has a full-featured JavaScript client tha
72
72
  Still have the server running? Copy and paste the code below into Firebug or the WebKit console.
73
73
 
74
74
  ```javascript
75
- new Firehose.Client()
76
- .url({
77
- websocket: 'ws://localhost:7478/hello',
78
- longpoll: 'http://localhost:7478/hello'
79
- })
80
- .params({
81
- cid: '024023948234'
82
- })
83
- .options({
84
- timeout: 5000
85
- })
86
- .message(function(msg){
75
+ new Firehose.Client({
76
+ message: function(msg){
87
77
  console.log(msg);
88
- })
89
- .connected(function(){
90
- console.log('Howdy friend!');
91
- })
92
- .disconnected(function(){
93
- console.log('Bu bye.');
94
- })
95
- .connect()
78
+ },
79
+ connected: function(){
80
+ console.log("Great Scotts!! We're connected!");
81
+ },
82
+ disconnected: function(){
83
+ console.log("Well shucks, we're not connected anymore");
84
+ },
85
+ error: function(){
86
+ console.log("Well then, something went horribly wrong.");
87
+ },
88
+ // Note that we do NOT specify a protocol here because we don't
89
+ // know that yet.
90
+ uri: '//localhost:7478/hello'
91
+ }).connect();
96
92
  ```
97
93
 
98
94
  Then publish another message.
@@ -1,8 +1,45 @@
1
1
  class Firehose.Client
2
- constructor: (args) ->
3
- @transports = ['WebSocket', 'LongPoll']
2
+ # Transports that are available to Firehose.
3
+ @transports: ['WebSocket', 'LongPoll']
4
4
 
5
+ # Generate a random client_id.
6
+ @nextClientId: ->
7
+ Math.floor((Math.random()*99999999999)+1)
8
+
9
+ constructor: (config={}) ->
10
+ # The clientId is used by the server to remember messages between requests. In a production environment,
11
+ # this should probably be some combination of "user_id-rand". Why the rand? Because a user may have multiple
12
+ # tabs open to the application, and each tab needs a different channel on the server.
13
+ config.clientId ||= Firehose.Client.nextClientId()
14
+ # List of transport stragies we have to use.
15
+ config.transports ||= Firehose.Client.transports
16
+ # Empty handler for messages.
17
+ config.message ||= ->
18
+ # Empty handler for error handling.
19
+ config.error ||= ->
20
+ # Empty handler for when we establish a connection.
21
+ config.connected ||= ->
22
+ # Empty handler for when we're disconnected.
23
+ config.disconnected ||= ->
24
+ # Additional options.
25
+ config.options ||= {}
26
+ # URL that we'll connect to.
27
+ config.uri ||= undefined
28
+ # Params that we'll tack on to the URL. We include a clientId in here for kicks.
29
+ config.params ||= { cid: config.clientId }
30
+ # Do stuff before we send the message into config.message. The sensible
31
+ # default on the webs is to parse JSON.
32
+ config.parse ||= (body) ->
33
+ $.parseJSON(body)
34
+
35
+ # Hang on to these config for when we connect.
36
+ @config = config
37
+ # Make sure we return ourself out of the constructor so we can chain.
38
+ this
39
+
40
+ connect: =>
5
41
  # Figure out what transport is supported and return it.
6
- for transport in @transports
42
+ # TODO if the initial connection fails, try the next transport mmmkay?
43
+ for transport in @config.transports
7
44
  if transport = Firehose[transport]
8
- return new transport(args) if transport.supported()
45
+ return new transport(@config).connect() if transport.supported()
@@ -5,22 +5,28 @@ class Firehose.LongPoll extends Firehose.Transport
5
5
 
6
6
  @supported: =>
7
7
  # IE 8+, FF 3.5+, Chrome 4+, Safari 4+, Opera 12+, iOS 3.2+, Android 2.1+
8
- $.support.cors || LongPoll.ieSupported()
8
+ $.support.cors || Firehose.LongPoll.ieSupported()
9
9
 
10
10
  constructor: (args) ->
11
11
  super args
12
12
 
13
+ # Configrations specifically for web sockets
14
+ @config.longPoll ||= {}
15
+ # Protocol schema we should use for talking to WS server.
16
+ @config.longPoll.url ||= "http:#{@config.uri}"
17
+
13
18
  # We use the lag time to make the client live longer than the server.
14
19
  @_lagTime = 5000
15
- @_timeout = @options.timeout + @_lagTime
16
- @_dataType = "json"
20
+ @_timeout = @config.options.timeout + @_lagTime
17
21
  @_offlineTimer
18
22
  @_okInterval = 0
19
23
 
20
24
  @registerIETransport()
21
25
 
22
26
  registerIETransport: =>
23
- if LongPoll.ieSupported()
27
+ if Firehose.LongPoll.ieSupported()
28
+ # TODO - Ask Steel what this is for. Looks like some kind of polygot fill, but I want
29
+ # to take the 'json' transport out and do that myself.
24
30
  $.ajaxTransport 'json', (options, orignalOptions, jqXhr) ->
25
31
  xdr = null
26
32
  send: (_, callback) ->
@@ -44,15 +50,14 @@ class Firehose.LongPoll extends Firehose.Transport
44
50
  $.support.cors = true;
45
51
 
46
52
  connect: (delay = 0) =>
47
- @onConnected()
53
+ @config.connected()
48
54
  super(delay)
49
55
 
50
56
  _request: =>
51
- $.ajax @url["longpoll"],
57
+ $.ajax @config.longPoll.url,
52
58
  crossDomain: true
53
59
  cache: false
54
- dataType: @_dataType
55
- data: @params
60
+ data: @config.params
56
61
  timeout: @_timeout
57
62
  success: @_success
58
63
  error: @_error
@@ -67,13 +72,13 @@ class Firehose.LongPoll extends Firehose.Transport
67
72
  # in thise case
68
73
  @connect(@_okInterval)
69
74
  else
70
- @onMessage(data)
75
+ @config.message(@config.parse(data))
71
76
  @connect(@_okInterval)
72
77
 
73
78
  # We need this custom handler to have the connection status
74
79
  # properly displayed
75
80
  _error: (jqXhr, status, error) =>
76
81
  clearTimeout(@_offlineTimer)
77
- @onDisconnected()
78
- @_offlineTimer = setTimeout(@onConnected, @_errorInterval + @_lagTime)
79
- @connect(@_errorInterval)
82
+ @config.disconnected()
83
+ @_offlineTimer = setTimeout(@config.connected, @_retryDelay + @_lagTime)
84
+ @connect(@_retryDelay)
@@ -1,21 +1,15 @@
1
1
  class Firehose.Transport
2
- # Class method to determine whether transport is supported by the current browser
2
+ # Class method to determine whether transport is supported by the current browser. Note that while
3
+ # the transport may be supported by the browser, its possible that the network connection won't
4
+ # succeed. That should be accounted for during the initial connecting to the server.
3
5
  @supported: =>
4
6
  false
5
7
 
6
- constructor: (args) ->
7
- @_errorInterval = 5000
8
+ constructor: (config={}) ->
9
+ @config = config
10
+ @_retryDelay = 5000
8
11
 
9
- # Chainable config
10
- connected: (@onConnected) -> this
11
- disconnected: (@onDisconnected) -> this
12
- message: (@onMessage) -> this
13
- error: (@onError) -> this
14
- url: (@url) -> this
15
- params: (@params) -> this
16
- options: (@options) -> this
17
-
18
- # Lets rock'n'roll
12
+ # Lets rock'n'roll! Connect to the server.
19
13
  connect: (delay = 0) =>
20
14
  setTimeout =>
21
15
  @_request()
@@ -28,13 +22,13 @@ class Firehose.Transport
28
22
  # Default error handler
29
23
  _error: (event) =>
30
24
  # Lets try to connect again with delay
31
- @onDisconnected()
32
- @connect(@_errorInterval)
25
+ @config.disconnected()
26
+ @connect(@_retryDelay)
33
27
 
34
28
  # Default connection established handler
35
29
  _open: (event) =>
36
- @onConnected()
30
+ @config.connected()
37
31
 
38
32
  # Default connection closed handler
39
33
  _close: (event) =>
40
- @onDisconnected()
34
+ @config.disconnected()
@@ -10,24 +10,28 @@ class Firehose.WebSocket extends Firehose.Transport
10
10
  constructor: (args) ->
11
11
  super args
12
12
 
13
- # Set flash socket path.
14
- WebSocket.__swfLocation = "/flash/firehose/WebSocketMain.swf"
15
-
16
- # Mozilla decided to have their own implementation of Web Sockets so detect for that
13
+ # Configrations specifically for web sockets
14
+ @config.webSocket ||= {}
15
+ # Protocol schema we should use for talking to WS server.
16
+ @config.webSocket.url ||= "ws:#{@config.uri}?#{$.param(@config.params)}"
17
+ # Path of the swf WebSocket that we use in non-WS flash browsers.
18
+ @config.webSocket.swf_path ||= "/flash/firehose/WebSocketMain.swf"
19
+
20
+ # Set flash socket path for the WS SWF polyfill.
21
+ WebSocket.__swfLocation = @config.webSocket.swf_path
22
+
23
+ # Mozilla decided to have their own implementation of Web Sockets so detect for that.
17
24
  window.WebSocket = window.MozWebSocket if window["MozWebSocket"] and window.MozWebSocket
18
25
 
19
26
  _request: =>
20
- @socket = new window.WebSocket(@url["websocket"] + "?" + $.param(@params))
27
+ @socket = new window.WebSocket(@config.webSocket.url)
21
28
  @socket.onopen = @_open
22
29
  @socket.onclose = @_close
23
30
  @socket.onerror = @_error
24
31
  @socket.onmessage = @_message
25
32
 
26
33
  _message: (event) =>
27
- try
28
- @onMessage($.parseJSON(event.data))
29
- catch e # If JSON parsing doesn't work, send the rest of it on through
30
- @onMessage(event.data)
34
+ @config.message(@config.parse(event.data))
31
35
 
32
36
  _close: (event) =>
33
37
  if !event || (event and !event.wasClean)
@@ -44,5 +48,5 @@ class Firehose.WebSocket extends Firehose.Transport
44
48
  @socket.onmessage = null
45
49
  @socket.close()
46
50
  delete(@socket)
47
-
51
+
48
52
  super
data/lib/firehose/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'thor'
2
+ require 'thin'
2
3
 
3
4
  module Firehose
4
5
  class CLI < Thor
@@ -6,12 +7,9 @@ module Firehose
6
7
  method_option :port, :type => :numeric, :default => 7474, :required => true, :aliases => '-p'
7
8
  method_option :host, :type => :string, :default => '0.0.0.0', :required => true, :aliases => '-h'
8
9
  def server
9
- require 'thin'
10
-
11
10
  server = Thin::Server.new(options[:host], options[:port]) do
12
11
  run Firehose::Rack::App.new
13
12
  end
14
-
15
13
  server.start!
16
14
  end
17
15
  end
data/lib/firehose/rack.rb CHANGED
@@ -10,19 +10,50 @@ module Firehose
10
10
  cid = req.params['cid']
11
11
  path = req.path
12
12
  method = req.request_method
13
+ timeout = 30
14
+ cors_headers = {
15
+ 'Access-Control-Allow-Origin' => env['HTTP_ORIGIN'],
16
+ 'Access-Control-Allow-Methods' => 'GET',
17
+ 'Access-Control-Max-Age' => '1728000'
18
+ }
13
19
 
14
20
  case method
15
21
  # GET is how clients subscribe to the queue. When a messages comes in, we flush out a response,
16
22
  # close down the requeust, and the client then reconnects.
17
23
  when 'GET'
24
+ p [:subscribed, cid, path]
25
+
18
26
  EM.next_tick do
27
+ # Setup a subscription with a client id. We haven't subscribed yet here.
19
28
  subscription = Firehose::Subscription.new(cid)
29
+
30
+ # Setup a timeout timer to tell clients that time out that everything is OK
31
+ # and they should come back for more
32
+ timer = EM.add_timer(timeout) do
33
+ # We send a 204 OK to tell the client to reconnect.
34
+ env['async.callback'].call [204, cors_headers, []]
35
+ p [:timeout]
36
+ end
37
+
38
+ # Ok, now subscribe to the subscription.
20
39
  subscription.subscribe path do |payload|
21
40
  subscription.unsubscribe
22
- env['async.callback'].call([200, {}, [payload]])
41
+ subscription = nil # Set this to nil so that our heart beat timer doesn't try to double unsub.
42
+ EM.cancel_timer timer # Turn off the heart beat so we don't execute any of that business.
43
+ env['async.callback'].call [200, cors_headers, [payload]]
44
+ end
45
+
46
+ # Unsubscribe from the subscription if its still open and something bad happened
47
+ # or the heart beat triggered before we could finish.
48
+ env['async.close'].callback do
49
+ if subscription
50
+ subscription.unsubscribe
51
+ p [:close_unsubscription]
52
+ end
23
53
  end
24
54
  end
25
55
 
56
+ # Tell the web server that this will be an async response.
26
57
  Firehose::Rack::AsyncResponse
27
58
 
28
59
  # PUT is how we throw messages on to the fan-out queue.
@@ -33,7 +64,7 @@ module Firehose
33
64
 
34
65
  [202, {}, []]
35
66
  else
36
- [501, {}, ["#{method} not supported."]]
67
+ [501, {'Content-Type' => 'text/plain'}, ["#{method} not supported."]]
37
68
  end
38
69
  end
39
70
  end
@@ -1,4 +1,4 @@
1
1
  module Firehose
2
- VERSION = "0.0.8"
3
- CODENAME = "Trickle"
2
+ VERSION = "0.0.9"
3
+ CODENAME = "Garden Hose"
4
4
  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.0.8
4
+ version: 0.0.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-04-18 00:00:00.000000000 Z
13
+ date: 2012-04-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: eventmachine
17
- requirement: &70270938629600 !ruby/object:Gem::Requirement
17
+ requirement: &70248633218840 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 1.0.0.beta
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70270938629600
25
+ version_requirements: *70248633218840
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: amqp
28
- requirement: &70270938629080 !ruby/object:Gem::Requirement
28
+ requirement: &70248633201380 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 0.9.4
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70270938629080
36
+ version_requirements: *70248633201380
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: thin
39
- requirement: &70270938628700 !ruby/object:Gem::Requirement
39
+ requirement: &70248633200480 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :runtime
46
46
  prerelease: false
47
- version_requirements: *70270938628700
47
+ version_requirements: *70248633200480
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: thor
50
- requirement: &70270938628240 !ruby/object:Gem::Requirement
50
+ requirement: &70248633199280 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: '0'
56
56
  type: :runtime
57
57
  prerelease: false
58
- version_requirements: *70270938628240
58
+ version_requirements: *70248633199280
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: websocket-rack
61
- requirement: &70270938627800 !ruby/object:Gem::Requirement
61
+ requirement: &70248633198520 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ! '>='
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: '0'
67
67
  type: :runtime
68
68
  prerelease: false
69
- version_requirements: *70270938627800
69
+ version_requirements: *70248633198520
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rspec
72
- requirement: &70270938627240 !ruby/object:Gem::Requirement
72
+ requirement: &70248633197580 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ! '>='
@@ -77,10 +77,10 @@ dependencies:
77
77
  version: '0'
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *70270938627240
80
+ version_requirements: *70248633197580
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: rack-test
83
- requirement: &70270938626720 !ruby/object:Gem::Requirement
83
+ requirement: &70248633196960 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
86
  - - ! '>='
@@ -88,10 +88,10 @@ dependencies:
88
88
  version: '0'
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *70270938626720
91
+ version_requirements: *70248633196960
92
92
  - !ruby/object:Gem::Dependency
93
93
  name: guard-rspec
94
- requirement: &70270938626240 !ruby/object:Gem::Requirement
94
+ requirement: &70248633195800 !ruby/object:Gem::Requirement
95
95
  none: false
96
96
  requirements:
97
97
  - - ! '>='
@@ -99,10 +99,10 @@ dependencies:
99
99
  version: '0'
100
100
  type: :development
101
101
  prerelease: false
102
- version_requirements: *70270938626240
102
+ version_requirements: *70248633195800
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: guard-bundler
105
- requirement: &70270938625660 !ruby/object:Gem::Requirement
105
+ requirement: &70248633195200 !ruby/object:Gem::Requirement
106
106
  none: false
107
107
  requirements:
108
108
  - - ! '>='
@@ -110,10 +110,10 @@ dependencies:
110
110
  version: '0'
111
111
  type: :development
112
112
  prerelease: false
113
- version_requirements: *70270938625660
113
+ version_requirements: *70248633195200
114
114
  - !ruby/object:Gem::Dependency
115
115
  name: em-http-request
116
- requirement: &70270938624920 !ruby/object:Gem::Requirement
116
+ requirement: &70248633178620 !ruby/object:Gem::Requirement
117
117
  none: false
118
118
  requirements:
119
119
  - - ~>
@@ -121,10 +121,10 @@ dependencies:
121
121
  version: 0.3.0
122
122
  type: :development
123
123
  prerelease: false
124
- version_requirements: *70270938624920
124
+ version_requirements: *70248633178620
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: guard-coffeescript
127
- requirement: &70270938624500 !ruby/object:Gem::Requirement
127
+ requirement: &70248633177760 !ruby/object:Gem::Requirement
128
128
  none: false
129
129
  requirements:
130
130
  - - ! '>='
@@ -132,7 +132,7 @@ dependencies:
132
132
  version: '0'
133
133
  type: :development
134
134
  prerelease: false
135
- version_requirements: *70270938624500
135
+ version_requirements: *70248633177760
136
136
  description: Firehose is a realtime web application toolkit for building realtime
137
137
  Ruby web applications.
138
138
  email:
@@ -166,7 +166,6 @@ files:
166
166
  - lib/firehose/publisher.rb
167
167
  - lib/firehose/rack.rb
168
168
  - lib/firehose/subscription.rb
169
- - lib/firehose/thin.rb
170
169
  - lib/firehose/version.rb
171
170
  - spec/integrations/amqp_resources_spec.rb
172
171
  - spec/integrations/thin_spec.rb
data/lib/firehose/thin.rb DELETED
@@ -1,7 +0,0 @@
1
- require 'thin'
2
- require 'thin/websockets'
3
-
4
- module Firehose
5
- module Thin
6
- end
7
- end