unicorn 5.5.2 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.manifest +3 -2
  3. data/.olddoc.yml +15 -7
  4. data/CONTRIBUTORS +6 -2
  5. data/Documentation/unicorn.1 +4 -4
  6. data/Documentation/unicorn_rails.1 +4 -4
  7. data/FAQ +1 -1
  8. data/GIT-VERSION-FILE +1 -1
  9. data/GIT-VERSION-GEN +1 -1
  10. data/GNUmakefile +105 -60
  11. data/HACKING +2 -9
  12. data/ISSUES +24 -23
  13. data/KNOWN_ISSUES +2 -2
  14. data/LATEST +23 -22
  15. data/Links +5 -5
  16. data/NEWS +132 -0
  17. data/README +15 -9
  18. data/SIGNALS +1 -1
  19. data/Sandbox +3 -3
  20. data/archive/slrnpull.conf +1 -1
  21. data/examples/big_app_gc.rb +1 -1
  22. data/examples/logrotate.conf +2 -2
  23. data/examples/nginx.conf +1 -1
  24. data/examples/unicorn.conf.minimal.rb +2 -2
  25. data/examples/unicorn.conf.rb +2 -2
  26. data/ext/unicorn_http/c_util.h +5 -13
  27. data/ext/unicorn_http/common_field_optimization.h +0 -1
  28. data/ext/unicorn_http/epollexclusive.h +124 -0
  29. data/ext/unicorn_http/ext_help.h +0 -24
  30. data/ext/unicorn_http/extconf.rb +2 -6
  31. data/ext/unicorn_http/global_variables.h +1 -1
  32. data/ext/unicorn_http/httpdate.c +1 -0
  33. data/ext/unicorn_http/unicorn_http.c +258 -228
  34. data/ext/unicorn_http/unicorn_http.rl +48 -18
  35. data/lib/unicorn/configurator.rb +13 -3
  36. data/lib/unicorn/http_request.rb +11 -1
  37. data/lib/unicorn/http_server.rb +56 -29
  38. data/lib/unicorn/oob_gc.rb +5 -5
  39. data/lib/unicorn/select_waiter.rb +6 -0
  40. data/lib/unicorn/version.rb +1 -1
  41. data/lib/unicorn.rb +1 -3
  42. data/man/man1/unicorn.1 +4 -4
  43. data/man/man1/unicorn_rails.1 +4 -4
  44. data/t/GNUmakefile +3 -72
  45. data/t/README +1 -1
  46. data/t/test-lib.sh +2 -1
  47. data/test/exec/test_exec.rb +14 -12
  48. data/test/test_helper.rb +38 -30
  49. data/test/unit/test_ccc.rb +4 -3
  50. data/test/unit/test_http_parser_ng.rb +81 -0
  51. data/test/unit/test_server.rb +81 -7
  52. data/test/unit/test_signals.rb +6 -6
  53. data/test/unit/test_socket_helper.rb +1 -1
  54. data/test/unit/test_upload.rb +9 -14
  55. data/test/unit/test_util.rb +4 -3
  56. data/test/unit/test_waiter.rb +34 -0
  57. data/unicorn.gemspec +8 -7
  58. metadata +11 -8
  59. data/t/hijack.ru +0 -55
  60. 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
@@ -1 +1 @@
1
- Unicorn::Const::UNICORN_VERSION = '5.5.2'
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)
data/man/man1/unicorn.1 CHANGED
@@ -154,7 +154,7 @@ TTIN \- increment the number of worker processes by one
154
154
  .IP \[bu] 2
155
155
  TTOU \- decrement the number of worker processes by one
156
156
  .PP
157
- See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
157
+ See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
158
158
  full description of all signals used by Unicorn.
159
159
  .SH RACK ENVIRONMENT
160
160
  .PP
@@ -204,11 +204,11 @@ the unicorn config file.
204
204
  \f[I]Rack::Builder\f[] ri/RDoc
205
205
  .IP \[bu] 2
206
206
  \f[I]Unicorn::Configurator\f[] ri/RDoc
207
- .UR https://bogomips.org/unicorn/Unicorn/Configurator.html
207
+ .UR https://yhbt.net/unicorn/Unicorn/Configurator.html
208
208
  .UE
209
209
  .IP \[bu] 2
210
210
  unicorn RDoc
211
- .UR https://bogomips.org/unicorn/
211
+ .UR https://yhbt.net/unicorn/
212
212
  .UE
213
213
  .IP \[bu] 2
214
214
  Rack RDoc
@@ -219,4 +219,4 @@ Rackup HowTo
219
219
  .UR https://github.com/rack/rack/wiki/(tutorial)-rackup-howto
220
220
  .UE
221
221
  .SH AUTHORS
222
- The Unicorn Community <unicorn-public@bogomips.org>.
222
+ The Unicorn Community <unicorn-public@yhbt.net>.
@@ -180,7 +180,7 @@ TTIN \- increment the number of worker processes by one
180
180
  .IP \[bu] 2
181
181
  TTOU \- decrement the number of worker processes by one
182
182
  .PP
183
- See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
183
+ See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
184
184
  full description of all signals used by Unicorn.
