midori.rb 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +9 -0
- data/LICENSE +21 -0
- data/ext/midori/extconf.rb +4 -0
- data/ext/midori/websocket.c +32 -0
- data/lib/midori/api.rb +426 -0
- data/lib/midori/api_engine.rb +109 -0
- data/lib/midori/clean_room.rb +24 -0
- data/lib/midori/configure.rb +12 -0
- data/lib/midori/connection.rb +59 -0
- data/lib/midori/const.rb +118 -0
- data/lib/midori/core_ext/configurable.rb +33 -0
- data/lib/midori/core_ext/define_class.rb +29 -0
- data/lib/midori/core_ext/proc.rb +13 -0
- data/lib/midori/core_ext/string.rb +29 -0
- data/lib/midori/env.rb +18 -0
- data/lib/midori/eventsource.rb +20 -0
- data/lib/midori/exception.rb +22 -0
- data/lib/midori/logger.rb +15 -0
- data/lib/midori/middleware.rb +31 -0
- data/lib/midori/request.rb +115 -0
- data/lib/midori/response.rb +34 -0
- data/lib/midori/route.rb +20 -0
- data/lib/midori/runner.rb +63 -0
- data/lib/midori/sandbox.rb +46 -0
- data/lib/midori/server.rb +106 -0
- data/lib/midori/version.rb +5 -0
- data/lib/midori/websocket.rb +105 -0
- data/lib/midori.rb +37 -0
- data/midori.sublime-project +16 -0
- data/tutorial/README.md +11 -0
- data/tutorial/SUMMARY.md +28 -0
- data/tutorial/advanced/custom_extensions.md +0 -0
- data/tutorial/advanced/deploying_for_production.md +0 -0
- data/tutorial/advanced/error_handling.md +0 -0
- data/tutorial/advanced/rerl.md +0 -0
- data/tutorial/advanced/scaling_project.md +0 -0
- data/tutorial/advanced/unit_testing.md +0 -0
- data/tutorial/essentials/extensions.md +31 -0
- data/tutorial/essentials/getting_started.md +27 -0
- data/tutorial/essentials/installation.md +93 -0
- data/tutorial/essentials/middlewares.md +88 -0
- data/tutorial/essentials/request_handling.md +74 -0
- data/tutorial/essentials/routing.md +136 -0
- data/tutorial/essentials/runner.md +59 -0
- data/tutorial/meta/comparison_with_other_frameworks.md +0 -0
- data/tutorial/meta/join_the_midori_community.md +0 -0
- data/tutorial/meta/next_steps.md +0 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 710b54063e97c0088cb335bbf2c4719154f0e991
|
4
|
+
data.tar.gz: 0753fd460e35cc8c19e04b6c2ccb561e54d0db69
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a2190c4be4ba8bf93f23d89cb9efb5ed49d5886351689be6bdf2fa0d174d026fad10a13970fcc2a931ff2eb9a384b115615f6f3b2146dbfbb468281f0884f3e9
|
7
|
+
data.tar.gz: a5fa0332c7a506523700728b1970ef32fec0ed9b31c7d8f86d489734fdffcad4499a58ab591247d686769683f97d8d302e255cab8213265988c5f7416b900b56
|
data/.editorconfig
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016-2017 HeckPsi Lab
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
VALUE Midori = Qnil;
|
4
|
+
VALUE MidoriWebSocket = Qnil;
|
5
|
+
|
6
|
+
void Init_midori_ext();
|
7
|
+
VALUE method_midori_websocket_mask(VALUE self, VALUE payload, VALUE mask);
|
8
|
+
|
9
|
+
void Init_midori_ext() {
|
10
|
+
Midori = rb_define_module("Midori");
|
11
|
+
MidoriWebSocket = rb_define_class_under(Midori, "WebSocket", rb_cObject);
|
12
|
+
rb_define_protected_method(MidoriWebSocket, "mask", method_midori_websocket_mask, 2);
|
13
|
+
}
|
14
|
+
|
15
|
+
VALUE method_midori_websocket_mask(VALUE self, VALUE payload, VALUE mask) {
|
16
|
+
long n = RARRAY_LEN(payload), i, p, m;
|
17
|
+
VALUE unmasked = rb_ary_new2(n);
|
18
|
+
|
19
|
+
int mask_array[] = {
|
20
|
+
NUM2INT(rb_ary_entry(mask, 0)),
|
21
|
+
NUM2INT(rb_ary_entry(mask, 1)),
|
22
|
+
NUM2INT(rb_ary_entry(mask, 2)),
|
23
|
+
NUM2INT(rb_ary_entry(mask, 3))
|
24
|
+
};
|
25
|
+
|
26
|
+
for (i = 0; i < n; i++) {
|
27
|
+
p = NUM2INT(rb_ary_entry(payload, i));
|
28
|
+
m = mask_array[i % 4];
|
29
|
+
rb_ary_store(unmasked, i, INT2NUM(p ^ m));
|
30
|
+
}
|
31
|
+
return unmasked;
|
32
|
+
}
|
data/lib/midori/api.rb
ADDED
@@ -0,0 +1,426 @@
|
|
1
|
+
##
|
2
|
+
# This class provides methods to be inherited as route definition.
|
3
|
+
class Midori::API
|
4
|
+
class << self
|
5
|
+
# @!attribute routes
|
6
|
+
# @return [Hash] merged routes defined in the instance
|
7
|
+
# @!attribute scope_middlewares
|
8
|
+
# @return [Array] global middlewares under the scope
|
9
|
+
attr_accessor :routes, :scope_middlewares
|
10
|
+
|
11
|
+
# Init private variables of class
|
12
|
+
# @return [nil] nil
|
13
|
+
def class_initialize
|
14
|
+
@routes = {}
|
15
|
+
Midori::Const::ROUTE_METHODS.map {|method| @routes[method] = []}
|
16
|
+
@routes[:MOUNT] = []
|
17
|
+
@scope_middlewares = []
|
18
|
+
@temp_middlewares = []
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
# Add DELETE method as a DSL for route definition
|
23
|
+
# @param [ String ] path Accepts as part of path in route definition
|
24
|
+
# @yield what to run when route matched
|
25
|
+
# @return [ nil ] nil
|
26
|
+
# @example String as router
|
27
|
+
# delete '/' do
|
28
|
+
# puts 'Hello World'
|
29
|
+
# end
|
30
|
+
def delete(path, &block) end
|
31
|
+
|
32
|
+
# Add GET method as a DSL for route definition
|
33
|
+
# @param [String] path Accepts as part of path in route definition
|
34
|
+
# @yield what to run when route matched
|
35
|
+
# @return [nil] nil
|
36
|
+
# @example String as router
|
37
|
+
# get '/' do
|
38
|
+
# puts 'Hello World'
|
39
|
+
# end
|
40
|
+
def get(path, &block) end
|
41
|
+
|
42
|
+
# Add HEAD method as a DSL for route definition
|
43
|
+
# @param [ String ] path Accepts as part of path in route definition
|
44
|
+
# @yield what to run when route matched
|
45
|
+
# @return [ nil ] nil
|
46
|
+
# @example String as router
|
47
|
+
# head '/' do
|
48
|
+
# puts 'Hello World'
|
49
|
+
# end
|
50
|
+
def head(path, &block) end
|
51
|
+
|
52
|
+
# Add POST method as a DSL for route definition
|
53
|
+
# @param [String] path Accepts as part of path in route definition
|
54
|
+
# @yield what to run when route matched
|
55
|
+
# @return [nil] nil
|
56
|
+
# @example String as router
|
57
|
+
# post '/' do
|
58
|
+
# puts 'Hello World'
|
59
|
+
# end
|
60
|
+
def post(path, &block) end
|
61
|
+
|
62
|
+
# Add PUT method as a DSL for route definition
|
63
|
+
# @param [String] path Accepts as part of path in route definition
|
64
|
+
# @yield what to run when route matched
|
65
|
+
# @return [nil] nil
|
66
|
+
# @example String as router
|
67
|
+
# put '/' do
|
68
|
+
# puts 'Hello World'
|
69
|
+
# end
|
70
|
+
def put(path, &block) end
|
71
|
+
|
72
|
+
# Add CONNECT method as a DSL for route definition
|
73
|
+
# @param [String] path Accepts as part of path in route definition
|
74
|
+
# @yield what to run when route matched
|
75
|
+
# @return [nil] nil
|
76
|
+
# @example String as router
|
77
|
+
# connect '/' do
|
78
|
+
# puts 'Hello World'
|
79
|
+
# end
|
80
|
+
def connect(path, &block) end
|
81
|
+
|
82
|
+
# Add OPTIONS method as a DSL for route definition
|
83
|
+
# @param [String] path Accepts as part of path in route definition
|
84
|
+
# @return [nil] nil
|
85
|
+
# @example String as router
|
86
|
+
# options '/' do
|
87
|
+
# puts 'Hello World'
|
88
|
+
# end
|
89
|
+
def options(path, &block) end
|
90
|
+
|
91
|
+
# Add TRACE method as a DSL for route definition
|
92
|
+
# @param [ String ] path Accepts as part of path in route definition
|
93
|
+
# @yield what to run when route matched
|
94
|
+
# @return [ nil ] nil
|
95
|
+
# @example String as router
|
96
|
+
# trace '/' do
|
97
|
+
# puts 'Hello World'
|
98
|
+
# end
|
99
|
+
def trace(path, &block) end
|
100
|
+
|
101
|
+
# Add COPY method as a DSL for route definition
|
102
|
+
# @param [ String ] path Accepts as part of path in route definition
|
103
|
+
# @yield what to run when route matched
|
104
|
+
# @return [ nil ] nil
|
105
|
+
# @example String as router
|
106
|
+
# copy '/' do
|
107
|
+
# puts 'Hello World'
|
108
|
+
# end
|
109
|
+
def copy(path, &block) end
|
110
|
+
|
111
|
+
# Add LOCK method as a DSL for route definition
|
112
|
+
# @param [ String ] path Accepts as part of path in route definition
|
113
|
+
# @yield what to run when route matched
|
114
|
+
# @return [ nil ] nil
|
115
|
+
# @example String as router
|
116
|
+
# lock '/' do
|
117
|
+
# puts 'Hello World'
|
118
|
+
# end
|
119
|
+
def lock(path, &block) end
|
120
|
+
|
121
|
+
# Add MKCOK method as a DSL for route definition
|
122
|
+
# @param [ String ] path Accepts as part of path in route definition
|
123
|
+
# @yield what to run when route matched
|
124
|
+
# @return [ nil ] nil
|
125
|
+
# @example String as router
|
126
|
+
# mkcol '/' do
|
127
|
+
# puts 'Hello World'
|
128
|
+
# end
|
129
|
+
def mkcol(path, &block) end
|
130
|
+
|
131
|
+
# Add MOVE method as a DSL for route definition
|
132
|
+
# @param [ String ] path Accepts as part of path in route definition
|
133
|
+
# @yield what to run when route matched
|
134
|
+
# @return [ nil ] nil
|
135
|
+
# @example String as router
|
136
|
+
# move '/' do
|
137
|
+
# puts 'Hello World'
|
138
|
+
# end
|
139
|
+
def move(path, &block) end
|
140
|
+
|
141
|
+
|
142
|
+
# Add PROPFIND method as a DSL for route definition
|
143
|
+
# @param [ String ] path Accepts as part of path in route definition
|
144
|
+
# @yield what to run when route matched
|
145
|
+
# @return [ nil ] nil
|
146
|
+
# @example String as router
|
147
|
+
# propfind '/' do
|
148
|
+
# puts 'Hello World'
|
149
|
+
# end
|
150
|
+
def propfind(path, &block) end
|
151
|
+
|
152
|
+
# Add PROPPATCH method as a DSL for route definition
|
153
|
+
# @param [ String ] path Accepts as part of path in route definition
|
154
|
+
# @yield what to run when route matched
|
155
|
+
# @return [ nil ] nil
|
156
|
+
# @example String as router
|
157
|
+
# proppatch '/' do
|
158
|
+
# puts 'Hello World'
|
159
|
+
# end
|
160
|
+
def proppatch(path, &block) end
|
161
|
+
|
162
|
+
# Add UNLOCK method as a DSL for route definition
|
163
|
+
# @param [ String ] path Accepts as part of path in route definition
|
164
|
+
# @yield what to run when route matched
|
165
|
+
# @return [ nil ] nil
|
166
|
+
# @example String as router
|
167
|
+
# unlock '/' do
|
168
|
+
# puts 'Hello World'
|
169
|
+
# end
|
170
|
+
def unlock(path, &block) end
|
171
|
+
|
172
|
+
# Add REPORT method as a DSL for route definition
|
173
|
+
# @param [ String ] path Accepts as part of path in route definition
|
174
|
+
# @yield what to run when route matched
|
175
|
+
# @return [ nil ] nil
|
176
|
+
# @example String as router
|
177
|
+
# report '/' do
|
178
|
+
# puts 'Hello World'
|
179
|
+
# end
|
180
|
+
def report(path, &block) end
|
181
|
+
|
182
|
+
# Add MKACTIVITY method as a DSL for route definition
|
183
|
+
# @param [ String ] path Accepts as part of path in route definition
|
184
|
+
# @yield what to run when route matched
|
185
|
+
# @return [ nil ] nil
|
186
|
+
# @example String as router
|
187
|
+
# mkactivity '/' do
|
188
|
+
# puts 'Hello World'
|
189
|
+
# end
|
190
|
+
def mkactivity(path, &block) end
|
191
|
+
|
192
|
+
# Add CHECKOUT method as a DSL for route definition
|
193
|
+
# @param [ String ] path Accepts as part of path in route definition
|
194
|
+
# @yield what to run when route matched
|
195
|
+
# @return [ nil ] nil
|
196
|
+
# @example String as router
|
197
|
+
# checkout '/' do
|
198
|
+
# puts 'Hello World'
|
199
|
+
# end
|
200
|
+
def checkout(path, &block) end
|
201
|
+
|
202
|
+
# Add MERGE method as a DSL for route definition
|
203
|
+
# @param [ String ] path Accepts as part of path in route definition
|
204
|
+
# @yield what to run when route matched
|
205
|
+
# @return [ nil ] nil
|
206
|
+
# @example String as router
|
207
|
+
# merge '/' do
|
208
|
+
# puts 'Hello World'
|
209
|
+
# end
|
210
|
+
def merge(path, &block) end
|
211
|
+
|
212
|
+
# Add M-SEARCH method as a DSL for route definition
|
213
|
+
# @param [ String ] path Accepts as part of path in route definition
|
214
|
+
# @yield what to run when route matched
|
215
|
+
# @return [ nil ] nil
|
216
|
+
# @example String as router
|
217
|
+
# msearch '/' do
|
218
|
+
# puts 'Hello World'
|
219
|
+
# end
|
220
|
+
def msearch(path, &block)
|
221
|
+
add_route(:'M-SEARCH', path, block)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Add NOTIFY method as a DSL for route definition
|
225
|
+
# @param [ String ] path Accepts as part of path in route definition
|
226
|
+
# @yield what to run when route matched
|
227
|
+
# @return [ nil ] nil
|
228
|
+
# @example String as router
|
229
|
+
# notify '/' do
|
230
|
+
# puts 'Hello World'
|
231
|
+
# end
|
232
|
+
def notify(path, &block) end
|
233
|
+
|
234
|
+
# Add SUBSCRIBE method as a DSL for route definition
|
235
|
+
# @param [ String ] path Accepts as part of path in route definition
|
236
|
+
# @yield what to run when route matched
|
237
|
+
# @return [ nil ] nil
|
238
|
+
# @example String as router
|
239
|
+
# subscribe '/' do
|
240
|
+
# puts 'Hello World'
|
241
|
+
# end
|
242
|
+
def subscribe(path, &block) end
|
243
|
+
|
244
|
+
# Add UNSUBSCRIBE method as a DSL for route definition
|
245
|
+
# @param [ String ] path Accepts as part of path in route definition
|
246
|
+
# @yield what to run when route matched
|
247
|
+
# @return [ nil ] nil
|
248
|
+
# @example String as router
|
249
|
+
# unsubscribe '/' do
|
250
|
+
# puts 'Hello World'
|
251
|
+
# end
|
252
|
+
def unsubscribe(path, &block) end
|
253
|
+
|
254
|
+
# Add PATCH method as a DSL for route definition
|
255
|
+
# @param [ String ] path Accepts as part of path in route definition
|
256
|
+
# @yield what to run when route matched
|
257
|
+
# @return [ nil ] nil
|
258
|
+
# @example String as router
|
259
|
+
# patch '/' do
|
260
|
+
# puts 'Hello World'
|
261
|
+
# end
|
262
|
+
def patch(path, &block) end
|
263
|
+
|
264
|
+
# Add PURGE method as a DSL for route definition
|
265
|
+
# @param [ String ] path Accepts as part of path in route definition
|
266
|
+
# @yield what to run when route matched
|
267
|
+
# @return [ nil ] nil
|
268
|
+
# @example String as router
|
269
|
+
# purge '/' do
|
270
|
+
# puts 'Hello World'
|
271
|
+
# end
|
272
|
+
def purge(path, &block) end
|
273
|
+
|
274
|
+
# Add LINK method as a DSL for route definition
|
275
|
+
# @param [String] path Accepts as part of path in route definition
|
276
|
+
# @yield what to run when route matched
|
277
|
+
# @return [nil] nil
|
278
|
+
# @example String as router
|
279
|
+
# link '/' do
|
280
|
+
# puts 'Hello World'
|
281
|
+
# end
|
282
|
+
def link(path, &block) end
|
283
|
+
|
284
|
+
# Add UNLINK method as a DSL for route definition
|
285
|
+
# @param [String] path Accepts as part of path in route definition
|
286
|
+
# @yield what to run when route matched
|
287
|
+
# @return [nil] nil
|
288
|
+
# @example String as router
|
289
|
+
# unlink '/' do
|
290
|
+
# puts 'Hello World'
|
291
|
+
# end
|
292
|
+
def unlink(path, &block) end
|
293
|
+
|
294
|
+
# Add WEBSOCKET method as a DSL for route definition
|
295
|
+
# @param [String] path Accepts as part of path in route definition
|
296
|
+
# @yield what to run when route matched
|
297
|
+
# @return [nil] nil
|
298
|
+
# @example String as router
|
299
|
+
# websocket '/' do
|
300
|
+
# puts 'Hello World'
|
301
|
+
# end
|
302
|
+
def websocket(path, &block) end
|
303
|
+
|
304
|
+
# Add EVENTSOURCE method as a DSL for route definition
|
305
|
+
# @param [String] path Accepts as part of path in route definition
|
306
|
+
# @yield what to run when route matched
|
307
|
+
# @return [nil] nil
|
308
|
+
# @example String as router
|
309
|
+
# eventsource '/' do
|
310
|
+
# puts 'Hello World'
|
311
|
+
# end
|
312
|
+
def eventsource(path, &block) end
|
313
|
+
|
314
|
+
# Mount a route prefix with another API defined
|
315
|
+
# @param [String] prefix prefix of the route String
|
316
|
+
# @param [Class] api inherited from Midori::API
|
317
|
+
# @return [nil] nil
|
318
|
+
def mount(prefix, api)
|
319
|
+
raise ArgumentError if prefix == '/' # Cannot mount route API
|
320
|
+
@routes[:MOUNT] << [prefix, api]
|
321
|
+
nil
|
322
|
+
end
|
323
|
+
|
324
|
+
# Definitions for global error handler
|
325
|
+
# @param [Class] error Error class, must be inherited form StandardError
|
326
|
+
# @yield what to do to deal with error
|
327
|
+
# @yieldparam [StandardError] e the detailed error
|
328
|
+
# @example Basic Usage
|
329
|
+
# capture Midori::InternalError do |e|
|
330
|
+
# Midori::Response(500, {}, e.backtrace)
|
331
|
+
# end
|
332
|
+
def capture(error, &block)
|
333
|
+
Midori::Sandbox.add_rule(error, block)
|
334
|
+
nil
|
335
|
+
end
|
336
|
+
|
337
|
+
# Implementation of route DSL
|
338
|
+
# @param [String] method HTTP method
|
339
|
+
# @param [String, Regexp] path path definition
|
340
|
+
# @param [Proc] block process to run when route matched
|
341
|
+
# @return [nil] nil
|
342
|
+
private def add_route(method, path, block)
|
343
|
+
# Argument check
|
344
|
+
raise ArgumentError unless path.is_a?String
|
345
|
+
|
346
|
+
# Insert route to routes
|
347
|
+
route = Midori::Route.new(method, path, block)
|
348
|
+
route.middlewares = @scope_middlewares + @temp_middlewares
|
349
|
+
@routes[method] << route
|
350
|
+
|
351
|
+
# Clean up temp middleware
|
352
|
+
@temp_middlewares = []
|
353
|
+
nil
|
354
|
+
end
|
355
|
+
|
356
|
+
# Use a middleware in the all routes
|
357
|
+
# @param [Class] middleware Inherited from +Midori::Middleware+
|
358
|
+
# @return [nil] nil
|
359
|
+
def use(middleware, *args)
|
360
|
+
middleware = middleware.new(*args)
|
361
|
+
@scope_middlewares << middleware
|
362
|
+
nil
|
363
|
+
end
|
364
|
+
|
365
|
+
# Use a middleware in the next route
|
366
|
+
# @param [Class] middleware Inherited from +Midori::Middleware+
|
367
|
+
# @return [nil] nil
|
368
|
+
def filter(middleware, *args)
|
369
|
+
middleware = middleware.new(*args)
|
370
|
+
@temp_middlewares << middleware
|
371
|
+
nil
|
372
|
+
end
|
373
|
+
|
374
|
+
# Helper block for defining methods in APIs
|
375
|
+
# @param [Symbol] name name of the method
|
376
|
+
# @yield define what to run in CleanRoom
|
377
|
+
def helper(name, &block)
|
378
|
+
Midori::CleanRoom.class_exec do
|
379
|
+
define_method(name, &block)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
private def inherited(subclass)
|
384
|
+
subclass.class_initialize
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
# Constants of supported methods in route definition
|
389
|
+
METHODS = %w( delete
|
390
|
+
get
|
391
|
+
head
|
392
|
+
post
|
393
|
+
put
|
394
|
+
connect
|
395
|
+
options
|
396
|
+
trace
|
397
|
+
copy
|
398
|
+
lock
|
399
|
+
mkcol
|
400
|
+
move
|
401
|
+
propfind
|
402
|
+
proppatch
|
403
|
+
unlock
|
404
|
+
report
|
405
|
+
mkactivity
|
406
|
+
checkout
|
407
|
+
merge
|
408
|
+
notify
|
409
|
+
subscribe
|
410
|
+
unsubscribe
|
411
|
+
patch
|
412
|
+
purge
|
413
|
+
websocket
|
414
|
+
eventsource
|
415
|
+
).freeze
|
416
|
+
|
417
|
+
# Magics to fill DSL methods through dynamically class method definition
|
418
|
+
METHODS.each do |method|
|
419
|
+
define_singleton_method(method) do |*args, &block|
|
420
|
+
add_route(method.upcase.to_sym, args[0], block) # args[0]: path
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
singleton_class.send :alias_method, :ws, :websocket
|
425
|
+
singleton_class.send :alias_method, :es, :eventsource
|
426
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
##
|
2
|
+
# Merge and manage all APIs.
|
3
|
+
# @attr [Hash] routes A hash of all routes merged
|
4
|
+
class Midori::APIEngine
|
5
|
+
attr_accessor :routes
|
6
|
+
|
7
|
+
# Init an API Engine
|
8
|
+
# @param [Class] root_api API inherited from [Midori::API]
|
9
|
+
# @param [Symbol] type type mustermann support
|
10
|
+
def initialize(root_api, type = :sinatra)
|
11
|
+
@routes = {}
|
12
|
+
Midori::Const::ROUTE_METHODS.map {|method| @routes[method] = []}
|
13
|
+
@root_api = root_api
|
14
|
+
@type = type
|
15
|
+
@routes = merge('', root_api, [])
|
16
|
+
@routes.delete :MOUNT
|
17
|
+
@routes.each do |method|
|
18
|
+
method[1].each do |route|
|
19
|
+
route.path = Mustermann.new(route.path, type: type)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Merge all routes with a Depth-first search
|
25
|
+
private def merge(prefix, root_api, middlewares)
|
26
|
+
root_api.routes[:MOUNT].each do |mount|
|
27
|
+
root_api.routes.merge!(merge(mount[0], mount[1], root_api.scope_middlewares)) do |_key, old_val, new_val|
|
28
|
+
old_val + new_val
|
29
|
+
end
|
30
|
+
end
|
31
|
+
root_api.routes.delete :MOUNT
|
32
|
+
root_api.routes.each do |method|
|
33
|
+
method[1].each do |route|
|
34
|
+
route.path = prefix + route.path
|
35
|
+
route.middlewares = middlewares + route.middlewares
|
36
|
+
end
|
37
|
+
end
|
38
|
+
root_api.routes
|
39
|
+
end
|
40
|
+
|
41
|
+
# Process after receive data from client
|
42
|
+
# @param request [Midori::Request] Http Raw Request
|
43
|
+
# @param connection [Midori::Connection] A connection created by EventMachine
|
44
|
+
# @return [Midori::Response] Http Response
|
45
|
+
# @raise [Midori::Error::NotFound] If no route matched
|
46
|
+
def receive(request, connection = nil)
|
47
|
+
@routes[request.method].each do |route|
|
48
|
+
params = route.path.params(request.path)
|
49
|
+
next unless params # Skip if not matched
|
50
|
+
request.params = params
|
51
|
+
clean_room = Midori::CleanRoom.new(request)
|
52
|
+
if request.websocket?
|
53
|
+
# Send 101 Switching Protocol
|
54
|
+
connection.send_data Midori::Response.new(
|
55
|
+
status: 101,
|
56
|
+
header: Midori::APIEngine.websocket_header(request.header['Sec-WebSocket-Key']),
|
57
|
+
body: '')
|
58
|
+
connection.websocket.request = request
|
59
|
+
Midori::Sandbox.run(clean_room, route.function, connection.websocket)
|
60
|
+
return Midori::Response.new
|
61
|
+
elsif request.eventsource?
|
62
|
+
connection.send_data Midori::Response.new(
|
63
|
+
status: 200,
|
64
|
+
header: Midori::Const::EVENTSOURCE_HEADER)
|
65
|
+
Midori::Sandbox.run(clean_room, route.function, connection.eventsource)
|
66
|
+
return Midori::Response.new
|
67
|
+
else
|
68
|
+
request = middleware_exec(route.middlewares, clean_room, request)
|
69
|
+
return request if request.is_a? Midori::Response # Early stop
|
70
|
+
result = Midori::Sandbox.run(clean_room, route.function)
|
71
|
+
clean_room.body = result
|
72
|
+
response = result.is_a?(Midori::Response) ? result : clean_room.raw_response
|
73
|
+
response = middleware_exec(route.middlewares, clean_room, request, response)
|
74
|
+
return response
|
75
|
+
end
|
76
|
+
end
|
77
|
+
raise Midori::Exception::NotFound
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return websocket header with given key
|
81
|
+
# @param [String] key 'Sec-WebSocket-Key' in request header
|
82
|
+
# @return [Hash] header
|
83
|
+
def self.websocket_header(key)
|
84
|
+
header = Midori::Const::WEBSOCKET_HEADER.clone
|
85
|
+
header['Sec-WebSocket-Accept'] = Digest::SHA1.base64digest(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
86
|
+
header
|
87
|
+
end
|
88
|
+
|
89
|
+
# Exec middlewares
|
90
|
+
private def middleware_exec(middlewares, clean_room, request, response=nil)
|
91
|
+
result = response.nil? ? request : response
|
92
|
+
middlewares.each do |middleware|
|
93
|
+
if response.nil?
|
94
|
+
result = Midori::Sandbox.run(
|
95
|
+
clean_room,
|
96
|
+
proc { |req| middleware.before(req) },
|
97
|
+
result)
|
98
|
+
else
|
99
|
+
result = Midori::Sandbox.run(
|
100
|
+
clean_room,
|
101
|
+
proc { |req, resp| middleware.after(req, resp) },
|
102
|
+
request,
|
103
|
+
result)
|
104
|
+
end
|
105
|
+
return result if response.nil? && result.is_a?(Midori::Response) # Early stop
|
106
|
+
end
|
107
|
+
result
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
##
|
2
|
+
# This class is used to be sandbox of requests processing.
|
3
|
+
# @attr [Integer] status HTTP response code
|
4
|
+
# @attr [Hash] header HTTP response header
|
5
|
+
# @attr [Object] body HTTP response body. String could is accepted by default, but could leave for further process with +Midori::Middleware+
|
6
|
+
# @attr [Midori::Request] request HTTP request
|
7
|
+
class Midori::CleanRoom
|
8
|
+
attr_accessor :status, :header, :body, :request
|
9
|
+
|
10
|
+
# Init a Cleanroom for running
|
11
|
+
# @param [Midori::Request] request HTTP request
|
12
|
+
def initialize(request)
|
13
|
+
@status = 200
|
14
|
+
@header = Midori::Const::DEFAULT_HEADER.clone
|
15
|
+
@body = ''
|
16
|
+
@request = request
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generate response from variables inside +Midori::CleanRoom+
|
20
|
+
# @return [Midori::Response] midori response
|
21
|
+
def raw_response
|
22
|
+
Midori::Response.new(status: @status, header: @header, body: @body)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
##
|
2
|
+
# Default configuration of Midori, extends +Configurable+
|
3
|
+
class Midori::Configure
|
4
|
+
extend Configurable
|
5
|
+
|
6
|
+
set :logger, ::Logger.new(STDOUT)
|
7
|
+
set :protocol, :http
|
8
|
+
set :bind, '127.0.0.1'
|
9
|
+
set :port, 8080
|
10
|
+
set :route_type, :sinatra
|
11
|
+
set :before, proc {}
|
12
|
+
end
|