em-midori 0.0.4.2 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,5 +1,9 @@
1
+ require 'em-midori/version'
1
2
  require 'em-midori/define_class'
3
+ require 'em-midori/clean_room'
2
4
  require 'em-midori/em_midori'
5
+ require 'em-midori/request'
6
+ require 'em-midori/response'
3
7
  require 'em-midori/api'
4
8
  require 'em-midori/server'
5
9
 
@@ -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,12 @@
1
+ class CleanRoom
2
+ attr_accessor :code, :header, :body
3
+ def initialize
4
+ @status = 200
5
+ @header = {}
6
+ @body = ''
7
+ end
8
+
9
+ def response
10
+ Midori::Response.new(@status, @header, @body)
11
+ end
12
+ 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,3 @@
1
+ class Midori::Request
2
+
3
+ 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