185
185
  .SH SEE ALSO
186
186
  .IP \[bu] 2
@@ -189,11 +189,11 @@ unicorn(1)
189
189
  \f[I]Rack::Builder\f[] ri/RDoc
190
190
  .IP \[bu] 2
191
191
  \f[I]Unicorn::Configurator\f[] ri/RDoc
192
- .UR https://bogomips.org/unicorn/Unicorn/Configurator.html
192
+ .UR https://yhbt.net/unicorn/Unicorn/Configurator.html
193
193
  .UE
194
194
  .IP \[bu] 2
195
195
  unicorn RDoc
196
- .UR https://bogomips.org/unicorn/
196
+ .UR https://yhbt.net/unicorn/
197
197
  .UE
198
198
  .IP \[bu] 2
199
199
  Rack RDoc
@@ -204,4 +204,4 @@ Rackup HowTo
204
204
  .UR https://github.com/rack/rack/wiki/(tutorial)-rackup-howto
205
205
  .UE
206
206
  .SH AUTHORS
207
- The Unicorn Community <unicorn-public@bogomips.org>.
207
+ The Unicorn Community <unicorn-public@yhbt.net>.
data/t/GNUmakefile CHANGED
@@ -1,74 +1,5 @@
1
- # we can run tests in parallel with GNU make
1
+ # there used to be more, here, but we stopped relying on recursive make
2
2
  all::
3
+ $(MAKE) -C .. test-integration
3
4
 
4
- pid := $(shell echo $$PPID)
5
-
6
- RUBY = ruby
7
- RAKE = rake
8
- -include ../local.mk
9
- ifeq ($(RUBY_VERSION),)
10
- RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
11
- endif
12
-
13
- ifeq ($(RUBY_VERSION),)
14
- $(error unable to detect RUBY_VERSION)
15
- endif
16
-
17
- RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
18
- export RUBY_ENGINE
19
-
20
- MYLIBS := $(RUBYLIB)
21
-
22
- T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
23
-
24
- all:: $(T)
25
-
26
- # can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
27
- t_pfx = trash/$@-$(RUBY_ENGINE)-$(RUBY_VERSION)
28
- TEST_OPTS =
29
- # TRACER = strace -f -o $(t_pfx).strace -s 100000
30
- # TRACER = /usr/bin/time -o $(t_pfx).time
31
-
32
- ifdef V
33
- ifeq ($(V),2)
34
- TEST_OPTS += --trace
35
- else
36
- TEST_OPTS += --verbose
37
- endif
38
- endif
39
-
40
- random_blob:
41
- dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
42
- mv $@.$(pid) $@
43
-
44
- $(T): random_blob
45
-
46
- dependencies := socat curl
47
- deps := $(addprefix .dep+,$(dependencies))
48
- $(deps): dep_bin = $(lastword $(subst +, ,$@))
49
- $(deps):
50
- @which $(dep_bin) > $@.$(pid) 2>/dev/null || :
51
- @test -s $@.$(pid) || \
52
- { echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
53
- @mv $@.$(pid) $@
54
- dep: $(deps)
55
-
56
- test_prefix := $(CURDIR)/../test/$(RUBY_ENGINE)-$(RUBY_VERSION)
57
- $(test_prefix)/.stamp:
58
- $(MAKE) -C .. test-install
59
-
60
- $(T): export RUBY := $(RUBY)
61
- $(T): export RAKE := $(RAKE)
62
- $(T): export PATH := $(test_prefix)/bin:$(PATH)
63
- $(T): export RUBYLIB := $(test_prefix)/lib:$(MYLIBS)
64
- $(T): dep $(test_prefix)/.stamp trash/.gitignore
65
- $(TRACER) $(SHELL) $(SH_TEST_OPTS) $@ $(TEST_OPTS)
66
-
67
- trash/.gitignore:
68
- mkdir -p $(@D)
69
- echo '*' > $@
70
-
71
- clean:
72
- $(RM) -r trash/*
73
-
74
- .PHONY: $(T) clean
5
+ .PHONY: all
data/t/README CHANGED
@@ -10,7 +10,7 @@ comfortable writing integration tests with.
10
10
 
11
11
  == Requirements
12
12
 
13
- * {Ruby 1.9.3+}[https://www.ruby-lang.org/en/] (duh!)
13
+ * {Ruby 2.0.0+}[https://www.ruby-lang.org/en/] (duh!)
14
14
  * {GNU make}[https://www.gnu.org/software/make/]
15
15
  * {socat}[http://www.dest-unreach.org/socat/]
16
16
  * {curl}[https://curl.haxx.se/]
data/t/test-lib.sh CHANGED
@@ -94,7 +94,8 @@ check_stderr () {
94
94
  set +u
95
95
  _r_err=${1-${r_err}}
96
96
  set -u
97
- if grep -v $T $_r_err | grep -i Error
97
+ if grep -v $T $_r_err | grep -i Error | \
98
+ grep -v NameError.*Unicorn::Waiter
98
99
  then
99
100
  die "Errors found in $_r_err"
100
101
  elif grep SIGKILL $_r_err