nyara 0.0.1.pre.6 → 0.0.1.pre.8
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/example/project.rb +11 -0
- data/example/stream.rb +6 -2
- data/ext/event.c +83 -32
- data/ext/hashes.c +6 -1
- data/ext/inc/epoll.h +1 -2
- data/ext/inc/kqueue.h +1 -2
- data/ext/nyara.h +2 -0
- data/ext/request.c +14 -9
- data/ext/test_response.c +2 -5
- data/ext/url_encoded.c +55 -1
- data/lib/nyara/config.rb +68 -17
- data/lib/nyara/controller.rb +76 -15
- data/lib/nyara/controllers/public_controller.rb +14 -0
- data/lib/nyara/cookie.rb +5 -4
- data/lib/nyara/flash.rb +2 -0
- data/lib/nyara/nyara.rb +153 -20
- data/lib/nyara/patches/to_query.rb +1 -2
- data/lib/nyara/request.rb +0 -5
- data/lib/nyara/route.rb +2 -2
- data/lib/nyara/route_entry.rb +5 -4
- data/lib/nyara/session.rb +47 -22
- data/lib/nyara/test.rb +13 -10
- data/lib/nyara/view.rb +27 -49
- data/lib/nyara/view_handlers/erb.rb +21 -0
- data/lib/nyara/view_handlers/erubis.rb +81 -0
- data/lib/nyara/view_handlers/haml.rb +17 -0
- data/lib/nyara/view_handlers/slim.rb +16 -0
- data/nyara.gemspec +3 -1
- data/readme.md +2 -2
- data/spec/apps/connect.rb +1 -1
- data/spec/config_spec.rb +76 -4
- data/spec/{test_spec.rb → integration_spec.rb} +47 -3
- data/spec/path_helper_spec.rb +1 -1
- data/spec/performance/escape.rb +10 -0
- data/spec/performance/layout.slim +14 -0
- data/spec/performance/page.slim +16 -0
- data/spec/performance_spec.rb +6 -1
- data/spec/public/empty file.html +0 -0
- data/spec/public/index.html +1 -0
- data/spec/request_delegate_spec.rb +1 -1
- data/spec/request_spec.rb +20 -0
- data/spec/route_entry_spec.rb +7 -0
- data/spec/session_spec.rb +8 -4
- data/spec/spec_helper.rb +1 -0
- data/spec/{ext_parse_spec.rb → url_encoded_spec.rb} +17 -5
- data/spec/views/edit.haml +2 -0
- data/spec/views/edit.slim +2 -0
- data/spec/views/index.liquid +0 -0
- data/spec/views/invalid_layout.liquid +0 -0
- data/spec/views/layout.erb +1 -0
- data/spec/views/show.slim +1 -0
- data/tools/foo.rb +9 -0
- data/tools/hello.rb +16 -11
- metadata +22 -4
@@ -0,0 +1,14 @@
|
|
1
|
+
module Nyara
|
2
|
+
# serve public dir
|
3
|
+
class PublicController < Controller
|
4
|
+
get '/%z' do |path|
|
5
|
+
path = Config.public_path path
|
6
|
+
if path and File.file?(path)
|
7
|
+
send_file path
|
8
|
+
else
|
9
|
+
status 404
|
10
|
+
Ext.request_send_data request, "HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/nyara/cookie.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
module Nyara
|
2
|
-
#
|
2
|
+
# rfc6265 (don't look at rfc2109)
|
3
3
|
module Cookie
|
4
4
|
extend self
|
5
5
|
|
6
6
|
# encode to string value
|
7
7
|
def encode h
|
8
8
|
h.map do |k, v|
|
9
|
-
"#{
|
9
|
+
"#{Ext.escape k.to_s, false}=#{Ext.escape v.to_s, false}"
|
10
10
|
end.join '; '
|
11
11
|
end
|
12
12
|
|
13
|
+
# for test
|
13
14
|
def decode header
|
14
15
|
res = ParamHash.new
|
15
16
|
if data = header['Cookie']
|
@@ -21,9 +22,9 @@ module Nyara
|
|
21
22
|
def add_set_cookie header_lines, k, v, expires: nil, max_age: nil, domain: nil, path: nil, secure: nil, httponly: true
|
22
23
|
r = "Set-Cookie: "
|
23
24
|
if v.nil? or v == true
|
24
|
-
r << "#{
|
25
|
+
r << "#{Ext.escape k.to_s, false}; "
|
25
26
|
else
|
26
|
-
r << "#{
|
27
|
+
r << "#{Ext.escape k.to_s, false}=#{Ext.escape v.to_s, false}; "
|
27
28
|
end
|
28
29
|
r << "Expires=#{expires.to_time.gmtime.rfc2822}; " if expires
|
29
30
|
r << "Max-Age=#{max_age.to_i}; " if max_age
|
data/lib/nyara/flash.rb
CHANGED
data/lib/nyara/nyara.rb
CHANGED
@@ -8,6 +8,7 @@ require "uri"
|
|
8
8
|
require "openssl"
|
9
9
|
require "socket"
|
10
10
|
require "tilt"
|
11
|
+
require "time"
|
11
12
|
|
12
13
|
require_relative "../../ext/nyara"
|
13
14
|
require_relative "hashes/param_hash"
|
@@ -26,6 +27,9 @@ require_relative "view"
|
|
26
27
|
require_relative "cpu_counter"
|
27
28
|
require_relative "part"
|
28
29
|
|
30
|
+
# default controllers
|
31
|
+
require_relative "controllers/public_controller"
|
32
|
+
|
29
33
|
module Nyara
|
30
34
|
HTTP_STATUS_FIRST_LINES = Hash[HTTP_STATUS_CODES.map{|k,v|[k, "HTTP/1.1 #{k} #{v}\r\n".freeze]}].freeze
|
31
35
|
|
@@ -41,6 +45,18 @@ module Nyara
|
|
41
45
|
OK_RESP_HEADER['X-Content-Type-Options'] = 'nosniff'
|
42
46
|
OK_RESP_HEADER['X-Frame-Options'] = 'SAMEORIGIN'
|
43
47
|
|
48
|
+
START_CTX = {
|
49
|
+
0 => $0.dup,
|
50
|
+
argv: ARGV.map(&:dup),
|
51
|
+
cwd: (begin
|
52
|
+
a = File.stat(pwd = ENV['PWD'])
|
53
|
+
b = File.stat(Dir.pwd)
|
54
|
+
a.ino == b.ino && a.dev == b.dev ? pwd : Dir.pwd
|
55
|
+
rescue
|
56
|
+
Dir.pwd
|
57
|
+
end)
|
58
|
+
}
|
59
|
+
|
44
60
|
class << self
|
45
61
|
def config
|
46
62
|
raise ArgumentError, 'block not accepted, did you mean Nyara::Config.config?' if block_given?
|
@@ -49,12 +65,14 @@ module Nyara
|
|
49
65
|
|
50
66
|
def setup
|
51
67
|
Session.init
|
68
|
+
Config.init
|
52
69
|
Route.compile
|
70
|
+
# todo lint if SomeController#request is re-defined
|
53
71
|
View.init
|
54
72
|
end
|
55
73
|
|
56
74
|
def start_server
|
57
|
-
port = Config[:port]
|
75
|
+
port = Config[:port]
|
58
76
|
|
59
77
|
puts "starting #{Config[:env]} server at 0.0.0.0:#{port}"
|
60
78
|
case Config[:env].to_s
|
@@ -74,28 +92,11 @@ module Nyara
|
|
74
92
|
require_relative "patches/tcp_socket"
|
75
93
|
end
|
76
94
|
|
77
|
-
def
|
78
|
-
workers = Config[:workers] || ((CpuCounter.count + 1)/ 2)
|
79
|
-
|
80
|
-
puts "workers: #{workers}"
|
81
|
-
server = TCPServer.new '0.0.0.0', port
|
82
|
-
server.listen 1000
|
95
|
+
def start_development_server port
|
83
96
|
trap :INT do
|
84
|
-
|
85
|
-
Process.kill :KILL, w
|
86
|
-
end
|
97
|
+
exit!
|
87
98
|
end
|
88
|
-
GC.start
|
89
|
-
@workers = workers.times.map do
|
90
|
-
fork {
|
91
|
-
Ext.init_queue
|
92
|
-
Ext.run_queue server.fileno
|
93
|
-
}
|
94
|
-
end
|
95
|
-
Process.waitall
|
96
|
-
end
|
97
99
|
|
98
|
-
def start_development_server port
|
99
100
|
t = Thread.new do
|
100
101
|
server = TCPServer.new '0.0.0.0', port
|
101
102
|
server.listen 1000
|
@@ -104,5 +105,137 @@ module Nyara
|
|
104
105
|
end
|
105
106
|
t.join
|
106
107
|
end
|
108
|
+
|
109
|
+
# Signals:
|
110
|
+
#
|
111
|
+
# [INT] kill -9 all workers, and exit
|
112
|
+
# [QUIT] graceful quit all workers, and exit if all children terminated
|
113
|
+
# [TERM] same as QUIT
|
114
|
+
# [USR1] restore worker number
|
115
|
+
# [USR2] graceful spawn a new master and workers, with all content respawned
|
116
|
+
# [TTIN] increase worker number
|
117
|
+
# [TTOUT] decrease worker number
|
118
|
+
#
|
119
|
+
# To make a graceful hot-restart:
|
120
|
+
#
|
121
|
+
# 1. USR2 -> old master
|
122
|
+
# 2. if good (workers are up, etc), QUIT -> old master, else QUIT -> new master and fail
|
123
|
+
# 3. if good (requests are working, etc), INT -> old master
|
124
|
+
# else QUIT -> new master and USR1 -> old master to restore workers
|
125
|
+
#
|
126
|
+
# NOTE in step 2/3 if an additional fork executed in new master and hangs,
|
127
|
+
# you may need send an additional INT to terminate it.<br>
|
128
|
+
# NOTE hot-restart reloads almost everything, including Gemfile changes and configures except port.
|
129
|
+
# but, if some critical environment variable or port configure needs change, you still need cold-restart.<br>
|
130
|
+
# TODO write to a file to show workers are good<br>
|
131
|
+
# TODO detect port config change
|
132
|
+
def start_production_server port
|
133
|
+
workers = Config[:workers]
|
134
|
+
|
135
|
+
puts "workers: #{workers}"
|
136
|
+
|
137
|
+
if (server_fd = ENV['NYARA_FD'].to_i) > 0
|
138
|
+
puts "inheriting server fd #{server_fd}"
|
139
|
+
@server = TCPServer.for_fd server_fd
|
140
|
+
end
|
141
|
+
unless @server
|
142
|
+
@server = TCPServer.new '0.0.0.0', port
|
143
|
+
@server.listen 1000
|
144
|
+
ENV['NYARA_FD'] = @server.fileno.to_s
|
145
|
+
end
|
146
|
+
|
147
|
+
GC.start
|
148
|
+
@workers = []
|
149
|
+
workers.times do
|
150
|
+
incr_workers nil
|
151
|
+
end
|
152
|
+
|
153
|
+
trap :INT, &method(:kill_all)
|
154
|
+
trap :QUIT, &method(:quit_all)
|
155
|
+
trap :TERM, &method(:quit_all)
|
156
|
+
trap :USR2, &method(:spawn_new_master)
|
157
|
+
trap :USR1, &method(:restore_workers)
|
158
|
+
trap :TTIN do
|
159
|
+
if Config[:workers] > 1
|
160
|
+
Config[:workers] -= 1
|
161
|
+
decr_workers nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
trap :TTOU do
|
165
|
+
Config[:workers] += 1
|
166
|
+
incr_workers nil
|
167
|
+
end
|
168
|
+
Process.waitall
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
# kill all workers and exit
|
174
|
+
def kill_all sig
|
175
|
+
@workers.each do |w|
|
176
|
+
Process.kill :KILL, w
|
177
|
+
end
|
178
|
+
exit!
|
179
|
+
end
|
180
|
+
|
181
|
+
# graceful quit all workers and exit
|
182
|
+
def quit_all sig
|
183
|
+
until @workers.empty?
|
184
|
+
decr_workers sig
|
185
|
+
end
|
186
|
+
# wait will finish the wait-and-quit job
|
187
|
+
end
|
188
|
+
|
189
|
+
# spawn a new master
|
190
|
+
def spawn_new_master sig
|
191
|
+
fork do
|
192
|
+
@server.close_on_exec = false
|
193
|
+
Dir.chdir START_CTX[:cwd]
|
194
|
+
if File.executable?(START_CTX[0])
|
195
|
+
exec START_CTX[0], *START_CTX[:argv], close_others: false
|
196
|
+
else
|
197
|
+
# gemset env should be correct because env is inherited
|
198
|
+
require "rbconfig"
|
199
|
+
ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
|
200
|
+
exec ruby, START_CTX[0], *START_CTX[:argv], close_others: false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# restore number of workers as Config
|
206
|
+
def restore_workers sig
|
207
|
+
(Config[:workers] - @workers.size).times do
|
208
|
+
incr_workers sig
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# graceful decrease worker number by 1
|
213
|
+
def decr_workers sig
|
214
|
+
w = @workers.shift
|
215
|
+
puts "killing worker #{w}"
|
216
|
+
Process.kill :QUIT, w
|
217
|
+
end
|
218
|
+
|
219
|
+
# increase worker number by 1
|
220
|
+
def incr_workers sig
|
221
|
+
pid = fork {
|
222
|
+
$0 = "(nyara:worker) ruby #{$0}"
|
223
|
+
|
224
|
+
trap :QUIT do
|
225
|
+
Ext.graceful_quit @server.fileno
|
226
|
+
end
|
227
|
+
|
228
|
+
trap :TERM do
|
229
|
+
Ext.graceful_quit @server.fileno
|
230
|
+
end
|
231
|
+
|
232
|
+
t = Thread.new do
|
233
|
+
Ext.init_queue
|
234
|
+
Ext.run_queue @server.fileno
|
235
|
+
end
|
236
|
+
t.join
|
237
|
+
}
|
238
|
+
@workers << pid
|
239
|
+
end
|
107
240
|
end
|
108
241
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# copied from activesupport
|
2
|
-
require "cgi"
|
3
2
|
|
4
3
|
=begin
|
5
4
|
Copyright (c) 2005-2013 David Heinemeier Hansson
|
@@ -93,7 +92,7 @@ class Object
|
|
93
92
|
#
|
94
93
|
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
|
95
94
|
def to_query(key)
|
96
|
-
"#{
|
95
|
+
"#{Nyara::Ext.escape key.to_param, false}=#{Nyara::Ext.escape to_param.to_s, false}"
|
97
96
|
end
|
98
97
|
end
|
99
98
|
|
data/lib/nyara/request.rb
CHANGED
data/lib/nyara/route.rb
CHANGED
@@ -8,7 +8,7 @@ module Nyara
|
|
8
8
|
raise ArgumentError, "route prefix should be a string"
|
9
9
|
end
|
10
10
|
scope = scope.dup.freeze
|
11
|
-
(@controllers ||=
|
11
|
+
(@controllers ||= []) << [scope, controller]
|
12
12
|
end
|
13
13
|
|
14
14
|
def compile
|
@@ -51,7 +51,7 @@ module Nyara
|
|
51
51
|
def clear
|
52
52
|
# gc mark fail if wrong order?
|
53
53
|
Ext.clear_route
|
54
|
-
@controllers =
|
54
|
+
@controllers = []
|
55
55
|
end
|
56
56
|
|
57
57
|
# private
|
data/lib/nyara/route_entry.rb
CHANGED
@@ -70,7 +70,7 @@ module Nyara
|
|
70
70
|
def compile_re suffix
|
71
71
|
return ['', []] unless suffix
|
72
72
|
conv = []
|
73
|
-
re_segs = suffix.split(/(?<=%[
|
73
|
+
re_segs = suffix.split(/(?<=%[dfsuxz])|(?=%[dfsuxz])/).map do |s|
|
74
74
|
case s
|
75
75
|
when '%d'
|
76
76
|
conv << :to_i
|
@@ -90,7 +90,7 @@ module Nyara
|
|
90
90
|
'([^/]+)'
|
91
91
|
when '%z'
|
92
92
|
conv << :to_s
|
93
|
-
'(
|
93
|
+
'(.*)'
|
94
94
|
else
|
95
95
|
Regexp.quote s
|
96
96
|
end
|
@@ -98,13 +98,14 @@ module Nyara
|
|
98
98
|
["^#{re_segs.join}$", conv]
|
99
99
|
end
|
100
100
|
|
101
|
-
# split the path into parts
|
101
|
+
# split the path into 2 parts: <br>
|
102
|
+
# fixed prefix and variable suffix
|
102
103
|
def analyse_path path
|
103
104
|
raise 'path must contain no new line' if path.index "\n"
|
104
105
|
raise 'path must start with /' unless path.start_with? '/'
|
105
106
|
path = path.sub(/\/$/, '') if path != '/'
|
106
107
|
|
107
|
-
path.split(/(?=%[
|
108
|
+
path.split(/(?=%[dfsuxz])/, 2)
|
108
109
|
end
|
109
110
|
end
|
110
111
|
end
|
data/lib/nyara/session.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Nyara
|
2
|
-
#
|
2
|
+
# cookie based session<br>
|
3
3
|
# (usually it's no need to call cache or database data a "session")<br><br>
|
4
4
|
# session is by default DSA + SHA2/SHA1 signed, sub config options are:
|
5
5
|
#
|
@@ -10,8 +10,11 @@ module Nyara
|
|
10
10
|
# - +true+: always add +Secure+
|
11
11
|
# - +false+: always no +Secure+
|
12
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
|
14
|
-
#
|
13
|
+
# [cipher_key] if exist, use aes-256-cbc to cipher the json instead of just base64 it<br>
|
14
|
+
# it's useful if you need to hide something but can't stop yourself from putting it into session,<br>
|
15
|
+
# and one of the following condition matches:
|
16
|
+
# - not using http, and need to hide the info from middlemen
|
17
|
+
# - you've put something in session current_user should not see
|
15
18
|
#
|
16
19
|
# = example
|
17
20
|
#
|
@@ -20,15 +23,27 @@ module Nyara
|
|
20
23
|
# set 'session', 'expire', 30 * 60
|
21
24
|
# end
|
22
25
|
#
|
23
|
-
|
24
|
-
|
26
|
+
# Please be careful with session key and cipher key, they should be separated from source code, and never shown to public.
|
27
|
+
class Session < ParamHash
|
28
|
+
attr_reader :init_digest, :init_data
|
29
|
+
|
30
|
+
# if the session is init with nothing, and flash is clear
|
31
|
+
def vanila?
|
32
|
+
if @init_digest.nil?
|
33
|
+
empty? or size == 1 && has_key?('flash.next') && self['flash.next'].empty?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
25
37
|
|
38
|
+
class << Session
|
26
39
|
CIPHER_BLOCK_SIZE = 256/8
|
40
|
+
CIPHER_RAND_MAX = 36**CIPHER_BLOCK_SIZE
|
41
|
+
JSON_DECODE_OPTS = {create_additions: false, object_class: Session}
|
27
42
|
|
28
43
|
# init from config
|
29
44
|
def init
|
30
45
|
c = Config['session'] ? Config['session'].dup : {}
|
31
|
-
@name = (c.delete('name') || 'spare_me_plz').to_s
|
46
|
+
@name = Ext.escape (c.delete('name') || 'spare_me_plz').to_s, false
|
32
47
|
|
33
48
|
if c['key']
|
34
49
|
@dsa = OpenSSL::PKey::DSA.new c.delete 'key'
|
@@ -56,41 +71,51 @@ module Nyara
|
|
56
71
|
cookie[@name] = encode h
|
57
72
|
end
|
58
73
|
|
59
|
-
# encode to value
|
74
|
+
# encode to value<br>
|
75
|
+
# return h.init_data if not changed
|
60
76
|
def encode h
|
77
|
+
return h.init_data if h.vanila?
|
61
78
|
str = h.to_json
|
62
|
-
|
63
|
-
|
64
|
-
|
79
|
+
str = @cipher_key ? cipher(str) : encode64(str)
|
80
|
+
digest = @dss.digest str
|
81
|
+
return h.init_data if digest == h.init_digest
|
82
|
+
|
83
|
+
sig = @dsa.syssign digest
|
84
|
+
"#{encode64 sig}/#{str}"
|
65
85
|
end
|
66
86
|
|
67
87
|
# encode as header line
|
68
88
|
def encode_set_cookie h, secure
|
69
89
|
secure = @secure unless @secure.nil?
|
70
90
|
expire = (Time.now + @expire).gmtime.rfc2822 if @expire
|
71
|
-
|
91
|
+
# NOTE +encode h+ may return empty value, but it's still fine
|
92
|
+
"Set-Cookie: #{@name}=#{encode h}; Path=/; HttpOnly#{'; Secure' if secure}#{"; Expires=#{expire}" if expire}\r\n"
|
72
93
|
end
|
73
94
|
|
95
|
+
# decode the session hash from cookie
|
74
96
|
def decode cookie
|
75
|
-
|
76
|
-
return empty_hash if
|
97
|
+
data = cookie[@name].to_s
|
98
|
+
return empty_hash if data.empty?
|
77
99
|
|
78
|
-
str =
|
79
|
-
sig, str = str.split '/', 2
|
100
|
+
sig, str = data.split '/', 2
|
80
101
|
return empty_hash unless str
|
81
102
|
|
103
|
+
h = nil
|
104
|
+
digest = nil
|
82
105
|
begin
|
83
106
|
sig = decode64 sig
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
h = JSON.parse str,
|
107
|
+
digest = @dss.digest str
|
108
|
+
if @dsa.sysverify(digest, sig)
|
109
|
+
str = @cipher_key ? decipher(str) : decode64(str)
|
110
|
+
h = JSON.parse str, JSON_DECODE_OPTS
|
88
111
|
end
|
89
112
|
ensure
|
90
113
|
return empty_hash unless h
|
91
114
|
end
|
92
115
|
|
93
|
-
if h.is_a?(
|
116
|
+
if h.is_a?(Session)
|
117
|
+
h.instance_variable_set :@init_digest, digest
|
118
|
+
h.instance_variable_set :@init_data, data
|
94
119
|
h
|
95
120
|
else
|
96
121
|
empty_hash
|
@@ -112,7 +137,7 @@ module Nyara
|
|
112
137
|
end
|
113
138
|
|
114
139
|
def cipher str
|
115
|
-
iv = rand(
|
140
|
+
iv = rand(CIPHER_RAND_MAX).to_s(36).ljust CIPHER_BLOCK_SIZE
|
116
141
|
c = new_cipher true, iv
|
117
142
|
encode64(iv.dup << c.update(str) << c.final)
|
118
143
|
end
|
@@ -135,7 +160,7 @@ module Nyara
|
|
135
160
|
|
136
161
|
def empty_hash
|
137
162
|
# todo invoke hook?
|
138
|
-
|
163
|
+
Session.new
|
139
164
|
end
|
140
165
|
|
141
166
|
def new_cipher encrypt, iv
|
data/lib/nyara/test.rb
CHANGED
@@ -26,7 +26,7 @@ module Nyara
|
|
26
26
|
def initialize response_size_limit=5_000_000
|
27
27
|
self.response_size_limit = response_size_limit
|
28
28
|
self.cookie = ParamHash.new
|
29
|
-
self.session =
|
29
|
+
self.session = Session.new
|
30
30
|
end
|
31
31
|
|
32
32
|
def process_request_data data
|
@@ -39,14 +39,17 @@ module Nyara
|
|
39
39
|
response_data = client.read_nonblock response_size_limit
|
40
40
|
self.response = Response.new response_data
|
41
41
|
|
42
|
-
#
|
43
|
-
session
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
#
|
49
|
-
|
42
|
+
# no env when route not found
|
43
|
+
if request.session
|
44
|
+
# merge session
|
45
|
+
session.clear
|
46
|
+
session.merge! request.session
|
47
|
+
|
48
|
+
# merge Set-Cookie
|
49
|
+
response.set_cookies.each do |cookie_seg|
|
50
|
+
# todo distinguish delete, value and set
|
51
|
+
Ext.parse_url_encoded_seg cookie, cookie_seg, false
|
52
|
+
end
|
50
53
|
end
|
51
54
|
|
52
55
|
server.close
|
@@ -77,7 +80,7 @@ module Nyara
|
|
77
80
|
Session.encode_to_cookie session, cookie
|
78
81
|
headers['Cookie'] = Cookie.encode cookie
|
79
82
|
|
80
|
-
request_data = ["#{meth.upcase} #{path} HTTP/1.1\r\n"]
|
83
|
+
request_data = ["#{meth.upcase} #{Ext.escape path, true} HTTP/1.1\r\n"]
|
81
84
|
headers.each do |k, v|
|
82
85
|
request_data << "#{k}: #{v}\r\n"
|
83
86
|
end
|
data/lib/nyara/view.rb
CHANGED
@@ -17,8 +17,6 @@ module Nyara
|
|
17
17
|
#
|
18
18
|
# Friend layout and friend page shares one buffer, but enemy layout just concats +buffer.join+ before we flush friend layout.
|
19
19
|
# So the simple solution is: templates other than stream-friendly ones are not allowed to be a layout.
|
20
|
-
#
|
21
|
-
# Note on Erubis: to support streaming, Erubis is disabled even loaded.
|
22
20
|
class View
|
23
21
|
# ext (without dot) => most preferrable content type (e.g. "text/html")
|
24
22
|
ENGINE_DEFAULT_CONTENT_TYPES = ParamHash.new
|
@@ -26,7 +24,18 @@ module Nyara
|
|
26
24
|
# ext (without dot) => stream friendly
|
27
25
|
ENGINE_STREAM_FRIENDLY = ParamHash.new
|
28
26
|
|
27
|
+
autoload :ERB, File.join(__dir__, "view_handlers/erb")
|
28
|
+
autoload :Erubis, File.join(__dir__, "view_handlers/erubis")
|
29
|
+
autoload :Haml, File.join(__dir__, "view_handlers/haml")
|
30
|
+
autoload :Slim, File.join(__dir__, "view_handlers/slim")
|
31
|
+
|
29
32
|
class Buffer < Array
|
33
|
+
alias safe_append= <<
|
34
|
+
|
35
|
+
def append= thingy
|
36
|
+
self << CGI.escape_html(thingy.to_s)
|
37
|
+
end
|
38
|
+
|
30
39
|
def join
|
31
40
|
r = super
|
32
41
|
clear
|
@@ -39,6 +48,11 @@ module Nyara
|
|
39
48
|
@root = Config['views']
|
40
49
|
@meth2ext = {} # meth => ext (without dot)
|
41
50
|
@meth2sig = {}
|
51
|
+
@ext_list = Tilt.mappings.keys.delete_if(&:empty?).join ','
|
52
|
+
if @ext_list !~ /\bslim\b/
|
53
|
+
@ext_list = "slim,#{@ext_list}"
|
54
|
+
end
|
55
|
+
@ext_list = "{#{@ext_list}}"
|
42
56
|
end
|
43
57
|
attr_reader :root
|
44
58
|
|
@@ -80,7 +94,7 @@ module Nyara
|
|
80
94
|
sig = @meth2sig[meth].map{|k| "#{k}: nil" }.join ','
|
81
95
|
sig = '_={}' if sig.empty?
|
82
96
|
sig = "(#{sig})" # 2.0.0-p0 requirement
|
83
|
-
Renderable.class_eval <<-RUBY, path,
|
97
|
+
Renderable.class_eval <<-RUBY, path, 0
|
84
98
|
def render#{sig}
|
85
99
|
#{src}
|
86
100
|
end
|
@@ -133,7 +147,6 @@ module Nyara
|
|
133
147
|
# returns +[meth, ext_without_dot]+
|
134
148
|
def template path, locals={}
|
135
149
|
if File.extname(path).empty?
|
136
|
-
@ext_list ||= Tilt.mappings.keys.delete_if(&:empty?).join ','
|
137
150
|
Dir.chdir @root do
|
138
151
|
paths = Dir.glob("#{path}.{#@ext_list}")
|
139
152
|
if paths.size > 1
|
@@ -157,53 +170,18 @@ module Nyara
|
|
157
170
|
|
158
171
|
# Block is lazy invoked when it's ok to read the template source.
|
159
172
|
def precompile ext
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
173
|
+
case ext
|
174
|
+
when 'slim'
|
175
|
+
Slim.src yield
|
176
|
+
when 'erb', 'rhtml'
|
177
|
+
if Config['prefer_erb']
|
178
|
+
ERB.src yield
|
179
|
+
else
|
180
|
+
Erubis.src yield
|
168
181
|
end
|
169
|
-
|
170
|
-
|
171
|
-
send src_method, yield
|
172
|
-
end
|
173
|
-
|
174
|
-
def erb_src template
|
175
|
-
@erb_compiler ||= begin
|
176
|
-
c = ERB::Compiler.new '<>' # trim mode
|
177
|
-
c.pre_cmd = ["_erbout = @_nyara_view.out"]
|
178
|
-
c.put_cmd = "_erbout.push" # after newline
|
179
|
-
c.insert_cmd = "_erbout.push" # before newline
|
180
|
-
c.post_cmd = ["_erbout.join"]
|
181
|
-
c
|
182
|
+
when 'haml'
|
183
|
+
Haml.src yield
|
182
184
|
end
|
183
|
-
src, enc = @erb_compiler.compile template
|
184
|
-
# todo do sth with enc?
|
185
|
-
src
|
186
|
-
end
|
187
|
-
|
188
|
-
def slim_src template
|
189
|
-
# todo pretty by env
|
190
|
-
t = Slim::Template.new(nil, nil, pretty: false){ template }
|
191
|
-
src = t.instance_variable_get :@src
|
192
|
-
if src.start_with?('_buf = []')
|
193
|
-
src.sub! '_buf = []', '_buf = @_nyara_view.out'
|
194
|
-
end
|
195
|
-
src
|
196
|
-
end
|
197
|
-
|
198
|
-
def haml_src template
|
199
|
-
e = Haml::Engine.new template
|
200
|
-
# todo trim mode
|
201
|
-
<<-RUBY
|
202
|
-
_hamlout = Haml::Buffer.new(nil, encoding: 'utf-8')
|
203
|
-
_hamlout.buffer = @_nyara_view.out
|
204
|
-
#{e.precompiled}
|
205
|
-
_hamlout.buffer.join
|
206
|
-
RUBY
|
207
185
|
end
|
208
186
|
|
209
187
|
def path2meth path
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "erb"
|
2
|
+
|
3
|
+
module Nyara
|
4
|
+
class View
|
5
|
+
module ERB
|
6
|
+
def self.src template
|
7
|
+
@erb_compiler ||= begin
|
8
|
+
c = ::ERB::Compiler.new '<>' # trim mode
|
9
|
+
c.pre_cmd = ["_erbout = @_nyara_view.out"]
|
10
|
+
c.put_cmd = "_erbout.push" # after newline
|
11
|
+
c.insert_cmd = "_erbout.push" # before newline
|
12
|
+
c.post_cmd = ["_erbout.join"]
|
13
|
+
c
|
14
|
+
end
|
15
|
+
src, enc = @erb_compiler.compile template
|
16
|
+
# todo do sth with enc?
|
17
|
+
src
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|