unicorn 5.5.0 → 6.1.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.manifest +8 -5
  3. data/.olddoc.yml +15 -7
  4. data/CONTRIBUTORS +6 -2
  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 +112 -57
  12. data/HACKING +2 -9
  13. data/ISSUES +24 -31
  14. data/KNOWN_ISSUES +2 -2
  15. data/LATEST +16 -22
  16. data/Links +5 -5
  17. data/NEWS +155 -0
  18. data/README +15 -9
  19. data/SIGNALS +1 -1
  20. data/Sandbox +3 -3
  21. data/archive/slrnpull.conf +1 -1
  22. data/bin/unicorn_rails +2 -2
  23. data/examples/big_app_gc.rb +1 -1
  24. data/examples/logrotate.conf +2 -2
  25. data/examples/nginx.conf +1 -1
  26. data/examples/unicorn.conf.minimal.rb +2 -2
  27. data/examples/unicorn.conf.rb +2 -2
  28. data/examples/unicorn@.service +7 -0
  29. data/ext/unicorn_http/c_util.h +5 -13
  30. data/ext/unicorn_http/common_field_optimization.h +0 -1
  31. data/ext/unicorn_http/epollexclusive.h +124 -0
  32. data/ext/unicorn_http/ext_help.h +0 -24
  33. data/ext/unicorn_http/extconf.rb +2 -6
  34. data/ext/unicorn_http/global_variables.h +1 -1
  35. data/ext/unicorn_http/httpdate.c +1 -0
  36. data/ext/unicorn_http/unicorn_http.c +258 -228
  37. data/ext/unicorn_http/unicorn_http.rl +48 -18
  38. data/lib/unicorn/configurator.rb +13 -3
  39. data/lib/unicorn/http_request.rb +11 -1
  40. data/lib/unicorn/http_server.rb +56 -29
  41. data/lib/unicorn/oob_gc.rb +5 -5
  42. data/lib/unicorn/select_waiter.rb +6 -0
  43. data/lib/unicorn/tmpio.rb +8 -2
  44. data/lib/unicorn/version.rb +1 -1
  45. data/lib/unicorn.rb +4 -3
  46. data/man/man1/unicorn.1 +89 -88
  47. data/man/man1/unicorn_rails.1 +78 -83
  48. data/t/GNUmakefile +3 -72
  49. data/t/README +1 -1
  50. data/t/test-lib.sh +2 -1
  51. data/test/benchmark/README +14 -4
  52. data/test/benchmark/ddstream.ru +50 -0
  53. data/test/benchmark/readinput.ru +40 -0
  54. data/test/benchmark/uconnect.perl +66 -0
  55. data/test/exec/test_exec.rb +14 -12
  56. data/test/test_helper.rb +38 -30
  57. data/test/unit/test_ccc.rb +4 -3
  58. data/test/unit/test_http_parser_ng.rb +81 -0
  59. data/test/unit/test_server.rb +81 -7
  60. data/test/unit/test_signals.rb +6 -6
  61. data/test/unit/test_socket_helper.rb +1 -1
  62. data/test/unit/test_upload.rb +9 -14
  63. data/test/unit/test_util.rb +5 -4
  64. data/test/unit/test_waiter.rb +34 -0
  65. data/unicorn.gemspec +8 -7
  66. metadata +16 -11
  67. data/Documentation/GNUmakefile +0 -30
  68. data/Documentation/unicorn.1.txt +0 -187
  69. data/Documentation/unicorn_rails.1.txt +0 -175
  70. data/t/hijack.ru +0 -55
  71. data/t/t0200-rack-hijack.sh +0 -51
@@ -12,6 +12,7 @@
12
12
  #include "common_field_optimization.h"
13
13
  #include "global_variables.h"
14
14
  #include "c_util.h"
15
+ #include "epollexclusive.h"
15
16
 
16
17
  void init_unicorn_httpdate(void);
17
18
 
@@ -62,19 +63,8 @@ struct http_parser {
62
63
  } len;
63
64
  };
64
65
 
