unicorn 5.5.4 → 6.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67fce6dee5ff186d34900c6d95f974e225f51811102623e1f343cfbff700dde2
4
- data.tar.gz: ef054e9c76e847ec2d3d9300ea95352da1df2e1e78f5525c02dac920d0d04ddf
3
+ metadata.gz: f3857fb0079316977174fc2e513749fd8b76297e719caf6e172097099eb57752
4
+ data.tar.gz: 68edc58cd61e17a3f409b56d963ed2f8b458f9d457772c7cfa9df933b76af6a4
5
5
  SHA512:
6
- metadata.gz: cd4c2ad09a0f315cb43a8ba8c229df23b52c4356d76a69818e2a56503022654a063070a69306a7853b6bedecc8552cc881e1e2c7e916ecc3de80c11125a973f1
7
- data.tar.gz: c83258e4ed3534bae563c7b9dab30e1f3001982bff24a2d6e2d6c3925e01d0aa6ccfe3538ae9895a1bf49e1f9cba977b3e2a60533571605694315be35eec8d6f
6
+ metadata.gz: 01cae9ce7852f28d4484ffbf458ca9a40b3b19b4531c2b633287751fabc5547076eddd7e773ab2bad6b2812e95b4a717a6ae598fa183061844948f0cd417dcfc
7
+ data.tar.gz: 850460b0d5caa60e23b2a386a5d2dcaf8321722c81dcb9aa65d55c46a0b81978fd305eda915478e566efa29dd8e605bc73ca3a3ae214af6e91083b64df5468e1
data/GIT-VERSION-GEN CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- DEF_VER = "v5.5.4"
2
+ DEF_VER = "v6.0.0"
3
3
  CONSTANT = "Unicorn::Const::UNICORN_VERSION"
4
4
  RVF = "lib/unicorn/version.rb"
5
5
  GVF = "GIT-VERSION-FILE"
data/GNUmakefile CHANGED
@@ -10,6 +10,7 @@ RAGEL = ragel
10
10
  RSYNC = rsync
11
11
  OLDDOC = olddoc
12
12
  RDOC = rdoc
13
+ INSTALL = install
13
14
 
14
15
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
15
16
  @./GIT-VERSION-GEN
@@ -25,7 +26,38 @@ endif
25
26
 
26
27
  RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
27
28
 
28
- MYLIBS = $(RUBYLIB)
29
+ # we should never package more than one ext to avoid DSO proliferation:
30
+ # https://udrepper.livejournal.com/8790.html
31
+ ext := $(firstword $(wildcard ext/*))
32
+
33
+ ragel: $(ext)/unicorn_http.c
34
+
35
+ rl_files := $(wildcard $(ext)/*.rl)
36
+ ragel: $(ext)/unicorn_http.c
37
+ $(ext)/unicorn_http.c: $(rl_files)
38
+ cd $(@D) && $(RAGEL) unicorn_http.rl -C $(RLFLAGS) -o $(@F)
39
+ ext_pfx := test/$(RUBY_ENGINE)-$(RUBY_VERSION)
40
+ tmp_bin := $(ext_pfx)/bin
41
+ ext_h := $(wildcard $(ext)/*/*.h $(ext)/*.h)
42
+ ext_src := $(sort $(wildcard $(ext)/*.c) $(ext_h) $(ext)/unicorn_http.c)
43
+ ext_pfx_src := $(addprefix $(ext_pfx)/,$(ext_src))
44
+ ext_dir := $(ext_pfx)/$(ext)
45
+ $(ext)/extconf.rb: $(wildcard $(ext)/*.h)
46
+ @>>$@
47
+ $(ext_dir) $(tmp_bin) man/man1 doc/man1 pkg t/trash:
48
+ @mkdir -p $@
49
+ $(ext_pfx)/$(ext)/%: $(ext)/% | $(ext_dir)
50
+ $(INSTALL) -m 644 $< $@
51
+ $(ext_pfx)/$(ext)/Makefile: $(ext)/extconf.rb $(ext_h) | $(ext_dir)
52
+ $(RM) -f $(@D)/*.o
53
+ cd $(@D) && $(RUBY) $(CURDIR)/$(ext)/extconf.rb $(EXTCONF_ARGS)
54
+ ext_sfx := _ext.$(DLEXT)
55
+ ext_dl := $(ext_pfx)/$(ext)/$(notdir $(ext)_ext.$(DLEXT))
56
+ $(ext_dl): $(ext_src) $(ext_pfx_src) $(ext_pfx)/$(ext)/Makefile
57
+ $(MAKE) -C $(@D)
58
+ lib := $(CURDIR)/lib:$(CURDIR)/$(ext_pfx)/$(ext)
59
+ http build: $(ext_dl)
60
+ $(ext_pfx)/$(ext)/unicorn_http.c: ext/unicorn_http/unicorn_http.c
29
61
 
30
62
  # dunno how to implement this as concisely in Ruby, and hell, I love awk
31
63
  awk_slow := awk '/def test_/{print FILENAME"--"$$2".n"}' 2>/dev/null
@@ -37,44 +69,21 @@ T := $(filter-out $(slow_tests), $(wildcard test/*/test*.rb))
37
69
  T_n := $(shell $(awk_slow) $(slow_tests))
38
70
  T_log := $(subst .rb,$(log_suffix),$(T))
39
71
  T_n_log := $(subst .n,$(log_suffix),$(T_n))
40
- test_prefix = $(CURDIR)/test/$(RUBY_ENGINE)-$(RUBY_VERSION)
41
72
 
