nyara 0.0.1.pre.2 → 0.0.1.pre.3

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.
data/ext/url_encoded.c CHANGED
@@ -1,4 +1,5 @@
1
- // parse path / query / url-encoded body
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 path, const char*s, long len, char stop_char) {
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(path, last_s, last_len);\
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(path, (char*)&r, 1);
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(path, " ", 1);
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
- return _decode_url_seg(output, s, len, '?');
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
- # require_relative "lib/nyara"
1
+ require_relative "lib/nyara"
2
+ require "open-uri"
3
+ require "pry"
2
4
 
3
- require 'nyara'
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
 
@@ -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 << s
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
- def set_cookie k, v=nil, opts
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.output_set_cookie response.response_header_extra_lines, k, v, opts
223
+ Cookie.add_set_cookie request.response_header_extra_lines, name, value, opts
205
224
  end
206
225
 
207
- def delete_cookie k
226
+ def delete_cookie name
208
227
  # todo domain ? path ?
209
- set_cookie k, expires: Time.now, max_age: 0
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.send_data r, HTTP_STATUS_FIRST_LINES[r.status]
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.send_data r, data.join
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.send_data request, data.to_s
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.send_chunk request, data.to_s
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 r, k, v, expires: nil, max_age: nil, domain: nil, path: nil, secure: nil, httponly: true
15
- r << "Set-Cookie: "
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
- server = TCPServer.new '0.0.0.0', port
53
- server.listen 1000
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
- t = Thread.new do
73
- server = TCPServer.new '0.0.0.0', port
74
- server.listen 1000
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
- end
78
- t.join
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
- puts "not found"
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "nyara"
3
- s.version = "0.0.1.pre.2"
3
+ s.version = "0.0.1.pre.3"
4
4
  s.author = "Zete Lui"
5
5
  s.email = "nobody@example.com"
6
6
  s.homepage = "https://github.com/luikore/nyara"
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
- c = 0
129
- Dir.glob('{**/*.rb,ext/*.{c,cc,h}}') do |f|
130
- c += (File.read(f).count "\n")
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
- puts "#{c} lines"
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
- puts Nyara::Ext.methods - Module.methods
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 (TODO):
117
+ Performance is feature, there are specs on:
91
118
 
92
- - Accept-* parse vs rack
93
- - MIME matching vs rack
119
+ - Accept-* parse vs sinatra
94
120
  - param parse vs ruby
95
121
  - layout rendering vs tilt
96
- - evented IO vs eventmachine
122
+
123
+ # License
124
+
125
+ BSD, see copying
@@ -0,0 +1,14 @@
1
+ # connect remote server, and read / write data
2
+
3
+ require_relative "../../lib/nyara"
4
+ require "pry"
5
+ require "open-uri"
6
+
7
+ configure do
8
+ port 3003
9
+ end
10
+
11
+ get '/' do
12
+ data = open "http://baidu.com", &:read
13
+ send_string data
14
+ end
@@ -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
@@ -63,47 +63,66 @@ module Nyara
63
63
  @output = ''
64
64
  end
65
65
 
66
- it "parses" do
66
+ it "converts '%' bytes but not '+'" do
67
67
  i = '/%23+%24'
68
68
  assert_equal i.bytesize, parse(i)
69
- assert_equal "/\x23 \x24", @output
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