riddl 0.99.105

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.
Files changed (92) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +165 -0
  3. data/INSTALL +24 -0
  4. data/README.rdoc +2 -0
  5. data/Rakefile +17 -0
  6. data/TODO +17 -0
  7. data/contrib/riddl.jpg +0 -0
  8. data/contrib/riddl.png +0 -0
  9. data/contrib/riddl.svg +138 -0
  10. data/lib/riddl/client.rb +423 -0
  11. data/lib/riddl/commonlogger.rb +16 -0
  12. data/lib/riddl/constants.rb +5 -0
  13. data/lib/riddl/error.rb +8 -0
  14. data/lib/riddl/handlers.rb +14 -0
  15. data/lib/riddl/handlers/oauth.rb +19 -0
  16. data/lib/riddl/handlers/plain-type.rb +21 -0
  17. data/lib/riddl/handlers/relaxng.rb +16 -0
  18. data/lib/riddl/handlers/xmlschema.rb +16 -0
  19. data/lib/riddl/header.rb +9 -0
  20. data/lib/riddl/implementation.rb +57 -0
  21. data/lib/riddl/ns/common-patterns/addon-security/request.xml +25 -0
  22. data/lib/riddl/ns/common-patterns/addon-security/response.xml +25 -0
  23. data/lib/riddl/ns/common-patterns/downloadify/1.0/downloadify.xml +18 -0
  24. data/lib/riddl/ns/common-patterns/notifications-consumer/1.0/consumer.xml +100 -0
  25. data/lib/riddl/ns/common-patterns/notifications-producer/1.0/producer.xml +204 -0
  26. data/lib/riddl/ns/common-patterns/properties/1.0/properties.schema.schema +140 -0
  27. data/lib/riddl/ns/common-patterns/properties/1.0/properties.schema.xsl +89 -0
  28. data/lib/riddl/ns/common-patterns/properties/1.0/properties.xml +150 -0
  29. data/lib/riddl/ns/common/datatypes-1_0.rng +79 -0
  30. data/lib/riddl/ns/common/relaxng-modular.rng +330 -0
  31. data/lib/riddl/ns/common/relaxng.rng +10 -0
  32. data/lib/riddl/ns/declaration/1.0/declaration.rng +114 -0
  33. data/lib/riddl/ns/description/1.0/description.rng +302 -0
  34. data/lib/riddl/option.rb +9 -0
  35. data/lib/riddl/parameter.rb +54 -0
  36. data/lib/riddl/protocols/http/generator.rb +121 -0
  37. data/lib/riddl/protocols/http/parser.rb +199 -0
  38. data/lib/riddl/protocols/websocket.rb +103 -0
  39. data/lib/riddl/protocols/xmpp/generator.rb +176 -0
  40. data/lib/riddl/protocols/xmpp/parser.rb +118 -0
  41. data/lib/riddl/roles.rb +15 -0
  42. data/lib/riddl/roles/http%3A%2F%2Foauth.net%2F1.0%2Faccess_token.rb +30 -0
  43. data/lib/riddl/roles/http%3A%2F%2Foauth.net%2F1.0%2Fon_behalf.rb +22 -0
  44. data/lib/riddl/roles/http%3A%2F%2Foauth.net%2F1.0%2Frequest_token.rb +30 -0
  45. data/lib/riddl/roles/http%3A%2F%2Foauth.net%2F1.0/base.rb +67 -0
  46. data/lib/riddl/server.rb +519 -0
  47. data/lib/riddl/utils/description.rb +29 -0
  48. data/lib/riddl/utils/downloadify.rb +14 -0
  49. data/lib/riddl/utils/erbserve.rb +23 -0
  50. data/lib/riddl/utils/fileserve.rb +31 -0
  51. data/lib/riddl/utils/notifications_producer.rb +310 -0
  52. data/lib/riddl/utils/properties.rb +474 -0
  53. data/lib/riddl/utils/xsloverlay.rb +21 -0
  54. data/lib/riddl/wrapper.rb +280 -0
  55. data/lib/riddl/wrapper/declaration.rb +103 -0
  56. data/lib/riddl/wrapper/declaration/facade.rb +94 -0
  57. data/lib/riddl/wrapper/declaration/interface.rb +34 -0
  58. data/lib/riddl/wrapper/declaration/tile.rb +107 -0
  59. data/lib/riddl/wrapper/description.rb +69 -0
  60. data/lib/riddl/wrapper/description/access.rb +108 -0
  61. data/lib/riddl/wrapper/description/message_and_transformation.rb +131 -0
  62. data/lib/riddl/wrapper/description/resource.rb +271 -0
  63. data/lib/riddl/wrapper/layerchecker.rb +33 -0
  64. data/lib/riddl/wrapper/messageparser.rb +221 -0
  65. data/lib/riddl/wrapper/resourcechecker.rb +98 -0
  66. data/ns/common-patterns/addon-security/request.xml +25 -0
  67. data/ns/common-patterns/addon-security/response.xml +25 -0
  68. data/ns/common-patterns/downloadify/1.0/downloadify.xml +18 -0
  69. data/ns/common-patterns/notifications-consumer/1.0/consumer.xml +100 -0
  70. data/ns/common-patterns/notifications-producer/1.0/producer.xml +204 -0
  71. data/ns/common-patterns/properties/1.0/properties.schema.schema +140 -0
  72. data/ns/common-patterns/properties/1.0/properties.schema.xsl +89 -0
  73. data/ns/common-patterns/properties/1.0/properties.xml +150 -0
  74. data/ns/common/datatypes-1_0.rng +79 -0
  75. data/ns/common/relaxng-modular.rng +330 -0
  76. data/ns/common/relaxng.rng +10 -0
  77. data/ns/declaration/1.0/declaration.rng +114 -0
  78. data/ns/description/1.0/description.rng +302 -0
  79. data/riddl.gemspec +33 -0
  80. data/test/smartrunner.rb +48 -0
  81. data/test/tc_declaration-distributed.rb +79 -0
  82. data/test/tc_declaration-hybrid.rb +71 -0
  83. data/test/tc_declaration-local.rb +47 -0
  84. data/test/tc_helloworld.rb +17 -0
  85. data/test/tc_producer.rb +54 -0
  86. data/test/tc_properties.rb +72 -0
  87. data/tools/flash-policy-server.rb +12 -0
  88. data/tools/riddlcheck +36 -0
  89. data/tools/riddlcheck-1_0 +36 -0
  90. data/tools/riddlprocess +51 -0
  91. data/tools/riddlprocess-1_0 +51 -0
  92. metadata +291 -0
