nyara 0.0.1.pre.6 → 0.0.1.pre.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|