bossan 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa6c20fc1586a9072c8edd0ca46364486fa86976
4
- data.tar.gz: e36462b54639d280f13210aa607d2f3c7e4ea31a
3
+ metadata.gz: 943923d13677bcb0574eae13e2bfe1145af1fd0a
4
+ data.tar.gz: b16bc4e09fb61dca0f8f84a4f011f744e1a15ed6
5
5
  SHA512:
6
- metadata.gz: 179adc2a4ffd737a67b19763691bdb4b8be9188552b47163390d2aeb6943b394fa920dbaabb834cbe4c14c13fcc8003244073ae6aacaf9a6e94739bcec4baf36
7
- data.tar.gz: 62e47f8c8f4e3cd5f51ea5bf260dc7ea6e1842ce075d96e075a9e0912988bdd9a33bc324e495ecc8be7738030e25350839301f1d701ca2d368b150a933445e23
6
+ metadata.gz: dfc563ed9e3ba7221fb7099b2ccfdc5cc2e20588b34fbf967a31f7cf2d89423851c60033679a699d2290f1350f9d890caa353ab4bd09bb0bae589c2684d57ca6
7
+ data.tar.gz: 0164c6c0bee4469c55077a81a1e6457c913fec1c4cb31b1ebba099530b329670f58305482489607de785cdf99011ea86c29deaf24a058c95e2c076709bd0ea44
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ### v0.4.2
4
+
5
+ (Bug release. release 2013-07-25)
6
+
7
+ * using tmpfile for very large message body(over 512K)
8
+
9
+ * fix infinte accept loop while high load
10
+
3
11
  ### v0.4.1
4
12
 
5
13
  (Bug release. release 2013-07-20)
@@ -16,7 +16,7 @@ ActiveRecord::Base.establish_connection(
16
16
  )
17
17
 
18
18
  get '/' do
19
- @entries = Entries.find(:all).map do |row|
19
+ @entries = Entries.all.map do |row|
20
20
  {:title => row.title,
21
21
  :text => row.text }
22
22
  end
@@ -23,7 +23,7 @@
23
23
  #define CRLF "\r\n"
24
24
  #define DELIM ": "
25
25
 
26
- #define SERVER "bossan/0.4.1"
26
+ #define SERVER "bossan/0.4.2"
27
27
 
28
28
  #define H_MSG_500 "HTTP/1.0 500 Internal Server Error\r\nContent-Type: text/html\r\nServer: " SERVER "\r\n\r\n"
29
29
 
@@ -88,7 +88,7 @@ static VALUE rack_input;
88
88
  static VALUE http_connection;
89
89
 
90
90
  static VALUE content_type;
91
- static VALUE content_length;
91
+ static VALUE content_length_key;
92
92
 
93
93
  static VALUE h_content_type;
94
94
  static VALUE h_content_length;
@@ -158,6 +158,7 @@ static int keep_alive_timeout = 5;
158
158
  static int max_fd = 1024 * 4; // picoev max_fd size
159
159
  static int backlog = 1024; // backlog size
160
160
  int max_content_length = 1024 * 1024 * 16; //max_content_length
161
+ int client_body_buffer_size = 1024 * 500; //client_body_buffer_size
161
162
 
162
163
  static VALUE StringIO;
163
164
 
@@ -254,7 +255,7 @@ write_log(const char *new_path, int fd, const char *data, size_t len)
254
255
  static int
255
256
  write_access_log(client_t *cli, int log_fd, const char *log_path)
