em-midori 0.1.0 → 0.1.1
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/CONTRIBUTOR_COVENANT_CODE_OF_CONDUCT.md +74 -0
- data/lib/em-midori.rb +7 -4
- data/lib/em-midori/api.rb +60 -140
- data/lib/em-midori/api_engine.rb +84 -0
- data/lib/em-midori/clean_room.rb +2 -20
- data/lib/em-midori/configure.rb +2 -1
- data/lib/em-midori/core_ext/configurable.rb +23 -24
- data/lib/em-midori/{define_class.rb → core_ext/define_class.rb} +0 -0
- data/lib/em-midori/{promise.rb → core_ext/promise.rb} +3 -3
- data/lib/em-midori/{string.rb → core_ext/string.rb} +0 -0
- data/lib/em-midori/middleware.rb +0 -6
- data/lib/em-midori/request.rb +8 -6
- data/lib/em-midori/route.rb +2 -1
- data/lib/em-midori/runner.rb +1 -2
- data/lib/em-midori/version.rb +1 -1
- data/lib/em-midori/websocket.rb +2 -2
- metadata +21 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2dcc440db5d1538e1e4530d9d6879c9a4115ee85
|
4
|
+
data.tar.gz: b6912e55681783ebcd555de7b13c5d8681a213a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b004b9d39d894840e6c2652dda4162d25be4be82f5532c3bedf61a189103e64b9d874312a686ccaa60469977d4f070bc4a6e09959cbcc24db3f7448ca0af648
|
7
|
+
data.tar.gz: 9d4c89c1930ce59fe8590f2f26f2fda1972d768145c054803e8f0f2d8aecf840a44f52c50d026c5026cb0233ed8759ff6f443b11e9dd89043ee301a09a2fae88
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at dsh0416@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/lib/em-midori.rb
CHANGED
@@ -4,19 +4,22 @@ require 'eventmachine'
|
|
4
4
|
require 'fiber'
|
5
5
|
require 'logger'
|
6
6
|
require 'http/parser'
|
7
|
+
require 'mustermann'
|
7
8
|
require 'socket'
|
8
9
|
|
9
|
-
require_relative 'em-midori/version'
|
10
10
|
require_relative 'em-midori/core_ext/configurable'
|
11
|
-
require_relative 'em-midori/string'
|
12
|
-
require_relative 'em-midori/promise'
|
11
|
+
require_relative 'em-midori/core_ext/string'
|
12
|
+
require_relative 'em-midori/core_ext/promise'
|
13
|
+
require_relative 'em-midori/core_ext/define_class'
|
14
|
+
|
15
|
+
require_relative 'em-midori/version'
|
13
16
|
require_relative 'em-midori/const'
|
14
|
-
require_relative 'em-midori/define_class'
|
15
17
|
require_relative 'em-midori/error'
|
16
18
|
require_relative 'em-midori/clean_room'
|
17
19
|
require_relative 'em-midori/request'
|
18
20
|
require_relative 'em-midori/response'
|
19
21
|
require_relative 'em-midori/api'
|
22
|
+
require_relative 'em-midori/api_engine'
|
20
23
|
require_relative 'em-midori/route'
|
21
24
|
require_relative 'em-midori/server'
|
22
25
|
require_relative 'em-midori/websocket'
|
data/lib/em-midori/api.rb
CHANGED
@@ -2,138 +2,120 @@
|
|
2
2
|
# This class provides methods to be inherited as route definition.
|
3
3
|
class Midori::API
|
4
4
|
class << self
|
5
|
+
attr_accessor :routes, :scope_middlewares
|
6
|
+
def class_initialize
|
7
|
+
@routes = {
|
8
|
+
GET: [],
|
9
|
+
POST: [],
|
10
|
+
PUT: [],
|
11
|
+
DELETE: [],
|
12
|
+
OPTIONS: [],
|
13
|
+
LINK: [],
|
14
|
+
UNLINK: [],
|
15
|
+
WEBSOCKET: [],
|
16
|
+
EVENTSOURCE: [],
|
17
|
+
MOUNT: []
|
18
|
+
}
|
19
|
+
@scope_middlewares = []
|
20
|
+
@temp_middlewares = []
|
21
|
+
end
|
22
|
+
|
5
23
|
# Add GET method as a DSL for route definition
|
6
|
-
# @param [ String
|
24
|
+
# @param [ String ] path Accepts as part of path in route definition
|
7
25
|
# @yield what to run when route matched
|
8
26
|
# @return [ nil ] nil
|
9
27
|
# @example String as router
|
10
28
|
# get '/' do
|
11
29
|
# puts 'Hello World'
|
12
30
|
# end
|
13
|
-
#
|
14
|
-
# @example Regex as router
|
15
|
-
# get /\/hello\/(.*?)/ do
|
16
|
-
# puts 'Hello World'
|
17
|
-
# end
|
18
31
|
def get(path, &block) end
|
19
32
|
|
20
33
|
# Add POST method as a DSL for route definition
|
21
|
-
# @param [ String
|
34
|
+
# @param [ String ] path Accepts as part of path in route definition
|
22
35
|
# @yield what to run when route matched
|
23
36
|
# @return [ nil ] nil
|
24
37
|
# @example String as router
|
25
38
|
# post '/' do
|
26
39
|
# puts 'Hello World'
|
27
40
|
# end
|
28
|
-
#
|
29
|
-
# @example Regex as router
|
30
|
-
# post /\/hello\/(.*?)/ do
|
31
|
-
# puts 'Hello World'
|
32
|
-
# end
|
33
41
|
def post(path, &block) end
|
34
42
|
|
35
43
|
# Add PUT method as a DSL for route definition
|
36
|
-
# @param [ String
|
44
|
+
# @param [ String ] path Accepts as part of path in route definition
|
37
45
|
# @yield what to run when route matched
|
38
46
|
# @return [ nil ] nil
|
39
47
|
# @example String as router
|
40
48
|
# put '/' do
|
41
49
|
# puts 'Hello World'
|
42
50
|
# end
|
43
|
-
#
|
44
|
-
# @example Regex as router
|
45
|
-
# put /\/hello\/(.*?)/ do
|
46
|
-
# puts 'Hello World'
|
47
|
-
# end
|
48
51
|
def put(path, &block) end
|
49
52
|
|
50
53
|
# Add DELETE method as a DSL for route definition
|
51
|
-
# @param [ String
|
54
|
+
# @param [ String ] path Accepts as part of path in route definition
|
52
55
|
# @yield what to run when route matched
|
53
56
|
# @return [ nil ] nil
|
54
57
|
# @example String as router
|
55
58
|
# delete '/' do
|
56
59
|
# puts 'Hello World'
|
57
60
|
# end
|
58
|
-
#
|
59
|
-
# @example Regex as router
|
60
|
-
# delete /\/hello\/(.*?)/ do
|
61
|
-
# puts 'Hello World'
|
62
|
-
# end
|
63
61
|
def delete(path, &block) end
|
64
62
|
|
65
63
|
# Add OPTIONS method as a DSL for route definition
|
66
|
-
# @param [ String
|
64
|
+
# @param [ String ] path Accepts as part of path in route definition
|
67
65
|
# @return [ nil ] nil
|
68
66
|
# @example String as router
|
69
67
|
# options '/' do
|
70
68
|
# puts 'Hello World'
|
71
69
|
# end
|
72
|
-
#
|
73
|
-
# @example Regex as router
|
74
|
-
# options /\/hello\/(.*?)/ do
|
75
|
-
# puts 'Hello World'
|
76
|
-
# end
|
77
70
|
def options(path, &block) end
|
78
71
|
|
79
72
|
# Add LINK method as a DSL for route definition
|
80
|
-
# @param [ String
|
73
|
+
# @param [ String ] path Accepts as part of path in route definition
|
81
74
|
# @yield what to run when route matched
|
82
75
|
# @return [ nil ] nil
|
83
76
|
# @example String as router
|
84
77
|
# link '/' do
|
85
78
|
# puts 'Hello World'
|
86
79
|
# end
|
87
|
-
#
|
88
|
-
# @example Regex as router
|
89
|
-
# link /\/hello\/(.*?)/ do
|
90
|
-
# puts 'Hello World'
|
91
|
-
# end
|
92
80
|
def link(path, &block) end
|
93
81
|
|
94
82
|
# Add UNLINK method as a DSL for route definition
|
95
|
-
# @param [ String
|
83
|
+
# @param [ String ] path Accepts as part of path in route definition
|
96
84
|
# @yield what to run when route matched
|
97
85
|
# @return [ nil ] nil
|
98
86
|
# @example String as router
|
99
87
|
# unlink '/' do
|
100
88
|
# puts 'Hello World'
|
101
89
|
# end
|
102
|
-
#
|
103
|
-
# @example Regex as router
|
104
|
-
# unlink /\/hello\/(.*?)/ do
|
105
|
-
# puts 'Hello World'
|
106
|
-
# end
|
107
90
|
def unlink(path, &block) end
|
108
91
|
|
109
92
|
# Add WEBSOCKET method as a DSL for route definition
|
110
|
-
# @param [ String
|
93
|
+
# @param [ String ] path Accepts as part of path in route definition
|
111
94
|
# @yield what to run when route matched
|
112
95
|
# @return [ nil ] nil
|
113
96
|
# @example String as router
|
114
97
|
# websocket '/' do
|
115
98
|
# puts 'Hello World'
|
116
99
|
# end
|
117
|
-
#
|
118
|
-
# @example Regex as router
|
119
|
-
# websocket /\/hello\/(.*?)/ do
|
120
|
-
# puts 'Hello World'
|
121
|
-
# end
|
122
100
|
def websocket(path, &block) end
|
123
101
|
|
124
102
|
# Add EVENTSOURCE method as a DSL for route definition
|
125
|
-
# @param [ String
|
103
|
+
# @param [ String ] path Accepts as part of path in route definition
|
126
104
|
# @return [ nil ] nil
|
127
105
|
# @example String as router
|
128
106
|
# eventsource '/' do
|
129
107
|
# puts 'Hello World'
|
130
108
|
# end
|
131
|
-
#
|
132
|
-
# @example Regex as router
|
133
|
-
# eventsource /\/hello\/(.*?)/ do
|
134
|
-
# puts 'Hello World'
|
135
|
-
# end
|
136
109
|
def eventsource(path, &block) end
|
110
|
+
|
111
|
+
# Mount a route prefix with another API defined
|
112
|
+
# @param [String] prefix prefix of the route String
|
113
|
+
# @param [Class] api inherited from Midori::API
|
114
|
+
# @return [nil] nil
|
115
|
+
def mount(prefix, api)
|
116
|
+
raise ArgumentError if prefix == '/' # Cannot mount route API
|
117
|
+
@routes[:MOUNT] << [prefix, api]
|
118
|
+
end
|
137
119
|
|
138
120
|
# Implementation of route DSL
|
139
121
|
# @param [ String ] method HTTP method
|
@@ -141,82 +123,17 @@ class Midori::API
|
|
141
123
|
# @param [ Proc ] block process to run when route matched
|
142
124
|
# @return [ nil ] nil
|
143
125
|
def add_route(method, path, block)
|
144
|
-
|
145
|
-
|
146
|
-
path = convert_route path
|
147
|
-
end
|
148
|
-
@route = @route || []
|
149
|
-
@route << Midori::Route.new(method, path, block)
|
150
|
-
nil
|
151
|
-
end
|
126
|
+
# Argument check
|
127
|
+
raise ArgumentError unless path.is_a?String
|
152
128
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
# @return [ Midori::Response ] Http Response
|
158
|
-
# @raise [ Midori::Error::NotFound ] If no route matched
|
159
|
-
def receive(request, connection = nil)
|
160
|
-
@route.each do |route|
|
161
|
-
matched = match(route.method, route.path, request.method, request.path)
|
162
|
-
next unless matched
|
163
|
-
if @middleware.nil?
|
164
|
-
middlewares, body_accept = [], [String]
|
165
|
-
else
|
166
|
-
middlewares, body_accept = @middleware.clone, @body_accept.clone
|
167
|
-
end
|
168
|
-
middlewares.each { |middleware| request = middleware.before(request) }
|
169
|
-
clean_room = Midori::CleanRoom.new(request, middlewares, body_accept)
|
170
|
-
if request.websocket?
|
171
|
-
# Send 101 Switching Protocol
|
172
|
-
connection.send_data Midori::Response.new(101, websocket_header(request.header['Sec-WebSocket-Key']), '')
|
173
|
-
-> { clean_room.instance_exec(connection.websocket, *matched, &route.function) }.call
|
174
|
-
return Midori::Response.new
|
175
|
-
elsif request.eventsource?
|
176
|
-
connection.send_data Midori::Response.new(200, Midori::Const::EVENTSOURCE_HEADER, '')
|
177
|
-
-> { clean_room.instance_exec(connection.eventsource, *matched, &route.function) }.call
|
178
|
-
return Midori::Response.new
|
179
|
-
else
|
180
|
-
result = -> { clean_room.instance_exec(*matched, &route.function) }.call
|
181
|
-
clean_room.body = result if body_accept.include?(result.class)
|
182
|
-
response = clean_room.raw_response
|
183
|
-
middlewares.reverse_each { |middleware| response = middleware.after(request, response) }
|
184
|
-
return response
|
185
|
-
end
|
186
|
-
end
|
187
|
-
raise Midori::Error::NotFound
|
188
|
-
end
|
129
|
+
# Insert route to routes
|
130
|
+
route = Midori::Route.new(method, path, block)
|
131
|
+
route.middlewares = @scope_middlewares + @temp_middlewares
|
132
|
+
@routes[method] << route
|
189
133
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
# @param [ String ] request_method HTTP Request Method
|
194
|
-
# @param [ String ] request_path HTTP Request Path
|
195
|
-
# @return [ Array ] matched parameters
|
196
|
-
# @return [ Boolean ] false if not matched
|
197
|
-
# @example match a route
|
198
|
-
# match('GET', /^\/user\/(.*?)\/order\/(.*?)$/, '/user/foo/order/bar') # => ['foo', 'bar']
|
199
|
-
def match(method, path, request_method, request_path)
|
200
|
-
if request_method == method
|
201
|
-
result = request_path.match(path)
|
202
|
-
return result.to_a[1..-1] if result
|
203
|
-
false
|
204
|
-
else
|
205
|
-
false
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
# Convert String path to its Regexp equivalent
|
210
|
-
# @param [ String ] path String route definition
|
211
|
-
# @return [ Regexp ] Regexp equivalent
|
212
|
-
# @example
|
213
|
-
# convert_route('/user/:id/order/:order_id') # => Regexp
|
214
|
-
def convert_route(path)
|
215
|
-
path = '^' + path
|
216
|
-
.gsub(/\/(:[_a-z][_a-z0-9]+?)\//, '/([^/]+?)/')
|
217
|
-
.gsub(/\/(:[_a-z][_a-z0-9]+?)$/, '/([^/]+?)$')
|
218
|
-
path += '$' if path[-1] != '$'
|
219
|
-
Regexp.new path
|
134
|
+
# Clean up temp middleware
|
135
|
+
@temp_middlewares = []
|
136
|
+
nil
|
220
137
|
end
|
221
138
|
|
222
139
|
# Use a middleware in the all routes
|
@@ -224,20 +141,16 @@ class Midori::API
|
|
224
141
|
# @return [nil] nil
|
225
142
|
def use(middleware, *args)
|
226
143
|
middleware = middleware.new(*args)
|
227
|
-
CleanRoom.class_exec { middleware.helper }
|
228
|
-
@
|
229
|
-
@middleware << middleware
|
230
|
-
@body_accept = middleware.body_accept
|
144
|
+
Midori::CleanRoom.class_exec { middleware.helper }
|
145
|
+
@scope_middlewares << middleware
|
231
146
|
nil
|
232
147
|
end
|
233
148
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
header['Sec-WebSocket-Accept'] = Digest::SHA1.base64digest(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
240
|
-
header
|
149
|
+
def filter(middleware, *args)
|
150
|
+
middleware = middleware.new(*args)
|
151
|
+
Midori::CleanRoom.class_exec { middleware.helper }
|
152
|
+
@temp_middlewares << middleware
|
153
|
+
nil
|
241
154
|
end
|
242
155
|
|
243
156
|
# Helper block for defining methods in APIs
|
@@ -245,6 +158,10 @@ class Midori::API
|
|
245
158
|
def helper(&block)
|
246
159
|
Midori::CleanRoom.class_exec(&block)
|
247
160
|
end
|
161
|
+
|
162
|
+
def inherited(subclass)
|
163
|
+
subclass.class_initialize
|
164
|
+
end
|
248
165
|
end
|
249
166
|
|
250
167
|
private_class_method :add_route
|
@@ -255,7 +172,10 @@ class Midori::API
|
|
255
172
|
# Magics to fill DSL methods through dynamically class method definition
|
256
173
|
METHODS.each do |method|
|
257
174
|
define_singleton_method(method) do |*args, &block|
|
258
|
-
add_route(method.upcase, args[0], block) # args[0]: path
|
175
|
+
add_route(method.upcase.to_sym, args[0], block) # args[0]: path
|
259
176
|
end
|
260
177
|
end
|
178
|
+
|
179
|
+
singleton_class.send :alias_method, :ws, :websocket
|
180
|
+
singleton_class.send :alias_method, :es, :eventsource
|
261
181
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class Midori::APIEngine
|
2
|
+
attr_accessor :routes
|
3
|
+
def initialize(root_api, type = :sinatra)
|
4
|
+
@routes = {
|
5
|
+
GET: [],
|
6
|
+
POST: [],
|
7
|
+
PUT: [],
|
8
|
+
DELETE: [],
|
9
|
+
OPTIONS: [],
|
10
|
+
LINK: [],
|
11
|
+
UNLINK: [],
|
12
|
+
WEBSOCKET: [],
|
13
|
+
EVENTSOURCE: []
|
14
|
+
}
|
15
|
+
@root_api = root_api
|
16
|
+
@type = type
|
17
|
+
@routes = merge('', root_api, [])
|
18
|
+
@routes.delete :MOUNT
|
19
|
+
@routes.each do |method|
|
20
|
+
method[1].each do |route|
|
21
|
+
route.path = Mustermann.new(route.path, type: type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def merge(prefix, root_api, middlewares)
|
27
|
+
# Merge all routes with a Depth-first search
|
28
|
+
root_api.routes[:MOUNT].each do |mount|
|
29
|
+
root_api.routes.merge!(merge(mount[0], mount[1], root_api.scope_middlewares)) do |_key, old_val, new_val|
|
30
|
+
old_val + new_val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
root_api.routes.delete :MOUNT
|
34
|
+
root_api.routes.each do |method|
|
35
|
+
method[1].each do |route|
|
36
|
+
route.path = prefix + route.path
|
37
|
+
route.middlewares = middlewares + route.middlewares
|
38
|
+
end
|
39
|
+
end
|
40
|
+
root_api.routes
|
41
|
+
end
|
42
|
+
|
43
|
+
# Process after receive data from client
|
44
|
+
# @param request [ Midori::Request ] Http Raw Request
|
45
|
+
# @param connection [ EM::Connection ] A connection created by EventMachine
|
46
|
+
# @return [ Midori::Response ] Http Response
|
47
|
+
# @raise [ Midori::Error::NotFound ] If no route matched
|
48
|
+
def receive(request, connection = nil)
|
49
|
+
@routes[request.method].each do |route|
|
50
|
+
params = route.path.params(request.path)
|
51
|
+
next unless params
|
52
|
+
request.params = params
|
53
|
+
route.middlewares.each { |middleware| request = middleware.before(request) }
|
54
|
+
clean_room = Midori::CleanRoom.new(request)
|
55
|
+
if request.websocket?
|
56
|
+
# Send 101 Switching Protocol
|
57
|
+
connection.send_data Midori::Response.new(101, Midori::APIEngine.websocket_header(request.header['Sec-WebSocket-Key']), '')
|
58
|
+
connection.websocket.request = request
|
59
|
+
-> { clean_room.instance_exec(connection.websocket, &route.function) }.call
|
60
|
+
return Midori::Response.new
|
61
|
+
elsif request.eventsource?
|
62
|
+
connection.send_data Midori::Response.new(200, Midori::Const::EVENTSOURCE_HEADER, '')
|
63
|
+
-> { clean_room.instance_exec(connection.eventsource, &route.function) }.call
|
64
|
+
return Midori::Response.new
|
65
|
+
else
|
66
|
+
result = -> { clean_room.instance_exec(&route.function) }.call
|
67
|
+
clean_room.body = result if result.is_a?String
|
68
|
+
response = clean_room.raw_response
|
69
|
+
route.middlewares.reverse_each { |middleware| response = middleware.after(request, response) }
|
70
|
+
return response
|
71
|
+
end
|
72
|
+
end
|
73
|
+
raise Midori::Error::NotFound
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return websocket header with given key
|
77
|
+
# @param [String] key 'Sec-WebSocket-Key' in request header
|
78
|
+
# @return [Hash] header
|
79
|
+
def self.websocket_header(key)
|
80
|
+
header = Midori::Const::WEBSOCKET_HEADER.clone
|
81
|
+
header['Sec-WebSocket-Accept'] = Digest::SHA1.base64digest(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
82
|
+
header
|
83
|
+
end
|
84
|
+
end
|
data/lib/em-midori/clean_room.rb
CHANGED
@@ -7,34 +7,16 @@
|
|
7
7
|
class Midori::CleanRoom
|
8
8
|
attr_accessor :code, :header, :body, :request
|
9
9
|
# @param [Midori::Request] request HTTP request
|
10
|
-
|
11
|
-
# @param [Array<Class>] body_accept what class for body could last middleware accept by default
|
12
|
-
def initialize(request, middleware = [], body_accept = [String])
|
10
|
+
def initialize(request)
|
13
11
|
@status = 200
|
14
12
|
@header = Midori::Const::DEFAULT_HEADER.clone
|
15
13
|
@body = ''
|
16
14
|
@request = request
|
17
|
-
@middleware = middleware
|
18
|
-
@body_accept = body_accept
|
19
15
|
end
|
20
16
|
|
21
|
-
#
|
17
|
+
# Generate response from variables inside +Midori::CleanRoom+
|
22
18
|
# @return [Midori::Response] midori response
|
23
19
|
def raw_response
|
24
20
|
Midori::Response.new(@status, @header, @body)
|
25
21
|
end
|
26
|
-
|
27
|
-
# Add a middleware to a specific route
|
28
|
-
# @param [Class] middleware inherited form +Midori::Middleware+ class
|
29
|
-
# @param [Array<Object>] args for middleware initialize
|
30
|
-
# @return [nil] nil
|
31
|
-
def use(middleware, *args)
|
32
|
-
middleware = middleware.new(*args)
|
33
|
-
middleware.helper
|
34
|
-
@middleware = [] if @middleware.nil?
|
35
|
-
@middleware << middleware
|
36
|
-
@body_accept.replace middleware.body_accept
|
37
|
-
@request = middleware.before(request)
|
38
|
-
nil
|
39
|
-
end
|
40
22
|
end
|
data/lib/em-midori/configure.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
##
|
2
2
|
# Default configuration of Midori, extends +Midori::Configurable+
|
3
3
|
class Midori::Configure
|
4
|
-
extend
|
4
|
+
extend Configurable
|
5
5
|
|
6
6
|
set :logger, ::Logger.new(STDOUT)
|
7
7
|
set :bind, '127.0.0.1'
|
8
8
|
set :port, 8080
|
9
|
+
set :route_type, :sinatra
|
9
10
|
end
|
@@ -1,32 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
##
|
2
|
+
# Modified from Sinatra
|
3
|
+
# Provide flexible configuration for Midori Server
|
4
|
+
module Configurable
|
5
|
+
# Sets an option to the given value.
|
6
|
+
# @param [Symbol] option the name of config
|
7
|
+
# @param [Object] value value to the name
|
8
|
+
# @param [Boolean] read_only Generate option= method or not
|
9
|
+
def set(option, value = (not_set = true), read_only = false)
|
10
|
+
raise ArgumentError if not_set
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
setter = proc { |val| set option, val }
|
13
|
+
getter = proc { value }
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
define_singleton("#{option}=", setter) unless read_only
|
16
|
+
define_singleton(option, getter)
|
17
|
+
define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
self
|
20
|
+
end
|
21
21
|
|
22
|
-
|
22
|
+
private
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
24
|
+
# Dynamically defines a method on settings.
|
25
|
+
def define_singleton(name, content = Proc.new)
|
26
|
+
singleton_class.class_eval do
|
27
|
+
undef_method(name) if method_defined? name
|
28
|
+
String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|
File without changes
|
@@ -7,9 +7,9 @@ class Promise
|
|
7
7
|
@callback = callback
|
8
8
|
end
|
9
9
|
|
10
|
-
# Define what to do after a method
|
11
|
-
# @param [Proc] resolve what
|
12
|
-
# @param [Proc] reject what
|
10
|
+
# Define what to do after a method callbacks
|
11
|
+
# @param [Proc] resolve what on callback
|
12
|
+
# @param [Proc] reject what on callback failed
|
13
13
|
# @return [nil] nil
|
14
14
|
def then(resolve = ->() {}, reject = ->() {})
|
15
15
|
@callback.call(resolve, reject)
|
File without changes
|
data/lib/em-midori/middleware.rb
CHANGED
data/lib/em-midori/request.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
class Midori::Request
|
12
12
|
attr_accessor :ip, :port,
|
13
13
|
:protocol, :method, :path, :query_string,
|
14
|
-
:header, :body, :parsed
|
14
|
+
:header, :body, :parsed, :params
|
15
15
|
|
16
16
|
# Init Request
|
17
17
|
def initialize
|
@@ -19,6 +19,7 @@ class Midori::Request
|
|
19
19
|
@is_websocket = false
|
20
20
|
@is_eventsource = false
|
21
21
|
@parser = Http::Parser.new
|
22
|
+
@params = {}
|
22
23
|
@parser.on_headers_complete = proc do
|
23
24
|
@protocol = @parser.http_version
|
24
25
|
@method = @parser.http_method
|
@@ -39,32 +40,33 @@ class Midori::Request
|
|
39
40
|
|
40
41
|
# Deal with WebSocket
|
41
42
|
if @header['Upgrade'] == 'websocket' && @header['Connection'] == 'Upgrade'
|
42
|
-
@method =
|
43
|
+
@method = :WEBSOCKET
|
43
44
|
@is_websocket = true
|
44
45
|
end
|
45
46
|
|
46
47
|
# Deal with EventSource
|
47
48
|
if @header['Accept'] == 'text/event-stream'
|
48
|
-
@method =
|
49
|
+
@method = :EVENTSOURCE
|
49
50
|
@is_eventsource = true
|
50
51
|
end
|
51
52
|
|
53
|
+
@method = @method.to_sym
|
52
54
|
@parsed = true
|
53
55
|
end
|
54
56
|
|
55
|
-
# Syntatic
|
57
|
+
# Syntatic sugar for whether a request is parsed
|
56
58
|
# @return [Boolean] parsed or not
|
57
59
|
def parsed?
|
58
60
|
@parsed
|
59
61
|
end
|
60
62
|
|
61
|
-
# Syntatic
|
63
|
+
# Syntatic sugar for whether a request is a websocket request
|
62
64
|
# @return [Boolean] websocket or not
|
63
65
|
def websocket?
|
64
66
|
@is_websocket
|
65
67
|
end
|
66
68
|
|
67
|
-
# Syntatic
|
69
|
+
# Syntatic sugar for whether a request is an eventsource request
|
68
70
|
# @return [Boolean] eventsource or not
|
69
71
|
def eventsource?
|
70
72
|
@is_eventsource
|
data/lib/em-midori/route.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# @attr [Regexp] path regex to match
|
5
5
|
# @attr [Proc] function what to do after matched
|
6
6
|
class Midori::Route
|
7
|
-
attr_accessor :method, :path, :function
|
7
|
+
attr_accessor :method, :path, :function, :middlewares
|
8
8
|
|
9
9
|
# @param [String] method HTTP method
|
10
10
|
# @param [Regexp] path regex to match
|
@@ -13,5 +13,6 @@ class Midori::Route
|
|
13
13
|
@method = method
|
14
14
|
@path = path
|
15
15
|
@function = function
|
16
|
+
@middlewares = []
|
16
17
|
end
|
17
18
|
end
|
data/lib/em-midori/runner.rb
CHANGED
@@ -7,8 +7,7 @@ class Midori::Runner
|
|
7
7
|
@logger = configure.logger
|
8
8
|
@bind = configure.bind
|
9
9
|
@port = configure.port
|
10
|
-
|
11
|
-
@api = api
|
10
|
+
@api = ((api.is_a?Midori::APIEngine) ? api : Midori::APIEngine.new(api, configure.route_type))
|
12
11
|
end
|
13
12
|
|
14
13
|
# Get Midori server whether running
|
data/lib/em-midori/version.rb
CHANGED
data/lib/em-midori/websocket.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# @attr [Hash] events response for different event
|
6
6
|
# @attr [EM::Connection] connection raw EventMachine connection
|
7
7
|
class Midori::WebSocket
|
8
|
-
attr_accessor :msg, :opcode, :events, :connection
|
8
|
+
attr_accessor :msg, :opcode, :events, :connection, :request
|
9
9
|
|
10
10
|
# @param [EM::Connection] connection raw EventMachine connection
|
11
11
|
def initialize(connection)
|
@@ -70,7 +70,7 @@ class Midori::WebSocket
|
|
70
70
|
elsif msg.is_a?Array
|
71
71
|
output << 0b10000010 << msg.size
|
72
72
|
output.concat msg
|
73
|
-
@connection.send_data(output.pack(
|
73
|
+
@connection.send_data(output.pack('C*'))
|
74
74
|
else
|
75
75
|
raise Midori::Error::OpCodeError
|
76
76
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-midori
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HeckPsi Lab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -17,9 +17,6 @@ dependencies:
|
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.2'
|
20
|
-
- - ">"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 1.2.0.0
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,9 +24,6 @@ dependencies:
|
|
27
24
|
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '1.2'
|
30
|
-
- - ">"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: 1.2.0.0
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: http_parser.rb
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,6 +38,20 @@ dependencies:
|
|
44
38
|
- - "~>"
|
45
39
|
- !ruby/object:Gem::Version
|
46
40
|
version: '0.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mustermann
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.4'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.4'
|
47
55
|
description: EM Midori is an EventMachine-based Web Framework written in pure Ruby,
|
48
56
|
providing high performance and proper abstraction.
|
49
57
|
email: business@heckpsi.com
|
@@ -51,24 +59,26 @@ executables: []
|
|
51
59
|
extensions: []
|
52
60
|
extra_rdoc_files: []
|
53
61
|
files:
|
62
|
+
- CONTRIBUTOR_COVENANT_CODE_OF_CONDUCT.md
|
54
63
|
- LICENSE
|
55
64
|
- lib/em-midori.rb
|
56
65
|
- lib/em-midori/api.rb
|
66
|
+
- lib/em-midori/api_engine.rb
|
57
67
|
- lib/em-midori/clean_room.rb
|
58
68
|
- lib/em-midori/configure.rb
|
59
69
|
- lib/em-midori/const.rb
|
60
70
|
- lib/em-midori/core_ext/configurable.rb
|
61
|
-
- lib/em-midori/define_class.rb
|
71
|
+
- lib/em-midori/core_ext/define_class.rb
|
72
|
+
- lib/em-midori/core_ext/promise.rb
|
73
|
+
- lib/em-midori/core_ext/string.rb
|
62
74
|
- lib/em-midori/error.rb
|
63
75
|
- lib/em-midori/eventsource.rb
|
64
76
|
- lib/em-midori/middleware.rb
|
65
|
-
- lib/em-midori/promise.rb
|
66
77
|
- lib/em-midori/request.rb
|
67
78
|
- lib/em-midori/response.rb
|
68
79
|
- lib/em-midori/route.rb
|
69
80
|
- lib/em-midori/runner.rb
|
70
81
|
- lib/em-midori/server.rb
|
71
|
-
- lib/em-midori/string.rb
|
72
82
|
- lib/em-midori/version.rb
|
73
83
|
- lib/em-midori/websocket.rb
|
74
84
|
homepage: https://github.com/heckpsi-lab/em-midori
|