nyara 0.0.1.pre.5 → 0.0.1.pre.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/example/factorial.rb +19 -0
- data/ext/accept.c +2 -2
- data/ext/event.c +48 -23
- data/ext/extconf.rb +2 -0
- data/ext/hashes.c +28 -3
- data/ext/http-parser/http_parser.h +1 -0
- data/ext/nyara.c +20 -3
- data/ext/nyara.h +13 -2
- data/ext/request.c +90 -13
- data/ext/request.h +8 -2
- data/ext/request_parse.c +135 -6
- data/ext/route.cc +7 -10
- data/ext/test_response.c +155 -0
- data/ext/url_encoded.c +0 -5
- data/lib/nyara/config.rb +5 -0
- data/lib/nyara/controller.rb +91 -28
- data/lib/nyara/cookie.rb +7 -0
- data/lib/nyara/flash.rb +23 -0
- data/lib/nyara/hashes/header_hash.rb +2 -0
- data/lib/nyara/nyara.rb +14 -2
- data/lib/nyara/part.rb +156 -0
- data/lib/nyara/patches/array.rb +5 -0
- data/lib/nyara/patches/blank.rb +128 -0
- data/lib/nyara/patches/json.rb +15 -0
- data/lib/nyara/patches/mini_support.rb +6 -0
- data/lib/nyara/patches/string.rb +21 -0
- data/lib/nyara/patches/to_query.rb +113 -0
- data/lib/nyara/request.rb +13 -15
- data/lib/nyara/route.rb +15 -80
- data/lib/nyara/route_entry.rb +69 -2
- data/lib/nyara/session.rb +66 -21
- data/lib/nyara/test.rb +170 -0
- data/lib/nyara/view.rb +5 -6
- data/lib/nyara.rb +7 -6
- data/nyara.gemspec +2 -2
- data/rakefile +34 -4
- data/readme.md +8 -1
- data/spec/config_spec.rb +28 -0
- data/spec/cpu_counter_spec.rb +9 -0
- data/spec/evented_io_spec.rb +1 -0
- data/spec/flash_spec.rb +29 -0
- data/spec/hashes_spec.rb +8 -0
- data/spec/mini_support_spec.rb +54 -0
- data/spec/part_spec.rb +52 -0
- data/spec/path_helper_spec.rb +22 -14
- data/spec/request_delegate_spec.rb +19 -11
- data/spec/route_entry_spec.rb +55 -0
- data/spec/session_spec.rb +69 -7
- data/spec/spec_helper.rb +3 -0
- data/spec/test_spec.rb +58 -0
- data/tools/hello.rb +11 -3
- data/tools/memcheck.rb +33 -0
- data/tools/s.rb +11 -0
- metadata +23 -7
- data/example/design.rb +0 -62
- data/example/fib.rb +0 -15
- data/spec/route_spec.rb +0 -84
- /data/ext/inc/{status_codes.inc → status_codes.h} +0 -0
data/lib/nyara/route.rb
CHANGED
@@ -12,52 +12,46 @@ module Nyara
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def compile
|
15
|
-
global_path_templates = {} # "name#id" => path
|
16
|
-
|
15
|
+
@global_path_templates = {} # "name#id" => path
|
16
|
+
mapped_controllers = {}
|
17
17
|
|
18
|
-
|
18
|
+
route_entries = @controllers.flat_map do |scope, c|
|
19
19
|
if c.is_a?(String)
|
20
20
|
c = name2const c
|
21
21
|
end
|
22
22
|
name = c.controller_name || const2name(c)
|
23
23
|
raise "#{c.inspect} is not a Nyara::Controller" unless Controller > c
|
24
24
|
|
25
|
-
if
|
25
|
+
if mapped_controllers[c]
|
26
26
|
raise "controller #{c.inspect} was already mapped"
|
27
27
|
end
|
28
|
+
mapped_controllers[c] = true
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
route_entries.each do |e|
|
32
|
-
id = e.id.to_s
|
33
|
-
path = File.join scope, e.path
|
34
|
-
global_path_templates[name + id] = path
|
35
|
-
@path_templates[c][id] = path
|
30
|
+
c.compile_route_entries(scope).each do |e|
|
31
|
+
@global_path_templates[name + e.id] = e.path_template
|
36
32
|
end
|
37
|
-
|
38
|
-
[scope, c, route_entries]
|
39
33
|
end
|
34
|
+
route_entries.sort_by! &:prefix
|
35
|
+
route_entries.reverse!
|
40
36
|
|
41
|
-
|
42
|
-
|
37
|
+
mapped_controllers.each do |c, _|
|
38
|
+
c.path_templates = @global_path_templates.merge c.path_templates
|
43
39
|
end
|
44
40
|
|
45
41
|
Ext.clear_route
|
46
|
-
|
47
|
-
|
48
|
-
Ext.register_route entry
|
42
|
+
route_entries.each do |e|
|
43
|
+
Ext.register_route e
|
49
44
|
end
|
50
45
|
end
|
51
46
|
|
52
|
-
def
|
53
|
-
@
|
47
|
+
def global_path_template id
|
48
|
+
@global_path_templates[id]
|
54
49
|
end
|
55
50
|
|
56
51
|
def clear
|
57
52
|
# gc mark fail if wrong order?
|
58
53
|
Ext.clear_route
|
59
54
|
@controllers = {}
|
60
|
-
@path_templates = {}
|
61
55
|
end
|
62
56
|
|
63
57
|
# private
|
@@ -75,64 +69,5 @@ module Nyara
|
|
75
69
|
name << 'Controller'
|
76
70
|
Module.const_get name
|
77
71
|
end
|
78
|
-
|
79
|
-
def process preprocessed
|
80
|
-
entries = []
|
81
|
-
preprocessed.each do |(scope, controller, route_entries)|
|
82
|
-
route_entries.each do |e|
|
83
|
-
e = e.dup # in case there is controller used in more than 1 maps
|
84
|
-
path = scope.sub /\/?$/, e.path
|
85
|
-
if path.empty?
|
86
|
-
path = '/'
|
87
|
-
end
|
88
|
-
e.prefix, suffix = analyse_path path
|
89
|
-
e.suffix, e.conv = compile_re suffix
|
90
|
-
e.scope = scope
|
91
|
-
e.controller = controller
|
92
|
-
entries << e
|
93
|
-
end
|
94
|
-
end
|
95
|
-
entries.sort_by! &:prefix
|
96
|
-
entries.reverse!
|
97
|
-
entries
|
98
|
-
end
|
99
|
-
|
100
|
-
# returns [str_re, conv]
|
101
|
-
def compile_re suffix
|
102
|
-
return ['', []] unless suffix
|
103
|
-
conv = []
|
104
|
-
re_segs = suffix.split(/(?<=%[dfsux])|(?=%[dfsux])/).map do |s|
|
105
|
-
case s
|
106
|
-
when '%d'
|
107
|
-
conv << :to_i
|
108
|
-
'(-?[0-9]+)'
|
109
|
-
when '%f'
|
110
|
-
conv << :to_f
|
111
|
-
# just copied from scanf
|
112
|
-
'([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))'
|
113
|
-
when '%u'
|
114
|
-
conv << :to_i
|
115
|
-
'([0-9]+)'
|
116
|
-
when '%x'
|
117
|
-
conv << :hex
|
118
|
-
'(\h+)'
|
119
|
-
when '%s'
|
120
|
-
conv << :to_s
|
121
|
-
'([^/]+)'
|
122
|
-
else
|
123
|
-
Regexp.quote s
|
124
|
-
end
|
125
|
-
end
|
126
|
-
["^#{re_segs.join}$", conv]
|
127
|
-
end
|
128
|
-
|
129
|
-
# split the path into parts
|
130
|
-
def analyse_path path
|
131
|
-
raise 'path must contain no new line' if path.index "\n"
|
132
|
-
raise 'path must start with /' unless path.start_with? '/'
|
133
|
-
path = path.sub(/\/$/, '') if path != '/'
|
134
|
-
|
135
|
-
path.split(/(?=%[dfsux])/, 2)
|
136
|
-
end
|
137
72
|
end
|
138
73
|
end
|
data/lib/nyara/route_entry.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
module Nyara
|
2
2
|
class RouteEntry
|
3
3
|
REQUIRED_ATTRS = [:http_method, :scope, :prefix, :suffix, :controller, :id, :conv]
|
4
|
-
|
4
|
+
attr_reader *REQUIRED_ATTRS
|
5
|
+
attr_writer :http_method, :id
|
6
|
+
# stores symbol for C conenience, and returns string for Ruby side goodness
|
7
|
+
def id
|
8
|
+
@id.to_s
|
9
|
+
end
|
5
10
|
|
6
11
|
# optional
|
7
12
|
attr_accessor :accept_exts, :accept_mimes
|
@@ -13,6 +18,25 @@ module Nyara
|
|
13
18
|
instance_eval &p if p
|
14
19
|
end
|
15
20
|
|
21
|
+
def path_template
|
22
|
+
File.join @scope, (@path.gsub '%z', '%s')
|
23
|
+
end
|
24
|
+
|
25
|
+
# compute prefix, suffix, conv
|
26
|
+
# NOTE route_entries may be inherited, so late-setting controller is necessary
|
27
|
+
def compile controller, scope
|
28
|
+
@controller = controller
|
29
|
+
@scope = scope
|
30
|
+
|
31
|
+
path = scope.sub /\/?$/, @path
|
32
|
+
if path.empty?
|
33
|
+
path = '/'
|
34
|
+
end
|
35
|
+
@prefix, suffix = analyse_path path
|
36
|
+
@suffix, @conv = compile_re suffix
|
37
|
+
end
|
38
|
+
|
39
|
+
# compute accept_exts, accept_mimes
|
16
40
|
def set_accept_exts a
|
17
41
|
@accept_exts = {}
|
18
42
|
@accept_mimes = []
|
@@ -37,7 +61,50 @@ module Nyara
|
|
37
61
|
raise ArgumentError, "missing #{attr}"
|
38
62
|
end
|
39
63
|
end
|
40
|
-
raise ArgumentError, "id must be symbol" unless id.is_a?(Symbol)
|
64
|
+
raise ArgumentError, "id must be symbol" unless @id.is_a?(Symbol)
|
65
|
+
end
|
66
|
+
|
67
|
+
# private
|
68
|
+
|
69
|
+
# returns [str_re, conv]
|
70
|
+
def compile_re suffix
|
71
|
+
return ['', []] unless suffix
|
72
|
+
conv = []
|
73
|
+
re_segs = suffix.split(/(?<=%[dfsux])|(?=%[dfsux])/).map do |s|
|
74
|
+
case s
|
75
|
+
when '%d'
|
76
|
+
conv << :to_i
|
77
|
+
'(-?[0-9]+)'
|
78
|
+
when '%f'
|
79
|
+
conv << :to_f
|
80
|
+
# just copied from scanf
|
81
|
+
'([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))'
|
82
|
+
when '%u'
|
83
|
+
conv << :to_i
|
84
|
+
'([0-9]+)'
|
85
|
+
when '%x'
|
86
|
+
conv << :hex
|
87
|
+
'(\h+)'
|
88
|
+
when '%s'
|
89
|
+
conv << :to_s
|
90
|
+
'([^/]+)'
|
91
|
+
when '%z'
|
92
|
+
conv << :to_s
|
93
|
+
'(.+)'
|
94
|
+
else
|
95
|
+
Regexp.quote s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
["^#{re_segs.join}$", conv]
|
99
|
+
end
|
100
|
+
|
101
|
+
# split the path into parts
|
102
|
+
def analyse_path path
|
103
|
+
raise 'path must contain no new line' if path.index "\n"
|
104
|
+
raise 'path must start with /' unless path.start_with? '/'
|
105
|
+
path = path.sub(/\/$/, '') if path != '/'
|
106
|
+
|
107
|
+
path.split(/(?=%[dfsux])/, 2)
|
41
108
|
end
|
42
109
|
end
|
43
110
|
end
|
data/lib/nyara/session.rb
CHANGED
@@ -1,42 +1,74 @@
|
|
1
1
|
module Nyara
|
2
|
-
# cookie based
|
3
|
-
# usually it's no need to call cache or database data a "session"
|
2
|
+
# helper module for session management, cookie based<br>
|
3
|
+
# (usually it's no need to call cache or database data a "session")<br><br>
|
4
|
+
# session is by default DSA + SHA2/SHA1 signed, sub config options are:
|
5
|
+
#
|
6
|
+
# [name] session entry name in cookie, default is +'spare_me_plz'+
|
7
|
+
# [expire] expire session after seconds. default is +nil+, which means session expires when browser is closed<br>
|
8
|
+
# [expires] same as +expire+
|
9
|
+
# [secure] - +nil+(default): if request is https, add +Secure+ option to it
|
10
|
+
# - +true+: always add +Secure+
|
11
|
+
# - +false+: always no +Secure+
|
12
|
+
# [key] DSA private key string, in der or pem format, use random if not given
|
13
|
+
# [cipher_key] if exist, use aes-256-cbc to cipher the "sig/json"<br>
|
14
|
+
# NOTE: it's no need to set +cipher_key+ if using https
|
15
|
+
#
|
16
|
+
# = example
|
17
|
+
#
|
18
|
+
# configure do
|
19
|
+
# set 'session', 'key', File.read(project_path 'config/session.key')
|
20
|
+
# set 'session', 'expire', 30 * 60
|
21
|
+
# end
|
22
|
+
#
|
4
23
|
module Session
|
5
24
|
extend self
|
6
25
|
|
7
26
|
CIPHER_BLOCK_SIZE = 256/8
|
8
27
|
|
9
|
-
# session is by default DSA + SHA2/SHA1 signed, sub config options are:
|
10
|
-
#
|
11
|
-
# - name (session entry name in cookie, default is 'spare_me_plz')
|
12
|
-
# - key (DSA private key string, in der or pem format, use random if not given)
|
13
|
-
# - cipher_key (if exist, use aes-256-cbc to cipher the "sig&json", the first 256bit is sliced for iv)
|
14
|
-
# (it's no need to set cipher_key if using https)
|
15
|
-
|
16
28
|
# init from config
|
17
29
|
def init
|
18
|
-
c = Config['session']
|
19
|
-
@name = (c
|
30
|
+
c = Config['session'] ? Config['session'].dup : {}
|
31
|
+
@name = (c.delete('name') || 'spare_me_plz').to_s
|
20
32
|
|
21
33
|
if c['key']
|
22
|
-
@dsa = OpenSSL::PKey::DSA.new c
|
34
|
+
@dsa = OpenSSL::PKey::DSA.new c.delete 'key'
|
23
35
|
else
|
24
|
-
@dsa =
|
36
|
+
@dsa = generate_key
|
25
37
|
end
|
26
38
|
|
27
39
|
# DSA can sign on any digest since 1.0.0
|
28
40
|
@dss = OpenSSL::VERSION >= '1.0.0' ? OpenSSL::Digest::SHA256 : OpenSSL::Digest::DSS1
|
29
41
|
|
30
|
-
@cipher_key = pad_256_bit c
|
42
|
+
@cipher_key = pad_256_bit c.delete 'cipher_key'
|
43
|
+
|
44
|
+
@expire = c.delete('expire') || c.delete('expires')
|
45
|
+
@secure = c.delete('secure')
|
46
|
+
|
47
|
+
unless c.empty?
|
48
|
+
raise "unknown options in Nyara::Config[:session]: #{c.inspect}"
|
49
|
+
end
|
31
50
|
end
|
32
51
|
|
33
52
|
attr_reader :name
|
34
53
|
|
35
|
-
|
54
|
+
# encode into a cookie hash, for test environment
|
55
|
+
def encode_to_cookie h, cookie
|
56
|
+
cookie[@name] = encode h
|
57
|
+
end
|
58
|
+
|
59
|
+
# encode to value
|
60
|
+
def encode h
|
36
61
|
str = h.to_json
|
37
62
|
sig = @dsa.syssign @dss.digest str
|
38
|
-
str = "#{
|
39
|
-
|
63
|
+
str = "#{encode64 sig}/#{encode64 str}"
|
64
|
+
@cipher_key ? cipher(str) : str
|
65
|
+
end
|
66
|
+
|
67
|
+
# encode as header line
|
68
|
+
def encode_set_cookie h, secure
|
69
|
+
secure = @secure unless @secure.nil?
|
70
|
+
expire = (Time.now + @expire).gmtime.rfc2822 if @expire
|
71
|
+
"Set-Cookie: #{@name}=#{encode h}; HttpOnly#{'; Secure' if secure}#{"; Expires=#{expire}" if expire}\r\n"
|
40
72
|
end
|
41
73
|
|
42
74
|
def decode cookie
|
@@ -44,11 +76,12 @@ module Nyara
|
|
44
76
|
return empty_hash if str.empty?
|
45
77
|
|
46
78
|
str = decipher(str) if @cipher_key
|
47
|
-
sig, str = str.split '
|
79
|
+
sig, str = str.split '/', 2
|
48
80
|
return empty_hash unless str
|
49
81
|
|
50
82
|
begin
|
51
|
-
sig =
|
83
|
+
sig = decode64 sig
|
84
|
+
str = decode64 str
|
52
85
|
verified = @dsa.sysverify @dss.digest(str), sig
|
53
86
|
if verified
|
54
87
|
h = JSON.parse str, create_additions: false, object_class: ParamHash
|
@@ -64,16 +97,28 @@ module Nyara
|
|
64
97
|
end
|
65
98
|
end
|
66
99
|
|
100
|
+
def generate_key
|
101
|
+
OpenSSL::PKey::DSA.generate 256
|
102
|
+
end
|
103
|
+
|
67
104
|
# private
|
68
105
|
|
106
|
+
def encode64 s
|
107
|
+
[s].pack('m0').tr("+/", "-_")
|
108
|
+
end
|
109
|
+
|
110
|
+
def decode64 s
|
111
|
+
s.tr("-_", "+/").unpack('m0').first
|
112
|
+
end
|
113
|
+
|
69
114
|
def cipher str
|
70
115
|
iv = rand(36**CIPHER_BLOCK_SIZE).to_s(36).ljust CIPHER_BLOCK_SIZE
|
71
116
|
c = new_cipher true, iv
|
72
|
-
|
117
|
+
encode64(iv.dup << c.update(str) << c.final)
|
73
118
|
end
|
74
119
|
|
75
120
|
def decipher str
|
76
|
-
str =
|
121
|
+
str = decode64 str
|
77
122
|
iv = str.byteslice 0...CIPHER_BLOCK_SIZE
|
78
123
|
str = str.byteslice CIPHER_BLOCK_SIZE..-1
|
79
124
|
return '' if !str or str.empty?
|
data/lib/nyara/test.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
module Nyara
|
2
|
+
# test helper
|
3
|
+
module Test
|
4
|
+
class Response
|
5
|
+
# whether request is success
|
6
|
+
def success?
|
7
|
+
status < 400
|
8
|
+
end
|
9
|
+
|
10
|
+
def redirect_location
|
11
|
+
if HTTP_REDIRECT_STATUS.include?(status)
|
12
|
+
header['Location']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# C-ext methods: header, body, status, initialize(data), set_cookies
|
17
|
+
end
|
18
|
+
|
19
|
+
Env = Struct.new :cookie, :session, :request, :controller, :response, :response_size_limit
|
20
|
+
class Env
|
21
|
+
# :call-seq:
|
22
|
+
#
|
23
|
+
# # change size limit of response data to 100M:
|
24
|
+
# @_env = Env.new 10**8
|
25
|
+
#
|
26
|
+
def initialize response_size_limit=5_000_000
|
27
|
+
self.response_size_limit = response_size_limit
|
28
|
+
self.cookie = ParamHash.new
|
29
|
+
self.session = ParamHash.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_request_data data
|
33
|
+
client, server = Socket.pair :UNIX, :STREAM
|
34
|
+
self.request = Ext.request_new
|
35
|
+
Ext.request_set_fd request, server.fileno
|
36
|
+
|
37
|
+
client << data
|
38
|
+
self.controller = Ext.handle_request request
|
39
|
+
response_data = client.read_nonblock response_size_limit
|
40
|
+
self.response = Response.new response_data
|
41
|
+
|
42
|
+
# merge session
|
43
|
+
session.clear
|
44
|
+
session.merge! request.session
|
45
|
+
|
46
|
+
# merge Set-Cookie
|
47
|
+
response.set_cookies.each do |cookie_seg|
|
48
|
+
# todo distinguish delete, value and set
|
49
|
+
Ext.parse_url_encoded_seg cookie, cookie_seg, false
|
50
|
+
end
|
51
|
+
|
52
|
+
server.close
|
53
|
+
client.close
|
54
|
+
end
|
55
|
+
|
56
|
+
def http meth, path, headers={}, body_string_or_hash=''
|
57
|
+
headers = (headers || {}).dup
|
58
|
+
|
59
|
+
# serialize body
|
60
|
+
# todo build multipart for file
|
61
|
+
if body_string_or_hash.is_a?(Hash)
|
62
|
+
body = body_string_or_hash.to_param
|
63
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
64
|
+
else
|
65
|
+
body = body_string_or_hash.to_s
|
66
|
+
headers['Content-Type'] ||= 'text/plain'
|
67
|
+
end
|
68
|
+
if body.bytesize > 0
|
69
|
+
headers['Content-Length'] = body.bytesize
|
70
|
+
end
|
71
|
+
|
72
|
+
# serialize cookie / session
|
73
|
+
if headers['Cookie']
|
74
|
+
cookie.clear
|
75
|
+
cookie.merge! Cookie.decode headers
|
76
|
+
end
|
77
|
+
Session.encode_to_cookie session, cookie
|
78
|
+
headers['Cookie'] = Cookie.encode cookie
|
79
|
+
|
80
|
+
request_data = ["#{meth.upcase} #{path} HTTP/1.1\r\n"]
|
81
|
+
headers.each do |k, v|
|
82
|
+
request_data << "#{k}: #{v}\r\n"
|
83
|
+
end
|
84
|
+
request_data << "\r\n"
|
85
|
+
request_data << body
|
86
|
+
process_request_data request_data.join
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def env
|
91
|
+
@_env ||= Env.new
|
92
|
+
end
|
93
|
+
|
94
|
+
# :call-seq:
|
95
|
+
#
|
96
|
+
# get '/', headers
|
97
|
+
#
|
98
|
+
def get path, header={}, body_string_or_hash=""
|
99
|
+
env.http 'GET', path, header, body_string_or_hash
|
100
|
+
end
|
101
|
+
|
102
|
+
# :call-seq:
|
103
|
+
#
|
104
|
+
# post '/', {}, page: 3
|
105
|
+
# post '/', { 'content-type' => 'application/json' }, '{"page":3}'
|
106
|
+
#
|
107
|
+
def post path, header={}, body_string_or_hash=""
|
108
|
+
env.http 'POST', path, header, body_string_or_hash
|
109
|
+
end
|
110
|
+
|
111
|
+
def put path, header={}, body_string_or_hash=""
|
112
|
+
env.http 'PUT', path, header, body_string_or_hash
|
113
|
+
end
|
114
|
+
|
115
|
+
def delete path, header={}, body_string_or_hash=""
|
116
|
+
env.http 'DELETE', path, header, body_string_or_hash
|
117
|
+
end
|
118
|
+
|
119
|
+
def patch path, header={}, body_string_or_hash=""
|
120
|
+
env.http 'PATCH', path, header, body_string_or_hash
|
121
|
+
end
|
122
|
+
|
123
|
+
def options path, header={}, body_string_or_hash=""
|
124
|
+
env.http 'OPTIONS', path, header, body_string_or_hash
|
125
|
+
end
|
126
|
+
|
127
|
+
def path_to id, *args
|
128
|
+
# similar to Controller#path_to, but without local query
|
129
|
+
if args.last.is_a?(Hash)
|
130
|
+
opts = args.pop
|
131
|
+
end
|
132
|
+
|
133
|
+
r = Route.global_path_template(id) % args
|
134
|
+
|
135
|
+
if opts
|
136
|
+
format = opts.delete :format
|
137
|
+
r << ".#{format}" if format
|
138
|
+
r << '?' << opts.to_param unless query.empty?
|
139
|
+
end
|
140
|
+
r
|
141
|
+
end
|
142
|
+
|
143
|
+
def cookie
|
144
|
+
env.cookie
|
145
|
+
end
|
146
|
+
|
147
|
+
def session
|
148
|
+
env.session
|
149
|
+
end
|
150
|
+
|
151
|
+
def request
|
152
|
+
env.request
|
153
|
+
end
|
154
|
+
|
155
|
+
def response
|
156
|
+
env.response
|
157
|
+
end
|
158
|
+
|
159
|
+
def redirect_location
|
160
|
+
env.response.redirect_location
|
161
|
+
end
|
162
|
+
|
163
|
+
def follow_redirect
|
164
|
+
# todo validate scheme and host
|
165
|
+
u = URI.parse(redirect_location)
|
166
|
+
path = [u.path, u.query].compact.join '?'
|
167
|
+
get path
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/nyara/view.rb
CHANGED
@@ -42,7 +42,7 @@ module Nyara
|
|
42
42
|
end
|
43
43
|
attr_reader :root
|
44
44
|
|
45
|
-
# +path+ needs extension
|
45
|
+
# NOTE: +path+ needs extension
|
46
46
|
def on_delete path
|
47
47
|
meth = path2meth path
|
48
48
|
Renderable.class_eval do
|
@@ -64,7 +64,7 @@ module Nyara
|
|
64
64
|
@meth2ext.clear
|
65
65
|
end
|
66
66
|
|
67
|
-
# +path+ needs extension
|
67
|
+
# NOTE: +path+ needs extension<br>
|
68
68
|
# returns dot_ext for further use
|
69
69
|
def on_update path
|
70
70
|
meth = path2meth path
|
@@ -108,7 +108,7 @@ module Nyara
|
|
108
108
|
line = 1
|
109
109
|
|
110
110
|
if stream_friendly
|
111
|
-
Renderable.class_eval <<-RUBY, __FILE__, __LINE__
|
111
|
+
Renderable.class_eval <<-RUBY, __FILE__, __LINE__
|
112
112
|
def render locals={}
|
113
113
|
@_nyara_locals = locals
|
114
114
|
src = locals.map{|k, _| "\#{k} = @_nyara_locals[:\#{k}];" }.join
|
@@ -119,7 +119,7 @@ module Nyara
|
|
119
119
|
RUBY
|
120
120
|
ENGINE_STREAM_FRIENDLY[ext] = true
|
121
121
|
else
|
122
|
-
Renderable.class_eval <<-RUBY, __FILE__, __LINE__
|
122
|
+
Renderable.class_eval <<-RUBY, __FILE__, __LINE__
|
123
123
|
def render locals=nil
|
124
124
|
Tilt[#{ext.inspect}].new(#{file}, #{line}){ @_nyara_view.in }.render self, locals
|
125
125
|
end
|
@@ -278,8 +278,7 @@ RUBY
|
|
278
278
|
Fiber.yield :term_close
|
279
279
|
end
|
280
280
|
|
281
|
-
# :nodoc:
|
282
|
-
def _render
|
281
|
+
def _render # :nodoc:
|
283
282
|
t, _ = @rest_layouts.pop
|
284
283
|
if @rest_layouts.empty?
|
285
284
|
@instance.send t, @locals
|
data/lib/nyara.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
# auto run
|
2
2
|
require_relative "nyara/nyara"
|
3
3
|
|
4
|
-
|
5
|
-
Nyara
|
6
|
-
|
7
|
-
|
8
|
-
end
|
4
|
+
END {
|
5
|
+
if $!.nil? and !Nyara.config.test?
|
6
|
+
Nyara.setup
|
7
|
+
Nyara.start_server
|
8
|
+
end
|
9
|
+
}
|
9
10
|
|
10
11
|
module Nyara
|
11
12
|
class SimpleController < Controller
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
15
|
-
%w[on tag get post put delete patch options].each do |m|
|
16
|
+
%w[on tag get post put delete patch options meta].each do |m|
|
16
17
|
eval <<-RUBY
|
17
18
|
def #{m} *xs, &blk
|
18
19
|
Nyara::SimpleController.#{m} *xs, &blk
|
data/nyara.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "nyara"
|
3
|
-
s.version = "0.0.1.pre.
|
3
|
+
s.version = "0.0.1.pre.6"
|
4
4
|
s.author = "Zete Lui"
|
5
5
|
s.email = "nobody@example.com"
|
6
6
|
s.homepage = "https://github.com/luikore/nyara"
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.summary = "Fast, slim and fuzzy ruby web framework + server"
|
9
9
|
s.description = "Fast, slim and fuzzy ruby web framework + server, based on preforked event queue and Fiber. NO rack NOR eventmachine are used."
|
10
10
|
s.required_ruby_version = ">=2.0.0"
|
11
|
-
s.licenses = ['BSD']
|
11
|
+
s.licenses = ['BSD 3-Clause']
|
12
12
|
|
13
13
|
s.files = Dir.glob('{rakefile,nyara.gemspec,readme.md,**/*.{rb,h,c,cc,inc}}')
|
14
14
|
s.files += Dir.glob('ext/http-parser/{AUTHORS,CONTRIBUTIONS,LICENSE-MIT}')
|
data/rakefile
CHANGED
@@ -2,13 +2,13 @@ require "rake"
|
|
2
2
|
|
3
3
|
Dir.chdir __dir__
|
4
4
|
|
5
|
-
status_file = "ext/inc/status_codes.
|
5
|
+
status_file = "ext/inc/status_codes.h"
|
6
6
|
version_file = "ext/inc/version.inc"
|
7
7
|
makefile = "ext/Makefile"
|
8
8
|
extconf = "ext/extconf.rb"
|
9
9
|
|
10
10
|
desc "code generate"
|
11
|
-
task :gen => [
|
11
|
+
task :gen => [version_file]
|
12
12
|
|
13
13
|
desc "generate #{status_file}"
|
14
14
|
file status_file => __FILE__ do
|
@@ -47,6 +47,7 @@ end
|
|
47
47
|
desc "generate makefile"
|
48
48
|
file makefile => extconf do
|
49
49
|
Dir.chdir 'ext' do
|
50
|
+
sh 'make clean' if File.exist?('Makefile')
|
50
51
|
sh 'ruby extconf.rb'
|
51
52
|
end
|
52
53
|
end
|
@@ -112,14 +113,20 @@ end
|
|
112
113
|
desc "list Nyara::Ext methods"
|
113
114
|
task :list_ext do
|
114
115
|
require_relative "lib/nyara/nyara"
|
116
|
+
puts_methods = lambda{|methods|
|
117
|
+
methods.each do |m|
|
118
|
+
puts "#{m} /#{Nyara::Ext.method(m).arity}"
|
119
|
+
end
|
120
|
+
}
|
121
|
+
|
115
122
|
methods = (Nyara::Ext.methods - Module.methods).sort
|
116
123
|
[/queue/, /route/, /parse/, /request/].each do |r|
|
117
124
|
group = methods.grep r
|
118
|
-
|
125
|
+
puts_methods[group]
|
119
126
|
puts
|
120
127
|
methods -= group
|
121
128
|
end
|
122
|
-
|
129
|
+
puts_methods[methods]
|
123
130
|
end
|
124
131
|
|
125
132
|
def term_color n
|
@@ -158,3 +165,26 @@ task :audit_arity do
|
|
158
165
|
end
|
159
166
|
end
|
160
167
|
end
|
168
|
+
|
169
|
+
desc "audit http_parser / multipart_parser struct size, they should be dividable by 8 so that Request fields are aligned"
|
170
|
+
task :audit_sizeof_parsers do
|
171
|
+
require "mkmf"
|
172
|
+
$CFLAGS << " -Iext/http-parser -Iext/multipart-parser-c"
|
173
|
+
{
|
174
|
+
'http_parser' => 'http_parser.h',
|
175
|
+
'multipart_parser*' => 'multipart_parser.h' # multipart_parser is opaque -_-
|
176
|
+
}.each do |struct, header|
|
177
|
+
have_header header
|
178
|
+
res = check_sizeof(struct, header)
|
179
|
+
if res and res % 8 == 0
|
180
|
+
puts "OK"
|
181
|
+
else
|
182
|
+
term_color 9
|
183
|
+
print "Need padding"
|
184
|
+
reset_color
|
185
|
+
puts
|
186
|
+
end
|
187
|
+
puts
|
188
|
+
end
|
189
|
+
sh 'rm', 'mkmf.log'
|
190
|
+
end
|