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 +4 -4
- data/examples/library/server.rb +1 -0
- data/examples/sse/server.rb +11 -6
- data/lib/ruby/riddl/ns/description/1.0/description.rng +27 -19
- data/lib/ruby/riddl/protocols/http/generator.rb +2 -1
- data/lib/ruby/riddl/protocols/sse.rb +142 -46
- data/lib/ruby/riddl/protocols/websocket.rb +24 -89
- data/lib/ruby/riddl/server.rb +66 -45
- data/lib/ruby/riddl/wrapper.rb +20 -12
- data/ns/description/1.0/description.rng +27 -19
- data/riddl.gemspec +2 -2
- data/test/tc_sse.rb +82 -0
- data/test/tc_websocket.rb +29 -13
- data/tools/riddlcheck +8 -2
- data/tools/riddlprocess +6 -1
- metadata +12 -19
- data/examples/declaration-test/main.log +0 -57
- data/lib/ruby/riddl/protocols/generator.rb +0 -150
- data/lib/ruby/riddl/protocols/parser.rb +0 -208
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ca83f5fe99631dcde4aaa4ad94204809098fd7b23c500c6dc5b46cd065c7759
|
|
4
|
+
data.tar.gz: 78f8b8be0e7063354fbf1242dad5daf1f0302bd39cce9953fec99c2ed7c5a8f8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6563f7de6527989c4cd6f7a6dbb225ebd9da1ed9c593dcf355df00c90fbb5d80060972248eaebebdfe692ffd95dfbf5885ad70629ed1717bd7a1a95745090eab
|
|
7
|
+
data.tar.gz: a21e42d70f98fedd372a5911e007d23e2426a4a6d4406b2a8ac3ff26bdb565d2ec8e82558a7770286fb8b8fa7c8997d3b79981d9cb91662343345196e02afb56
|
data/examples/library/server.rb
CHANGED
data/examples/sse/server.rb
CHANGED
|
@@ -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
|
-
$
|
|
8
|
-
|
|
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
|
-
$
|
|
36
|
-
c
|
|
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
|
-
<
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
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
|
-
|
|
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">
|
|
@@ -1,72 +1,168 @@
|
|
|
1
1
|
module Riddl
|
|
2
2
|
module Protocols
|
|
3
|
-
|
|
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
|
|
11
|
-
|
|
10
|
+
class Base #{{{
|
|
11
|
+
def initialize(app, env)
|
|
12
|
+
@app = app
|
|
13
|
+
@env = env
|
|
14
|
+
@closed = true
|
|
15
|
+
end
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
end
|
|
17
|
+
def send_with_id(id, data)
|
|
18
|
+
return if closed?
|
|
19
|
+
push("#{id}: #{data}" + EOL + EOL)
|
|
20
|
+
end
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
def close
|
|
23
|
+
return if closed?
|
|
24
|
+
@closed = true
|
|
25
|
+
do_close
|
|
26
|
+
end
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@body.call [ "#{id}: #{data}" + EOL + EOL ]
|
|
28
|
+
def closed?
|
|
29
|
+
@closed
|
|
27
30
|
end
|
|
28
|
-
end
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
47
|
+
protected :response_headers
|
|
48
|
+
end #}}}
|
|
35
49
|
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
class Thin < Base #{{{
|
|
51
|
+
class DeferrableBody #{{{
|
|
52
|
+
include EventMachine::Deferrable
|
|
38
53
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
+
[200, headers, @body]
|
|
61
151
|
else
|
|
62
|
-
@body.
|
|
63
|
-
|
|
152
|
+
@body.close
|
|
153
|
+
[404, headers, []]
|
|
64
154
|
end
|
|
65
|
-
|
|
66
|
-
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def push(str)
|
|
158
|
+
@body.call [str]
|
|
159
|
+
end
|
|
160
|
+
protected :push
|
|
67
161
|
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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
|
|
6
|
+
class WebSocket
|
|
57
7
|
class Error < RuntimeError; end
|
|
58
8
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
76
|
-
|
|
77
|
-
@socket.send_data(data) unless closed?
|
|
78
|
-
end
|
|
20
|
+
def dispatch
|
|
21
|
+
@socket.rack_response
|
|
79
22
|
end
|
|
80
23
|
|
|
81
|
-
def
|
|
82
|
-
EM.next_tick
|
|
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
|
|
91
|
-
|
|
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
|