65
- static ID id_set_backtrace;
66
-
67
- #ifdef HAVE_RB_HASH_CLEAR /* Ruby >= 2.0 */
68
- # define my_hash_clear(h) (void)rb_hash_clear(h)
69
- #else /* !HAVE_RB_HASH_CLEAR - Ruby <= 1.9.3 */
70
-
71
- static ID id_clear;
72
-
73
- static void my_hash_clear(VALUE h)
74
- {
75
- rb_funcall(h, id_clear, 0);
76
- }
77
- #endif /* HAVE_RB_HASH_CLEAR */
66
+ static ID id_set_backtrace, id_is_chunked_p;
67
+ static VALUE cHttpParser;
78
68
 
79
69
  static void finalize_header(struct http_parser *hp);
80
70
 
@@ -220,6 +210,19 @@ static void write_cont_value(struct http_parser *hp,
220
210
  rb_str_buf_cat(hp->cont, vptr, end + 1);
221
211
  }
222
212
 
213
+ static int is_chunked(VALUE v)
214
+ {
215
+ /* common case first */
216
+ if (STR_CSTR_CASE_EQ(v, "chunked"))
217
+ return 1;
218
+
219
+ /*
220
+ * call Ruby function in unicorn/http_request.rb to deal with unlikely
221
+ * comma-delimited case
222
+ */
223
+ return rb_funcall(cHttpParser, id_is_chunked_p, 1, v) != Qfalse;
224
+ }
225
+
223
226
  static void write_value(struct http_parser *hp,
224
227
  const char *buffer, const char *p)
