nyara 0.0.1.pre.2 → 0.0.1.pre.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/accept.c +29 -33
- data/ext/event.c +218 -22
- data/ext/extconf.rb +3 -3
- data/ext/hashes.c +2 -1
- data/ext/http_parser.c +6 -0
- data/ext/inc/epoll.h +11 -9
- data/ext/inc/kqueue.h +14 -17
- data/ext/inc/str_intern.h +2 -2
- data/ext/mime.c +2 -0
- data/ext/multipart_parser.c +2 -0
- data/ext/nyara.c +2 -0
- data/ext/nyara.h +15 -9
- data/ext/request.c +88 -266
- data/ext/request.h +43 -0
- data/ext/request_parse.c +123 -0
- data/ext/route.cc +2 -0
- data/ext/url_encoded.c +66 -6
- data/hello.rb +8 -2
- data/lib/nyara/controller.rb +29 -10
- data/lib/nyara/cookie.rb +3 -2
- data/lib/nyara/nyara.rb +41 -27
- data/lib/nyara/patch_tcp_socket.rb +22 -0
- data/lib/nyara/request.rb +1 -3
- data/lib/nyara/view.rb +1 -0
- data/nyara.gemspec +1 -1
- data/rakefile +63 -42
- data/readme.md +34 -5
- data/spec/apps/connect.rb +14 -0
- data/spec/evented_io_spec.rb +23 -0
- data/spec/ext_parse_spec.rb +26 -7
- data/spec/performance/layout_render.rb +52 -0
- data/spec/performance/parse_accept_value.rb +13 -0
- data/spec/performance/parse_param.rb +18 -0
- data/spec/performance/performance_helper.rb +25 -0
- data/spec/performance_spec.rb +33 -0
- data/spec/request_delegate_spec.rb +41 -16
- data/spec/request_spec.rb +4 -7
- data/spec/spec_helper.rb +20 -10
- metadata +15 -6
- /data/lib/nyara/{config_hash.rb → hashes/config_hash.rb} +0 -0
- /data/lib/nyara/{header_hash.rb → hashes/header_hash.rb} +0 -0
- /data/lib/nyara/{param_hash.rb → hashes/param_hash.rb} +0 -0
data/ext/url_encoded.c
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
/* url-encoded parsing */
|
2
|
+
|
2
3
|
#include "nyara.h"
|
3
4
|
#include <ruby/encoding.h>
|
4
5
|
|
@@ -18,13 +19,13 @@ static char _half_octet(char c) {
|
|
18
19
|
}
|
19
20
|
}
|
20
21
|
|
21
|
-
static long _decode_url_seg(VALUE
|
22
|
+
static long _decode_url_seg(VALUE output, const char*s, long len, char stop_char) {
|
22
23
|
const char* last_s = s;
|
23
24
|
long last_len = 0;
|
24
25
|
|
25
26
|
# define FLUSH_UNESCAPED\
|
26
27
|
if (last_len) {\
|
27
|
-
rb_str_cat(
|
28
|
+
rb_str_cat(output, last_s, last_len);\
|
28
29
|
last_s += last_len;\
|
29
30
|
last_len = 0;\
|
30
31
|
}
|
@@ -50,7 +51,7 @@ static long _decode_url_seg(VALUE path, const char*s, long len, char stop_char)
|
|
50
51
|
unsigned char r = ((unsigned char)r1 << 4) | (unsigned char)r2;
|
51
52
|
FLUSH_UNESCAPED;
|
52
53
|
last_s += 3;
|
53
|
-
rb_str_cat(
|
54
|
+
rb_str_cat(output, (char*)&r, 1);
|
54
55
|
|
55
56
|
} else if (s[i] == stop_char) {
|
56
57
|
i++;
|
@@ -58,7 +59,7 @@ static long _decode_url_seg(VALUE path, const char*s, long len, char stop_char)
|
|
58
59
|
|
59
60
|
} else if (s[i] == '+') {
|
60
61
|
FLUSH_UNESCAPED;
|
61
|
-
rb_str_cat(
|
62
|
+
rb_str_cat(output, " ", 1);
|
62
63
|
|
63
64
|
} else {
|
64
65
|
last_len++;
|
@@ -70,9 +71,68 @@ static long _decode_url_seg(VALUE path, const char*s, long len, char stop_char)
|
|
70
71
|
return i;
|
71
72
|
}
|
72
73
|
|
74
|
+
// s should contain no space
|
73
75
|
// return parsed len, s + return == start of query
|
76
|
+
// NOTE it's similar to _decode_url_seg, but:
|
77
|
+
// - "+" is not escaped
|
78
|
+
// - matrix uri params (segments starting with ";") are ignored
|
74
79
|
long nyara_parse_path(VALUE output, const char* s, long len) {
|
75
|
-
|
80
|
+
const char* last_s = s;
|
81
|
+
long last_len = 0;
|
82
|
+
|
83
|
+
# define FLUSH_UNESCAPED\
|
84
|
+
if (last_len) {\
|
85
|
+
rb_str_cat(output, last_s, last_len);\
|
86
|
+
last_s += last_len;\
|
87
|
+
last_len = 0;\
|
88
|
+
}
|
89
|
+
|
90
|
+
long i;
|
91
|
+
for (i = 0; i < len; i++) {
|
92
|
+
if (s[i] == '%') {
|
93
|
+
if (i + 2 >= len) {
|
94
|
+
last_len++;
|
95
|
+
continue;
|
96
|
+
}
|
97
|
+
char r1 = _half_octet(s[i + 1]);
|
98
|
+
if (r1 < 0) {
|
99
|
+
last_len++;
|
100
|
+
continue;
|
101
|
+
}
|
102
|
+
char r2 = _half_octet(s[i + 2]);
|
103
|
+
if (r2 < 0) {
|
104
|
+
last_len++;
|
105
|
+
continue;
|
106
|
+
}
|
107
|
+
i += 2;
|
108
|
+
unsigned char r = ((unsigned char)r1 << 4) | (unsigned char)r2;
|
109
|
+
FLUSH_UNESCAPED;
|
110
|
+
last_s += 3;
|
111
|
+
rb_str_cat(output, (char*)&r, 1);
|
112
|
+
|
113
|
+
} else if (s[i] == ';') {
|
114
|
+
// skip matrix uri params
|
115
|
+
i++;
|
116
|
+
for (; i < len; i++) {
|
117
|
+
if (s[i] == '?') {
|
118
|
+
i++;
|
119
|
+
break;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
break;
|
123
|
+
|
124
|
+
} else if (s[i] == '?') {
|
125
|
+
i++;
|
126
|
+
break;
|
127
|
+
|
128
|
+
} else {
|
129
|
+
last_len++;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
FLUSH_UNESCAPED;
|
133
|
+
# undef FLUSH_UNESCAPED
|
134
|
+
|
135
|
+
return i;
|
76
136
|
}
|
77
137
|
|
78
138
|
static VALUE ext_parse_path(VALUE self, VALUE output, VALUE input) {
|
data/hello.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
-
|
1
|
+
require_relative "lib/nyara"
|
2
|
+
require "open-uri"
|
3
|
+
require "pry"
|
2
4
|
|
3
|
-
|
5
|
+
configure do
|
6
|
+
# set :env, 'production'
|
7
|
+
# set :workers, 2
|
8
|
+
end
|
4
9
|
|
5
10
|
get '/' do
|
11
|
+
open 'http://baidu.com', &:read
|
6
12
|
send_string 'hello world'
|
7
13
|
end
|
8
14
|
|
data/lib/nyara/controller.rb
CHANGED
@@ -183,7 +183,7 @@ module Nyara
|
|
183
183
|
def add_header_line h
|
184
184
|
raise 'can not modify sent header' if request.response_header.frozen?
|
185
185
|
h = h.sub /(?<![\r\n])\z/, "\r\n"
|
186
|
-
request.response_header_extra_lines <<
|
186
|
+
request.response_header_extra_lines << h
|
187
187
|
end
|
188
188
|
|
189
189
|
# todo args helper
|
@@ -198,15 +198,34 @@ module Nyara
|
|
198
198
|
end
|
199
199
|
alias cookies cookie
|
200
200
|
|
201
|
-
|
201
|
+
# Set cookie, if expires is +Time.now+, will remove the cookie entry
|
202
|
+
#
|
203
|
+
# :call-seq:
|
204
|
+
#
|
205
|
+
# set_cookie 'JSESSIONID', 'not-exist'
|
206
|
+
# set_cookie 'key-without-value'
|
207
|
+
#
|
208
|
+
# +opt: default_value+ are:
|
209
|
+
#
|
210
|
+
# expires: nil
|
211
|
+
# max_age: nil
|
212
|
+
# domain: nil
|
213
|
+
# path: nil
|
214
|
+
# secure: nil
|
215
|
+
# httponly: true
|
216
|
+
#
|
217
|
+
def set_cookie name, value=nil, opts={}
|
218
|
+
if value.is_a?(Hash)
|
219
|
+
raise ArgumentError, 'hash not allowed in cookie value, did you mean to use it as options?'
|
220
|
+
end
|
202
221
|
# todo default domain ?
|
203
222
|
opts = Hash[opts.map{|k,v| [k.to_sym,v]}]
|
204
|
-
Cookie.
|
223
|
+
Cookie.add_set_cookie request.response_header_extra_lines, name, value, opts
|
205
224
|
end
|
206
225
|
|
207
|
-
def delete_cookie
|
226
|
+
def delete_cookie name
|
208
227
|
# todo domain ? path ?
|
209
|
-
set_cookie
|
228
|
+
set_cookie name, nil, expires: Time.now, max_age: 0
|
210
229
|
end
|
211
230
|
|
212
231
|
def clear_cookie
|
@@ -238,7 +257,7 @@ module Nyara
|
|
238
257
|
r = request
|
239
258
|
header = r.response_header
|
240
259
|
|
241
|
-
Ext.
|
260
|
+
Ext.request_send_data r, HTTP_STATUS_FIRST_LINES[r.status]
|
242
261
|
|
243
262
|
header.aset_content_type \
|
244
263
|
r.response_content_type ||
|
@@ -254,7 +273,7 @@ module Nyara
|
|
254
273
|
end
|
255
274
|
data.concat r.response_header_extra_lines
|
256
275
|
data << "\r\n"
|
257
|
-
Ext.
|
276
|
+
Ext.request_send_data r, data.join
|
258
277
|
|
259
278
|
# forbid further modification
|
260
279
|
header.freeze
|
@@ -263,17 +282,17 @@ module Nyara
|
|
263
282
|
# Send raw data, that is, not wrapped in chunked encoding<br>
|
264
283
|
# NOTE: often you should call send_header before doing this.
|
265
284
|
def send_data data
|
266
|
-
Ext.
|
285
|
+
Ext.request_send_data request, data.to_s
|
267
286
|
end
|
268
287
|
|
269
288
|
# Send a data chunk, it can send_header first if header is not sent.
|
270
|
-
#
|
289
|
+
#
|
271
290
|
# :call-seq:
|
272
291
|
#
|
273
292
|
# send_chunk 'hello world!'
|
274
293
|
def send_chunk data
|
275
294
|
send_header unless request.response_header.frozen?
|
276
|
-
Ext.
|
295
|
+
Ext.request_send_chunk request, data.to_s
|
277
296
|
end
|
278
297
|
alias send_string send_chunk
|
279
298
|
|
data/lib/nyara/cookie.rb
CHANGED
@@ -11,8 +11,8 @@ module Nyara
|
|
11
11
|
res
|
12
12
|
end
|
13
13
|
|
14
|
-
def add_set_cookie
|
15
|
-
r
|
14
|
+
def add_set_cookie header_lines, k, v, expires: nil, max_age: nil, domain: nil, path: nil, secure: nil, httponly: true
|
15
|
+
r = "Set-Cookie: "
|
16
16
|
if v.nil? or v == true
|
17
17
|
r << "#{CGI.escape k.to_s}; "
|
18
18
|
else
|
@@ -26,6 +26,7 @@ module Nyara
|
|
26
26
|
r << "Secure; " if secure
|
27
27
|
r << "HttpOnly; " if httponly
|
28
28
|
r << "\r\n"
|
29
|
+
header_lines << r
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
data/lib/nyara/nyara.rb
CHANGED
@@ -8,9 +8,9 @@ require "socket"
|
|
8
8
|
require "tilt"
|
9
9
|
|
10
10
|
require_relative "../../ext/nyara"
|
11
|
-
require_relative "param_hash"
|
12
|
-
require_relative "header_hash"
|
13
|
-
require_relative "config_hash"
|
11
|
+
require_relative "hashes/param_hash"
|
12
|
+
require_relative "hashes/header_hash"
|
13
|
+
require_relative "hashes/config_hash"
|
14
14
|
require_relative "mime_types"
|
15
15
|
require_relative "controller"
|
16
16
|
require_relative "request"
|
@@ -43,40 +43,54 @@ module Nyara
|
|
43
43
|
|
44
44
|
def start_server
|
45
45
|
port = Config[:port] || 3000
|
46
|
-
workers = Config[:workers] || (CpuCounter.count - 1)
|
47
46
|
|
48
47
|
puts "starting #{Config[:env]} server at 0.0.0.0:#{port}"
|
49
48
|
case Config[:env].to_s
|
50
|
-
|
51
49
|
when 'production'
|
52
|
-
|
53
|
-
|
54
|
-
trap :INT do
|
55
|
-
@workers.each do |w|
|
56
|
-
Process.kill :KILL, w
|
57
|
-
end
|
58
|
-
end
|
59
|
-
GC.start
|
60
|
-
@workers = workers.times.map do
|
61
|
-
fork {
|
62
|
-
Ext.init_queue
|
63
|
-
Ext.run_queue server.fileno
|
64
|
-
}
|
65
|
-
end
|
66
|
-
Process.waitall
|
67
|
-
|
50
|
+
patch_tcp_socket
|
51
|
+
start_production_server port
|
68
52
|
when 'test'
|
69
53
|
# don't
|
70
|
-
|
71
54
|
else
|
72
|
-
|
73
|
-
|
74
|
-
|
55
|
+
patch_tcp_socket
|
56
|
+
start_development_server port
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def patch_tcp_socket
|
61
|
+
puts "patching TCPSocket"
|
62
|
+
require_relative "patch_tcp_socket"
|
63
|
+
end
|
64
|
+
|
65
|
+
def start_production_server port
|
66
|
+
workers = Config[:workers] || ((CpuCounter.count + 1)/ 2)
|
67
|
+
|
68
|
+
puts "workers: #{workers}"
|
69
|
+
server = TCPServer.new '0.0.0.0', port
|
70
|
+
server.listen 1000
|
71
|
+
trap :INT do
|
72
|
+
@workers.each do |w|
|
73
|
+
Process.kill :KILL, w
|
74
|
+
end
|
75
|
+
end
|
76
|
+
GC.start
|
77
|
+
@workers = workers.times.map do
|
78
|
+
fork {
|
75
79
|
Ext.init_queue
|
76
80
|
Ext.run_queue server.fileno
|
77
|
-
|
78
|
-
|
81
|
+
}
|
82
|
+
end
|
83
|
+
Process.waitall
|
84
|
+
end
|
85
|
+
|
86
|
+
def start_development_server port
|
87
|
+
t = Thread.new do
|
88
|
+
server = TCPServer.new '0.0.0.0', port
|
89
|
+
server.listen 1000
|
90
|
+
Ext.init_queue
|
91
|
+
Ext.run_queue server.fileno
|
79
92
|
end
|
93
|
+
t.join
|
80
94
|
end
|
81
95
|
end
|
82
96
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# patch TCPSocket to make operations synchrony
|
2
|
+
class TCPSocket
|
3
|
+
alias _orig_initialize initialize
|
4
|
+
|
5
|
+
def initialize *xs
|
6
|
+
_orig_initialize *xs
|
7
|
+
Nyara::Ext.set_nonblock fileno
|
8
|
+
Nyara::Ext.fd_watch fileno
|
9
|
+
end
|
10
|
+
|
11
|
+
def send data, flags, dest_addr=nil, &blk
|
12
|
+
if dest_addr
|
13
|
+
super
|
14
|
+
else
|
15
|
+
Nyara::Ext.fd_send fileno, data, flags, &blk
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def recv max_len, flags=0
|
20
|
+
Nyara::Ext.fd_recv fileno, max_len, flags
|
21
|
+
end
|
22
|
+
end
|
data/lib/nyara/request.rb
CHANGED
@@ -133,9 +133,7 @@ module Nyara
|
|
133
133
|
|
134
134
|
# todo rename and move it into Ext
|
135
135
|
def not_found
|
136
|
-
|
137
|
-
Ext.send_data self, "HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"
|
138
|
-
Ext.close self
|
136
|
+
Ext.request_send_data self, "HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"
|
139
137
|
end
|
140
138
|
end
|
141
139
|
end
|
data/lib/nyara/view.rb
CHANGED
@@ -128,6 +128,7 @@ module Nyara
|
|
128
128
|
ENGINE_DEFAULT_CONTENT_TYPES[ext] = default_content_type
|
129
129
|
end
|
130
130
|
|
131
|
+
# local keys are for first-time code generation, values not used
|
131
132
|
# returns +[meth, ext_without_dot]+
|
132
133
|
def template path, locals={}
|
133
134
|
if File.extname(path).empty?
|
data/nyara.gemspec
CHANGED
data/rakefile
CHANGED
@@ -58,43 +58,6 @@ task :build => makefile do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def term_color n
|
62
|
-
print "\e[38;5;#{n}m"
|
63
|
-
end
|
64
|
-
|
65
|
-
def reset_color
|
66
|
-
print "\e[00m"
|
67
|
-
end
|
68
|
-
|
69
|
-
desc "check arity of rb_define_method/rb_define_singleton_method"
|
70
|
-
task :check_arity do
|
71
|
-
Dir.glob 'ext/*.{c,cc}' do |f|
|
72
|
-
puts "validatign #{f}"
|
73
|
-
arities = {}
|
74
|
-
data = File.read f
|
75
|
-
data.scan /^(?:static )?VALUE (\w+)\((.+)\)/ do |func, params|
|
76
|
-
arities[func] = params.count(',')
|
77
|
-
puts " scan: #{func}/#{arities[func]}"
|
78
|
-
end
|
79
|
-
data.scan /rb_define(?:_singleton)?_method\(.*?(\w+)\s*\,\s*(\d+)\)/ do |func, arity|
|
80
|
-
print " check: #{func}/#{arity} "
|
81
|
-
if arities[func].nil?
|
82
|
-
term_color 5
|
83
|
-
print "UNSCANNED"
|
84
|
-
reset_color
|
85
|
-
puts
|
86
|
-
elsif arities[func] != arity.to_i
|
87
|
-
term_color 9
|
88
|
-
print "MISMATCH #{arities[func]}"
|
89
|
-
reset_color
|
90
|
-
puts
|
91
|
-
else
|
92
|
-
puts "OK"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
61
|
desc "test"
|
99
62
|
task :test => :build do
|
100
63
|
sh 'rspec', '-c'
|
@@ -118,6 +81,7 @@ task :clean do
|
|
118
81
|
sh 'rm', '-f', '*.gem'
|
119
82
|
Dir.chdir 'ext' do
|
120
83
|
sh 'make', 'clean'
|
84
|
+
sh 'rm', '-f', 'Makefile'
|
121
85
|
end
|
122
86
|
end
|
123
87
|
|
@@ -125,15 +89,72 @@ end
|
|
125
89
|
|
126
90
|
desc "collect line stat"
|
127
91
|
task :lines do
|
128
|
-
|
129
|
-
Dir.glob('
|
130
|
-
|
92
|
+
rb_c = 0
|
93
|
+
Dir.glob('**/*.rb') do |f|
|
94
|
+
rb_c += (File.read(f).count "\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
c_c = 0
|
98
|
+
Dir.glob('ext/*.{c,cc,h}') do |f|
|
99
|
+
c_c += (File.read(f).count "\n")
|
100
|
+
end
|
101
|
+
|
102
|
+
spec_c = 0
|
103
|
+
Dir.glob('spec/**/*.rb') do |f|
|
104
|
+
spec_c += (File.read(f).count "\n")
|
131
105
|
end
|
132
|
-
|
106
|
+
|
107
|
+
puts "c: #{c_c} lines"
|
108
|
+
puts "rb: #{rb_c - spec_c} lines"
|
109
|
+
puts "spec: #{spec_c} lines"
|
133
110
|
end
|
134
111
|
|
135
112
|
desc "list Nyara::Ext methods"
|
136
113
|
task :list_ext do
|
137
114
|
require_relative "lib/nyara/nyara"
|
138
|
-
|
115
|
+
methods = (Nyara::Ext.methods - Module.methods).sort
|
116
|
+
[/queue/, /route/, /parse/, /request/].each do |r|
|
117
|
+
group = methods.grep r
|
118
|
+
puts group
|
119
|
+
puts
|
120
|
+
methods -= group
|
121
|
+
end
|
122
|
+
puts methods
|
123
|
+
end
|
124
|
+
|
125
|
+
def term_color n
|
126
|
+
print "\e[38;5;#{n}m"
|
127
|
+
end
|
128
|
+
|
129
|
+
def reset_color
|
130
|
+
print "\e[00m"
|
131
|
+
end
|
132
|
+
|
133
|
+
desc "audit arity of rb_define_method/rb_define_singleton_method"
|
134
|
+
task :audit_arity do
|
135
|
+
Dir.glob 'ext/*.{c,cc}' do |f|
|
136
|
+
puts "validatign #{f}"
|
137
|
+
arities = {}
|
138
|
+
data = File.read f
|
139
|
+
data.scan /^(?:static )?VALUE (\w+)\((.+)\)/ do |func, params|
|
140
|
+
arities[func] = params.count(',')
|
141
|
+
puts " scan: #{func}/#{arities[func]}"
|
142
|
+
end
|
143
|
+
data.scan /rb_define(?:_singleton)?_method\(.*?(\w+)\s*\,\s*(\d+)\)/ do |func, arity|
|
144
|
+
print " check: #{func}/#{arity} "
|
145
|
+
if arities[func].nil?
|
146
|
+
term_color 5
|
147
|
+
print "UNSCANNED"
|
148
|
+
reset_color
|
149
|
+
puts
|
150
|
+
elsif arities[func] != arity.to_i
|
151
|
+
term_color 9
|
152
|
+
print "MISMATCH #{arities[func]}"
|
153
|
+
reset_color
|
154
|
+
puts
|
155
|
+
else
|
156
|
+
puts "OK"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
139
160
|
end
|
data/readme.md
CHANGED
@@ -8,7 +8,7 @@ Not Yet Another Ruby Async web framework and server. Not on rack nor rack-compat
|
|
8
8
|
|
9
9
|
# Getting started
|
10
10
|
|
11
|
-
Requires Ruby 2.0+, BSD/Linux/Mac OS X.
|
11
|
+
Requires Ruby 2.0+, BSD/Linux/Mac OS X, GCC4.7+ or Clang.
|
12
12
|
|
13
13
|
Install
|
14
14
|
|
@@ -42,6 +42,33 @@ rake gen
|
|
42
42
|
rake gem
|
43
43
|
```
|
44
44
|
|
45
|
+
If you have cloned the repo once, and want to update code
|
46
|
+
|
47
|
+
```bash
|
48
|
+
git pull --recurse-submodules
|
49
|
+
git submodule foreach git fetch
|
50
|
+
```
|
51
|
+
|
52
|
+
# Testing
|
53
|
+
|
54
|
+
Simply run the test
|
55
|
+
|
56
|
+
```bash
|
57
|
+
rspec -c
|
58
|
+
```
|
59
|
+
|
60
|
+
Test in GC.stress mode
|
61
|
+
|
62
|
+
```bash
|
63
|
+
rspec -c -f d
|
64
|
+
```
|
65
|
+
|
66
|
+
With coverage (generates *coverage/index.html*)
|
67
|
+
|
68
|
+
```bash
|
69
|
+
COVERAGE=1 rspec -c
|
70
|
+
```
|
71
|
+
|
45
72
|
# Why fast
|
46
73
|
|
47
74
|
### Solid http parsers written in C
|
@@ -87,10 +114,12 @@ In Nyara, nested templates of Slim, ERB or Haml share the same output buffer, so
|
|
87
114
|
|
88
115
|
# How fast
|
89
116
|
|
90
|
-
Performance is feature, there are specs on
|
117
|
+
Performance is feature, there are specs on:
|
91
118
|
|
92
|
-
- Accept-* parse vs
|
93
|
-
- MIME matching vs rack
|
119
|
+
- Accept-* parse vs sinatra
|
94
120
|
- param parse vs ruby
|
95
121
|
- layout rendering vs tilt
|
96
|
-
|
122
|
+
|
123
|
+
# License
|
124
|
+
|
125
|
+
BSD, see copying
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
module Nyara
|
4
|
+
describe 'evented IO' do
|
5
|
+
before :all do
|
6
|
+
pid = Process.pid
|
7
|
+
@server = fork do
|
8
|
+
exec 'ruby', __dir__ + '/apps/connect.rb'
|
9
|
+
end
|
10
|
+
sleep 1 # wait for server startup
|
11
|
+
end
|
12
|
+
|
13
|
+
after :all do
|
14
|
+
Process.kill :KILL, @server
|
15
|
+
end
|
16
|
+
|
17
|
+
it "works" do
|
18
|
+
result1 = open "http://localhost:3003", &:read
|
19
|
+
result2 = open "http://baidu.com", &:read
|
20
|
+
assert_equal result2, result1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/spec/ext_parse_spec.rb
CHANGED
@@ -63,47 +63,66 @@ module Nyara
|
|
63
63
|
@output = ''
|
64
64
|
end
|
65
65
|
|
66
|
-
it "
|
66
|
+
it "converts '%' bytes but not '+'" do
|
67
67
|
i = '/%23+%24'
|
68
68
|
assert_equal i.bytesize, parse(i)
|
69
|
-
assert_equal "/\x23
|
69
|
+
assert_equal "/\x23+\x24", @output
|
70
70
|
end
|
71
71
|
|
72
|
-
it "truncates ? after %" do
|
72
|
+
it "truncates '?' after %" do
|
73
73
|
i = '/hello%f3%?world'
|
74
74
|
len = parse i
|
75
75
|
assert_equal '/hello%f3%?'.bytesize, len
|
76
76
|
assert_equal "/hello\xf3%", @output
|
77
77
|
end
|
78
78
|
|
79
|
-
it "truncates ? after begin" do
|
79
|
+
it "truncates '?' after begin" do
|
80
80
|
i = '?a'
|
81
81
|
len = parse i
|
82
82
|
assert_equal 1, len
|
83
83
|
assert_equal '', @output
|
84
84
|
end
|
85
85
|
|
86
|
-
it "truncates ? before end" do
|
86
|
+
it "truncates '?' before end" do
|
87
87
|
i = 'a?'
|
88
88
|
len = parse i
|
89
89
|
assert_equal 2, len
|
90
90
|
assert_equal 'a', @output
|
91
91
|
end
|
92
92
|
|
93
|
-
it "truncates ? after unescaped char" do
|
93
|
+
it "truncates '?' after unescaped char" do
|
94
94
|
i = 'a?a'
|
95
95
|
len = parse i
|
96
96
|
assert_equal 2, len
|
97
97
|
assert_equal 'a', @output
|
98
98
|
end
|
99
99
|
|
100
|
-
it "truncates ? after escaped char" do
|
100
|
+
it "truncates '?' after escaped char" do
|
101
101
|
i = '%40?'
|
102
102
|
len = parse i
|
103
103
|
assert_equal 4, len
|
104
104
|
assert_equal "\x40", @output
|
105
105
|
end
|
106
106
|
|
107
|
+
it "removes matrix uri params" do
|
108
|
+
i = '/a;matrix;matrix=3'
|
109
|
+
len = parse i
|
110
|
+
assert_equal i.size, len
|
111
|
+
assert_equal "/a", @output
|
112
|
+
|
113
|
+
@output = ''
|
114
|
+
i += '?'
|
115
|
+
len = parse i
|
116
|
+
assert_equal i.size, len
|
117
|
+
assert_equal "/a", @output
|
118
|
+
|
119
|
+
@output = ''
|
120
|
+
i += 'query'
|
121
|
+
len = parse i
|
122
|
+
assert_equal i.size - 'query'.size, len
|
123
|
+
assert_equal '/a', @output
|
124
|
+
end
|
125
|
+
|
107
126
|
def parse input
|
108
127
|
Ext.parse_path @output, input
|
109
128
|
end
|