unicorn 5.0.1 → 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 (92) hide show
  1. checksums.yaml +5 -5
  2. data/.manifest +11 -5
  3. data/.olddoc.yml +16 -6
  4. data/Application_Timeouts +4 -4
  5. data/CONTRIBUTORS +6 -2
  6. data/Documentation/.gitignore +1 -3
  7. data/Documentation/unicorn.1 +222 -0
  8. data/Documentation/unicorn_rails.1 +207 -0
  9. data/FAQ +1 -1
  10. data/GIT-VERSION-FILE +1 -1
  11. data/GIT-VERSION-GEN +1 -1
  12. data/GNUmakefile +118 -58
  13. data/HACKING +2 -10
  14. data/ISSUES +40 -35
  15. data/KNOWN_ISSUES +2 -2
  16. data/LATEST +23 -28
  17. data/LICENSE +2 -2
  18. data/Links +13 -11
  19. data/NEWS +612 -0
  20. data/README +30 -29
  21. data/SIGNALS +1 -1
  22. data/Sandbox +8 -7
  23. data/TODO +0 -2
  24. data/TUNING +19 -1
  25. data/archive/slrnpull.conf +1 -1
  26. data/bin/unicorn +3 -1
  27. data/bin/unicorn_rails +2 -2
  28. data/examples/big_app_gc.rb +1 -1
  29. data/examples/init.sh +36 -8
  30. data/examples/logrotate.conf +17 -2
  31. data/examples/nginx.conf +4 -3
  32. data/examples/unicorn.conf.minimal.rb +2 -2
  33. data/examples/unicorn.conf.rb +2 -2
  34. data/examples/unicorn@.service +14 -0
  35. data/ext/unicorn_http/c_util.h +5 -13
  36. data/ext/unicorn_http/common_field_optimization.h +22 -5
  37. data/ext/unicorn_http/epollexclusive.h +124 -0
  38. data/ext/unicorn_http/ext_help.h +0 -44
  39. data/ext/unicorn_http/extconf.rb +32 -6
  40. data/ext/unicorn_http/global_variables.h +2 -2
  41. data/ext/unicorn_http/httpdate.c +2 -1
  42. data/ext/unicorn_http/unicorn_http.c +853 -498
  43. data/ext/unicorn_http/unicorn_http.rl +86 -30
  44. data/ext/unicorn_http/unicorn_http_common.rl +1 -1
  45. data/lib/unicorn/configurator.rb +93 -13
  46. data/lib/unicorn/http_request.rb +101 -11
  47. data/lib/unicorn/http_response.rb +8 -4
  48. data/lib/unicorn/http_server.rb +141 -72
  49. data/lib/unicorn/launcher.rb +1 -1
  50. data/lib/unicorn/oob_gc.rb +6 -6
  51. data/lib/unicorn/select_waiter.rb +6 -0
  52. data/lib/unicorn/socket_helper.rb +23 -7
  53. data/lib/unicorn/stream_input.rb +5 -4
  54. data/lib/unicorn/tee_input.rb +8 -10
  55. data/lib/unicorn/tmpio.rb +8 -2
  56. data/lib/unicorn/util.rb +3 -3
  57. data/lib/unicorn/version.rb +1 -1
  58. data/lib/unicorn/worker.rb +33 -8
  59. data/lib/unicorn.rb +55 -29
  60. data/man/man1/unicorn.1 +120 -118
  61. data/man/man1/unicorn_rails.1 +106 -107
  62. data/t/GNUmakefile +3 -72
  63. data/t/README +4 -4
  64. data/t/t0011-active-unix-socket.sh +1 -1
  65. data/t/t0012-reload-empty-config.sh +2 -1
  66. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  67. data/t/t0301.ru +13 -0
  68. data/t/test-lib.sh +4 -3
  69. data/test/benchmark/README +14 -4
  70. data/test/benchmark/ddstream.ru +50 -0
  71. data/test/benchmark/readinput.ru +40 -0
  72. data/test/benchmark/uconnect.perl +66 -0
  73. data/test/exec/test_exec.rb +26 -24
  74. data/test/test_helper.rb +38 -30
  75. data/test/unit/test_ccc.rb +91 -0
  76. data/test/unit/test_droplet.rb +1 -1
  77. data/test/unit/test_http_parser.rb +46 -16
  78. data/test/unit/test_http_parser_ng.rb +81 -0
  79. data/test/unit/test_request.rb +10 -10
  80. data/test/unit/test_server.rb +86 -12
  81. data/test/unit/test_signals.rb +8 -8
  82. data/test/unit/test_socket_helper.rb +13 -9
  83. data/test/unit/test_upload.rb +9 -14
  84. data/test/unit/test_util.rb +31 -5
  85. data/test/unit/test_waiter.rb +34 -0
  86. data/unicorn.gemspec +21 -22
  87. metadata +21 -28
  88. data/Documentation/GNUmakefile +0 -30
  89. data/Documentation/unicorn.1.txt +0 -188
  90. data/Documentation/unicorn_rails.1.txt +0 -175
  91. data/t/hijack.ru +0 -43
  92. data/t/t0200-rack-hijack.sh +0 -30
