unicorn 5.4.1 → 5.7.0

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.manifest +7 -3
  3. data/.olddoc.yml +12 -7
  4. data/Application_Timeouts +4 -4
  5. data/Documentation/.gitignore +1 -3
  6. data/Documentation/unicorn.1 +222 -0
  7. data/Documentation/unicorn_rails.1 +207 -0
  8. data/FAQ +1 -1
  9. data/GIT-VERSION-FILE +1 -1
  10. data/GIT-VERSION-GEN +1 -1
  11. data/GNUmakefile +111 -57
  12. data/HACKING +1 -1
  13. data/ISSUES +21 -23
  14. data/KNOWN_ISSUES +2 -2
  15. data/LATEST +22 -28
  16. data/LICENSE +2 -2
  17. data/Links +13 -11
  18. data/NEWS +65 -0
  19. data/README +25 -11
  20. data/SIGNALS +1 -1
  21. data/Sandbox +4 -4
  22. data/archive/slrnpull.conf +1 -1
  23. data/bin/unicorn +3 -1
  24. data/bin/unicorn_rails +2 -2
  25. data/examples/big_app_gc.rb +1 -1
  26. data/examples/logrotate.conf +3 -3
  27. data/examples/nginx.conf +4 -3
  28. data/examples/unicorn.conf.minimal.rb +2 -2
  29. data/examples/unicorn.conf.rb +2 -2
  30. data/examples/unicorn@.service +7 -0
  31. data/ext/unicorn_http/common_field_optimization.h +24 -6
  32. data/ext/unicorn_http/extconf.rb +35 -0
  33. data/ext/unicorn_http/global_variables.h +2 -2
  34. data/ext/unicorn_http/httpdate.c +2 -2
  35. data/ext/unicorn_http/unicorn_http.c +257 -224
  36. data/ext/unicorn_http/unicorn_http.rl +47 -14
  37. data/lib/unicorn/configurator.rb +25 -4
  38. data/lib/unicorn/http_request.rb +12 -2
  39. data/lib/unicorn/http_server.rb +38 -11
  40. data/lib/unicorn/launcher.rb +1 -1
  41. data/lib/unicorn/oob_gc.rb +2 -2
  42. data/lib/unicorn/socket_helper.rb +1 -0
  43. data/lib/unicorn/tmpio.rb +8 -2
  44. data/lib/unicorn/util.rb +1 -1
  45. data/lib/unicorn/version.rb +1 -1
  46. data/lib/unicorn/worker.rb +16 -2
  47. data/lib/unicorn.rb +23 -9
  48. data/man/man1/unicorn.1 +89 -88
  49. data/man/man1/unicorn_rails.1 +78 -83
  50. data/t/GNUmakefile +3 -72
  51. data/t/README +4 -4
  52. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  53. data/t/t0301.ru +13 -0
  54. data/test/benchmark/README +14 -4
  55. data/test/benchmark/ddstream.ru +50 -0
  56. data/test/benchmark/readinput.ru +40 -0
  57. data/test/benchmark/uconnect.perl +66 -0
  58. data/test/exec/test_exec.rb +9 -7
  59. data/test/test_helper.rb +22 -30
  60. data/test/unit/test_http_parser.rb +16 -0
  61. data/test/unit/test_http_parser_ng.rb +81 -0
  62. data/test/unit/test_server.rb +30 -0
  63. data/test/unit/test_upload.rb +4 -9
  64. data/test/unit/test_util.rb +25 -0
  65. data/unicorn.gemspec +8 -7
  66. metadata +15 -11
  67. data/Documentation/GNUmakefile +0 -30
  68. data/Documentation/unicorn.1.txt +0 -187
  69. data/Documentation/unicorn_rails.1.txt +0 -175
@@ -13,7 +13,7 @@
13
13
  #include "global_variables.h"
14
14
  #include "c_util.h"
15
15
 
16
- void init_unicorn_httpdate(VALUE mark_ary);
16
+ void init_unicorn_httpdate(void);
17
17
 
