em-midori 0.0.4.2 → 0.0.5
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/.codeclimate.yml +16 -0
- data/.gitignore +51 -0
- data/.resources/midori_tokiwa.gif +0 -0
- data/.resources/sapphire_kawashima.gif +0 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +40 -0
- data/LICENSE +21 -0
- data/lib/em-midori.rb +4 -0
- data/lib/em-midori/api.rb +246 -0
- data/lib/em-midori/clean_room.rb +12 -0
- data/lib/em-midori/define_class.rb +9 -0
- data/lib/em-midori/em_midori.rb +25 -0
- data/lib/em-midori/request.rb +3 -0
- data/lib/em-midori/response.rb +56 -0
- data/lib/em-midori/server.rb +15 -0
- data/lib/em-midori/version.rb +3 -0
- metadata +18 -3
data/.travis.yml
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
language: ruby
|
2
|
+
sudo: false
|
3
|
+
rvm:
|
4
|
+
- 2.2.5
|
5
|
+
- 2.3.1
|
6
|
+
- jruby-head
|
7
|
+
- jruby-9.0.4.0
|
8
|
+
- rbx-3.20
|
9
|
+
|
10
|
+
jdk:
|
11
|
+
- openjdk7
|
12
|
+
- oraclejdk7
|
13
|
+
|
14
|
+
matrix:
|
15
|
+
exclude:
|
16
|
+
- rvm: 2.2.5
|
17
|
+
jdk: oraclejdk7
|
18
|
+
- rvm: 2.3.1
|
19
|
+
jdk: oraclejdk7
|
20
|
+
- rvm: rbx-3.20
|
21
|
+
jdk: oraclejdk7
|
22
|
+
allow_failures:
|
23
|
+
- rvm: jruby-head
|
24
|
+
- rvm: rbx-3.20
|
25
|
+
|
26
|
+
os:
|
27
|
+
- linux
|
28
|
+
|
29
|
+
before_install:
|
30
|
+
- gem install bundler
|
31
|
+
|
32
|
+
script:
|
33
|
+
- gem list -l
|
34
|
+
- rake spec
|
35
|
+
- gem build ./em-midori.gemspec
|
36
|
+
|
37
|
+
bundler_args: --jobs 1 --retry 3
|
38
|
+
|
39
|
+
notifications:
|
40
|
+
email: false
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016
|
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.
|
data/lib/em-midori.rb
CHANGED
@@ -0,0 +1,246 @@
|
|
1
|
+
##
|
2
|
+
# This class provides methods to be inherited as route definition.
|
3
|
+
class Midori::API
|
4
|
+
class << self
|
5
|
+
# Add GET method as a DSL for route definition
|
6
|
+
# === Attributes
|
7
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
8
|
+
# === Returns
|
9
|
+
# nil
|
10
|
+
# === Examples
|
11
|
+
# String as router
|
12
|
+
# get '/' do
|
13
|
+
# puts 'Hello World'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Regex as router
|
17
|
+
# get /\/hello\/(.*?)/ do
|
18
|
+
# puts 'Hello World'
|
19
|
+
# end
|
20
|
+
def get(path, &block) end
|
21
|
+
|
22
|
+
# Add POST method as a DSL for route definition
|
23
|
+
# === Attributes
|
24
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
25
|
+
# === Returns
|
26
|
+
# nil
|
27
|
+
# === Examples
|
28
|
+
# String as router
|
29
|
+
# post '/' do
|
30
|
+
# puts 'Hello World'
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# Regex as router
|
34
|
+
# post /\/hello\/(.*?)/ do
|
35
|
+
# puts 'Hello World'
|
36
|
+
# end
|
37
|
+
def post(path, &block) end
|
38
|
+
|
39
|
+
# Add PUT method as a DSL for route definition
|
40
|
+
# === Attributes
|
41
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
42
|
+
# === Returns
|
43
|
+
# nil
|
44
|
+
# === Examples
|
45
|
+
# String as router
|
46
|
+
# put '/' do
|
47
|
+
# puts 'Hello World'
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Regex as router
|
51
|
+
# put /\/hello\/(.*?)/ do
|
52
|
+
# puts 'Hello World'
|
53
|
+
# end
|
54
|
+
def put(path, &block) end
|
55
|
+
|
56
|
+
# Add DELETE method as a DSL for route definition
|
57
|
+
# === Attributes
|
58
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
59
|
+
# === Returns
|
60
|
+
# nil
|
61
|
+
# === Examples
|
62
|
+
# String as router
|
63
|
+
# delete '/' do
|
64
|
+
# puts 'Hello World'
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Regex as router
|
68
|
+
# delete /\/hello\/(.*?)/ do
|
69
|
+
# puts 'Hello World'
|
70
|
+
# end
|
71
|
+
def delete(path, &block) end
|
72
|
+
|
73
|
+
# Add OPTIONS method as a DSL for route definition
|
74
|
+
# === Attributes
|
75
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
76
|
+
# === Returns
|
77
|
+
# nil
|
78
|
+
# === Examples
|
79
|
+
# String as router
|
80
|
+
# options '/' do
|
81
|
+
# puts 'Hello World'
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# Regex as router
|
85
|
+
# options /\/hello\/(.*?)/ do
|
86
|
+
# puts 'Hello World'
|
87
|
+
# end
|
88
|
+
def options(path, &block) end
|
89
|
+
|
90
|
+
# Add LINK method as a DSL for route definition
|
91
|
+
# === Attributes
|
92
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
93
|
+
# === Returns
|
94
|
+
# nil
|
95
|
+
# === Examples
|
96
|
+
# String as router
|
97
|
+
# link '/' do
|
98
|
+
# puts 'Hello World'
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# Regex as router
|
102
|
+
# link /\/hello\/(.*?)/ do
|
103
|
+
# puts 'Hello World'
|
104
|
+
# end
|
105
|
+
def link(path, &block) end
|
106
|
+
|
107
|
+
# Add UNLINK method as a DSL for route definition
|
108
|
+
# === Attributes
|
109
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
110
|
+
# === Returns
|
111
|
+
# nil
|
112
|
+
# === Examples
|
113
|
+
# String as router
|
114
|
+
# unlink '/' do
|
115
|
+
# puts 'Hello World'
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# Regex as router
|
119
|
+
# unlink /\/hello\/(.*?)/ do
|
120
|
+
# puts 'Hello World'
|
121
|
+
# end
|
122
|
+
def unlink(path, &block) end
|
123
|
+
|
124
|
+
# Add WEBSOCKET method as a DSL for route definition
|
125
|
+
# === Attributes
|
126
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
127
|
+
# === Returns
|
128
|
+
# nil
|
129
|
+
# === Examples
|
130
|
+
# String as router
|
131
|
+
# unlink '/' do
|
132
|
+
# puts 'Hello World'
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# Regex as router
|
136
|
+
# unlink /\/hello\/(.*?)/ do
|
137
|
+
# puts 'Hello World'
|
138
|
+
# end
|
139
|
+
def websocket(path, &block) end
|
140
|
+
|
141
|
+
# Add EVENTSOURCE method as a DSL for route definition
|
142
|
+
# === Attributes
|
143
|
+
# * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
|
144
|
+
# === Returns
|
145
|
+
# nil
|
146
|
+
# === Examples
|
147
|
+
# String as router
|
148
|
+
# unlink '/' do
|
149
|
+
# puts 'Hello World'
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# Regex as router
|
153
|
+
# unlink /\/hello\/(.*?)/ do
|
154
|
+
# puts 'Hello World'
|
155
|
+
# end
|
156
|
+
def eventsource(path, &block) end
|
157
|
+
|
158
|
+
def add_route(method, path, block)
|
159
|
+
@route = Array.new if @route.nil?
|
160
|
+
if path.class == String
|
161
|
+
# Convert String to Regexp to provide performance boost (Precompiled Regexp)
|
162
|
+
path = convert_route path
|
163
|
+
end
|
164
|
+
@route << Midori::Route.new(method, path, block)
|
165
|
+
nil
|
166
|
+
end
|
167
|
+
|
168
|
+
def receive(request)
|
169
|
+
@route.each do |route|
|
170
|
+
matched = match(route.method, route.path, request)
|
171
|
+
if matched
|
172
|
+
# puts "route matched: #{route.method} #{route.path}"
|
173
|
+
clean_room = CleanRoom.new
|
174
|
+
begin
|
175
|
+
result = lambda {clean_room.instance_exec(*matched, &route.function)}.call
|
176
|
+
clean_room.body = result if result.class == String
|
177
|
+
return clean_room.response
|
178
|
+
rescue => e
|
179
|
+
puts e
|
180
|
+
return Midori::Response.new(500, {}, 'Internal Server Error')
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
# 404
|
185
|
+
Midori::Response.new(404, {}, '404 Not Found')
|
186
|
+
end
|
187
|
+
|
188
|
+
# Match route with given definition
|
189
|
+
# === Attributes
|
190
|
+
# * +method+ [+String+] - Accepts an HTTP/1.1 method like GET POST PUT ...
|
191
|
+
# * +path+ [+Regexp+] - Precompiled route definition.
|
192
|
+
# * +request+ [+String+] - HTTP Request String
|
193
|
+
# === Returns
|
194
|
+
# if not matched returns false
|
195
|
+
#
|
196
|
+
# else returns an array of parameter string matched
|
197
|
+
# === Examples
|
198
|
+
# match('GET', /^\/user\/(.*?)\/order\/(.*?)$/, '/user/foo/order/bar') # => ['foo', 'bar']
|
199
|
+
def match(method, path, request)
|
200
|
+
request = request.lines.first.split
|
201
|
+
if request[0] == method
|
202
|
+
result = request[1].match(path)
|
203
|
+
return result.to_a[1..-1] if result
|
204
|
+
false
|
205
|
+
else
|
206
|
+
false
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Convert String path to its Regexp equivalent
|
211
|
+
# === Attributes
|
212
|
+
# * +path+ [+String+] - String route definition
|
213
|
+
# === Returns
|
214
|
+
# Regexp equivalent
|
215
|
+
# === Examples
|
216
|
+
# convert_route('/user/:id/order/:order_id') # => Regexp
|
217
|
+
def convert_route(path)
|
218
|
+
path = '^' + path
|
219
|
+
.gsub(/\/(:[_a-z][_a-z0-9]+?)\//, '/([^/]+?)/')
|
220
|
+
.gsub(/\/(:[_a-z][_a-z0-9]+?)$/, '/([^/]+?)$')
|
221
|
+
path += '$' if path[-1] != '$'
|
222
|
+
Regexp.new path
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
private_class_method :add_route
|
227
|
+
|
228
|
+
METHODS = %w'get post put delete options link unlink websocket eventsource' # :nodoc:
|
229
|
+
|
230
|
+
# Magics to fill DSL methods through dynamically class method definition
|
231
|
+
METHODS.each do |method|
|
232
|
+
define_singleton_method(method) do |*args, &block|
|
233
|
+
add_route(method.upcase, args[0], block) #args[0]: path
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
class Midori::Route
|
240
|
+
attr_accessor :method, :path, :function
|
241
|
+
def initialize(method, path, function)
|
242
|
+
@method = method
|
243
|
+
@path = path
|
244
|
+
@function = function
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Kernel #:nodoc:
|
2
|
+
# This method is implemented to dynamically generate class with given name and template.
|
3
|
+
# Referenced from {Ruby China}[https://ruby-china.org/topics/17382]
|
4
|
+
def define_class(name, ancestor = Object)
|
5
|
+
Object.const_set(name, Class.new(ancestor))
|
6
|
+
Object.const_get(name).class_eval(&Proc.new) if block_given?
|
7
|
+
Object.const_get(name)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module Midori
|
4
|
+
def self.run(api=(Midori::API), ip=nil, port=nil)
|
5
|
+
ip ||= '127.0.0.1'
|
6
|
+
port ||= 8081
|
7
|
+
EventMachine.run do
|
8
|
+
puts "Midori #{Midori::VERSION} is now running on #{ip}:#{port}"
|
9
|
+
Midori::Server.api = api
|
10
|
+
@midori_server = EventMachine.start_server ip, port, Midori::Server
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.stop
|
15
|
+
if @midori_server.nil?
|
16
|
+
puts 'Midori Server has NOT been started'
|
17
|
+
return false
|
18
|
+
else
|
19
|
+
EventMachine.stop_server(@midori_server)
|
20
|
+
@midori_server = nil
|
21
|
+
puts 'Goodbye Midori'
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class Midori::Response
|
2
|
+
STATUS_CODE = {
|
3
|
+
100 => '100 Continue',
|
4
|
+
101 => '101 Switching Protocols',
|
5
|
+
200 => '200 OK',
|
6
|
+
201 => '201 Created',
|
7
|
+
202 => '202 Accepted',
|
8
|
+
203 => '203 Non-Authoritative Information',
|
9
|
+
204 => '204 No Content',
|
10
|
+
205 => '205 Reset Content',
|
11
|
+
206 => '206 Partial Content',
|
12
|
+
300 => '300 Multiple Choices',
|
13
|
+
301 => '301 Moved Permanently',
|
14
|
+
304 => '304 Not Modified',
|
15
|
+
305 => '305 Use Proxy',
|
16
|
+
307 => '307 Temporary Redirect',
|
17
|
+
400 => '400 Bad Request',
|
18
|
+
401 => '401 Unauthorized',
|
19
|
+
402 => '402 Payment Required',
|
20
|
+
403 => '403 Forbidden',
|
21
|
+
404 => '404 Not Found',
|
22
|
+
405 => '405 Method Not Allowed',
|
23
|
+
406 => '406 Not Acceptable',
|
24
|
+
407 => '407 Proxy Authentication Required',
|
25
|
+
408 => '408 Request Time-out',
|
26
|
+
409 => '409 Conflict',
|
27
|
+
410 => '410 Gone',
|
28
|
+
411 => '411 Length Required',
|
29
|
+
412 => '412 Precondition Failed',
|
30
|
+
413 => '413 Request Entity Too Large',
|
31
|
+
414 => '414 Request-URI Too Large',
|
32
|
+
415 => '415 Unsupported Media Type',
|
33
|
+
416 => '416 Requested range not satisfiable',
|
34
|
+
417 => '417 Expectation Failed',
|
35
|
+
500 => '500 Internal Server Error',
|
36
|
+
501 => '501 Not Implemented',
|
37
|
+
502 => '502 Bad Gateway',
|
38
|
+
503 => '503 Service Unavailable',
|
39
|
+
504 => '504 Gateway Time-out',
|
40
|
+
505 => '505 HTTP Version not supported'
|
41
|
+
}
|
42
|
+
STATUS_CODE.default= '500 Internal Server Error'
|
43
|
+
STATUS_CODE.freeze
|
44
|
+
|
45
|
+
attr_accessor :status_code, :header, :body
|
46
|
+
|
47
|
+
def initialize(code=200, header={}, body='')
|
48
|
+
@status_code = STATUS_CODE[code]
|
49
|
+
@header = header
|
50
|
+
@body = body
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
"HTTP/1.1 #{@status_code}\r\nServer: Midori/#{Midori::VERSION}\r\n\r\n#{@body}"
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Midori::Server
|
2
|
+
class << self
|
3
|
+
attr_accessor :api
|
4
|
+
end
|
5
|
+
|
6
|
+
def receive_data(data)
|
7
|
+
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
8
|
+
response = Midori::Server.api.receive(data)
|
9
|
+
puts "from #{ip}:#{port} comes a message:"
|
10
|
+
puts data # Debug
|
11
|
+
puts response # Debug
|
12
|
+
send_data response
|
13
|
+
close_connection_after_writing
|
14
|
+
end
|
15
|
+
end
|