@@ -17,12 +17,40 @@ class TestHandler
17
17
  while env['rack.input'].read(4096)
18
18
  end
19
19
  [200, { 'Content-Type' => 'text/plain' }, ['hello!\n']]
20
- rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
21
- $stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
22
- raise e
20
+ rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
21
+ $stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
22
+ raise e
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
 
@@ -80,8 +108,54 @@ class WebServerTest < Test::Unit::TestCase
80
108
  loader_pid = tmp.sysread(4096).to_i
81
109
  assert_equal $$, loader_pid
82
110
  assert worker_pid != loader_pid
83
- ensure
84
- tmp.close!
111
+ ensure
112
+ tmp.close!
113
+ end
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
85
159
  end
86
160
 
87
161
  def test_broken_app
@@ -92,7 +166,7 @@ class WebServerTest < Test::Unit::TestCase
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 @@ class WebServerTest < Test::Unit::TestCase
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 @@ class WebServerTest < Test::Unit::TestCase
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 @@ class WebServerTest < Test::Unit::TestCase
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 @@ class WebServerTest < Test::Unit::TestCase
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 @@ class WebServerTest < Test::Unit::TestCase
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 @@ class SignalsTest < Test::Unit::TestCase
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 @@ class SignalsTest < Test::Unit::TestCase
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 @@ class SignalsTest < Test::Unit::TestCase
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
@@ -114,8 +114,8 @@ class SignalsTest < Test::Unit::TestCase
114
114
  assert_nil buf
115
115
  assert diff > 1.0, "diff was #{diff.inspect}"
116
116
  assert diff < 60.0
117
- ensure
118
- Process.kill(:TERM, pid) rescue nil
117
+ ensure
118
+ Process.kill(:TERM, pid) rescue nil
119
119
  end
120
120
 
121
121
  def test_response_write
@@ -125,7 +125,7 @@ class SignalsTest < Test::Unit::TestCase
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 @@ class SignalsTest < Test::Unit::TestCase
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) }
@@ -57,8 +57,8 @@ class TestSocketHelper < Test::Unit::TestCase
57
57
  assert File.readable?(@unix_listener_path), "not readable"
58
58
  assert File.writable?(@unix_listener_path), "not writable"
59
59
  assert_equal 0777, File.umask
60
- ensure
61
- File.umask(old_umask)
60
+ ensure
61
+ File.umask(old_umask)
62
62
  end
63
63
 
64
64
  def test_bind_listen_unix_umask
@@ -71,8 +71,8 @@ class TestSocketHelper < Test::Unit::TestCase
71
71
  assert_equal @unix_listener_path, sock_name(@unix_listener)
72
72
  assert_equal 0140700, File.stat(@unix_listener_path).mode
73
73
  assert_equal 0777, File.umask
74
- ensure
75
- File.umask(old_umask)
74
+ ensure
75
+ File.umask(old_umask)
76
76
  end
77
77
 
78
78
  def test_bind_listen_unix_idempotent
@@ -116,7 +116,7 @@ class TestSocketHelper < Test::Unit::TestCase
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)
@@ -150,28 +150,31 @@ class TestSocketHelper < Test::Unit::TestCase
150
150
  end
