unicorn 5.0.1 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
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