18
18
  #define UH_FL_CHUNKED 0x1
19
19
  #define UH_FL_HASBODY 0x2
@@ -62,7 +62,8 @@ struct http_parser {
62
62
  } len;
63
63
  };
64
64
 
65
- static ID id_set_backtrace;
65
+ static ID id_set_backtrace, id_is_chunked_p;
66
+ static VALUE cHttpParser;
66
67
 
67
68
  #ifdef HAVE_RB_HASH_CLEAR /* Ruby >= 2.0 */
68
69
  # define my_hash_clear(h) (void)rb_hash_clear(h)
@@ -220,6 +221,19 @@ static void write_cont_value(struct http_parser *hp,
220
221
  rb_str_buf_cat(hp->cont, vptr, end + 1);
221
222
  }
222
223
 
224
+ static int is_chunked(VALUE v)
225
+ {
226
+ /* common case first */
227
+ if (STR_CSTR_CASE_EQ(v, "chunked"))
228
+ return 1;
229
+
230
+ /*
231
+ * call Ruby function in unicorn/http_request.rb to deal with unlikely
232
+ * comma-delimited case
233
+ */
234
+ return rb_funcall(cHttpParser, id_is_chunked_p, 1, v) != Qfalse;
235
+ }
236
+
223
237
  static void write_value(struct http_parser *hp,
224
238
  const char *buffer, const char *p)