256
257
  {
257
- request *req = (cli);
258
+ request *req = cli->current_req;
258
259
 
259
260
  char buf[1024*4];
260
261
  if(log_fd > 0){
@@ -624,10 +625,8 @@ write_headers(client_t *client, char *data, size_t datalen)
624
625
  //write header
625
626
  for(i=0; i < hlen; i++){
626
627
  object1 = rb_ary_entry(arr, i);
627
- /* Check_Type(object1, T_STRING); */
628
628
 
629
629
  object2 = rb_hash_aref(client->headers, object1);
630
- /* Check_Type(object2, T_STRING); */
631
630
 
632
631
  name = StringValuePtr(object1);
633
632
  namelen = RSTRING_LEN(object1);
@@ -834,16 +833,17 @@ static response_status
834
833
  start_response_write(client_t *client)
835
834
  {
836
835
  VALUE item;
837
- char *buf;
838
- ssize_t buflen;
836
+ char *buf = NULL;
837
+ ssize_t buflen = NULL;
839
838
 
840
- item = rb_funcall(client->response_iter, i_next, 0);
841
- /* Check_Type(item, T_STRING); */
839
+ item = rb_rescue(rb_body_iterator, client->response_iter, ret_qnil, NULL);
842
840
  DEBUG("client %p :fd %d", client, client->fd);
843
841
 
844
- //write string only
845
- buf = StringValuePtr(item);
846
- buflen = RSTRING_LEN(item);
842
+ if (item != Qnil) {
843
+ //write string only
844
+ buf = StringValuePtr(item);
845
+ buflen = RSTRING_LEN(item);
846
+ }
847
847
 
848
848
  /* DEBUG("status_code %d body:%.*s", client->status_code, (int)buflen, buf); */
849
849
  return write_headers(client, buf, buflen);
@@ -889,6 +889,17 @@ key_upper(char *s, const char *key, size_t len)
889
889
  }
890
890
 
891
891
 
892
+ static int
893
+ write_body2file(request *req, const char *buffer, size_t buf_len)
894
+ {
895
+ FILE *tmp = (FILE *)req->body;
896
+ fwrite(buffer, 1, buf_len, tmp);
897
+ req->body_readed += buf_len;
898
+ DEBUG("write_body2file %d bytes", (int)buf_len);
899
+ return req->body_readed;
900
+ }
901
+
902
+
892
903
  static int
893
904
  write_body2mem(request *req, const char *buffer, size_t buf_len)
894
905
  {
@@ -902,7 +913,11 @@ write_body2mem(request *req, const char *buffer, size_t buf_len)
902
913
  static int
903
914
  write_body(request *req, const char *buffer, size_t buffer_len)
904
915
  {
905
- return write_body2mem(req, buffer, buffer_len);
916
+ if (req->body_type == BODY_TYPE_TMPFILE) {
917
+ return write_body2file(req, buffer, buffer_len);
918
+ } else {
919
+ return write_body2mem(req, buffer, buffer_len);
920
+ }
906
921
  }
907
922
 
908
923
 
@@ -1009,11 +1024,11 @@ static int
1009
1024
  replace_env_key(VALUE dict, VALUE old_key, VALUE new_key)
1010
1025
  {
1011
1026
  int ret = 1;
1012
-
1013
1027
  VALUE value = rb_hash_aref(dict, old_key);
1014
- if(value != Qnil) {
1015
- rb_hash_aset(dict, old_key, Qnil);
1016
- ret = rb_hash_aset(dict, new_key, value);
1028
+
1029
+ if (value != Qnil) {
1030
+ rb_hash_delete(dict, old_key);
1031
+ rb_hash_aset(dict, new_key, value);
1017
1032
  }
1018
1033
  return ret;
1019
1034
  }
@@ -1249,11 +1264,23 @@ body_cb(http_parser *p, const char *buf, size_t len)
1249
1264
  req->bad_request_code = 411;
1250
1265
  return -1;
1251
1266
  }
1252
- //default memory stream
1253
- DEBUG("client->body_length %d \n", req->body_length);
1254
- req->body = rb_funcall(StringIO, i_new, 1, rb_str_new2(""));
1255
- req->body_type = BODY_TYPE_BUFFER;
1256
- DEBUG("BODY_TYPE_BUFFER \n");
1267
+ if(req->body_length > client_body_buffer_size){
1268
+ //large size request
1269
+ FILE *tmp = tmpfile();
1270
+ if(tmp < 0){
1271
+ req->bad_request_code = 500;
1272
+ return -1;
1273
+ }
1274
+ req->body = tmp;
1275
+ req->body_type = BODY_TYPE_TMPFILE;
1276
+ DEBUG("BODY_TYPE_TMPFILE");
1277
+ }else{
1278
+ //default memory stream
1279
+ DEBUG("client->body_length %d \n", req->body_length);
1280
+ req->body = rb_funcall(StringIO, i_new, 1, rb_str_new2(""));
1281
+ req->body_type = BODY_TYPE_BUFFER;
1282
+ DEBUG("BODY_TYPE_BUFFER");
1283
+ }
1257
1284
  }
1258
1285
  write_body(req, buf, len);
1259
1286
  return 0;
@@ -1314,7 +1341,7 @@ headers_complete_cb(http_parser *p)
1314
1341
  if(unlikely(ret == -1)){
1315
1342
  return -1;
1316
1343
  }
1317
- ret = replace_env_key(env, h_content_length, content_length);
1344
+ ret = replace_env_key(env, h_content_length, content_length_key);
1318
1345
  if(unlikely(ret == -1)){
1319
1346
  return -1;
1320
1347
  }
@@ -1483,7 +1510,7 @@ setup_static_env(char *name, int port)
1483
1510
  http_connection = rb_obj_freeze(rb_str_new2("HTTP_CONNECTION"));
1484
1511
 
1485
1512
  content_type = rb_obj_freeze(rb_str_new2("CONTENT_TYPE"));
1486
- content_length = rb_obj_freeze(rb_str_new2("CONTENT_LENGTH"));
1513
+ content_length_key = rb_obj_freeze(rb_str_new2("CONTENT_LENGTH"));
1487
1514
 
1488
1515
  h_content_type = rb_obj_freeze(rb_str_new2("HTTP_CONTENT_TYPE"));
1489
1516
  h_content_length = rb_obj_freeze(rb_str_new2("HTTP_CONTENT_LENGTH"));
@@ -1535,7 +1562,6 @@ setup_listen_sock(int fd)
1535
1562
  int on = 1, r = -1;
1536
1563
  #ifdef linux
1537
1564
  r = setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &on, sizeof(on));
1538
- assert(r == 0);
1539
1565
  #endif
1540
1566
  r = fcntl(fd, F_SETFL, O_NONBLOCK);
1541
1567
  assert(r == 0);
@@ -1823,6 +1849,7 @@ process_rack_app(client_t *cli)
1823
1849
  char buff[256];
1824
1850
  sprintf(buff, "HTTP/1.%d %d %s\r\n", cli->http_parser->http_minor, cli->status_code, reason_phrase);
1825
1851
  cli->http_status = rb_str_new(buff, strlen(buff));
1852
+ rb_gc_register_address(&cli->http_status);
1826
1853
 
1827
1854
  //check response
1828
1855
  if(cli->response && cli->response == Qnil){
@@ -1955,13 +1982,24 @@ prepare_call_rack(client_t *client)
1955
1982
  return;
1956
1983
  }
1957
1984
 
1958
- if(req->body_type == BODY_TYPE_BUFFER) {
1985
+ if (req->body_type == BODY_TYPE_TMPFILE) {
1986
+ int fd;
1987
+ VALUE io;
1988
+ request *req = client->current_req;
1989
+ FILE *tmp = (FILE *)req->body;
1990
+
1991
+ fflush(tmp);
1992
+ rewind(tmp);
1993
+
1994
+ fd = fileno(tmp);
1995
+ io = rb_io_fdopen(fd, O_RDWR, NULL);
1996
+ rb_hash_aset(req->environ, rack_input, io);
1997
+ } else if(req->body_type == BODY_TYPE_BUFFER) {
1959
1998
  rb_funcall((VALUE)req->body, i_seek, 1, INT2NUM(0));
1960
1999
  rb_hash_aset(req->environ, rack_input, (VALUE)req->body);
1961
2000
  } else {
1962
2001
  object = rb_str_new2("");
1963
2002
  input = rb_funcall(StringIO, i_new, 1, object);
1964
- rb_gc_register_address(&input);
1965
2003
  rb_hash_aset(req->environ, rack_input, input);
1966
2004
  req->body = input;
1967
2005
  }
@@ -2136,8 +2174,9 @@ accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
2136
2174
  // next turn or other process
2137
2175
  return;
2138
2176
  }else if ((events & PICOEV_READ) != 0) {
2177
+ int i;
2139
2178
  socklen_t client_len = sizeof(client_addr);
2140
- for(;;) {
2179
+ for(i=0; i<8; ++i) {
2141
2180
  #ifdef linux
2142
2181
  client_fd = accept4(fd, (struct sockaddr *)&client_addr, &client_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
2143
2182
  #else
@@ -2546,7 +2585,7 @@ Init_bossan_ext(void)
2546
2585
  rb_gc_register_address(&h_content_type);
2547
2586
  rb_gc_register_address(&h_content_length);
2548
2587
  rb_gc_register_address(&content_type);
2549
- rb_gc_register_address(&content_length);
2588
+ rb_gc_register_address(&content_length_key);
2550
2589
 
2551
2590
  rb_gc_register_address(&http_10);
2552
2591
  rb_gc_register_address(&http_11);
@@ -217,7 +217,9 @@ extern "C" {
217
217
  int picoev_add(picoev_loop* loop, int fd, int events, int timeout_in_secs,
218
218
  picoev_handler* callback, void* cb_arg) {
219
219
  picoev_fd* target;
220
- assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd));
220
+ if (unlikely(!PICOEV_IS_INITED_AND_FD_IN_RANGE(fd))) {
221
+ return -1;
222
+ }
221
223
  target = picoev.fds + fd;
222
224
  assert(target->loop_id == 0);
223
225
  target->callback = callback;
@@ -1,3 +1,3 @@
1
1
  module Bossan
2
- VERSION = "0.4.1"
2
+ VERSION = "0.4.2"
3
3
  end
@@ -0,0 +1,45 @@
1
+ require 'test/unit'
2
+ require "uri"
3
+ require 'net/http'
4
+ require_relative '../lib/bossan'
5
+
6
+
7
+ class RackEnvBigMessageBodyTest < Test::Unit::TestCase
8
+
9
+ RESPONSE = ["Hello ", "world!"].freeze
10
+ DEFAULT_HOST = "localhost"
11
+ DEFAULT_PORT = 8000
12
+
13
+ def test_query_app
14
+ pid = fork do
15
+ trap(:INT) { Bossan.stop }
16
+ Bossan.listen(DEFAULT_HOST, DEFAULT_PORT)
17
+ Bossan.run(proc {|env|
18
+ @env = env.dup
19
+ assert_equal(IO, @env["rack.input"].class)
20
+ body = RESPONSE
21
+ [200,
22
+ {
23
+ 'Content-type'=> 'text/plain',
24
+ 'Content-length'=> RESPONSE.join.size.to_s
25
+ },
26
+ body
27
+ ]
28
+ })
29
+ end
30
+ Process.detach pid
31
+ sleep 2
32
+
33
+ uri = URI.parse("http://0.0.0.0:8000/")
34
+ Net::HTTP.start(uri.host, uri.port){|http|
35
+ header = {
36
+ "user-agent" => "Ruby/#{RUBY_VERSION} MyHttpClient"
37
+ }
38
+ body = "A" * 1024 * 513 # 513K
39
+ response = http.post(uri.path, body, header)
40
+ }
41
+ ensure
42
+ Process.kill(:INT, pid)
43
+ end
44
+
45
+ end
@@ -4,44 +4,32 @@ require 'test/unit/testcase'
4
4
  require 'net/http'
5
5
 
6
6
 
7
- ASSERT_RESPONSE = "Hello world!"
8
- RESPONSE = ["Hello ", "world!"].freeze
9
7
  DEFAULT_HOST = "localhost"
10
8
  DEFAULT_PORT = 8000
9
+ ASSERT_RESPONSE = "Hello world!"
10
+ RESPONSE = ["Hello ", "world!"].freeze
11
11
 
12
12
 
13
- class App
13
+ class ErrApp
14
14
  def call env
15
15
  body = RESPONSE
16
- [200,
16
+ [500,
17
17
  {
18
- 'Content-type'=> 'text/plain',
19
- 'Content-length'=> RESPONSE.join.size.to_s
20
- },
21
- body
18
+ 'Content-type'=> 'text/html',
19
+ },
20
+ RESPONSE
22
21
  ]
23
22
  end
24
23
  end
25
24
 
26
25
 
27
- class LongUrlTest < Test::Unit::TestCase
28
-
29
- def test_long_url1
26
+ class RackErrSpecTest < Test::Unit::TestCase
27
+ def test_error
30
28
  response = Net::HTTP.start(DEFAULT_HOST, DEFAULT_PORT) {|http|
31
- query = "A" * 4095
32
- http.get("/#{query}")
29
+ http.get("/")
33
30
  }
34
-
35
- assert_equal("200", response.code)
36
- end
37
-
38
- def test_long_url2
39
- response = Net::HTTP.start(DEFAULT_HOST, DEFAULT_PORT) {|http|
40
- query = "A" * 4096
41
- http.get("/#{query}")
42
- }
43
-
44
- assert_equal("400", response.code)
31
+ assert_equal("500", response.code)
32
+ assert_equal(ASSERT_RESPONSE, response.body)
45
33
  end
46
34
  end
47
35
 
@@ -60,15 +48,17 @@ def server_is_wake_up? n=100
60
48
  end
61
49
 
62
50
 
51
+
63
52
  begin
64
53
  $stderr.puts RUBY_DESCRIPTION
65
-
66
54
  pid = fork do
67
55
  trap(:INT) { Bossan.stop }
68
56
  Bossan.listen(DEFAULT_HOST, DEFAULT_PORT)
69
- Bossan.run(App.new)
57
+ Bossan.run(ErrApp.new)
70
58
  end
59
+
71
60
  Process.detach pid
61
+
72
62
  unless server_is_wake_up?
73
63
  $stderr.puts "bossan won't wake up until you love it ..."
74
64
  exit 1
@@ -37,25 +37,27 @@ class App
37
37
  end
38
38
  end
39
39
 
40
- class BadHttpMethodTest < Test::Unit::TestCase
41
40
 
42
- def send_data method
43
- begin
44
- sock = TCPSocket.new DEFAULT_HOST, DEFAULT_PORT
45
- sock.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
46
- sock.send("#{method} #{DEFAULT_PATH} #{DEFAULT_VERSION}\r\n", 0)
47
- sock.send("Host: #{DEFAULT_HOST}\r\n", 0)
48
- DEFAULT_HEADER.each_pair {|k, v|
49
- sock.send("#{k}: #{v}\r\n", 0)
50
- }
51
- sock.send("\r\n", 0)
52
-
53
- data = sock.recv(1024 * 2)
54
- return data
55
- rescue
56
- raise
57
- end
41
+ def send_data method
42
+ begin
43
+ sock = TCPSocket.new DEFAULT_HOST, DEFAULT_PORT
44
+ sock.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
45
+ sock.send("#{method} #{DEFAULT_PATH} #{DEFAULT_VERSION}\r\n", 0)
46
+ sock.send("Host: #{DEFAULT_HOST}\r\n", 0)
47
+ DEFAULT_HEADER.each_pair {|k, v|
48
+ sock.send("#{k}: #{v}\r\n", 0)
49
+ }
50
+ sock.send("\r\n", 0)
51
+
52
+ data = sock.recv(1024 * 2)
53
+ return data
54
+ rescue
55
+ raise
58
56
  end
57
+ end
58
+
59
+
60
+ class BadHttpMethodTest < Test::Unit::TestCase
59
61
 
60
62
  def test_bad_method1
61
63
  response = send_data("")
@@ -71,6 +73,23 @@ class BadHttpMethodTest < Test::Unit::TestCase
71
73
  response = send_data("..")
72
74
  assert_equal(ERR_400, response.split("\r\n").first)
73
75
  end
76
+
77
+ def test_long_url1
78
+ response = Net::HTTP.start(DEFAULT_HOST, DEFAULT_PORT) {|http|
79
+ query = "A" * 4095
80
+ http.get("/#{query}")
81
+ }
82
+ assert_equal("200", response.code)
83
+ end
84
+
85
+ def test_long_url2
86
+ response = Net::HTTP.start(DEFAULT_HOST, DEFAULT_PORT) {|http|
87
+ query = "A" * 4096
88
+ http.get("/#{query}")
89
+ }
90
+ assert_equal("400", response.code)
91
+ end
92
+
74
93
  end
75
94
 
76
95
 
@@ -13,6 +13,7 @@ RESPONSE = ["Hello ", "world!"].freeze
13
13
 
14
14
  class App
15
15
  def call env
16
+ # p env
16
17
  body = RESPONSE
17
18
  [200,
18
19
  {
@@ -24,21 +25,20 @@ class App
24
25
  end
25
26
  end
26
27
 
28
+
27
29
  class RackSpecTest < Test::Unit::TestCase
28
30
 
29
- def test_simple_get
31
+ def test_get
30
32
  response = Net::HTTP.start(DEFAULT_HOST, DEFAULT_PORT) {|http|
31
33
  http.get("/")
32
34
  }
33
-
34
35
  assert_equal("200", response.code)
35
36
  assert_equal(ASSERT_RESPONSE, response.body)
36
37
  end
37
38
 
38
- def test_simple_post
39
+ def test_post
39
40
  response = Net::HTTP.post_form(URI.parse("http://#{DEFAULT_HOST}:#{DEFAULT_PORT}/"),
40
41
  {'key1'=> 'value1', 'key2'=> 'value2'})
41
-
42
42
  assert_equal("200", response.code)
43
43
  assert_equal(ASSERT_RESPONSE, response.body)
44
44
  end
@@ -61,7 +61,6 @@ end
61
61
 
62
62
  begin
63
63
  $stderr.puts RUBY_DESCRIPTION
64
-
65
64
  pid = fork do
66
65
  trap(:INT) { Bossan.stop }
67
66
  Bossan.listen(DEFAULT_HOST, DEFAULT_PORT)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bossan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroki Noda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-20 00:00:00.000000000 Z
11
+ date: 2013-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -39,7 +39,6 @@ files:
39
39
  - LICENSE.txt
40
40
  - README.md
41
41
  - Rakefile
42
- - TODO
43
42
  - bossan.gemspec
44
43
  - examples/blog/README.md
45
44
  - examples/blog/app.rb
@@ -72,11 +71,12 @@ files:
72
71
  - lib/bossan.rb
73
72
  - lib/bossan/version.rb
74
73
  - lib/rack/handler/bossan.rb
75
- - test/test_rack_bad_http_method.rb
76
74
  - test/test_rack_chunk_response.rb
75
+ - test/test_rack_env_big_message_body.rb
77
76
  - test/test_rack_env_simple_get.rb
78
77
  - test/test_rack_env_simple_query.rb
79
- - test/test_rack_long_url.rb
78
+ - test/test_rack_err_spec.rb
79
+ - test/test_rack_evil.rb
80
80
  - test/test_rack_spec.rb
81
81
  homepage: https://github.com/kubo39/bossan
82
82
  licenses:
data/TODO DELETED
@@ -1,4 +0,0 @@
1
- * set rack.input File-type object, if over 512K
2
- - avoid too much use memory
3
-
4
- * or else...