151
151
 
152
152
  def test_tcp_defer_accept_default
153
+ return unless defined?(TCP_DEFER_ACCEPT)
153
154
  port = unused_port @test_addr
154
155
  name = "#@test_addr:#{port}"
155
156
  sock = bind_listen(name)
156
157
  cur = sock.getsockopt(Socket::SOL_TCP, TCP_DEFER_ACCEPT).unpack('i')[0]
157
158
  assert cur >= 1
158
- end if defined?(TCP_DEFER_ACCEPT)
159
+ end
159
160
 
160
161
  def test_tcp_defer_accept_disable
162
+ return unless defined?(TCP_DEFER_ACCEPT)
161
163
  port = unused_port @test_addr
162
164
  name = "#@test_addr:#{port}"
163
165
  sock = bind_listen(name, :tcp_defer_accept => false)
164
166
  cur = sock.getsockopt(Socket::SOL_TCP, TCP_DEFER_ACCEPT).unpack('i')[0]
165
167
  assert_equal 0, cur
166
- end if defined?(TCP_DEFER_ACCEPT)
168
+ end
167
169
 
168
170
  def test_tcp_defer_accept_nr
171
+ return unless defined?(TCP_DEFER_ACCEPT)
169
172
  port = unused_port @test_addr
170
173
  name = "#@test_addr:#{port}"
171
174
  sock = bind_listen(name, :tcp_defer_accept => 60)
172
175
  cur = sock.getsockopt(Socket::SOL_TCP, TCP_DEFER_ACCEPT).unpack('i')[0]
173
176
  assert cur > 1
174
- end if defined?(TCP_DEFER_ACCEPT)
177
+ end
175
178
 
176
179
  def test_ipv6only
177
180
  port = begin
@@ -186,6 +189,7 @@ class TestSocketHelper < Test::Unit::TestCase
186
189
  end
187
190
 
188
191
  def test_reuseport
192
+ return unless defined?(Socket::SO_REUSEPORT)
189
193
  port = unused_port @test_addr
190
194
  name = "#@test_addr:#{port}"
191
195
  sock = bind_listen(name, :reuseport => true)
@@ -193,5 +197,5 @@ class TestSocketHelper < Test::Unit::TestCase
193
197
  assert_operator cur, :>, 0
194
198
  rescue Errno::ENOPROTOOPT
195
199
  # kernel does not support SO_REUSEPORT (older Linux)
196
- end if defined?(Socket::SO_REUSEPORT)
200
+ end
197
201
  end
@@ -60,7 +60,7 @@ class UploadTest < Test::Unit::TestCase
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 @@ class UploadTest < Test::Unit::TestCase
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 @@ class UploadTest < Test::Unit::TestCase
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 @@ class UploadTest < Test::Unit::TestCase
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 @@ class UploadTest < Test::Unit::TestCase
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
 
@@ -236,15 +236,10 @@ class UploadTest < Test::Unit::TestCase
236
236
  resp = Tempfile.new('resp')
237
237
  resp.sync = true
238
238
 
239
- rd, wr = IO.pipe
240
- wr.sync = rd.sync = true
241
- pid = fork {
242
- STDIN.reopen(rd)
243
- rd.close
244
- wr.close
245
- STDOUT.reopen(resp)
246
- exec cmd
247
- }
239
+ rd, wr = IO.pipe.each do |io|
240
+ io.sync = io.close_on_exec = true
241
+ end
242
+ pid = spawn(*cmd, { 0 => rd, 1 => resp })
248
243
  rd.close
249
244
 
250
245
  tmp.rewind
@@ -51,7 +51,7 @@ class TestUtil < Test::Unit::TestCase
51
51
  def test_reopen_logs_renamed_with_encoding
52
52
  tmp = Tempfile.new('')
53
53
  tmp_path = tmp.path.dup.freeze