42
- ext := ext/unicorn_http
43
- c_files := $(ext)/unicorn_http.c $(ext)/httpdate.c $(wildcard $(ext)/*.h)
44
- rl_files := $(wildcard $(ext)/*.rl)
45
73
  base_bins := unicorn unicorn_rails
46
74
  bins := $(addprefix bin/, $(base_bins))
47
75
  man1_rdoc := $(addsuffix _1, $(base_bins))
48
76
  man1_bins := $(addsuffix .1, $(base_bins))
49
77
  man1_paths := $(addprefix man/man1/, $(man1_bins))
50
- rb_files := $(bins) $(shell find lib ext -type f -name '*.rb')
51
- inst_deps := $(c_files) $(rb_files) GNUmakefile test/test_helper.rb
52
-
53
- ragel: $(ext)/unicorn_http.c
54
- $(ext)/unicorn_http.c: $(rl_files)
55
- cd $(@D) && $(RAGEL) unicorn_http.rl -C $(RLFLAGS) -o $(@F)
56
- $(ext)/Makefile: $(ext)/extconf.rb $(c_files)
57
- cd $(@D) && $(RUBY) extconf.rb
58
- $(ext)/unicorn_http.$(DLEXT): $(ext)/Makefile
59
- $(MAKE) -C $(@D)
60
- http: $(ext)/unicorn_http.$(DLEXT)
78
+ tmp_bins = $(addprefix $(tmp_bin)/, unicorn unicorn_rails)
79
+ pid := $(shell echo $$PPID)
61
80
 
62
- # only used for tests
63
- http-install: $(ext)/unicorn_http.$(DLEXT)
64
- install -m644 $< lib/
81
+ $(tmp_bin)/%: bin/% | $(tmp_bin)
82
+ $(INSTALL) -m 755 $< $@.$(pid)
83
+ $(MRI) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@.$(pid)
84
+ mv $@.$(pid) $@
65
85
 
66
- test-install: $(test_prefix)/.stamp
67
- $(test_prefix)/.stamp: $(inst_deps)
68
- mkdir -p $(test_prefix)/.ccache
69
- tar cf - $(inst_deps) GIT-VERSION-GEN | \
70
- (cd $(test_prefix) && tar xf -)
71
- $(MAKE) -C $(test_prefix) clean
72
- $(MAKE) -C $(test_prefix) http-install shebang RUBY="$(RUBY)"
73
- > $@
74
-
75
- # this is only intended to be run within $(test_prefix)
76
- shebang: $(bins)
77
- $(MRI) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $^
86
+ bins: $(tmp_bins)
78
87
 
79
88
  t_log := $(T_log) $(T_n_log)
80
89
  test: $(T) $(T_n)
@@ -83,15 +92,54 @@ test: $(T) $(T_n)
83
92
 
84
93
  test-exec: $(wildcard test/exec/test_*.rb)
85
94
  test-unit: $(wildcard test/unit/test_*.rb)
86
- $(slow_tests): $(test_prefix)/.stamp
95
+ $(slow_tests): $(ext_dl)
87
96
  @$(MAKE) $(shell $(awk_slow) $@)
88
97
 
89
98
  # ensure we can require just the HTTP parser without the rest of unicorn
90
- test-require: $(ext)/unicorn_http.$(DLEXT)
91
- $(RUBY) --disable-gems -I$(ext) -runicorn_http -e Unicorn
99
+ test-require: $(ext_dl)
100
+ $(RUBY) --disable-gems -I$(ext_pfx)/$(ext) -runicorn_http -e Unicorn
101
+
102
+ test_prereq := $(tmp_bins) $(ext_dl)
103
+
104
+ SH_TEST_OPTS =
105
+ ifdef V
106
+ ifeq ($(V),2)
107
+ SH_TEST_OPTS += --trace
108
+ else
109
+ SH_TEST_OPTS += --verbose
110
+ endif
111
+ endif
92
112
 
93
- test-integration: $(test_prefix)/.stamp
94
- $(MAKE) -C t
113
+ # do we trust Ruby behavior to be stable? some tests are
114
+ # (mostly) POSIX sh (not bash or ksh93, so no "set -o pipefail"
115
+ # TRACER = strace -f -o $(t_pfx).strace -s 100000
116
+ # TRACER = /usr/bin/time -o $(t_pfx).time
117
+ t_pfx = trash/$@-$(RUBY_ENGINE)-$(RUBY_VERSION)
118
+ T_sh = $(wildcard t/t[0-9][0-9][0-9][0-9]-*.sh)
119
+ $(T_sh): export RUBY := $(RUBY)
120
+ $(T_sh): export PATH := $(CURDIR)/$(tmp_bin):$(PATH)
121
+ $(T_sh): export RUBYLIB := $(lib):$(RUBYLIB)
122
+ $(T_sh): dep $(test_prereq) t/random_blob t/trash/.gitignore
123
+ cd t && $(TRACER) $(SHELL) $(SH_TEST_OPTS) $(@F) $(TEST_OPTS)
124
+
125
+ t/trash/.gitignore : | t/trash
126
+ echo '*' >$@
127
+
128
+ dependencies := socat curl
129
+ deps := $(addprefix t/.dep+,$(dependencies))
130
+ $(deps): dep_bin = $(lastword $(subst +, ,$@))
131
+ $(deps):
132
+ @which $(dep_bin) > $@.$(pid) 2>/dev/null || :
133
+ @test -s $@.$(pid) || \
134
+ { echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
135
+ @mv $@.$(pid) $@
136
+ dep: $(deps)
137
+
138
+ t/random_blob:
139
+ dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
140
+ mv $@.$(pid) $@
141
+
142
+ test-integration: $(T_sh)
95
143
 
96
144
  check: test-require test test-integration
97
145
  test-all: check
@@ -122,16 +170,16 @@ run_test = $(quiet_pre) \
122
170
 
123
171
  %.n: arg = $(subst .n,,$(subst --, -n ,$@))
124
172
  %.n: t = $(subst .n,$(log_suffix),$@)
125
- %.n: export PATH := $(test_prefix)/bin:$(PATH)
126
- %.n: export RUBYLIB := $(test_prefix)/lib:$(MYLIBS)
127
- %.n: $(test_prefix)/.stamp
173
+ %.n: export PATH := $(CURDIR)/$(tmp_bin):$(PATH)
174
+ %.n: export RUBYLIB := $(lib):$(RUBYLIB)
175
+ %.n: $(test_prereq)
128
176
  $(run_test)
129
177
 
130
178
  $(T): arg = $@
131
179
  $(T): t = $(subst .rb,$(log_suffix),$@)
132
- $(T): export PATH := $(test_prefix)/bin:$(PATH)
133
- $(T): export RUBYLIB := $(test_prefix)/lib:$(MYLIBS)
134
- $(T): $(test_prefix)/.stamp
180
+ $(T): export PATH := $(CURDIR)/$(tmp_bin):$(PATH)
181
+ $(T): export RUBYLIB := $(lib):$(RUBYLIB)
182
+ $(T): $(test_prereq)
135
183
  $(run_test)
136
184
 
137
185
  install: $(bins) $(ext)/unicorn_http.c
@@ -152,18 +200,16 @@ clean:
152
200
  -$(MAKE) -C $(ext) clean
153
201
  $(RM) $(ext)/Makefile
154
202
  $(RM) $(setup_rb_files) $(t_log)
155
- $(RM) -r $(test_prefix) man
156
- $(RM) $(man1) $(html1)
203
+ $(RM) -r $(ext_pfx) man t/trash
204
+ $(RM) $(html1)
157
205
 
158
206
  man1 := $(addprefix Documentation/, unicorn.1 unicorn_rails.1)
159
207
  html1 := $(addsuffix .html, $(man1))
160
- man :
161
- mkdir -p man/man1
162
- install -m 644 $(man1) man/man1
208
+ man : $(man1) | man/man1
209
+ $(INSTALL) -m 644 $(man1) man/man1
163
210
 
164
- html : $(html1)
165
- mkdir -p doc/man1
166
- install -m 644 $(html1) doc/man1
211
+ html : $(html1) | doc/man1
212
+ $(INSTALL) -m 644 $(html1) doc/man1
167
213
 
168
214
  %.1.html: %.1
169
215
  $(OLDDOC) man2html -o $@ ./$<
@@ -187,10 +233,10 @@ doc: .document $(ext)/unicorn_http.c man html .olddoc.yml $(PLACEHOLDERS)
187
233
  $(OLDDOC) prepare
188
234
  $(RDOC) -f dark216
189
235
  $(OLDDOC) merge
190
- install -m644 COPYING doc/COPYING
191
- install -m644 NEWS.atom.xml doc/NEWS.atom.xml
192
- install -m644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/
193
- install -m644 $(man1_paths) doc/
236
+ $(INSTALL) -m 644 COPYING doc/COPYING
237
+ $(INSTALL) -m 644 NEWS.atom.xml doc/NEWS.atom.xml
238
+ $(INSTALL) -m 644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/
239
+ $(INSTALL) -m 644 $(man1_paths) doc/
194
240
  tar cf - $$(git ls-files examples/) | (cd doc && tar xf -)
195
241
 
196
242
  # publishes docs to https://yhbt.net/unicorn/
@@ -199,7 +245,8 @@ publish_doc:
199
245
  $(MAKE) doc
200
246
  $(MAKE) doc_gz
201
247
  chmod 644 $$(find doc -type f)
202
- $(RSYNC) -av doc/ yhbt.net:/srv/yhbt/unicorn/
248
+ $(RSYNC) -av doc/ yhbt.net:/srv/yhbt/unicorn/ \
249
+ --exclude index.html* --exclude created.rid*
203
250
  git ls-files | xargs touch
204
251
 
205
252
  # Create gzip variants of the same timestamp as the original so nginx
@@ -231,9 +278,8 @@ gem: $(pkggem)
231
278
  install-gem: $(pkggem)
232
279
  gem install --local $(CURDIR)/$<
233
280
 
234
- $(pkggem): .manifest fix-perms
281
+ $(pkggem): .manifest fix-perms | pkg
235
282
  gem build $(rfpackage).gemspec
236
- mkdir -p pkg
237
283
  mv $(@F) $@
238
284
 
239
285
  $(pkgtgz): distdir = $(basename $@)
@@ -264,5 +310,4 @@ check-warnings:
264
310
  do $(RUBY) --disable-gems -d -W2 -c \
265
311
  $$i; done) | grep -v '^Syntax OK$$' || :
266
312
 
267
- .PHONY: .FORCE-GIT-VERSION-FILE doc $(T) $(slow_tests) man
268
- .PHONY: test-install
313
+ .PHONY: .FORCE-GIT-VERSION-FILE doc $(T) $(slow_tests) man $(T_sh) clean
data/ISSUES CHANGED
@@ -76,15 +76,18 @@ document distributed with git) on guidelines for patch submission.
76
76
  * public: mailto:unicorn-public@yhbt.net
77
77
  * nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
78
78
  * nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
79
+ * imaps://news.public-inbox.org/inbox.comp.lang.ruby.unicorn.0
79
80
  * https://yhbt.net/unicorn-public/
80
81
  * http://ou63pmih66umazou.onion/unicorn-public/
81
82
 
82
83
  Mailing list subscription is optional, so Cc: all participants.
83
84
 
84
- You can follow along via NNTP (read-only):
85
+ You can follow along via NNTP or IMAP (read-only):
85
86
 
86
87
  nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
87
88
  nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
89
+ imaps://news.public-inbox.org/inbox.comp.lang.ruby.unicorn.0
90
+ imap://ou63pmih66umazou.onion/inbox.comp.lang.ruby.unicorn.0
88
91
 
89
92
  Or Atom feeds:
90
93
 
data/README CHANGED
@@ -136,10 +136,17 @@ requests) go to the mailing list/newsgroup. See the ISSUES document for
136
136
  information on the {mailing list}[mailto:unicorn-public@yhbt.net].
137
137
 
138
138
  The mailing list is archived at https://yhbt.net/unicorn-public/
139
+
139
140
  Read-only NNTP access is available at:
140
141
  nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn and
141
142
  nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
142
143
 
144
+ Read-only IMAP access is also avaialble at:
145
+ imaps://news.public-inbox.org/inbox.comp.lang.ruby.unicorn.0 and
146
+ imap://ou63pmih66umazou.onion/inbox.comp.lang.ruby.unicorn.0
147
+ AUTH=ANONYMOUS mechanism is supported, as is any username+password
148
+ combination.
149
+
143
150
  For the latest on unicorn releases, you may also finger us at
144
151
  unicorn@yhbt.net or check our NEWS page (and subscribe to our Atom
145
152
  feed).
@@ -1,6 +1,11 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'mkmf'
3
3
 
4
+ unless RUBY_VERSION < '3.1'
5
+ warn "Unicorn was only tested against MRI up to 3.0.\n" \
6
+ "It might not properly work with #{RUBY_VERSION}"
7
+ end
8
+
4
9
  have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
5
10
  have_macro("SIZEOF_SIZE_T", "ruby.h") or check_sizeof("size_t", "sys/types.h")
6
11
  have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
@@ -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 @@ def default_middleware(bool)
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 @@ def self.check_client_connection=(bool)
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:
@@ -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,6 +587,25 @@ def handle_error(client, e)
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
@@ -602,7 +620,18 @@ def e100_response_write(client, env)
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 @@ def process_client(client)
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)
@@ -686,6 +717,7 @@ def worker_loop(worker)
686
717
  trap(:USR1) { nr = -65536 }
687
718
 
688
719
  ready = readers.dup
720
+ nr_listeners = readers.size
689
721
  @after_worker_ready.call(self, worker)
690
722
 
691
723
  begin
@@ -708,7 +740,7 @@ def worker_loop(worker)
708
740
  # we're probably reasonably busy, so avoid calling select()
709
741
  # and do a speculative non-blocking accept() on ready listeners
710
742
  # before we sleep again in select().
711
- unless nr == 0
743
+ if nr == nr_listeners
712
744
  tmp = ready.dup
713
745
  redo
714
746
  end
@@ -60,7 +60,6 @@ def self.new(app, interval = 5, path = %r{\A/})
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 @@ def self.new(app, interval = 5, path = %r{\A/})
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
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
@@ -574,7 +574,7 @@ def test_unicorn_config_per_worker_listen
574
574
  assert_equal String, results[0].class
575
575
  worker_pid = results[0].to_i
576
576
  assert_not_equal pid, worker_pid
577
- s = UNIXSocket.new(tmp.path)
577
+ s = unix_socket(tmp.path)
578
578
  s.syswrite("GET / HTTP/1.0\r\n\r\n")
579
579
  results = ''
580
580
  loop { results << s.sysread(4096) } rescue nil
@@ -732,7 +732,7 @@ def test_socket_unlinked_restore
732
732
  wait_for_file(sock_path)
733
733
  assert File.socket?(sock_path)
734
734
 
735
- sock = UNIXSocket.new(sock_path)
735
+ sock = unix_socket(sock_path)
736
736
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
737
737
  results = sock.sysread(4096)
738
738
 
@@ -742,7 +742,7 @@ def test_socket_unlinked_restore
742
742
  wait_for_file(sock_path)
743
743
  assert File.socket?(sock_path)
744
744
 
745
- sock = UNIXSocket.new(sock_path)
745
+ sock = unix_socket(sock_path)
746
746
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
747
747
  results = sock.sysread(4096)
748
748
 
@@ -777,7 +777,7 @@ def test_unicorn_config_file
777
777
  assert_equal pid, File.read(pid_file).to_i
778
778
  assert File.socket?(sock_path), "socket created"
779
779
 
780
- sock = UNIXSocket.new(sock_path)
780
+ sock = unix_socket(sock_path)
781
781
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
782
782
  results = sock.sysread(4096)
783
783
 
@@ -803,7 +803,7 @@ def test_unicorn_config_file
803
803
  wait_for_file(new_sock_path)
804
804
  assert File.socket?(new_sock_path), "socket exists"
805
805
  @sockets.each do |path|
806
- sock = UNIXSocket.new(path)
806
+ sock = unix_socket(path)
807
807
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
808
808
  results = sock.sysread(4096)
809
809
  assert_equal String, results.class
data/test/test_helper.rb CHANGED
@@ -28,22 +28,43 @@
28
28
  require 'fileutils'
29
29
  require 'logger'
30
30
  require 'unicorn'
31
+ require 'io/nonblock'
31
32
 
32
33
  if ENV['DEBUG']
33
34
  require 'ruby-debug'
34
35
  Debugger.start
35
36
  end
36
37
 
38
+ unless RUBY_VERSION < '3.1'
39
+ warn "Unicorn was only tested against MRI up to 3.0.\n" \
40
+ "It might not properly work with #{RUBY_VERSION}"
41
+ end
42
+
37
43
  def redirect_test_io
38
44
  orig_err = STDERR.dup
39
45
  orig_out = STDOUT.dup
40
- STDERR.reopen("test_stderr.#{$$}.log", "a")
41
- STDOUT.reopen("test_stdout.#{$$}.log", "a")
46
+ rdr_pid = $$
47
+ new_out = File.open("test_stdout.#$$.log", "a")
48
+ new_err = File.open("test_stderr.#$$.log", "a")
49
+ new_out.sync = new_err.sync = true
50
+
51
+ if tail = ENV['TAIL'] # "tail -F" if GNU, "tail -f" otherwise
52
+ require 'shellwords'
53
+ cmd = tail.shellsplit
54
+ cmd << new_out.path
55
+ cmd << new_err.path
56
+ pid = Process.spawn(*cmd, { 1 => 2, :pgroup => true })
57
+ sleep 0.1 # wait for tail(1) to startup
58
+ end
59
+ STDERR.reopen(new_err)
60
+ STDOUT.reopen(new_out)
42
61
  STDERR.sync = STDOUT.sync = true
43
62
 
44
63
  at_exit do
45
- File.unlink("test_stderr.#{$$}.log") rescue nil
46
- File.unlink("test_stdout.#{$$}.log") rescue nil
64
+ if rdr_pid == $$
65
+ File.unlink(new_out.path) rescue nil
66
+ File.unlink(new_err.path) rescue nil
67
+ end
47
68
  end
48
69
 
49
70
  begin
@@ -51,6 +72,7 @@ def redirect_test_io
51
72
  ensure
52
73
  STDERR.reopen(orig_err)
53
74
  STDOUT.reopen(orig_out)
75
+ Process.kill(:TERM, pid) if pid
54
76
  end
55
77
  end
56
78
 
@@ -270,3 +292,15 @@ def reset_sig_handlers
270
292
  trap(sig, "DEFAULT")
271
293
  end
272
294
  end
295
+
296
+ def tcp_socket(*args)
297
+ sock = TCPSocket.new(*args)
298
+ sock.nonblock = false
299
+ sock
300
+ end
301
+
302
+ def unix_socket(*args)
303
+ sock = UNIXSocket.new(*args)
304
+ sock.nonblock = false
305
+ sock
306
+ end
@@ -3,6 +3,7 @@
3
3
  require 'io/wait'
4
4
  require 'tempfile'
5
5
  require 'test/unit'
6
+ require './test/test_helper'
6
7
 
7
8
  class TestCccTCPI < Test::Unit::TestCase
8
9
  def test_ccc_tcpi
@@ -42,7 +43,7 @@ def test_ccc_tcpi
42
43
  wr.close
43
44
 
44
45
  # make sure the server is running, at least
45
- client = TCPSocket.new(host, port)
46
+ client = tcp_socket(host, port)
46
47
  client.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
47
48
  assert client.wait(10), 'never got response from server'
48
49
  res = client.read
@@ -51,13 +52,13 @@ def test_ccc_tcpi
51
52
  client.close
52
53
 
53
54
  # start a slow request...
54
- sleeper = TCPSocket.new(host, port)
55
+ sleeper = tcp_socket(host, port)
55
56
  sleeper.write("GET /sleep HTTP/1.1\r\nHost: example.com\r\n\r\n")
56
57
 
57
58
  # and a bunch of aborted ones
58
59
  nr = 100
59
60
  nr.times do |i|
60
- client = TCPSocket.new(host, port)
61
+ client = tcp_socket(host, port)
61
62
  client.write("GET /collections/#{rand(10000)} HTTP/1.1\r\n" \
62
63
  "Host: example.com\r\n\r\n")
63
64
  client.close
@@ -23,6 +23,34 @@ def call(env)
23
23
  end
24
24
  end
25
25
 
26
+ class TestEarlyHintsHandler
27
+ def call(env)
28
+ while env['rack.input'].read(4096)
29
+ end
30
+ env['rack.early_hints'].call(
31
+ "Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload"
32
+ )
33
+ [200, { 'Content-Type' => 'text/plain' }, ['hello!\n']]
34
+ end
35
+ end
36
+
37
+ class TestRackAfterReply
38
+ def initialize
39
+ @called = false
40
+ end
41
+
42
+ def call(env)
43
+ while env['rack.input'].read(4096)
44
+ end
45
+
46
+ env["rack.after_reply"] << -> { @called = true }
47
+
48
+ [200, { 'Content-Type' => 'text/plain' }, ["after_reply_called: #{@called}"]]
49
+ rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
50
+ $stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
51
+ raise e
52
+ end
53
+ end
26
54
 
27
55
  class WebServerTest < Test::Unit::TestCase
28
56
 
@@ -84,6 +112,52 @@ def test_preload_app_config
84
112
  tmp.close!
85
113
  end
86
114
 
115
+ def test_early_hints
116
+ teardown
117
+ redirect_test_io do
118
+ @server = HttpServer.new(TestEarlyHintsHandler.new,
119
+ :listeners => [ "127.0.0.1:#@port"],
120
+ :early_hints => true)
121
+ @server.start
122
+ end
123
+
124
+ sock = tcp_socket('127.0.0.1', @port)
125
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
126
+
127
+ responses = sock.read(4096)
128
+ assert_match %r{\AHTTP/1.[01] 103\b}, responses
129
+ assert_match %r{^Link: </style\.css>}, responses
130
+ assert_match %r{^Link: </script\.js>}, responses
131
+
132
+ assert_match %r{^HTTP/1.[01] 200\b}, responses
133
+ end
134
+
135
+ def test_after_reply
136
+ teardown
137
+
138
+ redirect_test_io do
139
+ @server = HttpServer.new(TestRackAfterReply.new,
140
+ :listeners => [ "127.0.0.1:#@port"])
141
+ @server.start
142
+ end
143
+
144
+ sock = tcp_socket('127.0.0.1', @port)
145
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
146
+
147
+ responses = sock.read(4096)
148
+ assert_match %r{\AHTTP/1.[01] 200\b}, responses
149
+ assert_match %r{^after_reply_called: false}, responses
150
+
151
+ sock = tcp_socket('127.0.0.1', @port)
152
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
153
+
154
+ responses = sock.read(4096)
155
+ assert_match %r{\AHTTP/1.[01] 200\b}, responses
156
+ assert_match %r{^after_reply_called: true}, responses
157
+
158
+ sock.close
159
+ end
160
+
87
161
  def test_broken_app
88
162
  teardown
89
163
  app = lambda { |env| raise RuntimeError, "hello" }
@@ -92,7 +166,7 @@ def test_broken_app
92
166
  @server = HttpServer.new(app, :listeners => [ "127.0.0.1:#@port"] )
93
167
  @server.start
94
168
  end
95
- sock = TCPSocket.new('127.0.0.1', @port)
169
+ sock = tcp_socket('127.0.0.1', @port)
96
170
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
97
171
  assert_match %r{\AHTTP/1.[01] 500\b}, sock.sysread(4096)
98
172
  assert_nil sock.close
@@ -105,7 +179,7 @@ def test_simple_server
105
179
 
106
180
  def test_client_shutdown_writes
107
181
  bs = 15609315 * rand
108
- sock = TCPSocket.new('127.0.0.1', @port)
182
+ sock = tcp_socket('127.0.0.1', @port)
109
183
  sock.syswrite("PUT /hello HTTP/1.1\r\n")
110
184
  sock.syswrite("Host: example.com\r\n")
111
185
  sock.syswrite("Transfer-Encoding: chunked\r\n")
@@ -132,7 +206,7 @@ def test_client_shutdown_writes
132
206
 
133
207
  def test_client_shutdown_write_truncates
134
208
  bs = 15609315 * rand
135
- sock = TCPSocket.new('127.0.0.1', @port)
209
+ sock = tcp_socket('127.0.0.1', @port)
136
210
  sock.syswrite("PUT /hello HTTP/1.1\r\n")
137
211
  sock.syswrite("Host: example.com\r\n")
138
212
  sock.syswrite("Transfer-Encoding: chunked\r\n")
@@ -158,7 +232,7 @@ def test_client_shutdown_write_truncates
158
232
 
159
233
  def test_client_malformed_body
160
234
  bs = 15653984
161
- sock = TCPSocket.new('127.0.0.1', @port)
235
+ sock = tcp_socket('127.0.0.1', @port)
162
236
  sock.syswrite("PUT /hello HTTP/1.1\r\n")
163
237
  sock.syswrite("Host: example.com\r\n")
164
238
  sock.syswrite("Transfer-Encoding: chunked\r\n")
@@ -180,7 +254,7 @@ def test_client_malformed_body
180
254
 
181
255
  def do_test(string, chunk, close_after=nil, shutdown_delay=0)
182
256
  # Do not use instance variables here, because it needs to be thread safe
183
- socket = TCPSocket.new("127.0.0.1", @port);
257
+ socket = tcp_socket("127.0.0.1", @port);
184
258
  request = StringIO.new(string)
185
259
  chunks_out = 0
186
260
 
@@ -225,14 +299,14 @@ def test_logger_changed
225
299
  end
226
300
 
227
301
  def test_bad_client_400
228
- sock = TCPSocket.new('127.0.0.1', @port)
302
+ sock = tcp_socket('127.0.0.1', @port)
229
303
  sock.syswrite("GET / HTTP/1.0\r\nHost: foo\rbar\r\n\r\n")
230
304
  assert_match %r{\AHTTP/1.[01] 400\b}, sock.sysread(4096)
231
305
  assert_nil sock.close
232
306
  end
233
307
 
234
308
  def test_http_0_9
235
- sock = TCPSocket.new('127.0.0.1', @port)
309
+ sock = tcp_socket('127.0.0.1', @port)
236
310
  sock.syswrite("GET /hello\r\n")
237
311
  assert_match 'hello!\n', sock.sysread(4096)
238
312
  assert_nil sock.close
@@ -52,7 +52,7 @@ def test_worker_dies_on_dead_master
52
52
  redirect_test_io { HttpServer.new(app, opts).start.join }
53
53
  }
54
54
  wait_workers_ready("test_stderr.#{pid}.log", 1)
55
- sock = TCPSocket.new('127.0.0.1', @port)
55
+ sock = tcp_socket('127.0.0.1', @port)
56
56
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
57
57
  buf = sock.readpartial(4096)
58
58
  assert_nil sock.close
@@ -79,7 +79,7 @@ def test_sleepy_kill
79
79
  }
80
80
  wr.close
81
81
  wait_workers_ready("test_stderr.#{pid}.log", 1)
82
- sock = TCPSocket.new('127.0.0.1', @port)
82
+ sock = tcp_socket('127.0.0.1', @port)
83
83
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
84
84
  buf = rd.readpartial(1)
85
85
  wait_master_ready("test_stderr.#{pid}.log")
@@ -102,7 +102,7 @@ def test_timeout_slow_response
102
102
  }
103
103
  t0 = Time.now
104
104
  wait_workers_ready("test_stderr.#{pid}.log", 1)
105
- sock = TCPSocket.new('127.0.0.1', @port)
105
+ sock = tcp_socket('127.0.0.1', @port)
106
106
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
107
107
 
108
108
  buf = nil
@@ -125,7 +125,7 @@ def test_response_write
125
125
  }
126
126
  redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
127
127
  wait_workers_ready("test_stderr.#{$$}.log", 1)
128
- sock = TCPSocket.new('127.0.0.1', @port)
128
+ sock = tcp_socket('127.0.0.1', @port)
129
129
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
130
130
  buf = ''
131
131
  header_len = pid = nil
@@ -163,13 +163,13 @@ def test_request_read
163
163
  redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
164
164
 
165
165
  wait_workers_ready("test_stderr.#{$$}.log", 1)
166
- sock = TCPSocket.new('127.0.0.1', @port)
166
+ sock = tcp_socket('127.0.0.1', @port)
167
167
  sock.syswrite("GET / HTTP/1.0\r\n\r\n")
168
168
  pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
169
169
  assert_nil sock.close
170
170
 
171
171
  assert pid > 0, "pid not positive: #{pid.inspect}"
172
- sock = TCPSocket.new('127.0.0.1', @port)
172
+ sock = tcp_socket('127.0.0.1', @port)
173
173
  sock.syswrite("PUT / HTTP/1.0\r\n")
174
174
  sock.syswrite("Content-Length: #{@bs * @count}\r\n\r\n")
175
175
  1000.times { Process.kill(:HUP, pid) }
@@ -116,7 +116,7 @@ def test_bind_listen_unix_rebind
116
116
  client.syswrite('abcde')
117
117
  exit 0
118
118
  end
119
- s = UNIXSocket.new(@unix_listener_path)
119
+ s = unix_socket(@unix_listener_path)
120
120
  IO.select([s])
121
121
  assert_equal 'abcde', s.sysread(5)
122
122
  pid, status = Process.waitpid2(pid)
@@ -60,7 +60,7 @@ def teardown
60
60
 
61
61
  def test_put
62
62
  start_server(@sha1_app)
63
- sock = TCPSocket.new(@addr, @port)
63
+ sock = tcp_socket(@addr, @port)
64
64
  sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
65
65
  @count.times do |i|
66
66
  buf = @random.sysread(@bs)
@@ -77,7 +77,7 @@ def test_put
77
77
  def test_put_content_md5
78
78
  md5 = Digest::MD5.new
79
79
  start_server(@sha1_app)
80
- sock = TCPSocket.new(@addr, @port)
80
+ sock = tcp_socket(@addr, @port)
81
81
  sock.syswrite("PUT / HTTP/1.0\r\nTransfer-Encoding: chunked\r\n" \
82
82
  "Trailer: Content-MD5\r\n\r\n")
83
83
  @count.times do |i|
@@ -103,7 +103,7 @@ def test_put_trickle_small
103
103
  @count, @bs = 2, 128
104
104
  start_server(@sha1_app)
105
105
  assert_equal 256, length
106
- sock = TCPSocket.new(@addr, @port)
106
+ sock = tcp_socket(@addr, @port)
107
107
  hdr = "PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n"
108
108
  @count.times do
109
109
  buf = @random.sysread(@bs)
@@ -122,7 +122,7 @@ def test_put_trickle_small
122
122
 
123
123
  def test_put_keepalive_truncates_small_overwrite
124
124
  start_server(@sha1_app)
125
- sock = TCPSocket.new(@addr, @port)
125
+ sock = tcp_socket(@addr, @port)
126
126
  to_upload = length + 1
127
127
  sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{to_upload}\r\n\r\n")
128
128
  @count.times do
@@ -155,7 +155,7 @@ def test_put_excessive_overwrite_closed
155
155
  tmp.write(nr.to_s)
156
156
  [ 200, @hdr, [] ]
157
157
  })
158
- sock = TCPSocket.new(@addr, @port)
158
+ sock = tcp_socket(@addr, @port)
159
159
  buf = ' ' * @bs
160
160
  sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
161
161
 
data/unicorn.gemspec CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  Gem::Specification.new do |s|
13
13
  s.name = %q{unicorn}
14
- s.version = (ENV['VERSION'] || '5.5.4').dup
14
+ s.version = (ENV['VERSION'] || '6.0.0').dup
15
15
  s.authors = ['unicorn hackers']
16
16
  s.summary = 'Rack HTTP server for fast clients and Unix'
17
17
  s.description = File.read('README').split("\n\n")[1]
@@ -25,10 +25,11 @@
25
25
  s.homepage = 'https://yhbt.net/unicorn/'
26
26
  s.test_files = test_files
27
27
 
28
- # technically we need ">= 1.9.3", too, but avoid the array here since
29
- # old rubygems versions (1.8.23.2 at least) do not support multiple
30
- # version requirements here.
31
- s.required_ruby_version = '< 3.0'
28
+ # 1.9.3 is the minumum supported version. We don't specify
29
+ # a maximum version to make it easier to test pre-releases,
30
+ # but we do warn users if they install unicorn on an untested
31
+ # version in extconf.rb
32
+ s.required_ruby_version = ">= 1.9.3"
32
33
 
33
34
  # We do not have a hard dependency on rack, it's possible to load
34
35
  # things which respond to #call. HTTP status lines in responses
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unicorn
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.4
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - unicorn hackers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-24 00:00:00.000000000 Z
11
+ date: 2021-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -197,7 +197,6 @@ files:
197
197
  - t/env.ru
198
198
  - t/fails-rack-lint.ru
199
199
  - t/heartbeat-timeout.ru
200
- - t/hijack.ru
201
200
  - t/listener_names.ru
202
201
  - t/my-tap-lib.sh
203
202
  - t/oob_gc.ru
@@ -235,7 +234,6 @@ files:
235
234
  - t/t0100-rack-input-tests.sh
236
235
  - t/t0116-client_body_buffer_size.sh
237
236
  - t/t0116.ru
238
- - t/t0200-rack-hijack.sh
239
237
  - t/t0300-no-default-middleware.sh
240
238
  - t/t0301-no-default-middleware-ignored-in-config.sh
241
239
  - t/t0301.ru
@@ -282,9 +280,9 @@ require_paths:
282
280
  - lib
283
281
  required_ruby_version: !ruby/object:Gem::Requirement
284
282
  requirements:
285
- - - "<"
283
+ - - ">="
286
284
  - !ruby/object:Gem::Version
287
- version: '3.0'
285
+ version: 1.9.3
288
286
  required_rubygems_version: !ruby/object:Gem::Requirement
289
287
  requirements:
290
288
  - - ">="
data/t/hijack.ru DELETED
@@ -1,55 +0,0 @@
1
- use Rack::Lint
2
- use Rack::ContentLength
3
- use Rack::ContentType, "text/plain"
4
- class DieIfUsed
5
- @@n = 0
6
- def each
7
- abort "body.each called after response hijack\n"
8
- end
9
-
10
- def close
11
- warn "closed DieIfUsed #{@@n += 1}\n"
12
- end
13
- end
14
-
15
- envs = []
16
-
17
- run lambda { |env|
18
- case env["PATH_INFO"]
19
- when "/hijack_req"
20
- if env["rack.hijack?"]
21
- io = env["rack.hijack"].call
22
- envs << env
23
- if io.respond_to?(:read_nonblock) &&
24
- env["rack.hijack_io"].respond_to?(:read_nonblock)
25
-
26
- # exercise both, since we Rack::Lint may use different objects
27
- env["rack.hijack_io"].write("HTTP/1.0 200 OK\r\n\r\n")
28
- io.write("request.hijacked")
29
- io.close
30
- return [ 500, {}, DieIfUsed.new ]
31
- end
32
- end
33
- [ 500, {}, [ "hijack BAD\n" ] ]
34
- when "/hijack_res"
35
- r = "response.hijacked"
36
- [ 200,
37
- {
38
- "Content-Length" => r.bytesize.to_s,
39
- "rack.hijack" => proc do |io|
40
- envs << env
41
- io.write(r)
42
- io.close
43
- end
44
- },
45
- DieIfUsed.new
46
- ]
47
- when "/normal_env_id"
48
- b = "#{env.object_id}\n"
49
- h = {
50
- 'Content-Type' => 'text/plain',
51
- 'Content-Length' => b.bytesize.to_s,
52
- }
53
- [ 200, h, [ b ] ]
54
- end
55
- }
@@ -1,51 +0,0 @@
1
- #!/bin/sh
2
- . ./test-lib.sh
3
- t_plan 9 "rack.hijack tests (Rack 1.5+ (Rack::VERSION >= [ 1,2]))"
4
-
5
- t_begin "setup and start" && {
6
- unicorn_setup
7
- unicorn -D -c $unicorn_config hijack.ru
8
- unicorn_wait_start
9
- }
10
-
11
- t_begin "normal env reused between requests" && {
12
- env_a="$(curl -sSf http://$listen/normal_env_id)"
13
- b="$(curl -sSf http://$listen/normal_env_id)"
14
- test x"$env_a" = x"$b"
15
- }
16
-
17
- t_begin "check request hijack" && {
18
- test "xrequest.hijacked" = x"$(curl -sSfv http://$listen/hijack_req)"
19
- }
20
-
21
- t_begin "env changed after request hijack" && {
22
- env_b="$(curl -sSf http://$listen/normal_env_id)"
23
- test x"$env_a" != x"$env_b"
24
- }
25
-
26
- t_begin "check response hijack" && {
27
- test "xresponse.hijacked" = x"$(curl -sSfv http://$listen/hijack_res)"
28
- }
29
-
30
- t_begin "env changed after response hijack" && {
31
- env_c="$(curl -sSf http://$listen/normal_env_id)"
32
- test x"$env_b" != x"$env_c"
33
- }
34
-
35
- t_begin "env continues to be reused between requests" && {
36
- b="$(curl -sSf http://$listen/normal_env_id)"
37
- test x"$env_c" = x"$b"
38
- }
39
-
40
- t_begin "killing succeeds after hijack" && {
41
- kill $unicorn_pid
42
- }
43
-
44
- t_begin "check stderr for hijacked body close" && {
45
- check_stderr
46
- grep 'closed DieIfUsed 1\>' $r_err
47
- grep 'closed DieIfUsed 2\>' $r_err
48
- ! grep 'closed DieIfUsed 3\>' $r_err
49
- }
50
-
51
- t_done