@@ -0,0 +1,118 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../constants')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../parameter')
3
+
4
+ module Riddl
5
+ module Protocols
6
+ module XMPP
7
+ class Parser
8
+ FORM_CONTENT_TYPES = [
9
+ #{{{
10
+ nil,
11
+ 'application/x-www-form-urlencoded'
12
+ #}}}
13
+ ].freeze
14
+
15
+ STD_ATTRIBUTES = [
16
+ #{{{
17
+ 'content-type',
18
+ 'content-disposition',
19
+ 'content-id',
20
+ 'content-transfer-type',
21
+ 'RIDDL-TYPE',
22
+ #}}}
23
+ ].freeze
24
+
25
+ def self::unescape(s)
26
+ #{{{
27
+ return s if s.nil?
28
+ s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
29
+ [$1.delete('%')].pack('H*')
30
+ }
31
+ #}}}
32
+ end
33
+
34
+ def parse_part(input,head,ctype,content_disposition,content_id,riddl_type)
35
+ #{{{
36
+ head = Hash[
37
+ head.map do |h|
38
+ STD_ATTRIBUTES.include?(h.qname.name) ? nil : [h.qname.name, value]
39
+ end.compact
40
+ ]
41
+ ctype = nil if riddl_type == 'simple'
42
+ filename = content_disposition[/ filename="?([^\";]*)"?/ni, 1]
43
+ name = content_disposition[/ name="?([^\";]*)"?/ni, 1] || content_id
44
+
45
+ if ctype || filename
46
+ body = Parameter::Tempfile.new("RiddlMultipart")
47
+ body.binmode if body.respond_to?(:binmode)
48
+ else
49
+ body = ''
50
+ end
51
+
52
+ body << input
53
+
54
+ add_to_params(name,body,filename,ctype,head)
55
+ #}}}
56
+ end
57
+
58
+ def add_to_params(name,body,filename,ctype,head)
59
+ #{{{
60
+ if filename == ""
61
+ # filename is blank which means no file has been selected
62
+ elsif filename && ctype
63
+ body.rewind
64
+
65
+ # Take the basename of the upload's original filename.
66
+ # This handles the full Windows paths given by Internet Explorer
67
+ # (and perhaps other broken user agents) without affecting
68
+ # those which give the lone filename.
69
+ filename =~ /^(?:.*[:\\\/])?(.*)/m
70
+ filename = $1
71
+
72
+ @params << Parameter::Complex.new(name, ctype, body, filename, head)
73
+ elsif !filename && ctype
74
+ body.rewind
75
+
76
+ # Generic multipart cases, not coming from a form
77
+ @params << Parameter::Complex.new(name, ctype, body, nil, head)
78
+ else
79
+ @params << Parameter::Simple.new(name, body, :body)
80
+ end
81
+ #}}}
82
+ end
83
+ private :add_to_params
84
+
85
+ def parse_nested_query(qs, type)
86
+ #{{{
87
+ (qs || '').split(/[#{D}] */n).each do |p|
88
+ k, v = self.class::unescape(p).split('=', 2)
89
+ @params << Parameter::Simple.new(k,v,type)
90
+ end
91
+ #}}}
92
+ end
93
+ private :parse_nested_query
94
+
95
+ def initialize(query_string,input)
96
+ #{{{
97
+ @params = Riddl::Parameter::Array.new
98
+
99
+ parse_nested_query(query_string,:query)
100
+
101
+ input.find('/message/xr:part').each do |p|
102
+ content_type = p.attributes['content-type'] || ''
103
+ media_type = content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
104
+ if FORM_CONTENT_TYPES.include?(media_type)
105
+ # sub is a fix for Safari Ajax postings that always append \0
106
+ parse_nested_query(p.text.sub(/\0\z/, ''),:body)
107
+ else
108
+ parse_part(p.text,p.attributes,content_type,p.attributes['content_disposition']||'',p.attributes['content-id']||'',p.attributes['RIDDL-TYPE']||'')
109
+ end
110
+ end
111
+ #}}}
112
+ end
113
+
114
+ attr_reader :params
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,15 @@
1
+ module Riddl
2
+ module Roles
3
+ @@roles = {}
4
+ def self::add(name,what)
5
+ @@roles[name] = what
6
+ end
7
+ def self::roles
8
+ @@roles
9
+ end
10
+ class Implementation
11
+ def self::before(method,parameters,headers,options); end
12
+ def self::after(method,code,response,headers,options); end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/http%3A%2F%2Foauth.net%2F1.0/base')
2
+
3
+ module Riddl
4
+ module Roles
5
+ module OAuth
6
+
7
+ module AccessToken
8
+ WANTED = [:consumer_key, :consumer_secret, :token, :token_secret, :verifier]
9
+
10
+ def self::after(fullpath,method,code,response,headers,options)
11
+ if code == 200
12
+ Riddl::Roles::OAuth::Response.new(response[0].value.read)
13
+ else
14
+ response
15
+ end
16
+ end
17
+
18
+ def self::before(fullpath,method,parameters,headers,options)
19
+ unless WANTED.all?{ |e| options.has_key?(e) }
20
+ raise ArgumentError, "Riddl::Options have to include: #{WANTED.join(', ')}"
21
+ end
22
+ Riddl::Roles::OAuth::sign(fullpath,method,parameters,headers,options)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+
30
+ Riddl::Roles::add("http://oauth.net/1.0/access_token",Riddl::Roles::OAuth::AccessToken)
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/http%3A%2F%2Foauth.net%2F1.0/base')
2
+
3
+ module Riddl
4
+ module Roles
5
+ module OAuth
6
+
7
+ module OnBehalf
8
+ WANTED = [:consumer_key, :consumer_secret, :token, :token_secret]
9
+
10
+ def self::before(fullpath,method,parameters,headers,options)
11
+ unless WANTED.all?{ |e| options.has_key?(e) }
12
+ raise ArgumentError, "Riddl::Options have to include: #{WANTED.join(', ')}"
13
+ end
14
+ Riddl::Roles::OAuth::sign(fullpath,method,parameters,headers,options)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+
22
+ Riddl::Roles::add("http://oauth.net/1.0/on_behalf",Riddl::Roles::OAuth::OnBehalf)
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/http%3A%2F%2Foauth.net%2F1.0/base')
2
+
3
+ module Riddl
4
+ module Roles
5
+ module OAuth
6
+
7
+ module RequestToken
8
+ WANTED = [:consumer_key, :consumer_secret]
9
+
10
+ def self::after(fullpath,method,code,response,headers,options)
11
+ if code == 200
12
+ Riddl::Roles::OAuth::Response.new(response[0].value.read)
13
+ else
14
+ response
15
+ end
16
+ end
17
+
18
+ def self::before(fullpath,method,parameters,headers,options)
19
+ unless WANTED.all?{ |e| options.has_key?(e) }
20
+ raise ArgumentError, "Riddl::Options have to include: #{WANTED.join(', ')}"
21
+ end
22
+ Riddl::Roles::OAuth::sign(fullpath,method,parameters,headers,options)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+
30
+ Riddl::Roles::add("http://oauth.net/1.0/request_token",Riddl::Roles::OAuth::RequestToken)
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../protocols/http/generator')
2
+ require 'openssl'
3
+ require 'base64'
4
+
5
+ module Riddl
6
+ module Roles
7
+ module OAuth
8
+ DIGEST = OpenSSL::Digest::Digest.new('sha1')
9
+ VERSION_MAJOR = 1
10
+ VERSION_MINOR = 0
11
+
12
+ class Response
13
+ def method_missing(name)
14
+ @provided[name]
15
+ end
16
+
17
+ def items
18
+ @provided.keys
19
+ end
20
+
21
+ def initialize(qs)
22
+ @provided = {}
23
+ (qs || '').split(/[&] */n).each do |p|
24
+ k, v = Protocols::HTTP::Parser::unescape(p).split('=', 2)
25
+ @provided[k.to_sym] = v
26
+ end
27
+ end
28
+ end
29
+
30
+ def self::sign(fullpath,method,parameters,headers,options)
31
+ signature_string = method.upcase + "&" + Protocols::HTTP::Generator::escape(fullpath) + "&"
32
+
33
+ sparams = []
34
+ if parameters.class == Array
35
+ parameters.each do |p|
36
+ if p.class == Riddl::Parameter::Simple
37
+ sparams << [Protocols::HTTP::Generator::escape(p.name),Protocols::HTTP::Generator::escape(p.value)]
38
+ end
39
+ end
40
+ end
41
+
42
+ oparams = []
43
+ oparams << ["oauth_consumer_key",Protocols::HTTP::Generator::escape(options[:consumer_key])]
44
+ oparams << ["oauth_signature_method","HMAC-SHA1"]
45
+ oparams << ["oauth_timestamp",Time.new.to_i.to_s]
46
+ oparams << ["oauth_version","1.0"]
47
+ oparams << ["oauth_nonce",Protocols::HTTP::Generator::escape(OpenSSL::Digest::SHA1.hexdigest(rand(10000).to_s)[0...32])]
48
+ oparams << ["oauth_token",Protocols::HTTP::Generator::escape(options[:token])] if options[:token]
49
+ oparams << ["oauth_verifier",Protocols::HTTP::Generator::escape(options[:verifier])] if options[:verifier]
50
+
51
+ params = (sparams + oparams).sort{|a,b|a[0]<=>b[0]}.map{ |e| e[0] + '=' + e[1] }.join('&')
52
+ signature_string += Protocols::HTTP::Generator::escape(params)
53
+
54
+ signature = OpenSSL::HMAC.digest(DIGEST,"#{Protocols::HTTP::Generator::escape(options[:consumer_secret])}&#{Protocols::HTTP::Generator::escape(options[:token_secret])}",signature_string)
55
+ signature = [signature].pack("m").gsub(/\n/, '')
56
+
57
+ oparams << ["oauth_signature", Protocols::HTTP::Generator::escape(signature)]
58
+
59
+ oparams.unshift(["realm", Protocols::HTTP::Generator::escape(options[:realm])]) if options[:realm]
60
+
61
+ headers['Authorization'] = 'OAuth ' + oparams.map{|e|e[0]+'='+"\"#{e[1]}\""}.join(', ')
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,519 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/constants')
2
+ require File.expand_path(File.dirname(__FILE__) + '/implementation')
3
+ require File.expand_path(File.dirname(__FILE__) + '/protocols/http/parser')
4
+ require File.expand_path(File.dirname(__FILE__) + '/protocols/http/generator')
5
+ require File.expand_path(File.dirname(__FILE__) + '/protocols/xmpp/parser')
6
+ require File.expand_path(File.dirname(__FILE__) + '/protocols/xmpp/generator')
7
+ require File.expand_path(File.dirname(__FILE__) + '/protocols/websocket')
8
+ require File.expand_path(File.dirname(__FILE__) + '/header')
9
+ require File.expand_path(File.dirname(__FILE__) + '/parameter')
10
+ require File.expand_path(File.dirname(__FILE__) + '/error')
11
+ require File.expand_path(File.dirname(__FILE__) + '/wrapper')
12
+ require File.expand_path(File.dirname(__FILE__) + '/utils/description')
13
+
14
+ require 'optparse'
15
+ require 'stringio'
16
+ require 'rack/content_length'
17
+ require 'rack/chunked'
18
+ require 'securerandom'
19
+ require 'yaml'
20
+ require 'blather/client/client'
21
+
22
+ module Riddl
23
+
24
+ class Server
25
+
26
+ class Execution #{{{
27
+ attr_reader :response,:headers
28
+ def initialize(response,headers)
29
+ @response = (response.is_a?(Array) ? response : [response])
30
+ @headers = (headers.is_a?(Array) ? headers : [headers])
31
+ @response.delete_if do |r|
32
+ r.class != Riddl::Parameter::Simple && r.class != Riddl::Parameter::Complex
33
+ end
34
+ @headers.delete_if do |h|
35
+ h.class != Riddl::Header
36
+ end
37
+ @headers.compact!
38
+ @response.compact!
39
+ @headers = Hash[ @headers.map{ |h| [h.name, h.value] } ]
40
+ end
41
+ end #}}}
42
+
43
+ OPTS = {
44
+ :host => 'localhost',
45
+ :port => 9292,
46
+ :secure => false,
47
+ :mode => :debug,
48
+ :basepath => File.expand_path(File.dirname($0)),
49
+ :pidfile => File.basename($0,'.rb') + '.pid',
50
+ :conffile => File.basename($0,'.rb') + '.conf'
51
+ }
52
+
53
+ def loop! #{{{
54
+ ########################################################################################################################
55
+ # parse arguments
56
+ ########################################################################################################################
57
+ verbose = false
58
+ operation = "start"
59
+ ARGV.options { |opt|
60
+ opt.summary_indent = ' ' * 4
61
+ opt.banner = "Usage:\n#{opt.summary_indent}ruby server.rb [options] start|startclean|stop|restart|info\n"
62
+ opt.on("Options:")
63
+ opt.on("--verbose", "-v", "Do not daemonize. Write ouput to console.") { verbose = true }
64
+ opt.on("--help", "-h", "This text.") { puts opt; exit }
65
+ opt.separator(opt.summary_indent + "start|stop|restart|info".ljust(opt.summary_width+1) + "Do operation start, stop, restart or get information.")
66
+ opt.separator(opt.summary_indent + "startclean".ljust(opt.summary_width+1) + "Delete all instances before starting.")
67
+ opt.parse!
68
+ }
69
+ unless %w{start startclean stop restart info}.include?(ARGV[0])
70
+ puts ARGV.options
71
+ exit
72
+ end
73
+ operation = ARGV[0]
74
+
75
+ ########################################################################################################################
76
+ # status and info
77
+ ########################################################################################################################
78
+ pid = File.read(@riddl_opts[:basepath] + '/' + @riddl_opts[:pidfile]).to_i rescue pid = 666
79
+ status = Proc.new do
80
+ begin
81
+ Process.getpgid pid
82
+ true
83
+ rescue Errno::ESRCH
84
+ false
85
+ end
86
+ end
87
+ if operation == "info" && status.call == false
88
+ puts "Server (#{@riddl_opts[:url]}) not running"
89
+ exit
90
+ end
91
+ if operation == "info" && status.call == true
92
+ puts "Server (#{@riddl_opts[:url]}) running as #{pid}"
93
+ begin
94
+ stats = `ps -o "vsz,rss,lstart,time" -p #{pid}`.split("\n")[1].strip.split(/ +/)
95
+ puts "Virtual: #{"%0.2f" % (stats[0].to_f/1024)} MiB"
96
+ puts "Resident: #{"%0.2f" % (stats[1].to_f/1024)} MiB"
97
+ puts "Started: #{stats[2..-2].join(' ')}"
98
+ puts "CPU Time: #{stats.last}"
99
+ rescue
100
+ end
101
+ exit
102
+ end
103
+ if %w{start startclean}.include?(operation) && status.call == true
104
+ puts "Server (#{@riddl_opts[:url]}) already started"
105
+ exit
106
+ end
107
+
108
+ ########################################################################################################################
109
+ # stop/restart server
110
+ ########################################################################################################################
111
+ if %w{stop restart}.include?(operation)
112
+ if status.call == false
113
+ puts "Server (#{@riddl_opts[:url]}) maybe not started?"
114
+ else
115
+ puts "Server (#{@riddl_opts[:url]}) stopped"
116
+ puts "Waiting while server goes down ..."
117
+ while status.call
118
+ Process.kill "SIGTERM", pid
119
+ sleep 0.3
120
+ end
121
+ end
122
+ exit unless operation == "restart"
123
+ end
124
+
125
+ ########################################################################################################################
126
+ # start server
127
+ ########################################################################################################################
128
+ if operation == 'startclean'
129
+ Dir.glob(File.expand_path(@riddl_opts[:basepath] + '/instances/*')).each do |d|
130
+ FileUtils.rm_r(d) if File.basename(d) =~ /^\d+$/
131
+ end
132
+ end
133
+
134
+ app = Rack::Builder.new self
135
+ unless @riddl_logger.nil?
136
+ app.use Rack::CommonLogger, @riddl_logger
137
+ end
138
+
139
+ server = if verbose
140
+ Rack::Server.new(
141
+ :app => app,
142
+ :Port => @riddl_opts[:port],
143
+ :environment => (@riddl_opts[:mode] == :debug ? 'development' : 'deployment'),
144
+ :server => 'thin',
145
+ :pid => File.expand_path(@riddl_opts[:basepath] + '/' + @riddl_opts[:pidfile])
146
+ )
147
+ else
148
+ server = Rack::Server.new(
149
+ :app => app,
150
+ :Port => @riddl_opts[:port],
151
+ :environment => 'none',
152
+ :server => 'thin',
153
+ :pid => File.expand_path(@riddl_opts[:basepath] + '/' + @riddl_opts[:pidfile]),
154
+ :daemonize => true
155
+ )
156
+ end
157
+
158
+ # remove LINT in any case as it breaks websockets
159
+ server.middleware.each do |k,v|
160
+ v.delete [Rack::Lint]
161
+ end
162
+
163
+ EM.run do
164
+ puts "Server (#{@riddl_opts[:url]}) started as #{Process.pid}"
165
+ puts "XMPP support (#{@riddl_xmpp_jid}) active" if @riddl_xmpp_jid && @riddl_xmpp_pass
166
+ server.start
167
+ if @riddl_xmpp_jid && @riddl_xmpp_pass
168
+ xmpp = Blather::Client.setup @riddl_xmpp_jid, @riddl_xmpp_pass
169
+ xmpp.register_handler(:message, :type => :normal) do |m|
170
+ began_at = Time.now
171
+ instance = dup
172
+ instance.__xmpp_call(xmpp,m)
173
+ now = Time.now
174
+ instance.riddl_log.write Rack::CommonLogger::FORMAT % [
175
+ @riddl_xmpp_jid || "-",
176
+ m.from || "-",
177
+ now.strftime("%d/%b/%Y %H:%M:%S"),
178
+ instance.riddl_method.upcase,
179
+ instance.riddl_pinfo,
180
+ '',
181
+ 'XMPP',
182
+ instance.riddl_status,
183
+ '?',
184
+ now - began_at ]
185
+
186
+ end
187
+ xmpp.connect
188
+ end
189
+ end
190
+ end #}}}
191
+
192
+ attr_reader :riddl_log, :riddl_method, :riddl_pinfo, :riddl_status
193
+
194
+ def initialize(riddl,opts={},&blk)# {{{
195
+ @riddl_opts = {}
196
+ OPTS.each do |k,v|
197
+ @riddl_opts[k] = opts.has_key?(k) ? opts[k] : v
198
+ end
199
+
200
+ if File.exists?(@riddl_opts[:basepath] + '/' + @riddl_opts[:conffile])
201
+ @riddl_opts.merge!(YAML::load_file(@riddl_opts[:basepath] + '/' + @riddl_opts[:conffile]))
202
+ end
203
+ @riddl_opts[:url] = (@riddl_opts[:secure] ? 'https://' : 'http://') + @riddl_opts[:host] + ':' + @riddl_opts[:port].to_s
204
+
205
+ @riddl_logger = nil
206
+ @riddl_process_out = true
207
+ @riddl_cross_site_xhr = false
208
+ @accessible_description = false
209
+ @riddl_description_string = ''
210
+ @riddl_paths = []
211
+
212
+ @riddl_interfaces = {}
213
+ instance_eval(&blk) if block_given?
214
+
215
+ @riddl = Riddl::Wrapper.new(riddl,@accessible_description)
216
+ if @riddl.description?
217
+ raise SpecificationError, 'RIDDL description does not conform to specification' unless @riddl.validate!
218
+ @riddl_description_string = @riddl.description.xml
219
+ elsif @riddl.declaration?
220
+ raise SpecificationError, 'RIDDL declaration does not conform to specification' unless @riddl.validate!
221
+ @riddl_description_string = @riddl.declaration.description_xml
222
+ else
223
+ raise SpecificationError, 'Not a RIDDL file'
224
+ end
225
+
226
+ @riddl.load_necessary_handlers!
227
+ @riddl_paths = @riddl.paths
228
+ end# }}}
229
+
230
+ def call(env)# {{{
231
+ dup.__http_call(env)
232
+ end# }}}
233
+
234
+ def __call #{{{
235
+ @riddl_message = @riddl.io_messages(@riddl_matching_path[0],@riddl_method,@riddl_parameters,@riddl_headers)
236
+ if @riddl_message.nil?
237
+ if @riddl_info[:env].has_key?('HTTP_ORIGIN') && @riddl_cross_site_xhr
238
+ @riddl_res['Access-Control-Allow-Origin'] = '*'
239
+ @riddl_res['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
240
+ @riddl_res['Access-Control-Allow-Headers'] = @riddl_info[:env]['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'] if @riddl_info[:env]['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']
241
+ @riddl_res['Access-Control-Max-Age'] = '0'
242
+ @riddl_res['Content-Length'] = '0'
243
+ @riddl_status = 200
244
+ else
245
+ @riddl_log.write "501: the #{@riddl_method} parameters are not matching anything in the description.\n"
246
+ @riddl_status = 501 # not implemented?!
247
+ end
248
+ else
249
+ if get 'riddl-description-request'
250
+ run Riddl::Utils::Description::XML, @riddl_description_string
251
+ else
252
+ if @riddl.description?
253
+ instance_exec(@riddl_info, &@riddl_interfaces[nil])
254
+ elsif @riddl.declaration?
255
+ ifs = @riddl_message.route? ? @riddl_message.route : [@riddl_message]
256
+ ifs.each do |m|
257
+ @riddl_path = '/'
258
+ if m.interface.base.nil?
259
+ if @riddl_interfaces.key? m.interface.name
260
+ @riddl_info[:r] = m.interface.real_path(@riddl_pinfo).sub(/\//,'').split('/')
261
+ @riddl_info[:h]['RIDDL_DECLARATION_PATH'] = @riddl_pinfo
262
+ @riddl_info[:h]['RIDDL_DECLARATION_RESOURCE'] = m.interface.top
263
+ @riddl_info[:s] = m.interface.sub.sub(/\//,'').split('/')
264
+ @riddl_info.merge!(:match => matching_path)
265
+ instance_exec(@riddl_info, &@riddl_interfaces[m.interface.name])
266
+ else
267
+ @riddl_log.write "501: not implemented (for remote: add @location in declaration; for local: add to Riddl::Server).\n"
268
+ @riddl_status = 501 # not implemented?!
269
+ break
270
+ end
271
+ else
272
+ run Riddl::Utils::Description::Call, @riddl_exe, @riddl_pinfo, m.interface.top, m.interface.base, m.interface.des.to_doc, m.interface.real_path(@riddl_pinfo)
273
+ end
274
+ break unless @riddl_status == 200
275
+ @riddl_info.merge!(:h => @riddl_exe.headers, :p => @riddl_exe.response)
276
+ end
277
+ end
278
+ end
279
+ if @riddl_info[:env].has_key?('HTTP_ORIGIN') && @riddl_cross_site_xhr
280
+ @riddl_res['Access-Control-Allow-Origin'] = '*'
281
+ @riddl_res['Access-Control-Max-Age'] = '0'
282
+ end
283
+ end
284
+ end #}}}
285
+
286
+ def __xmpp_call(env,raw) #{{{
287
+ Dir.chdir(@riddl_opts[:basepath]) if @riddl_opts[:basepath]
288
+ @riddl_log = @riddl_logger || STDOUT
289
+
290
+ @riddl_env = XML::Smart::Dom::Element.new(raw).parent
291
+ @riddl_env.register_namespace 'xr', Riddl::Protocols::XMPP::XR_NS
292
+ @riddl_res = env
293
+ @riddl_status = 404
294
+
295
+ @riddl_pinfo = ('/' + @riddl_env.root.attributes['to'].sub(/^[^\/]+/,'')).gsub(/\/+/,'/')
296
+ @riddl_pinfo.gsub!(/\?(.*)/).each do
297
+ @riddl_query_string = $1; ''
298
+ end
299
+ @riddl_matching_path = @riddl_paths.find{ |e| e[1] =~ @riddl_pinfo }
300
+
301
+ if @riddl_matching_path
302
+ @riddl_method = @riddl_env.find('string(/message/xr:operation)').downcase
303
+
304
+ @riddl_headers = {}
305
+ @riddl_env.find('/message/xr:header').each do |e|
306
+ @riddl_headers[e.attributes['name']] = e.text
307
+ end
308
+ @riddl_parameters = Protocols::XMPP::Parser.new(
309
+ @riddl_query_string,
310
+ @riddl_env
311
+ ).params
312
+
313
+ @riddl_path = '/'
314
+ @riddl_info = {
315
+ :h => @riddl_headers,
316
+ :p => @riddl_parameters,
317
+ :r => @riddl_pinfo.sub(/\//,'').split('/').map{|e|Protocols::HTTP::Parser::unescape(e)},
318
+ :s => @riddl_matching_path[0].sub(/\//,'').split('/').map{|e|Protocols::HTTP::Parser::unescape(e)},
319
+ :m => @riddl_method,
320
+ :env => Hash[@riddl_env.root.attributes.map{|a| [a.qname.name, a.value] }].merge({ 'riddl.transport' => 'xmpp' }),
321
+ :match => []
322
+ }
323
+
324
+ __call
325
+ else
326
+ @riddl_log.write "404: this resource for sure does not exist.\n"
327
+ @riddl_status = 404 # client requests wrong path
328
+ end
329
+ stanza = if @riddl_exe && @riddl_status == 200
330
+ Protocols::XMPP::Generator.new(@riddl_status,@riddl_exe.response,@riddl_exe.headers).generate
331
+ else
332
+ Protocols::XMPP::Error.new(@riddl_status).generate
333
+ end
334
+ stanza.from = raw.to
335
+ stanza.to = raw.from
336
+ stanza.id = raw.id
337
+ @riddl_res.write stanza
338
+ end #}}}
339
+
340
+ def __http_call(env) #{{{
341
+ Dir.chdir(@riddl_opts[:basepath]) if @riddl_opts[:basepath]
342
+
343
+ @riddl_env = env
344
+ @riddl_env['rack.logger'] = @riddl_logger if @riddl_logger
345
+ @riddl_log = @riddl_logger || @riddl_env['rack.errors']
346
+ @riddl_res = Rack::Response.new
347
+ @riddl_status = 404
348
+
349
+ @riddl_pinfo = @riddl_env["PATH_INFO"].gsub(/\/+/,'/')
350
+ @riddl_matching_path = @riddl_paths.find{ |e| e[1] =~ @riddl_pinfo }
351
+
352
+ if @riddl_matching_path
353
+ @riddl_query_string = @riddl_env['QUERY_STRING']
354
+ @riddl_raw = @riddl_env['rack.input']
355
+
356
+ @riddl_headers = {}
357
+ @riddl_env.each do |h,v|
358
+ @riddl_headers[$1] = v if h =~ /^HTTP_(.*)$/
359
+ end
360
+ @riddl_parameters = Protocols::HTTP::Parser.new(
361
+ @riddl_query_string,
362
+ @riddl_raw,
363
+ @riddl_env['CONTENT_TYPE'],
364
+ @riddl_env['CONTENT_LENGTH'],
365
+ @riddl_env['HTTP_CONTENT_DISPOSITION'],
366
+ @riddl_env['HTTP_CONTENT_ID'],
367
+ @riddl_env['HTTP_RIDDL_TYPE']
368
+ ).params
369
+
370
+ @riddl_method = @riddl_env['REQUEST_METHOD'].downcase
371
+ @riddl_path = '/'
372
+ @riddl_info = {
373
+ :h => @riddl_headers,
374
+ :p => @riddl_parameters,
375
+ :r => @riddl_pinfo.sub(/\//,'').split('/').map{|e|Protocols::HTTP::Parser::unescape(e)},
376
+ :s => @riddl_matching_path[0].sub(/\//,'').split('/').map{|e|Protocols::HTTP::Parser::unescape(e)},
377
+ :m => @riddl_method,
378
+ :env => @riddl_env.reject{|k,v| k =~ /^rack\./}.merge({'riddl.transport' => 'xmpp'}),
379
+ :match => []
380
+ }
381
+
382
+ if @riddl_info[:env]["HTTP_CONNECTION"] =~ /Upgrade/ && @riddl_info[:env]["HTTP_UPGRADE"] =~ /\AWebSocket\z/i
383
+ # TODO raise error when declaration and route or (not route and non-local interface)
384
+ # raise SpecificationError, 'RIDDL description does not conform to specification' unless @riddl.validate!
385
+ @riddl_info[:m] = @riddl_method = 'websocket'
386
+ if @riddl.description?
387
+ instance_exec(@riddl_info, &@riddl_interfaces[nil])
388
+ elsif @riddl.declaration?
389
+ @riddl_message = @riddl.io_messages(@riddl_matching_path[0],'websocket',@riddl_parameters,@riddl_headers)
390
+ # one ws connection, no overlay
391
+ unless @riddl_message.nil?
392
+ if @riddl_interfaces.key? @riddl_message.interface.name
393
+ @riddl_info[:r] = @riddl_message.interface.real_path(@riddl_pinfo).sub(/\//,'').split('/')
394
+ @riddl_info[:h]['RIDDL_DECLARATION_PATH'] = @riddl_pinfo
395
+ @riddl_info[:h]['RIDDL_DECLARATION_RESOURCE'] = @riddl_message.interface.top
396
+ @riddl_info[:s] = @riddl_message.interface.sub.sub(/\//,'').split('/')
397
+ @riddl_info.merge!(:match => matching_path)
398
+ instance_exec(@riddl_info, &@riddl_interfaces[@riddl_message.interface.name])
399
+ end
400
+ end
401
+ end
402
+ return [-1, {}, []]
403
+ else
404
+ __call
405
+ end
406
+ else
407
+ @riddl_log.write "404: this resource for sure does not exist.\n"
408
+ @riddl_status = 404 # client requests wrong path
409
+ end
410
+ if @riddl_exe
411
+ if @riddl_status == 200
412
+ @riddl_res.write Protocols::HTTP::Generator.new(@riddl_exe.response,@riddl_res).generate.read
413
+ end
414
+ @riddl_exe.headers.each do |n,h|
415
+ @riddl_res[n] = h
416
+ end
417
+ end
418
+ @riddl_res.status = @riddl_status
419
+ @riddl_res.finish
420
+ end #}}}
421
+
422
+ def process_out(pout)# {{{
423
+ @riddl_process_out = pout
424
+ end# }}}
425
+ def xmpp(jid,pass)# {{{
426
+ @riddl_xmpp_jid = jid
427
+ @riddl_xmpp_pass = pass
428
+ end# }}}
429
+ def cross_site_xhr(csxhr)# {{{
430
+ @riddl_cross_site_xhr = csxhr
431
+ end# }}}
432
+ def logger(lgr)# {{{
433
+ @riddl_logger = lgr
434
+ end# }}}
435
+ def accessible_description(ad)# {{{
436
+ @accessible_description = ad
437
+ end# }}}
438
+ def interface(name,&block) #{{{
439
+ @riddl_interfaces[name] = block
440
+ end #}}}
441
+
442
+ def on(resource, &block)# {{{
443
+ if @riddl_paths.empty? # default interface, when a description and "on" syntax in server
444
+ @riddl_interfaces[nil] = block
445
+ return
446
+ end
447
+
448
+ @riddl_path << (@riddl_path == '/' ? resource : '/' + resource)
449
+
450
+
451
+ ### only descend when there is a possibility that it holds the right path
452
+ rp = @riddl_path.sub(/\//,'').split('/')
453
+
454
+ block.call(@riddl_info.merge!(:match => matching_path)) if @riddl_info[:s][rp.length-1] == rp.last
455
+ @riddl_path = File.dirname(@riddl_path).gsub(/\/+/,'/')
456
+ end# }}}
457
+
458
+ def use(blk,*args)# {{{
459
+ instance_eval(&blk)
460
+ end# }}}
461
+
462
+ def run(what,*args)# {{{
463
+ return if @riddl_path == ''
464
+ if what.class == Class && what.superclass == Riddl::WebSocketImplementation
465
+ data = WebSocketParserData.new
466
+ data.request_path = @riddl_pinfo
467
+ data.request_url = @riddl_pinfo + '?' + @riddl_query_string
468
+ data.query_string = @riddl_query_string
469
+ data.http_method = @riddl_env['REQUEST_METHOD']
470
+ data.body = @riddl_env['rack.input'].read
471
+ data.headers = Hash[
472
+ @riddl_headers.map { |key, value| [key.downcase.gsub('_','-'), value] }
473
+ ]
474
+ w = what.new(@riddl_info.merge!(:a => args, :version => @riddl_env['HTTP_SEC_WEBSOCKET_VERSION'], :match => matching_path))
475
+ w.io = Riddl::WebSocket.new(w, @riddl_env['thin.connection'])
476
+ w.io.dispatch(data)
477
+ end
478
+ if what.class == Class && what.superclass == Riddl::Implementation
479
+ w = what.new(@riddl_info.merge!(:a => args, :match => matching_path))
480
+ @riddl_exe = Riddl::Server::Execution.new(w.response,w.headers)
481
+ @riddl_status = w.status
482
+ if @riddl_process_out && @riddl_status == 200
483
+ unless @riddl.check_message(@riddl_exe.response,@riddl_exe.headers,@riddl_message.out)
484
+ @riddl_log.write "500: the return for the #{@riddl_method} is not matching anything in the description.\n"
485
+ @riddl_status = 500
486
+ return
487
+ end
488
+ end
489
+ end
490
+ end# }}}
491
+
492
+ def method(what)# {{{
493
+ if !@riddl_message.nil? && what.class == Hash && what.length == 1
494
+ met, min = what.first
495
+ @riddl_path == @riddl_matching_path[0] && min == @riddl_message.in.name && @riddl_method == met.to_s.downcase
496
+ else
497
+ false
498
+ end
499
+ end # }}}
500
+ def post(min='*'); return false if @riddl_message.nil?; @riddl_path == '/' + @riddl_info[:s].join('/') && min == @riddl_message.in.name && @riddl_method == 'post' end
501
+ def get(min='*'); return false if @riddl_message.nil?; @riddl_path == '/' + @riddl_info[:s].join('/') && min == @riddl_message.in.name && @riddl_method == 'get' end
502
+ def delete(min='*'); return false if @riddl_message.nil?; @riddl_path == '/' + @riddl_info[:s].join('/') && min == @riddl_message.in.name && @riddl_method == 'delete' end
503
+ def put(min='*'); return false if @riddl_message.nil?; @riddl_path == '/' + @riddl_info[:s].join('/') && min == @riddl_message.in.name && @riddl_method == 'put' end
504
+ def websocket; return false if @riddl_message.nil?; @riddl_path == '/' + @riddl_info[:s].join('/') && @riddl_method == 'websocket' end
505
+ def resource(rname=nil); return rname.nil? ? '{}' : rname end
506
+
507
+ def matching_path #{{{
508
+ @riddl_path.sub(/\//,'').split('/')
509
+ end #}}}
510
+
511
+ def declaration_path #{{{
512
+ @riddl_info[:h]['RIDDL_DECLARATION_PATH']
513
+ end #}}}
514
+ def declaration_resource #{{{
515
+ @riddl_info[:h]['RIDDL_DECLARATION_RESOURCE']
516
+ end #}}}
517
+
518
+ end
519
+ end