54
- Encoding.list.each { |encoding|
54
+ Encoding.list.sample(5).each { |encoding|
55
55
  File.open(tmp_path, "a:#{encoding.to_s}") { |fp|
56
56
  fp.sync = true
57
57
  assert_equal encoding, fp.external_encoding
@@ -69,13 +69,14 @@ class TestUtil < Test::Unit::TestCase
69
69
  }
70
70
  }
71
71
  tmp.close!
72
- end if STDIN.respond_to?(:external_encoding)
72
+ end
73
73
 
74
74
  def test_reopen_logs_renamed_with_internal_encoding
75
75
  tmp = Tempfile.new('')
76
76
  tmp_path = tmp.path.dup.freeze
77
- Encoding.list.each { |ext|
78
- Encoding.list.each { |int|
77
+ full = Encoding.list
78
+ full.sample(2).each { |ext|
79
+ full.sample(2).each { |int|
79
80
  next if ext == int
80
81
  File.open(tmp_path, "a:#{ext.to_s}:#{int.to_s}") { |fp|
81
82
  fp.sync = true
@@ -101,5 +102,30 @@ class TestUtil < Test::Unit::TestCase
101
102
  }
102
103
  }
103
104
  tmp.close!
104
- end if STDIN.respond_to?(:external_encoding)
105
+ end
106
+
107
+ def test_pipe
108
+ r, w = Unicorn.pipe
109
+ assert r
110
+ assert w
111
+
112
+ return if RUBY_PLATFORM !~ /linux/
113
+
114
+ begin
115
+ f_getpipe_sz = 1032
116
+ IO.pipe do |a, b|
117
+ a_sz = a.fcntl(f_getpipe_sz)
118
+ b.fcntl(f_getpipe_sz)
119
+ assert_kind_of Integer, a_sz
120
+ r_sz = r.fcntl(f_getpipe_sz)
121
+ assert_equal Raindrops::PAGE_SIZE, r_sz
122
+ assert_operator a_sz, :>=, r_sz
123
+ end
124
+ rescue Errno::EINVAL
125
+ # Linux <= 2.6.34
126
+ end
127
+ ensure
128
+ w.close
129
+ r.close
130
+ end
105
131
  end
@@ -0,0 +1,34 @@
1
+ require 'test/unit'
2
+ require 'unicorn'
3
+ require 'unicorn/select_waiter'
4
+ class TestSelectWaiter < Test::Unit::TestCase
5
+
6
+ def test_select_timeout # n.b. this is level-triggered
7
+ sw = Unicorn::SelectWaiter.new
8
+ IO.pipe do |r,w|
9
+ sw.get_readers(ready = [], [r], 0)
10
+ assert_equal [], ready
11
+ w.syswrite '.'
12
+ sw.get_readers(ready, [r], 1000)
13
+ assert_equal [r], ready
14
+ sw.get_readers(ready, [r], 0)
15
+ assert_equal [r], ready
16
+ end
17
+ end
18
+
19
+ def test_linux # ugh, also level-triggered, unlikely to change
20
+ IO.pipe do |r,w|
21
+ wtr = Unicorn::Waiter.prep_readers([r])
22
+ wtr.get_readers(ready = [], [r], 0)
23
+ assert_equal [], ready
24
+ w.syswrite '.'
25
+ wtr.get_readers(ready = [], [r], 1000)
26
+ assert_equal [r], ready
27
+ wtr.get_readers(ready = [], [r], 1000)
28
+ assert_equal [r], ready, 'still ready (level-triggered :<)'
29
+ assert_nil wtr.close
30
+ end
31
+ rescue SystemCallError => e
32
+ warn "#{e.message} (#{e.class})"
33
+ end if Unicorn.const_defined?(:Waiter)
34
+ end
data/unicorn.gemspec CHANGED
@@ -1,9 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
- ENV["VERSION"] or abort "VERSION= must be specified"
3
- manifest = File.readlines('.manifest').map! { |x| x.chomp! }
4
- require 'olddoc'
5
- extend Olddoc::Gemspec
6
- name, summary, title = readme_metadata
2
+ manifest = File.exist?('.manifest') ?
3
+ IO.readlines('.manifest').map!(&:chomp!) : `git ls-files`.split("\n")
7
4
 
8
5
  # don't bother with tests that fork, not worth our time to get working
9
6
  # with `gem check -t` ... (of course we care for them when testing with
@@ -14,33 +11,35 @@ end.compact
14
11
 
15
12
  Gem::Specification.new do |s|
16
13
  s.name = %q{unicorn}
17
- s.version = ENV["VERSION"].dup
18
- s.authors = ["#{name} hackers"]
19
- s.summary = summary
20
- s.description = readme_description
21
- s.email = %q{unicorn-public@bogomips.org}
14
+ s.version = (ENV['VERSION'] || '6.1.0').dup
15
+ s.authors = ['unicorn hackers']
16
+ s.summary = 'Rack HTTP server for fast clients and Unix'
17
+ s.description = File.read('README').split("\n\n")[1]
18
+ s.email = %q{unicorn-public@yhbt.net}
22
19
  s.executables = %w(unicorn unicorn_rails)
23
20
  s.extensions = %w(ext/unicorn_http/extconf.rb)
24
- s.extra_rdoc_files = extra_rdoc_files(manifest)
21
+ s.extra_rdoc_files = IO.readlines('.document').map!(&:chomp!).keep_if do |f|
22
+ File.exist?(f)
23
+ end
25
24
  s.files = manifest
26
- s.homepage = Olddoc.config['rdoc_url']
25
+ s.homepage = 'https://yhbt.net/unicorn/'
27
26
  s.test_files = test_files
28
27
 
29
- # technically we need ">= 1.9.3", too, but avoid the array here since
30
- # old rubygems versions (1.8.23.2 at least) do not support multiple
31
- # version requirements here.
32
- s.required_ruby_version = '< 3.0'
28
+ # 2.0.0 is the minimum 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 = ">= 2.0.0"
33
+
34
+ # We do not have a hard dependency on rack, it's possible to load
35
+ # things which respond to #call. HTTP status lines in responses
36
+ # won't have descriptive text, only the numeric status.
37
+ s.add_development_dependency(%q<rack>)
33
38
 
34
- # for people that are absolutely stuck on Rails 2.3.2 and can't
35
- # up/downgrade to any other version, the Rack dependency may be
36
- # commented out. Nevertheless, upgrading to Rails 2.3.4 or later is
37
- # *strongly* recommended for security reasons.
38
- s.add_dependency(%q<rack>)
39
39
  s.add_dependency(%q<kgio>, '~> 2.6')
40
40
  s.add_dependency(%q<raindrops>, '~> 0.7')
41
41
 
42
42
  s.add_development_dependency('test-unit', '~> 3.0')
43
- s.add_development_dependency('olddoc', '~> 1.0')
44
43
 
45
44
  # Note: To avoid ambiguity, we intentionally avoid the SPDX-compatible
46
45
  # 'Ruby' here since Ruby 1.9.3 switched to BSD-2-Clause, but we
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.0.1
4
+ version: 6.1.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: 2015-11-17 00:00:00.000000000 Z
11
+ date: 2021-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -17,7 +17,7 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
- type: :runtime
20
+ type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
@@ -66,27 +66,13 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
- - !ruby/object:Gem::Dependency
70
- name: olddoc
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '1.0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '1.0'
83
69
  description: |-
84
70
  unicorn is an HTTP server for Rack applications designed to only serve
85
71
  fast clients on low-latency, high-bandwidth connections and take
86
72
  advantage of features in Unix/Unix-like kernels. Slow clients should
87
73
  only be served by placing a reverse proxy capable of fully buffering
88
74
  both the the request and response in between unicorn and slow clients.
89
- email: unicorn-public@bogomips.org
75
+ email: unicorn-public@yhbt.net
90
76
  executables:
91
77
  - unicorn
92
78
  - unicorn_rails
@@ -134,9 +120,8 @@ files:
134
120
  - COPYING
135
121
  - DESIGN
136
122
  - Documentation/.gitignore
137
- - Documentation/GNUmakefile
138
- - Documentation/unicorn.1.txt
139
- - Documentation/unicorn_rails.1.txt
123
+ - Documentation/unicorn.1
124
+ - Documentation/unicorn_rails.1
140
125
  - FAQ
141
126
  - GIT-VERSION-FILE
142
127
  - GIT-VERSION-GEN
@@ -172,6 +157,7 @@ files:
172
157
  - ext/unicorn_http/CFLAGS
173
158
  - ext/unicorn_http/c_util.h
174
159
  - ext/unicorn_http/common_field_optimization.h
160
+ - ext/unicorn_http/epollexclusive.h
175
161
  - ext/unicorn_http/ext_help.h
176
162
  - ext/unicorn_http/extconf.rb
177
163
  - ext/unicorn_http/global_variables.h
@@ -191,6 +177,7 @@ files:
191
177
  - lib/unicorn/launcher.rb
192
178
  - lib/unicorn/oob_gc.rb
193
179
  - lib/unicorn/preread_input.rb
180
+ - lib/unicorn/select_waiter.rb
194
181
  - lib/unicorn/socket_helper.rb
195
182
  - lib/unicorn/stream_input.rb
196
183
  - lib/unicorn/tee_input.rb
@@ -212,7 +199,6 @@ files:
212
199
  - t/env.ru
213
200
  - t/fails-rack-lint.ru
214
201
  - t/heartbeat-timeout.ru
215
- - t/hijack.ru
216
202
  - t/listener_names.ru
217
203
  - t/my-tap-lib.sh
218
204
  - t/oob_gc.ru
@@ -250,8 +236,9 @@ files:
250
236
  - t/t0100-rack-input-tests.sh
251
237
  - t/t0116-client_body_buffer_size.sh
252
238
  - t/t0116.ru
253
- - t/t0200-rack-hijack.sh
254
239
  - t/t0300-no-default-middleware.sh
240
+ - t/t0301-no-default-middleware-ignored-in-config.sh
241
+ - t/t0301.ru
255
242
  - t/t9000-preread-input.sh
256
243
  - t/t9001-oob_gc.sh
257
244
  - t/t9002-oob_gc-path.sh
@@ -260,10 +247,14 @@ files:
260
247
  - test/aggregate.rb
261
248
  - test/benchmark/README
262
249
  - test/benchmark/dd.ru
250
+ - test/benchmark/ddstream.ru
251
+ - test/benchmark/readinput.ru
263
252
  - test/benchmark/stack.ru
253
+ - test/benchmark/uconnect.perl
264
254
  - test/exec/README
265
255
  - test/exec/test_exec.rb
266
256
  - test/test_helper.rb
257
+ - test/unit/test_ccc.rb
267
258
  - test/unit/test_configurator.rb
268
259
  - test/unit/test_droplet.rb
269
260
  - test/unit/test_http_parser.rb
@@ -277,10 +268,11 @@ files:
277
268
  - test/unit/test_tee_input.rb
278
269
  - test/unit/test_upload.rb
279
270
  - test/unit/test_util.rb
271
+ - test/unit/test_waiter.rb
280
272
  - unicorn.gemspec
281
273
  - unicorn_1
282
274
  - unicorn_rails_1
283
- homepage: http://unicorn.bogomips.org/
275
+ homepage: https://yhbt.net/unicorn/
284
276
  licenses:
285
277
  - GPL-2.0+
286
278
  - Ruby-1.8
@@ -291,17 +283,16 @@ require_paths:
291
283
  - lib
292
284
  required_ruby_version: !ruby/object:Gem::Requirement
293
285
  requirements:
294
- - - "<"
286
+ - - ">="
295
287
  - !ruby/object:Gem::Version
296
- version: '3.0'
288
+ version: 2.0.0
297
289
  required_rubygems_version: !ruby/object:Gem::Requirement
298
290
  requirements:
299
291
  - - ">="
300
292
  - !ruby/object:Gem::Version
301
293
  version: '0'
302
294
  requirements: []
303
- rubyforge_project:
304
- rubygems_version: 2.5.0
295
+ rubygems_version: 3.0.2
305
296
  signing_key:
306
297
  specification_version: 4
307
298
  summary: Rack HTTP server for fast clients and Unix
@@ -311,4 +302,6 @@ test_files:
311
302
  - test/unit/test_http_parser_ng.rb
312
303
  - test/unit/test_request.rb
313
304
  - test/unit/test_server.rb
305
+ - test/unit/test_upload.rb
314
306
  - test/unit/test_util.rb
307
+ - test/unit/test_waiter.rb