225
239
  {
@@ -246,7 +260,9 @@ static void write_value(struct http_parser *hp,
246
260
  f = uncommon_field(field, flen);
247
261
  } else if (f == g_http_connection) {
248
262
  hp_keepalive_connection(hp, v);
249
- } else if (f == g_content_length) {
263
+ } else if (f == g_content_length && !HP_FL_TEST(hp, CHUNKED)) {
264
+ if (hp->len.content)
265
+ parser_raise(eHttpParserError, "Content-Length already set");
250
266
  hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
251
267
  if (hp->len.content < 0)
252
268
  parser_raise(eHttpParserError, "invalid Content-Length");
@@ -254,9 +270,30 @@ static void write_value(struct http_parser *hp,
254
270
  HP_FL_SET(hp, HASBODY);
255
271
  hp_invalid_if_trailer(hp);
256
272
  } else if (f == g_http_transfer_encoding) {
257
- if (STR_CSTR_CASE_EQ(v, "chunked")) {
273
+ if (is_chunked(v)) {
274
+ if (HP_FL_TEST(hp, CHUNKED))
275
+ /*
276
+ * RFC 7230 3.3.1:
277
+ * A sender MUST NOT apply chunked more than once to a message body
278
+ * (i.e., chunking an already chunked message is not allowed).
279
+ */
280
+ parser_raise(eHttpParserError, "Transfer-Encoding double chunked");
281
+
258
282
  HP_FL_SET(hp, CHUNKED);
259
283
  HP_FL_SET(hp, HASBODY);
284
+
285
+ /* RFC 7230 3.3.3, 3: favor chunked if Content-Length exists */
286
+ hp->len.content = 0;
287
+ } else if (HP_FL_TEST(hp, CHUNKED)) {
288
+ /*
289
+ * RFC 7230 3.3.3, point 3 states:
290
+ * If a Transfer-Encoding header field is present in a request and
291
+ * the chunked transfer coding is not the final encoding, the
292
+ * message body length cannot be determined reliably; the server
293
+ * MUST respond with the 400 (Bad Request) status code and then
294
+ * close the connection.
295
+ */
296
+ parser_raise(eHttpParserError, "invalid Transfer-Encoding");
260
297
  }
261
298
  hp_invalid_if_trailer(hp);
262
299
  } else if (f == g_http_trailer) {
@@ -487,7 +524,7 @@ static void set_url_scheme(VALUE env, VALUE *server_port)
487
524
  * and X-Forwarded-Proto handling from this parser? We've had it
488
525
  * forever and nobody has said anything against it, either.
489
526
  * Anyways, please send comments to our public mailing list:
490
- * unicorn-public@bogomips.org (no HTML mail, no subscription necessary)
527
+ * unicorn-public@yhbt.net (no HTML mail, no subscription necessary)
491
528
  */
492
529
  scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
493
530
  if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
@@ -931,11 +968,8 @@ static VALUE HttpParser_rssget(VALUE self)
931
968
 
932
969
  void Init_unicorn_http(void)
933
970
  {
934
- static VALUE mark_ary;
935
- VALUE mUnicorn, cHttpParser;
971
+ VALUE mUnicorn;
936
972
 
937
- mark_ary = rb_ary_new();
938
- rb_global_variable(&mark_ary);
939
973
  mUnicorn = rb_define_module("Unicorn");
940
974
  cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
941
975
  eHttpParserError =
@@ -945,7 +979,7 @@ void Init_unicorn_http(void)
945
979
  e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
946
980
  eHttpParserError);
947
981
 
948
- init_globals(mark_ary);
982
+ init_globals();
949
983
  rb_define_alloc_func(cHttpParser, HttpParser_alloc);
950
984
  rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
951
985
  rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
@@ -982,19 +1016,18 @@ void Init_unicorn_http(void)
982
1016
 
983
1017
  rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
984
1018
 
985
- init_common_fields(mark_ary);
1019
+ init_common_fields();
986
1020
  SET_GLOBAL(g_http_host, "HOST");
987
1021
  SET_GLOBAL(g_http_trailer, "TRAILER");
988
1022
  SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
989
1023
  SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
990
1024
  SET_GLOBAL(g_http_connection, "CONNECTION");
991
1025
  id_set_backtrace = rb_intern("set_backtrace");
992
- init_unicorn_httpdate(mark_ary);
993
-
994
- OBJ_FREEZE(mark_ary);
1026
+ init_unicorn_httpdate();
995
1027
 
996
1028
  #ifndef HAVE_RB_HASH_CLEAR
997
1029
  id_clear = rb_intern("clear");
998
1030
  #endif
1031
+ id_is_chunked_p = rb_intern("is_chunked?");
999
1032
  }
1000
1033
  #undef SET_GLOBAL
@@ -3,11 +3,11 @@ require 'logger'
3
3
 
4
4
  # Implements a simple DSL for configuring a unicorn server.
5
5
  #
6
- # See https://bogomips.org/unicorn/examples/unicorn.conf.rb and
7
- # https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
6
+ # See https://yhbt.net/unicorn/examples/unicorn.conf.rb and
7
+ # https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
8
8
  # example configuration files. An example config file for use with
9
9
  # nginx is also available at
10
- # https://bogomips.org/unicorn/examples/nginx.conf
10
+ # https://yhbt.net/unicorn/examples/nginx.conf
11
11
  #
12
12
  # See the link:/TUNING.html document for more information on tuning unicorn.
13
13
  class Unicorn::Configurator
@@ -53,6 +53,7 @@ class Unicorn::Configurator
53
53
  server.logger.info("worker=#{worker.nr} ready")
54
54
  },
55
55
  :pid => nil,
56
+ :early_hints => false,
56
57
  :worker_exec => false,
57
58
  :preload_app => false,
58
59
  :check_client_connection => false,
@@ -88,6 +89,9 @@ class Unicorn::Configurator
88
89
  RACKUP[:set_listener] and
89
90
  set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
90
91
 
92
+ RACKUP[:no_default_middleware] and
93
+ set[:default_middleware] = false
94
+
91
95
  # unicorn_rails creates dirs here after working_directory is bound
92
96
  after_reload.call if after_reload
93
97
 
@@ -235,7 +239,7 @@ class Unicorn::Configurator
235
239
  # server 192.168.0.9:8080 fail_timeout=0;
236
240
  # }
237
241
  #
238
- # See http://nginx.org/en/docs/http/ngx_http_upstream_module.html
242
+ # See https://nginx.org/en/docs/http/ngx_http_upstream_module.html
239
243
  # for more details on nginx upstream configuration.
240
244
  def timeout(seconds)
241
245
  set_int(:timeout, seconds, 3)
@@ -265,6 +269,23 @@ class Unicorn::Configurator
265
269
  set_int(:worker_processes, nr, 1)
266
270
  end
267
271
 
272
+ # sets whether to add default middleware in the development and
273
+ # deployment RACK_ENVs.
274
+ #
275
+ # default_middleware is only available in unicorn 5.5.0+
276
+ def default_middleware(bool)
277
+ set_bool(:default_middleware, bool)
278
+ end
279
+
280
+ # sets whether to enable the proposed early hints Rack API.
281
+ # If enabled, Rails 5.2+ will automatically send a 103 Early Hint
282
+ # for all the `javascript_include_tag` and `stylesheet_link_tag`
283
+ # in your response. See: https://api.rubyonrails.org/v5.2/classes/ActionDispatch/Request.html#method-i-send_early_hints
284
+ # See also https://tools.ietf.org/html/rfc8297
285
+ def early_hints(bool)
286
+ set_bool(:early_hints, bool)
287
+ end
288
+
268
289
  # sets listeners to the given +addresses+, replacing or augmenting the
269
290
  # current set. This is for the global listener pool shared by all
270
291
  # worker processes. For per-worker listeners, see the after_fork example
@@ -2,7 +2,6 @@
2
2
  # :enddoc:
3
3
  # no stable API here
4
4
  require 'unicorn_http'
5
- require 'raindrops'
6
5
 
7
6
  # TODO: remove redundant names
8
7
  Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
@@ -66,7 +65,7 @@ class Unicorn::HttpParser
66
65
  clear
67
66
  e = env
68
67
 
69
- # From http://www.ietf.org/rfc/rfc3875:
68
+ # From https://www.ietf.org/rfc/rfc3875:
70
69
  # "Script authors should be aware that the REMOTE_ADDR and
71
70
  # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
72
71
  # may not identify the ultimate source of the request. They
@@ -189,4 +188,15 @@ class Unicorn::HttpParser
189
188
  HTTP_RESPONSE_START.each { |c| socket.write(c) }
190
189
  end
191
190
  end
191
+
192
+ # called by ext/unicorn_http/unicorn_http.rl via rb_funcall
193
+ def self.is_chunked?(v) # :nodoc:
194
+ vals = v.split(/[ \t]*,[ \t]*/).map!(&:downcase)
195
+ if vals.pop == 'chunked'.freeze
196
+ return true unless vals.include?('chunked'.freeze)
197
+ raise Unicorn::HttpParserError, 'double chunked', []
198
+ end
199
+ return false unless vals.include?('chunked'.freeze)
200
+ raise Unicorn::HttpParserError, 'chunked not last', []
201
+ end
192
202
  end
@@ -6,7 +6,7 @@
6
6
  # forked worker children.
7
7
  #
8
8
  # Users do not need to know the internals of this class, but reading the
9
- # {source}[https://bogomips.org/unicorn.git/tree/lib/unicorn/http_server.rb]
9
+ # {source}[https://yhbt.net/unicorn.git/tree/lib/unicorn/http_server.rb]
10
10
  # is education for programmers wishing to learn how unicorn works.
11
11
  # See Unicorn::Configurator for information on how to configure unicorn.
12
12
  class Unicorn::HttpServer
@@ -14,7 +14,8 @@ class Unicorn::HttpServer
14
14
  attr_accessor :app, :timeout, :worker_processes,
15
15
  :before_fork, :after_fork, :before_exec,
16
16
  :listener_opts, :preload_app,
17
- :orig_app, :config, :ready_pipe, :user
17
+ :orig_app, :config, :ready_pipe, :user,
18
+ :default_middleware, :early_hints
18
19
  attr_writer :after_worker_exit, :after_worker_ready, :worker_exec
19
20
 
20
21
  attr_reader :pid, :logger
@@ -70,6 +71,7 @@ class Unicorn::HttpServer
70
71
  @app = app
71
72
  @request = Unicorn::HttpRequest.new
72
73
  @reexec_pid = 0
74
+ @default_middleware = true
73
75
  options = options.dup
74
76
  @ready_pipe = options.delete(:ready_pipe)
75
77
  @init_listeners = options[:listeners] ? options[:listeners].dup : []
@@ -82,7 +84,7 @@ class Unicorn::HttpServer
82
84
  # * The master process never closes or reinitializes this once
83
85
  # initialized. Signal handlers in the master process will write to
84
86
  # it to wake up the master from IO.select in exactly the same manner
85
- # djb describes in http://cr.yp.to/docs/selfpipe.html
87
+ # djb describes in https://cr.yp.to/docs/selfpipe.html
86
88
  #
87
89
  # * The workers immediately close the pipe they inherit. See the
88
90
  # Unicorn::Worker class for the pipe workers use.
@@ -380,7 +382,7 @@ class Unicorn::HttpServer
380
382
 
381
383
  # wait for a signal hander to wake us up and then consume the pipe
382
384
  def master_sleep(sec)
383
- @self_pipe[0].kgio_wait_readable(sec) or return
385
+ @self_pipe[0].wait(sec) or return
384
386
  # 11 bytes is the maximum string length which can be embedded within
385
387
  # the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
386
388
  # Most reads are only one byte here and uncommon, so it's not worth a
@@ -520,9 +522,6 @@ class Unicorn::HttpServer
520
522
  Unicorn::Configurator::RACKUP.clear
521
523
  @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
522
524
 
523
- # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/36450
524
- srand # remove in unicorn 6
525
-
526
525
  # The OpenSSL PRNG is seeded with only the pid, and apps with frequently
527
526
  # dying workers can recycle pids
528
527
  OpenSSL::Random.seed(rand.to_s) if defined?(OpenSSL::Random)
@@ -589,6 +588,25 @@ class Unicorn::HttpServer
589
588
  rescue
590
589
  end
591
590
 
591
+ def e103_response_write(client, headers)
592
+ response = if @request.response_start_sent
593
+ "103 Early Hints\r\n"
594
+ else
595
+ "HTTP/1.1 103 Early Hints\r\n"
596
+ end
597
+
598
+ headers.each_pair do |k, vs|
599
+ next if !vs || vs.empty?
600
+ values = vs.to_s.split("\n".freeze)
601
+ values.each do |v|
602
+ response << "#{k}: #{v}\r\n"
603
+ end
604
+ end
605
+ response << "\r\n".freeze
606
+ response << "HTTP/1.1 ".freeze if @request.response_start_sent
607
+ client.write(response)
608
+ end
609
+
592
610
  def e100_response_write(client, env)
593
611
  # We use String#freeze to avoid allocations under Ruby 2.1+
594
612
  # Not many users hit this code path, so it's better to reduce the
@@ -603,7 +621,15 @@ class Unicorn::HttpServer
603
621
  # once a client is accepted, it is processed in its entirety here
604
622
  # in 3 easy steps: read request, call app, write app response
605
623
  def process_client(client)
606
- status, headers, body = @app.call(env = @request.read(client))
624
+ env = @request.read(client)
625
+
626
+ if early_hints
627
+ env["rack.early_hints"] = lambda do |headers|
628
+ e103_response_write(client, headers)
629
+ end
630
+ end
631
+
632
+ status, headers, body = @app.call(env)
607
633
 
608
634
  begin
609
635
  return if @request.hijacked?
@@ -687,6 +713,7 @@ class Unicorn::HttpServer
687
713
  trap(:USR1) { nr = -65536 }
688
714
 
689
715
  ready = readers.dup
716
+ nr_listeners = readers.size
690
717
  @after_worker_ready.call(self, worker)
691
718
 
692
719
  begin
@@ -709,7 +736,7 @@ class Unicorn::HttpServer
709
736
  # we're probably reasonably busy, so avoid calling select()
710
737
  # and do a speculative non-blocking accept() on ready listeners
711
738
  # before we sleep again in select().
712
- unless nr == 0
739
+ if nr == nr_listeners
713
740
  tmp = ready.dup
714
741
  redo
715
742
  end
@@ -787,12 +814,12 @@ class Unicorn::HttpServer
787
814
  end
788
815
 
789
816
  def build_app!
790
- if app.respond_to?(:arity) && app.arity == 0
817
+ if app.respond_to?(:arity) && (app.arity == 0 || app.arity == 2)
791
818
  if defined?(Gem) && Gem.respond_to?(:refresh)
792
819
  logger.info "Refreshing Gem list"
793
820
  Gem.refresh
794
821
  end
795
- self.app = app.call
822
+ self.app = app.arity == 0 ? app.call : app.call(nil, self)
796
823
  end
797
824
  end
798
825
 
@@ -31,7 +31,7 @@ module Unicorn::Launcher
31
31
  # \_ parent - exits immediately ASAP
32
32
  # \_ unicorn master - writes to pipe when ready
33
33
 
34
- rd, wr = IO.pipe
34
+ rd, wr = Unicorn.pipe
35
35
  grandparent = $$
36
36
  if fork
37
37
  wr.close # grandparent does not write
@@ -43,8 +43,8 @@
43
43
  # use Unicorn::OobGC, 2, %r{\A/(?:expensive/foo|more_expensive/foo)}
44
44
  #
45
45
  # Feedback from users of early implementations of this module:
46
- # * https://bogomips.org/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
47
- # * https://bogomips.org/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
46
+ # * https://yhbt.net/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
47
+ # * https://yhbt.net/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
48
48
 
49
49
  module Unicorn::OobGC
50
50
 
@@ -83,6 +83,7 @@ module Unicorn
83
83
  rescue => e
84
84
  logger.error("#{sock_name(sock)} " \
85
85
  "failed to set accept_filter=#{name} (#{e.inspect})")
86
+ logger.error("perhaps accf_http(9) needs to be loaded".freeze)
86
87
  end if arg != got
87
88
  end
88
89
  end
data/lib/unicorn/tmpio.rb CHANGED
@@ -11,12 +11,18 @@ class Unicorn::TmpIO < File
11
11
  # immediately, switched to binary mode, and userspace output
12
12
  # buffering is disabled
13
13
  def self.new
14
+ path = nil
15
+
16
+ # workaround File#path being tainted:
17
+ # https://bugs.ruby-lang.org/issues/14485
14
18
  fp = begin
15
- super("#{Dir::tmpdir}/#{rand}", RDWR|CREAT|EXCL, 0600)
19
+ path = "#{Dir::tmpdir}/#{rand}"
20
+ super(path, RDWR|CREAT|EXCL, 0600)
16
21
  rescue Errno::EEXIST
17
22
  retry
18
23
  end
19
- unlink(fp.path)
24
+
25
+ unlink(path)
20
26
  fp.binmode
21
27
  fp.sync = true
22
28
  fp
data/lib/unicorn/util.rb CHANGED
@@ -64,7 +64,7 @@ module Unicorn::Util # :nodoc:
64
64
  fp.reopen(fp.path, "a")
65
65
  else
66
66
  # We should not need this workaround, Ruby can be fixed:
67
- # http://bugs.ruby-lang.org/issues/9036
67
+ # https://bugs.ruby-lang.org/issues/9036
68
68
  # MRI will not call call fclose(3) or freopen(3) here
69
69
  # since there's no associated std{in,out,err} FILE * pointer
70
70
  # This should atomically use dup3(2) (or dup2(2)) syscall
@@ -1 +1 @@
1
- Unicorn::Const::UNICORN_VERSION = '5.4.1'
1
+ Unicorn::Const::UNICORN_VERSION = '5.7.0'
@@ -122,6 +122,11 @@ class Unicorn::Worker
122
122
  # the +after_fork+ hook after any privileged functions need to be
123
123
  # run (e.g. to set per-worker CPU affinity, niceness, etc)
124
124
  #
125
+ # +group+ can be specified as a string, or as an array of two
126
+ # strings. If an array of two strings is given, the first string
127
+ # is used as the primary group of the process, and the second is
128
+ # used as the group of the log files.
129
+ #
125
130
  # Any and all errors raised within this method will be propagated
126
131
  # directly back to the caller (usually the +after_fork+ hook.
127
132
  # These errors commonly include ArgumentError for specifying an
@@ -134,8 +139,17 @@ class Unicorn::Worker
134
139
  # insufficient because modern systems have fine-grained
135
140
  # capabilities. Let the caller handle any and all errors.
136
141
  uid = Etc.getpwnam(user).uid
137
- gid = Etc.getgrnam(group).gid if group
138
- Unicorn::Util.chown_logs(uid, gid)
142
+
143
+ if group
144
+ if group.is_a?(Array)
145
+ group, log_group = group
146
+ log_gid = Etc.getgrnam(log_group).gid
147
+ end
148
+ gid = Etc.getgrnam(group).gid
149
+ log_gid ||= gid
150
+ end
151
+
152
+ Unicorn::Util.chown_logs(uid, log_gid)
139
153
  if gid && Process.egid != gid
140
154
  Process.initgroups(user, gid)
141
155
  Process::GID.change_privilege(gid)
data/lib/unicorn.rb CHANGED
@@ -2,6 +2,8 @@
2
2
  require 'etc'
3
3
  require 'stringio'
4
4
  require 'kgio'
5
+ require 'raindrops'
6
+ require 'io/wait'
5
7
 
6
8
  begin
7
9
  require 'rack'
@@ -43,12 +45,8 @@ module Unicorn
43
45
  abort "rack and Rack::Builder must be available for processing #{ru}"
44
46
  end
45
47
 
46
- # Op is going to get cleared before the returned lambda is called, so
47
- # save this value so that it's still there when we need it:
48
- no_default_middleware = op[:no_default_middleware]
49
-
50
48
  # always called after config file parsing, may be called after forking
51
- lambda do ||
49
+ lambda do |_, server|
52
50
  inner_app = case ru
53
51
  when /\.ru$/
54
52
  raw = File.read(ru)
@@ -64,7 +62,7 @@ module Unicorn
64
62
  pp({ :inner_app => inner_app })
65
63
  end
66
64
 
67
- return inner_app if no_default_middleware
65
+ return inner_app unless server.default_middleware
68
66
 
69
67
  middleware = { # order matters
70
68
  ContentLength: nil,
@@ -98,7 +96,7 @@ module Unicorn
98
96
 
99
97
  # returns an array of strings representing TCP listen socket addresses
100
98
  # and Unix domain socket paths. This is useful for use with
101
- # Raindrops::Middleware under Linux: https://bogomips.org/raindrops/
99
+ # Raindrops::Middleware under Linux: https://yhbt.net/raindrops/
102
100
  def self.listener_names
103
101
  Unicorn::HttpServer::LISTENERS.map do |io|
104
102
  Unicorn::SocketHelper.sock_name(io)
@@ -112,9 +110,25 @@ module Unicorn
112
110
  exc.backtrace.each { |line| logger.error(line) }
113
111
  end
114
112
 
115
- # remove this when we only support Ruby >= 2.0
113
+ F_SETPIPE_SZ = 1031 if RUBY_PLATFORM =~ /linux/
114
+
116
115
  def self.pipe # :nodoc:
117
- Kgio::Pipe.new.each { |io| io.close_on_exec = true }
116
+ Kgio::Pipe.new.each do |io|
117
+ io.close_on_exec = true # remove this when we only support Ruby >= 2.0
118
+
119
+ # shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
120
+ # limits.
121
+ if defined?(F_SETPIPE_SZ)
122
+ begin
123
+ io.fcntl(F_SETPIPE_SZ, Raindrops::PAGE_SIZE)
124
+ rescue Errno::EINVAL
125
+ # old kernel
126
+ rescue Errno::EPERM
127
+ # resizes fail if Linux is close to the pipe limit for the user
128
+ # or if the user does not have permissions to resize
129
+ end
130
+ end
131
+ end
118
132
  end
119
133
  # :startdoc:
120
134
  end