riddl 1.0.16 → 1.0.18

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e69bd3fbaa637fa772cb4be483543ea4add9a415688d76c39a01aa3cc627f31e
4
- data.tar.gz: 9750596f66325159bc58d156cb1ff71e7fa6b862034d8bf6de25a5f3960a165d
3
+ metadata.gz: 6ca83f5fe99631dcde4aaa4ad94204809098fd7b23c500c6dc5b46cd065c7759
4
+ data.tar.gz: 78f8b8be0e7063354fbf1242dad5daf1f0302bd39cce9953fec99c2ed7c5a8f8
5
5
  SHA512:
6
- metadata.gz: 06f71177f1844c68b56a2c5a022882304f1932b5c21b961cf89dc1ff13f8c4338bc1c196d72e454035cbafd4acaaaaba4ddb41e0d9693dde0a9d8e1c8b090b1e
7
- data.tar.gz: fd9102d88a796ccb51f4cb881dc7694d04570b4159c80c5e0a4f5e99b8294b999bc01ec6329e8bbfbd5565eb19e9d991f7b6a8042e3d4b18d3e301417f08bab5
6
+ metadata.gz: 6563f7de6527989c4cd6f7a6dbb225ebd9da1ed9c593dcf355df00c90fbb5d80060972248eaebebdfe692ffd95dfbf5885ad70629ed1717bd7a1a95745090eab
7
+ data.tar.gz: a21e42d70f98fedd372a5911e007d23e2426a4a6d4406b2a8ac3ff26bdb565d2ec8e82558a7770286fb8b8fa7c8997d3b79981d9cb91662343345196e02afb56
@@ -36,6 +36,7 @@ end
36
36
 
37
37
  Riddl::Server.new(File.dirname(__FILE__) + '/description.xml', :port => 9001, :bind => '::') do
38
38
  accessible_description true
39
+ p @riddl_logger
39
40
 
40
41
  on resource do
41
42
  run Riddl::Utils::ERBServe, "static/info.txt" if get