225
228
  {
@@ -246,7 +249,9 @@ static void write_value(struct http_parser *hp,
246
249
  f = uncommon_field(field, flen);
247
250
  } else if (f == g_http_connection) {
248
251
  hp_keepalive_connection(hp, v);
249
- } else if (f == g_content_length) {
252
+ } else if (f == g_content_length && !HP_FL_TEST(hp, CHUNKED)) {
253
+ if (hp->len.content)
254
+ parser_raise(eHttpParserError, "Content-Length already set");
250
255
  hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
251
256
  if (hp->len.content < 0)
252
257
  parser_raise(eHttpParserError, "invalid Content-Length");
@@ -254,9 +259,30 @@ static void write_value(struct http_parser *hp,
254
259
  HP_FL_SET(hp, HASBODY);
255
260
  hp_invalid_if_trailer(hp);
256
261
  } else if (f == g_http_transfer_encoding) {
257
- if (STR_CSTR_CASE_EQ(v, "chunked")) {
262
+ if (is_chunked(v)) {
263
+ if (HP_FL_TEST(hp, CHUNKED))
264
+ /*
265
+ * RFC 7230 3.3.1:
266
+ * A sender MUST NOT apply chunked more than once to a message body
267
+ * (i.e., chunking an already chunked message is not allowed).
268
+ */
269
+ parser_raise(eHttpParserError, "Transfer-Encoding double chunked");
270
+
258
271
  HP_FL_SET(hp, CHUNKED);
259
272
  HP_FL_SET(hp, HASBODY);
273
+
274
+ /* RFC 7230 3.3.3, 3: favor chunked if Content-Length exists */
275
+ hp->len.content = 0;
276
+ } else if (HP_FL_TEST(hp, CHUNKED)) {
277
+ /*
278
+ * RFC 7230 3.3.3, point 3 states:
279
+ * If a Transfer-Encoding header field is present in a request and
280
+ * the chunked transfer coding is not the final encoding, the
281
+ * message body length cannot be determined reliably; the server
282
+ * MUST respond with the 400 (Bad Request) status code and then
283
+ * close the connection.
284
+ */
285
+ parser_raise(eHttpParserError, "invalid Transfer-Encoding");
260
286
  }
261
287
  hp_invalid_if_trailer(hp);
262
288
  } else if (f == g_http_trailer) {
@@ -487,7 +513,7 @@ static void set_url_scheme(VALUE env, VALUE *server_port)
487
513
  * and X-Forwarded-Proto handling from this parser? We've had it
488
514
  * forever and nobody has said anything against it, either.
489
515
  * Anyways, please send comments to our public mailing list:
490
- * unicorn-public@bogomips.org (no HTML mail, no subscription necessary)
516
+ * unicorn-public@yhbt.net (no HTML mail, no subscription necessary)
491
517
  */
492
518
  scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
493
519
  if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
@@ -613,7 +639,7 @@ static VALUE HttpParser_clear(VALUE self)
613
639
  return HttpParser_init(self);
614
640
 
615
641
  http_parser_init(hp);
616
- my_hash_clear(hp->env);
642
+ rb_hash_clear(hp->env);
617
643
 
618
644
  return self;
619
645
  }
@@ -931,7 +957,7 @@ static VALUE HttpParser_rssget(VALUE self)
931
957
 
932
958
  void Init_unicorn_http(void)
933
959
  {
934
- VALUE mUnicorn, cHttpParser;
960
+ VALUE mUnicorn;
935
961
 
936
962
  mUnicorn = rb_define_module("Unicorn");
937
963
  cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
@@ -942,6 +968,7 @@ void Init_unicorn_http(void)
942
968
  e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
943
969
  eHttpParserError);
944
970
 
971
+ id_uminus = rb_intern("-@");
945
972
  init_globals();
946
973
  rb_define_alloc_func(cHttpParser, HttpParser_alloc);
947
974
  rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
@@ -991,5 +1018,8 @@ void Init_unicorn_http(void)
991
1018
  #ifndef HAVE_RB_HASH_CLEAR
992
1019
  id_clear = rb_intern("clear");
993
1020
  #endif
1021
+ id_is_chunked_p = rb_intern("is_chunked?");
1022
+
1023
+ init_epollexclusive(mUnicorn);
994
1024
  }
995
1025
  #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,
@@ -276,6 +277,15 @@ class Unicorn::Configurator
276
277
  set_bool(:default_middleware, bool)
277
278
  end
278
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
+
279
289
  # sets listeners to the given +addresses+, replacing or augmenting the
280
290
  # current set. This is for the global listener pool shared by all
281
291
  # worker processes. For per-worker listeners, see the after_fork example
@@ -62,7 +62,6 @@ class Unicorn::HttpParser
62
62
  # This does minimal exception trapping and it is up to the caller
63
63
  # to handle any socket errors (e.g. user aborted upload).
64
64
  def read(socket)
65
- clear
66
65
  e = env
67
66
 
68
67
  # From https://www.ietf.org/rfc/rfc3875:
@@ -188,4 +187,15 @@ class Unicorn::HttpParser
188
187
  HTTP_RESPONSE_START.each { |c| socket.write(c) }
189
188
  end
190
189
  end
190
+
191
+ # called by ext/unicorn_http/unicorn_http.rl via rb_funcall
192
+ def self.is_chunked?(v) # :nodoc:
193
+ vals = v.split(/[ \t]*,[ \t]*/).map!(&:downcase)
194
+ if vals.pop == 'chunked'.freeze
195
+ return true unless vals.include?('chunked'.freeze)
196
+ raise Unicorn::HttpParserError, 'double chunked', []
197
+ end
198
+ return false unless vals.include?('chunked'.freeze)
199
+ raise Unicorn::HttpParserError, 'chunked not last', []
200
+ end
191
201
  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
@@ -15,7 +15,7 @@ class Unicorn::HttpServer
15
15
  :before_fork, :after_fork, :before_exec,
16
16
  :listener_opts, :preload_app,
17
17
  :orig_app, :config, :ready_pipe, :user,
18
- :default_middleware
18
+ :default_middleware, :early_hints
19
19
  attr_writer :after_worker_exit, :after_worker_ready, :worker_exec
20
20
 
21
21
  attr_reader :pid, :logger
@@ -69,7 +69,6 @@ class Unicorn::HttpServer
69
69
  # incoming requests on the socket.
70
70
  def initialize(app, options = {})
71
71
  @app = app
72
- @request = Unicorn::HttpRequest.new
73
72
  @reexec_pid = 0
74
73
  @default_middleware = true
75
74
  options = options.dup
@@ -588,10 +587,29 @@ class Unicorn::HttpServer
588
587
  rescue
589
588
  end
590
589
 
590
+ def e103_response_write(client, headers)
591
+ response = if @request.response_start_sent
592
+ "103 Early Hints\r\n"
593
+ else
594
+ "HTTP/1.1 103 Early Hints\r\n"
595
+ end
596
+
597
+ headers.each_pair do |k, vs|
598
+ next if !vs || vs.empty?
599
+ values = vs.to_s.split("\n".freeze)
600
+ values.each do |v|
601
+ response << "#{k}: #{v}\r\n"
602
+ end
603
+ end
604
+ response << "\r\n".freeze
605
+ response << "HTTP/1.1 ".freeze if @request.response_start_sent
606
+ client.write(response)
607
+ end
608
+
591
609
  def e100_response_write(client, env)
592
610
  # We use String#freeze to avoid allocations under Ruby 2.1+
593
611
  # Not many users hit this code path, so it's better to reduce the
594
- # constant table sizes even for 1.9.3-2.0 users who'll hit extra
612
+ # constant table sizes even for Ruby 2.0 users who'll hit extra
595
613
  # allocations here.
596
614
  client.write(@request.response_start_sent ?
597
615
  "100 Continue\r\n\r\nHTTP/1.1 ".freeze :
@@ -602,7 +620,18 @@ class Unicorn::HttpServer
602
620
  # once a client is accepted, it is processed in its entirety here
603
621
  # in 3 easy steps: read request, call app, write app response
604
622
  def process_client(client)
605
- status, headers, body = @app.call(env = @request.read(client))
623
+ @request = Unicorn::HttpRequest.new
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
+ env["rack.after_reply"] = []
633
+
634
+ status, headers, body = @app.call(env)
606
635
 
607
636
  begin
608
637
  return if @request.hijacked?
@@ -624,6 +653,8 @@ class Unicorn::HttpServer
624
653
  end
625
654
  rescue => e
626
655
  handle_error(client, e)
656
+ ensure
657
+ env["rack.after_reply"].each(&:call) if env
627
658
  end
628
659
 
629
660
  def nuke_listeners!(readers)
@@ -654,7 +685,6 @@ class Unicorn::HttpServer
654
685
  LISTENERS.each { |sock| sock.close_on_exec = true }
655
686
 
656
687
  worker.user(*user) if user.kind_of?(Array) && ! worker.switched
657
- self.timeout /= 2.0 # halve it for select()
658
688
  @config = nil
659
689
  build_app! unless preload_app
660
690
  @after_fork = @listener_opts = @orig_app = nil
@@ -668,58 +698,55 @@ class Unicorn::HttpServer
668
698
  logger.info "worker=#{worker_nr} reopening logs..."
669
699
  Unicorn::Util.reopen_logs
670
700
  logger.info "worker=#{worker_nr} done reopening logs"
701
+ false
671
702
  rescue => e
672
703
  logger.error(e) rescue nil
673
704
  exit!(77) # EX_NOPERM in sysexits.h
674
705
  end
675
706
 
707
+ def prep_readers(readers)
708
+ wtr = Unicorn::Waiter.prep_readers(readers)
709
+ @timeout *= 500 # to milliseconds for epoll, but halved
710
+ wtr
711
+ rescue
712
+ require_relative 'select_waiter'
713
+ @timeout /= 2.0 # halved for IO.select
714
+ Unicorn::SelectWaiter.new
715
+ end
716
+
676
717
  # runs inside each forked worker, this sits around and waits
677
718
  # for connections and doesn't die until the parent dies (or is
678
719
  # given a INT, QUIT, or TERM signal)
679
720
  def worker_loop(worker)
680
- ppid = @master_pid
681
721
  readers = init_worker_process(worker)
682
- nr = 0 # this becomes negative if we need to reopen logs
722
+ waiter = prep_readers(readers)
723
+ reopen = false
683
724
 
684
725
  # this only works immediately if the master sent us the signal
685
726
  # (which is the normal case)
686
- trap(:USR1) { nr = -65536 }
727
+ trap(:USR1) { reopen = true }
687
728
 
688
729
  ready = readers.dup
689
730
  @after_worker_ready.call(self, worker)
690
731
 
691
732
  begin
692
- nr < 0 and reopen_worker_logs(worker.nr)
693
- nr = 0
733
+ reopen = reopen_worker_logs(worker.nr) if reopen
694
734
  worker.tick = time_now.to_i
695
- tmp = ready.dup
696
- while sock = tmp.shift
735
+ while sock = ready.shift
697
736
  # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
698
737
  # but that will return false
699
738
  if client = sock.kgio_tryaccept
700
739
  process_client(client)
701
- nr += 1
702
740
  worker.tick = time_now.to_i
703
741
  end
704
- break if nr < 0
742
+ break if reopen
705
743
  end
706
744
 
707
- # make the following bet: if we accepted clients this round,
708
- # we're probably reasonably busy, so avoid calling select()
709
- # and do a speculative non-blocking accept() on ready listeners
710
- # before we sleep again in select().
711
- unless nr == 0
712
- tmp = ready.dup
713
- redo
714
- end
715
-
716
- ppid == Process.ppid or return
717
-
718
- # timeout used so we can detect parent death:
745
+ # timeout so we can .tick and keep parent from SIGKILL-ing us
719
746
  worker.tick = time_now.to_i
720
- ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
747
+ waiter.get_readers(ready, readers, @timeout)
721
748
  rescue => e
722
- redo if nr < 0 && readers[0]
749
+ redo if reopen && readers[0]
723
750
  Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
724
751
  end while readers[0]
725
752
  end
@@ -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
 
@@ -60,7 +60,6 @@ module Unicorn::OobGC
60
60
  self.const_set :OOBGC_INTERVAL, interval
61
61
  ObjectSpace.each_object(Unicorn::HttpServer) do |s|
62
62
  s.extend(self)
63
- self.const_set :OOBGC_ENV, s.instance_variable_get(:@request).env
64
63
  end
65
64
  app # pretend to be Rack middleware since it was in the past
66
65
  end
@@ -68,9 +67,10 @@ module Unicorn::OobGC
68
67
  #:stopdoc:
69
68
  def process_client(client)
70
69
  super(client) # Unicorn::HttpServer#process_client
71
- if OOBGC_PATH =~ OOBGC_ENV['PATH_INFO'] && ((@@nr -= 1) <= 0)
70
+ env = instance_variable_get(:@request).env
71
+ if OOBGC_PATH =~ env['PATH_INFO'] && ((@@nr -= 1) <= 0)
72
72
  @@nr = OOBGC_INTERVAL
73
- OOBGC_ENV.clear
73
+ env.clear
74
74
  disabled = GC.enable
75
75
  GC.start
76
76
  GC.disable if disabled
@@ -0,0 +1,6 @@
1
+ # fallback for non-Linux and Linux <4.5 systems w/o EPOLLEXCLUSIVE
2
+ class Unicorn::SelectWaiter # :nodoc:
3
+ def get_readers(ready, readers, timeout) # :nodoc:
4
+ ret = IO.select(readers, nil, nil, timeout) and ready.replace(ret[0])
5
+ end
6
+ 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
@@ -1 +1 @@
1
- Unicorn::Const::UNICORN_VERSION = '5.5.0'
1
+ Unicorn::Const::UNICORN_VERSION = '6.1.0'
data/lib/unicorn.rb CHANGED
@@ -96,7 +96,7 @@ module Unicorn
96
96
 
97
97
  # returns an array of strings representing TCP listen socket addresses
98
98
  # and Unix domain socket paths. This is useful for use with
99
- # Raindrops::Middleware under Linux: https://bogomips.org/raindrops/
99
+ # Raindrops::Middleware under Linux: https://yhbt.net/raindrops/
100
100
  def self.listener_names
101
101
  Unicorn::HttpServer::LISTENERS.map do |io|
102
102
  Unicorn::SocketHelper.sock_name(io)
@@ -114,8 +114,6 @@ module Unicorn
114
114
 
115
115
  def self.pipe # :nodoc:
116
116
  Kgio::Pipe.new.each do |io|
117
- io.close_on_exec = true # remove this when we only support Ruby >= 2.0
118
-
119
117
  # shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
120
118
  # limits.
121
119
  if defined?(F_SETPIPE_SZ)
@@ -123,6 +121,9 @@ module Unicorn
123
121
  io.fcntl(F_SETPIPE_SZ, Raindrops::PAGE_SIZE)
124
122
  rescue Errno::EINVAL
125
123
  # old kernel
124
+ rescue Errno::EPERM
125
+ # resizes fail if Linux is close to the pipe limit for the user
126
+ # or if the user does not have permissions to resize
126
127
  end
127
128
  end
128
129
  end