bossan 0.4.1 → 0.4.2

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 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...