@@ -4,26 +4,29 @@ require File.expand_path(File.dirname(__FILE__) + '/../../lib/ruby/riddl/server'
4
4
 
5
5
  class Bar < Riddl::Implementation
6
6
  def response
7
- $connections.each do |c|
8
- c.send('some data')
7
+ $connections_mutex.synchronize do
8
+ $connections.each do |c|
9
+ c.send('some data')
10
+ end
9
11
  end
10
12
  Riddl::Parameter::Complex.new("return","text/plain","hello world")
11
13
  end
12
14
  end
13
15
 
14
16
  $connections = []
17
+ $connections_mutex = Mutex.new
15
18
 
16
19
  class Echo < Riddl::SSEImplementation
17
20
  def onopen
18
21
  # if true (or any object for that matter) is returned, then 200 is sent
19
22
  # if false (or nil) is returned 404, the sse is denied
20
23
  puts "Connection established"
21
- $connections << self
24
+ $connections_mutex.synchronize { $connections << self }
22
25
  end
23
26
 
24
27
  def onclose
25
28
  puts "Connection closed"
26
- $connections.delete(self)
29
+ $connections_mutex.synchronize { $connections.delete(self) }
27
30
  end
28
31
  end
29
32
 
@@ -32,8 +35,10 @@ Riddl::Server.new(File.dirname(__FILE__) + '/description.xml', :port => 9292) do
32
35
 
33
36
  parallel do
34
37
  loop do
35
- $connections.each do |c|
36
- c.send('heartbeat')
38
+ $connections_mutex.synchronize do
39
+ $connections.each do |c|
40
+ c.send('heartbeat')
41
+ end
37
42
  end
38
43
  sleep 15
39
44
  end
@@ -14,21 +14,23 @@
14
14
  <data type="anyURI"/>
15
15
  </attribute>
16
16
  <ref name="riddl-description-other-atts"/>
17
- <zeroOrMore>
18
- <choice>
19
- <element name="message">
20
- <ref name="riddl-description-other-atts"/>
21
- <ref name="riddl-message"/>
22
- </element>
23
- <element name="transformation">
24
- <ref name="riddl-description-other-atts"/>
25
- <ref name="riddl-transformation"/>
26
- </element>
27
- </choice>
28
- </zeroOrMore>
29
- <element name="resource">
30
- <ref name="riddl-resource-dynamic"/>
31
- </element>
17
+ <interleave>
18
+ <zeroOrMore>
19
+ <choice>
20
+ <element name="message">
21
+ <ref name="riddl-description-other-atts"/>
22
+ <ref name="riddl-message"/>
23
+ </element>
24
+ <element name="transformation">
25
+ <ref name="riddl-description-other-atts"/>
26
+ <ref name="riddl-transformation"/>
27
+ </element>
28
+ </choice>
29
+ </zeroOrMore>
30
+ <element name="resource">
31
+ <ref name="riddl-resource-dynamic"/>
32
+ </element>
33
+ </interleave>
32
34
  </define>
33
35
 
34
36
  <define name="riddl-message">
@@ -199,7 +201,6 @@
199
201
  <data type="anyURI"/>
200
202
  </attribute>
201
203
  </optional>
202
- <ref name="riddl-description-others"/>
203
204
  </define>
204
205
 
205
206
  <define name="riddl-resource-recursive-choice">
@@ -222,21 +223,28 @@
222
223
  <data type="boolean"/>
223
224
  </attribute>
224
225
  </optional>
225
- <ref name="riddl-resource-sub"/>
226
+ </define>
227
+
228
+ <define name="riddl-resource-body">
229
+ <interleave>
230
+ <ref name="riddl-description-others"/>
231
+ <ref name="riddl-resource-content"/>
232
+ <ref name="riddl-resource-sub"/>
233
+ </interleave>
226
234
  </define>
227
235
 
228
236
  <define name="riddl-resource-dynamic">
229
237
  <ref name="riddl-resource-attributes"/>
230
- <ref name="riddl-resource-content"/>
231
238
  <ref name="riddl-resource-recursive-choice"/>
239
+ <ref name="riddl-resource-body"/>
232
240
  </define>
233
241
  <define name="riddl-resource-named">
234
242
  <ref name="riddl-resource-attributes"/>
235
243
  <attribute name="relative">
236
244
  <ref name="riddl-datatype-relpath"/>
237
245
  </attribute>
238
- <ref name="riddl-resource-content"/>
239
246
  <ref name="riddl-resource-recursive-choice"/>
247
+ <ref name="riddl-resource-body"/>
240
248
  </define>
241
249
 
242
250
  <define name="riddl-resource-sub">
@@ -18,7 +18,8 @@ module Riddl
18
18
  if part.respond_to?(:read)
19
19
  part.rewind if part.respond_to?(:rewind)
20
20
  while chunk = part.read(CHUNK_SIZE)
21
- yield chunk
21
+ yield chunk
22
+ end
22
23
  else
23
24
  yield part.to_s
24
25
  end
@@ -1,72 +1,168 @@
1
1
  module Riddl
2
2
  module Protocols
3
- class SSE
3
+ module SSE
4
4
  class Error < RuntimeError; end
5
5
 
6
6
  class ParserData
7
7
  attr_accessor :headers, :request_path, :query_string, :http_method, :body, :request_url
8
8
  end
9
9
 
10
- class DeferrableBody
11
- include EventMachine::Deferrable
10
+ class Base #{{{
11
+ def initialize(app, env)
12
+ @app = app
13
+ @env = env
14
+ @closed = true
15
+ end
12
16
 
13
- def call(body)
14
- body.each do |chunk|
15
- @body_callback.call(chunk)
16
- end
17
- end
17
+ def send_with_id(id, data)
18
+ return if closed?
19
+ push("#{id}: #{data}" + EOL + EOL)
20
+ end
18
21
 
19
- def each(&blk)
20
- @body_callback = blk
21
- end
22
- end
22
+ def close
23
+ return if closed?
24
+ @closed = true
25
+ do_close
26
+ end
23
27
 
24
- def send_with_id(id, data)
25
- EM.next_tick do
26
- @body.call [ "#{id}: #{data}" + EOL + EOL ]
28
+ def closed?
29
+ @closed
27
30
  end
28
- end
29
31
 
30
- def close
31
- EM.next_tick do
32
- @body.succeed
32
+ def trigger_on_open; @closed = false; res = @app.onopen; res ? true : false; end
33
+ def trigger_on_close; @closed = true; @app.onclose; end
34
+
35
+ def response_headers(cross_site_xhr)
36
+ headers = {
37
+ 'Content-Type' => 'text/event-stream',
38
+ 'Cache-Control' => 'no-cache',
39
+ 'X-Accel-Buffering' => 'no'
40
+ }
41
+ if @env['HTTP_ORIGIN'] && cross_site_xhr
42
+ headers['Access-Control-Allow-Origin'] = '*'
43
+ headers['Access-Control-Max-Age'] = '0'
44
+ end
45
+ headers
33
46
  end
34
- end
47
+ protected :response_headers
48
+ end #}}}
35
49
 
36
- def trigger_on_open(); @closed = false; res = @app.onopen; res ? true : false; end
37
- def trigger_on_close; @closed = true; @app.onclose; end
50
+ class Thin < Base #{{{
51
+ class DeferrableBody #{{{
52
+ include EventMachine::Deferrable
38
53
 
39
- def initialize(app, env)
40
- @app = app
41
- @env = env
42
- @env['async.close'].callback { trigger_on_close }
43
- @body = DeferrableBody.new
44
- @closed = true
45
- end
54
+ def call(body)
55
+ body.each { |chunk| @body_callback.call(chunk) }
56
+ end
57
+
58
+ def each(&blk)
59
+ @body_callback = blk
60
+ end
61
+ end #}}}
62
+
63
+ def initialize(app, env)
64
+ super
65
+ @env['async.close'].callback { trigger_on_close }
66
+ @body = DeferrableBody.new
67
+ end
68
+
69
+ def dispatch(data, cross_site_xhr)
70
+ headers = response_headers(cross_site_xhr)
71
+ EventMachine::next_tick {
72
+ if trigger_on_open
73
+ @env['async.callback'].call [200, headers, @body]
74
+ else
75
+ @body.fail
76
+ @env['async.callback'].call [404, headers, {}]
77
+ end
78
+ }
79
+ nil
80
+ end
81
+
82
+ def push(str)
83
+ EM.next_tick { @body.call [str] }
84
+ end
85
+ protected :push
86
+
87
+ def do_close
88
+ EM.next_tick { @body.succeed }
89
+ end
90
+ protected :do_close
91
+ end #}}}
46
92
 
93
+ class Generic < Base # other servers like PUMA
94
+ class QueueBody #{{{
95
+ def initialize
96
+ @queue = Queue.new
97
+ @on_close = nil
98
+ @closed_fired = false
99
+ end
100
+
101
+ def on_close(&blk)
102
+ @on_close = blk
103
+ end
104
+
105
+ def call(chunk)
106
+ Array(chunk).each { |c| @queue << chunk_encode(c) }
107
+ end
47
108
 
48
- def dispatch(data,cross_site_xhr)
49
- headers = {
50
- 'Content-Type' => 'text/event-stream',
51
- 'Cache-Control' => 'no-cache',
52
- 'X-Accel-Buffering' => 'no'
53
- }
54
- if @env['HTTP_ORIGIN'] && cross_site_xhr
55
- headers['Access-Control-Allow-Origin'] = '*'
56
- headers['Access-Control-Max-Age'] = '0'
109
+ def each
110
+ loop do
111
+ chunk = @queue.pop
112
+ break if chunk.nil?
113
+ begin
114
+ yield chunk
115
+ rescue IOError, Errno::EPIPE, Errno::ECONNRESET
116
+ break
117
+ end
118
+ end
119
+ ensure
120
+ fire_on_close
121
+ end
122
+
123
+ def close
124
+ @queue << "0#{EOL}#{EOL}"
125
+ @queue << nil
126
+ end
127
+
128
+ def chunk_encode(c)
129
+ c.bytesize.to_s(16) + EOL + c + EOL
130
+ end
131
+ private :chunk_encode
132
+
133
+ def fire_on_close
134
+ return if @closed_fired
135
+ @closed_fired = true
136
+ @on_close.call if @on_close
137
+ end
138
+ private :fire_on_close
139
+ end #}}}
140
+
141
+ def initialize(app, env)
142
+ super
143
+ @body = QueueBody.new
144
+ @body.on_close { trigger_on_close }
57
145
  end
58
- EventMachine::next_tick {
146
+
147
+ def dispatch(data, cross_site_xhr)
148
+ headers = response_headers(cross_site_xhr).merge('Transfer-Encoding' => 'chunked')
59
149
  if trigger_on_open
60
- @env['async.callback'].call [200, headers, @body]
150
+ [200, headers, @body]
61
151
  else
62
- @body.fail
63
- @env['async.callback'].call [404, headers, {}]
152
+ @body.close
153
+ [404, headers, []]
64
154
  end
65
- }
66
- end
155
+ end
156
+
157
+ def push(str)
158
+ @body.call [str]
159
+ end
160
+ protected :push
67
161
 
68
- def closed?
69
- @closed
162
+ def do_close
163
+ @body.close
164
+ end
165
+ protected :do_close
70
166
  end
71
167
  end
72
168
  end
@@ -1,109 +1,44 @@
1
- gem 'em-websocket', '>= 0.4.0'
2
- require 'em-websocket'
3
- require 'thin'
4
-
5
- module Thin #{{{
6
- class Connection
7
- attr_accessor :websocket
8
- def websocket?
9
- !self.websocket.nil?
10
- end
11
-
12
- def pre_process_with_websocket
13
- @request.env['thin.connection'] = self
14
- pre_process_without_websocket
15
- end
16
- alias :pre_process_without_websocket :pre_process
17
- alias :pre_process :pre_process_with_websocket
18
-
19
- def receive_data_with_websocket(data)
20
- if self.websocket?
21
- self.websocket.receive_data(data)
22
- else
23
- receive_data_without_websocket(data)
24
- end
25
- end
26
- alias :receive_data_without_websocket :receive_data
27
- alias :receive_data :receive_data_with_websocket
28
-
29
- def unbind_with_websocket
30
- if self.websocket?
31
- self.websocket.unbind
32
- else
33
- unbind_without_websocket
34
- end
35
- end
36
- alias :unbind_without_websocket :unbind
37
- alias :unbind :unbind_with_websocket
38
- end
39
- end #}}}
40
-
41
- module EventMachine
42
- module WebSocket
43
- class Handshake
44
- def receive_data(data)
45
- data.request_url = Riddl::Protocols::Utils::escape(data.request_url)
46
- @parser = data
47
- @headers = data.headers
48
- process(@headers, data.body)
49
- end
50
- end
51
- end
52
- end
1
+ require 'faye/websocket'
2
+ Faye::WebSocket.load_adapter('thin')
53
3
 
54
4
  module Riddl
55
5
  module Protocols
56
- class WebSocket < ::EventMachine::WebSocket::Connection
6
+ class WebSocket
57
7
  class Error < RuntimeError; end
58
8
 
59
- class ParserData
60
- attr_accessor :headers, :request_path, :query_string, :http_method, :body, :request_url
61
- def match(what)
62
- @body =~ what
63
- end
64
- def upgrade?
65
- true
66
- end
67
- end
9
+ def initialize(app, env)
10
+ @app = app
11
+ @closed = true
12
+ @socket = Faye::WebSocket.new(env)
68
13
 
69
- def self.new(*args)
70
- instance = allocate
71
- instance.__send__(:initialize, *args)
72
- instance
14
+ @socket.on(:open) { trigger_on_open }
15
+ @socket.on(:message) { |event| trigger_on_message(event.data) }
16
+ @socket.on(:close) { trigger_on_close }
17
+ @socket.on(:error) { |event| trigger_on_error(event.message) }
73
18
  end
74
19
 
75
- def send_data(data)
76
- EM.next_tick do
77
- @socket.send_data(data) unless closed?
78
- end
20
+ def dispatch
21
+ @socket.rack_response
79
22
  end
80
23
 
81
- def close_connection(*args)
82
- EM.next_tick do
83
- unless closed?
84
- @socket.close_connection(*args)
85
- trigger_on_close
86
- end
87
- end
24
+ def send(data)
25
+ EM.next_tick { @socket.send(data) unless closed? }
88
26
  end
89
27
 
90
- def trigger_on_message(msg); @app.onmessage(msg); end
91
- def trigger_on_open(handshake); @closed = false; @app.onopen; end
92
- def trigger_on_close; @closed = true; @app.onclose; end
93
- def trigger_on_error(error); @closed = true; @app.onerror(error); true; end
94
-
95
- def initialize(app, socket)
96
- @app = app
97
- @socket = socket
98
- @ssl = socket.backend.respond_to?(:ssl?) && socket.backend.ssl?
99
- @closed = true
100
- socket.websocket = self
101
- socket.comm_inactivity_timeout = 0
28
+ def close_connection
29
+ EM.next_tick { @socket.close unless closed? }
102
30
  end
103
31
 
104
32
  def closed?
105
33
  @closed
106
34
  end
35
+
36
+ private
37
+
38
+ def trigger_on_open; @closed = false; @app.onopen; end
39
+ def trigger_on_message(m); @app.onmessage(m); end
40
+ def trigger_on_close; @closed = true; @app.onclose; end
41
+ def trigger_on_error(msg); @app.onerror(msg); end
107
42
  end
108
43
  end
109
44
  end