unicorn 4.4.0 → 4.5.0pre1
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.
- data/GIT-VERSION-GEN +1 -1
- data/Rakefile +2 -2
- data/examples/unicorn.conf.rb +8 -0
- data/ext/unicorn_http/unicorn_http.rl +3 -1
- data/lib/unicorn/configurator.rb +24 -0
- data/lib/unicorn/const.rb +9 -5
- data/lib/unicorn/http_request.rb +19 -0
- data/lib/unicorn/http_response.rb +4 -2
- data/lib/unicorn/http_server.rb +21 -2
- data/test/exec/test_exec.rb +6 -1
- data/test/test_helper.rb +14 -17
- data/test/unit/test_configurator.rb +29 -12
- data/test/unit/test_http_parser.rb +2 -8
- data/test/unit/test_http_parser_ng.rb +5 -7
- data/test/unit/test_request.rb +24 -39
- data/test/unit/test_server.rb +49 -70
- data/test/unit/test_signals.rb +37 -56
- data/test/unit/test_socket_helper.rb +12 -17
- data/test/unit/test_stream_input.rb +4 -5
- data/test/unit/test_tee_input.rb +11 -13
- data/test/unit/test_upload.rb +1 -1
- metadata +13 -9
data/GIT-VERSION-GEN
CHANGED
data/Rakefile
CHANGED
@@ -49,7 +49,7 @@ task :fm_update do
|
|
49
49
|
require 'net/netrc'
|
50
50
|
require 'json'
|
51
51
|
version = ENV['VERSION'] or abort "VERSION= needed"
|
52
|
-
uri = URI.parse('
|
52
|
+
uri = URI.parse('https://freecode.com/projects/unicorn/releases.json')
|
53
53
|
rc = Net::Netrc.locate('unicorn-fm') or abort "~/.netrc not found"
|
54
54
|
api_token = rc.password
|
55
55
|
_, subject, body = `git cat-file tag v#{version}`.split(/\n\n/, 3)
|
@@ -71,7 +71,7 @@ task :fm_update do
|
|
71
71
|
}.to_json
|
72
72
|
|
73
73
|
if ! changelog.strip.empty? && version =~ %r{\A[\d\.]+\d+\z}
|
74
|
-
Net::HTTP.start(uri.host, uri.port) do |http|
|
74
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
|
75
75
|
p http.post(uri.path, req, {'Content-Type'=>'application/json'})
|
76
76
|
end
|
77
77
|
else
|
data/examples/unicorn.conf.rb
CHANGED
@@ -46,6 +46,14 @@ preload_app true
|
|
46
46
|
GC.respond_to?(:copy_on_write_friendly=) and
|
47
47
|
GC.copy_on_write_friendly = true
|
48
48
|
|
49
|
+
# Enable this flag to have unicorn test client connections by writing the
|
50
|
+
# beginning of the HTTP headers before calling the application. This
|
51
|
+
# prevents calling the application for connections that have disconnected
|
52
|
+
# while queued. This is only guaranteed to detect clients on the same
|
53
|
+
# host unicorn runs on, and unlikely to detect disconnects even on a
|
54
|
+
# fast LAN.
|
55
|
+
check_client_connection false
|
56
|
+
|
49
57
|
before_fork do |server, worker|
|
50
58
|
# the following is highly recomended for Rails + "preload_app true"
|
51
59
|
# as there's no need for the master process to hold a connection
|
@@ -115,7 +115,7 @@ struct http_parser {
|
|
115
115
|
} len;
|
116
116
|
};
|
117
117
|
|
118
|
-
static ID id_clear, id_set_backtrace;
|
118
|
+
static ID id_clear, id_set_backtrace, id_response_start_sent;
|
119
119
|
|
120
120
|
static void finalize_header(struct http_parser *hp);
|
121
121
|
|
@@ -626,6 +626,7 @@ static VALUE HttpParser_clear(VALUE self)
|
|
626
626
|
|
627
627
|
http_parser_init(hp);
|
628
628
|
rb_funcall(hp->env, id_clear, 0);
|
629
|
+
rb_ivar_set(self, id_response_start_sent, Qfalse);
|
629
630
|
|
630
631
|
return self;
|
631
632
|
}
|
@@ -1031,6 +1032,7 @@ void Init_unicorn_http(void)
|
|
1031
1032
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
1032
1033
|
id_clear = rb_intern("clear");
|
1033
1034
|
id_set_backtrace = rb_intern("set_backtrace");
|
1035
|
+
id_response_start_sent = rb_intern("@response_start_sent");
|
1034
1036
|
init_unicorn_httpdate();
|
1035
1037
|
}
|
1036
1038
|
#undef SET_GLOBAL
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -45,6 +45,7 @@ class Unicorn::Configurator
|
|
45
45
|
},
|
46
46
|
:pid => nil,
|
47
47
|
:preload_app => false,
|
48
|
+
:check_client_connection => false,
|
48
49
|
:rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
|
49
50
|
:client_body_buffer_size => Unicorn::Const::MAX_BODY,
|
50
51
|
:trust_x_forwarded => true,
|
@@ -96,6 +97,14 @@ class Unicorn::Configurator
|
|
96
97
|
if ready_pipe = RACKUP.delete(:ready_pipe)
|
97
98
|
server.ready_pipe = ready_pipe
|
98
99
|
end
|
100
|
+
if set[:check_client_connection]
|
101
|
+
set[:listeners].each do |address|
|
102
|
+
if set[:listener_opts][address][:tcp_nopush] == true
|
103
|
+
raise ArgumentError,
|
104
|
+
"check_client_connection is incompatible with tcp_nopush:true"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
99
108
|
set.each do |key, value|
|
100
109
|
value == :unset and next
|
101
110
|
skip.include?(key) and next
|
@@ -454,6 +463,21 @@ class Unicorn::Configurator
|
|
454
463
|
set_int(:client_body_buffer_size, bytes, 0)
|
455
464
|
end
|
456
465
|
|
466
|
+
# When enabled, unicorn will check the client connection by writing
|
467
|
+
# the beginning of the HTTP headers before calling the application.
|
468
|
+
#
|
469
|
+
# This will prevent calling the application for clients who have
|
470
|
+
# disconnected while their connection was queued.
|
471
|
+
#
|
472
|
+
# This only affects clients connecting over Unix domain sockets
|
473
|
+
# and TCP via loopback (127.*.*.*). It is unlikely to detect
|
474
|
+
# disconnects if the client is on a remote host (even on a fast LAN).
|
475
|
+
#
|
476
|
+
# This option cannot be used in conjunction with :tcp_nopush.
|
477
|
+
def check_client_connection(bool)
|
478
|
+
set_bool(:check_client_connection, bool)
|
479
|
+
end
|
480
|
+
|
457
481
|
# Allow redirecting $stderr to a given path. Unlike doing this from
|
458
482
|
# the shell, this allows the unicorn process to know the path its
|
459
483
|
# writing to and rotate the file if it is used for logging. The
|
data/lib/unicorn/const.rb
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
# improve things much compared to constants.
|
9
9
|
module Unicorn::Const
|
10
10
|
|
11
|
-
UNICORN_VERSION = "4.
|
11
|
+
UNICORN_VERSION = "4.5.0pre1"
|
12
12
|
|
13
13
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
14
14
|
DEFAULT_HOST = "0.0.0.0"
|
@@ -29,12 +29,16 @@ module Unicorn::Const
|
|
29
29
|
|
30
30
|
# :stopdoc:
|
31
31
|
# common errors we'll send back
|
32
|
-
ERROR_400_RESPONSE = "
|
33
|
-
ERROR_414_RESPONSE = "
|
34
|
-
ERROR_413_RESPONSE = "
|
35
|
-
ERROR_500_RESPONSE = "
|
32
|
+
ERROR_400_RESPONSE = "400 Bad Request\r\n\r\n"
|
33
|
+
ERROR_414_RESPONSE = "414 Request-URI Too Long\r\n\r\n"
|
34
|
+
ERROR_413_RESPONSE = "413 Request Entity Too Large\r\n\r\n"
|
35
|
+
ERROR_500_RESPONSE = "500 Internal Server Error\r\n\r\n"
|
36
|
+
|
36
37
|
EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
|
38
|
+
EXPECT_100_RESPONSE_SUFFIXED = "100 Continue\r\n\r\nHTTP/1.1 "
|
37
39
|
|
40
|
+
HTTP_RESPONSE_START = ['HTTP', '/1.1 ']
|
38
41
|
HTTP_EXPECT = "HTTP_EXPECT"
|
42
|
+
|
39
43
|
# :startdoc:
|
40
44
|
end
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -22,11 +22,14 @@ class Unicorn::HttpParser
|
|
22
22
|
|
23
23
|
NULL_IO = StringIO.new("")
|
24
24
|
|
25
|
+
attr_accessor :response_start_sent
|
26
|
+
|
25
27
|
# :stopdoc:
|
26
28
|
# A frozen format for this is about 15% faster
|
27
29
|
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
28
30
|
RACK_INPUT = 'rack.input'.freeze
|
29
31
|
@@input_class = Unicorn::TeeInput
|
32
|
+
@@check_client_connection = false
|
30
33
|
|
31
34
|
def self.input_class
|
32
35
|
@@input_class
|
@@ -35,6 +38,15 @@ class Unicorn::HttpParser
|
|
35
38
|
def self.input_class=(klass)
|
36
39
|
@@input_class = klass
|
37
40
|
end
|
41
|
+
|
42
|
+
def self.check_client_connection
|
43
|
+
@@check_client_connection
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.check_client_connection=(bool)
|
47
|
+
@@check_client_connection = bool
|
48
|
+
end
|
49
|
+
|
38
50
|
# :startdoc:
|
39
51
|
|
40
52
|
# Does the majority of the IO processing. It has been written in
|
@@ -70,6 +82,13 @@ class Unicorn::HttpParser
|
|
70
82
|
# an Exception thrown from the parser will throw us out of the loop
|
71
83
|
false until add_parse(socket.kgio_read!(16384))
|
72
84
|
end
|
85
|
+
|
86
|
+
# detect if the socket is valid by writing a partial response:
|
87
|
+
if @@check_client_connection && headers?
|
88
|
+
@response_start_sent = true
|
89
|
+
Unicorn::Const::HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
90
|
+
end
|
91
|
+
|
73
92
|
e[RACK_INPUT] = 0 == content_length ?
|
74
93
|
NULL_IO : @@input_class.new(socket, self)
|
75
94
|
e.merge!(DEFAULTS)
|
@@ -18,11 +18,13 @@ module Unicorn::HttpResponse
|
|
18
18
|
CRLF = "\r\n"
|
19
19
|
|
20
20
|
# writes the rack_response to socket as an HTTP response
|
21
|
-
def http_response_write(socket, status, headers, body
|
21
|
+
def http_response_write(socket, status, headers, body,
|
22
|
+
response_start_sent=false)
|
22
23
|
status = CODES[status.to_i] || status
|
23
24
|
|
25
|
+
http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
|
24
26
|
if headers
|
25
|
-
buf = "
|
27
|
+
buf = "#{http_response_start}#{status}\r\n" \
|
26
28
|
"Date: #{httpdate}\r\n" \
|
27
29
|
"Status: #{status}\r\n" \
|
28
30
|
"Connection: close\r\n"
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -17,6 +17,7 @@ class Unicorn::HttpServer
|
|
17
17
|
:listener_opts, :preload_app,
|
18
18
|
:reexec_pid, :orig_app, :init_listeners,
|
19
19
|
:master_pid, :config, :ready_pipe, :user
|
20
|
+
|
20
21
|
attr_reader :pid, :logger
|
21
22
|
include Unicorn::SocketHelper
|
22
23
|
include Unicorn::HttpResponse
|
@@ -355,6 +356,14 @@ class Unicorn::HttpServer
|
|
355
356
|
Unicorn::HttpParser.trust_x_forwarded = bool
|
356
357
|
end
|
357
358
|
|
359
|
+
def check_client_connection
|
360
|
+
Unicorn::HttpRequest.check_client_connection
|
361
|
+
end
|
362
|
+
|
363
|
+
def check_client_connection=(bool)
|
364
|
+
Unicorn::HttpRequest.check_client_connection = bool
|
365
|
+
end
|
366
|
+
|
358
367
|
private
|
359
368
|
|
360
369
|
# wait for a signal hander to wake us up and then consume the pipe
|
@@ -524,23 +533,33 @@ class Unicorn::HttpServer
|
|
524
533
|
Unicorn.log_error(@logger, "app error", e)
|
525
534
|
Unicorn::Const::ERROR_500_RESPONSE
|
526
535
|
end
|
536
|
+
msg = "HTTP/1.1 #{msg}" unless @request.response_start_sent
|
527
537
|
client.kgio_trywrite(msg)
|
528
538
|
client.close
|
529
539
|
rescue
|
530
540
|
end
|
531
541
|
|
542
|
+
def expect_100_response
|
543
|
+
if @request.response_start_sent
|
544
|
+
Unicorn::Const::EXPECT_100_RESPONSE_SUFFIXED
|
545
|
+
else
|
546
|
+
Unicorn::Const::EXPECT_100_RESPONSE
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
532
550
|
# once a client is accepted, it is processed in its entirety here
|
533
551
|
# in 3 easy steps: read request, call app, write app response
|
534
552
|
def process_client(client)
|
535
553
|
status, headers, body = @app.call(env = @request.read(client))
|
536
554
|
|
537
555
|
if 100 == status.to_i
|
538
|
-
client.write(
|
556
|
+
client.write(expect_100_response)
|
539
557
|
env.delete(Unicorn::Const::HTTP_EXPECT)
|
540
558
|
status, headers, body = @app.call(env)
|
541
559
|
end
|
542
560
|
@request.headers? or headers = nil
|
543
|
-
http_response_write(client, status, headers, body
|
561
|
+
http_response_write(client, status, headers, body,
|
562
|
+
@request.response_start_sent)
|
544
563
|
client.shutdown # in case of fork() in Rack app
|
545
564
|
client.close # flush and uncork socket immediately, no keepalive
|
546
565
|
rescue => e
|
data/test/exec/test_exec.rb
CHANGED
@@ -871,13 +871,14 @@ EOF
|
|
871
871
|
wait_for_death(pid)
|
872
872
|
end
|
873
873
|
|
874
|
-
def hup_test_common(preload)
|
874
|
+
def hup_test_common(preload, check_client=false)
|
875
875
|
File.open("config.ru", "wb") { |fp| fp.syswrite(HI.gsub("HI", '#$$')) }
|
876
876
|
pid_file = Tempfile.new('pid')
|
877
877
|
ucfg = Tempfile.new('unicorn_test_config')
|
878
878
|
ucfg.syswrite("listen '#@addr:#@port'\n")
|
879
879
|
ucfg.syswrite("pid '#{pid_file.path}'\n")
|
880
880
|
ucfg.syswrite("preload_app true\n") if preload
|
881
|
+
ucfg.syswrite("check_client_connection true\n") if check_client
|
881
882
|
ucfg.syswrite("stderr_path 'test_stderr.#$$.log'\n")
|
882
883
|
ucfg.syswrite("stdout_path 'test_stdout.#$$.log'\n")
|
883
884
|
pid = xfork {
|
@@ -942,6 +943,10 @@ EOF
|
|
942
943
|
hup_test_common(false)
|
943
944
|
end
|
944
945
|
|
946
|
+
def test_check_client_hup
|
947
|
+
hup_test_common(false, true)
|
948
|
+
end
|
949
|
+
|
945
950
|
def test_default_listen_hup_holds_listener
|
946
951
|
default_listen_lock do
|
947
952
|
res, pid_path = default_listen_setup
|
data/test/test_helper.rb
CHANGED
@@ -155,9 +155,8 @@ end
|
|
155
155
|
|
156
156
|
def assert_shutdown(pid)
|
157
157
|
wait_master_ready("test_stderr.#{pid}.log")
|
158
|
-
|
159
|
-
status =
|
160
|
-
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
158
|
+
Process.kill(:QUIT, pid)
|
159
|
+
pid, status = Process.waitpid2(pid)
|
161
160
|
assert status.success?, "exited successfully"
|
162
161
|
end
|
163
162
|
|
@@ -191,8 +190,8 @@ end
|
|
191
190
|
def reexec_usr2_quit_test(pid, pid_file)
|
192
191
|
assert File.exist?(pid_file), "pid file OK"
|
193
192
|
assert ! File.exist?("#{pid_file}.oldbin"), "oldbin pid file"
|
194
|
-
|
195
|
-
|
193
|
+
Process.kill(:USR2, pid)
|
194
|
+
retry_hit(["http://#{@addr}:#{@port}/"])
|
196
195
|
wait_for_file("#{pid_file}.oldbin")
|
197
196
|
wait_for_file(pid_file)
|
198
197
|
|
@@ -202,35 +201,33 @@ def reexec_usr2_quit_test(pid, pid_file)
|
|
202
201
|
# kill old master process
|
203
202
|
assert_not_equal pid, new_pid
|
204
203
|
assert_equal pid, old_pid
|
205
|
-
|
206
|
-
|
204
|
+
Process.kill(:QUIT, old_pid)
|
205
|
+
retry_hit(["http://#{@addr}:#{@port}/"])
|
207
206
|
wait_for_death(old_pid)
|
208
207
|
assert_equal new_pid, File.read(pid_file).to_i
|
209
|
-
|
210
|
-
|
208
|
+
retry_hit(["http://#{@addr}:#{@port}/"])
|
209
|
+
Process.kill(:QUIT, new_pid)
|
211
210
|
end
|
212
211
|
|
213
212
|
def reexec_basic_test(pid, pid_file)
|
214
213
|
results = retry_hit(["http://#{@addr}:#{@port}/"])
|
215
214
|
assert_equal String, results[0].class
|
216
|
-
|
215
|
+
Process.kill(0, pid)
|
217
216
|
master_log = "#{@tmpdir}/test_stderr.#{pid}.log"
|
218
217
|
wait_master_ready(master_log)
|
219
218
|
File.truncate(master_log, 0)
|
220
219
|
nr = 50
|
221
220
|
kill_point = 2
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
i == kill_point and Process.kill(:HUP, pid)
|
226
|
-
end
|
221
|
+
nr.times do |i|
|
222
|
+
hit(["http://#{@addr}:#{@port}/#{i}"])
|
223
|
+
i == kill_point and Process.kill(:HUP, pid)
|
227
224
|
end
|
228
225
|
wait_master_ready(master_log)
|
229
226
|
assert File.exist?(pid_file), "pid=#{pid_file} exists"
|
230
227
|
new_pid = File.read(pid_file).to_i
|
231
228
|
assert_not_equal pid, new_pid
|
232
|
-
|
233
|
-
|
229
|
+
Process.kill(0, new_pid)
|
230
|
+
Process.kill(:QUIT, new_pid)
|
234
231
|
end
|
235
232
|
|
236
233
|
def wait_for_file(path)
|
@@ -9,7 +9,7 @@ TestStruct = Struct.new(
|
|
9
9
|
class TestConfigurator < Test::Unit::TestCase
|
10
10
|
|
11
11
|
def test_config_init
|
12
|
-
|
12
|
+
Unicorn::Configurator.new {}
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_expand_addr
|
@@ -66,7 +66,7 @@ class TestConfigurator < Test::Unit::TestCase
|
|
66
66
|
def test_config_defaults
|
67
67
|
cfg = Unicorn::Configurator.new(:use_defaults => true)
|
68
68
|
test_struct = TestStruct.new
|
69
|
-
|
69
|
+
cfg.commit!(test_struct)
|
70
70
|
Unicorn::Configurator::DEFAULTS.each do |key,value|
|
71
71
|
assert_equal value, test_struct.__send__(key)
|
72
72
|
end
|
@@ -76,7 +76,7 @@ class TestConfigurator < Test::Unit::TestCase
|
|
76
76
|
cfg = Unicorn::Configurator.new(:use_defaults => true)
|
77
77
|
skip = [ :logger ]
|
78
78
|
test_struct = TestStruct.new
|
79
|
-
|
79
|
+
cfg.commit!(test_struct, :skip => skip)
|
80
80
|
Unicorn::Configurator::DEFAULTS.each do |key,value|
|
81
81
|
next if skip.include?(key)
|
82
82
|
assert_equal value, test_struct.__send__(key)
|
@@ -89,12 +89,9 @@ class TestConfigurator < Test::Unit::TestCase
|
|
89
89
|
expect = { :sndbuf => 1, :rcvbuf => 2, :backlog => 10 }.freeze
|
90
90
|
listener = "127.0.0.1:12345"
|
91
91
|
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
92
|
-
cfg =
|
93
|
-
assert_nothing_raised do
|
94
|
-
cfg = Unicorn::Configurator.new(:config_file => tmp.path)
|
95
|
-
end
|
92
|
+
cfg = Unicorn::Configurator.new(:config_file => tmp.path)
|
96
93
|
test_struct = TestStruct.new
|
97
|
-
|
94
|
+
cfg.commit!(test_struct)
|
98
95
|
assert(listener_opts = test_struct.listener_opts)
|
99
96
|
assert_equal expect, listener_opts[listener]
|
100
97
|
end
|
@@ -124,9 +121,7 @@ class TestConfigurator < Test::Unit::TestCase
|
|
124
121
|
expect = { :delay => 0.5 }
|
125
122
|
listener = "127.0.0.1:12345"
|
126
123
|
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
127
|
-
|
128
|
-
Unicorn::Configurator.new(:config_file => tmp.path)
|
129
|
-
end
|
124
|
+
Unicorn::Configurator.new(:config_file => tmp.path)
|
130
125
|
end
|
131
126
|
|
132
127
|
def test_listen_option_int_delay
|
@@ -134,8 +129,30 @@ class TestConfigurator < Test::Unit::TestCase
|
|
134
129
|
expect = { :delay => 5 }
|
135
130
|
listener = "127.0.0.1:12345"
|
136
131
|
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
132
|
+
Unicorn::Configurator.new(:config_file => tmp.path)
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_check_client_connection
|
136
|
+
tmp = Tempfile.new('unicorn_config')
|
137
|
+
test_struct = TestStruct.new
|
138
|
+
tmp.syswrite("check_client_connection true\n")
|
139
|
+
|
137
140
|
assert_nothing_raised do
|
138
|
-
Unicorn::Configurator.new(:config_file => tmp.path)
|
141
|
+
Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
|
142
|
+
end
|
143
|
+
|
144
|
+
assert test_struct.check_client_connection
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_check_client_connection_with_tcp_bad
|
148
|
+
tmp = Tempfile.new('unicorn_config')
|
149
|
+
test_struct = TestStruct.new
|
150
|
+
listener = "127.0.0.1:12345"
|
151
|
+
tmp.syswrite("check_client_connection true\n")
|
152
|
+
tmp.syswrite("listen '#{listener}', :tcp_nopush => true\n")
|
153
|
+
|
154
|
+
assert_raises(ArgumentError) do
|
155
|
+
Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
|
139
156
|
end
|
140
157
|
end
|
141
158
|
|
@@ -689,10 +689,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
689
689
|
parser = HttpParser.new
|
690
690
|
req = parser.env
|
691
691
|
s = "#{m} /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
|
692
|
-
ok =
|
693
|
-
assert_nothing_raised do
|
694
|
-
ok = parser.headers(req, s)
|
695
|
-
end
|
692
|
+
ok = parser.headers(req, s)
|
696
693
|
assert ok
|
697
694
|
assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
|
698
695
|
assert_equal 'posts-17408', req['FRAGMENT']
|
@@ -708,10 +705,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
708
705
|
req = parser.env
|
709
706
|
get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
|
710
707
|
parser.buf << get
|
711
|
-
ok =
|
712
|
-
assert_nothing_raised do
|
713
|
-
ok = parser.parse
|
714
|
-
end
|
708
|
+
ok = parser.parse
|
715
709
|
assert ok
|
716
710
|
assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
|
717
711
|
assert_equal 'posts-17408', req['FRAGMENT']
|
@@ -34,7 +34,7 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
34
34
|
def test_connection_TE
|
35
35
|
@parser.buf << "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: TE\r\n"
|
36
36
|
@parser.buf << "TE: trailers\r\n\r\n"
|
37
|
-
|
37
|
+
@parser.parse
|
38
38
|
assert @parser.keepalive?
|
39
39
|
assert @parser.next?
|
40
40
|
end
|
@@ -94,10 +94,8 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
94
94
|
def test_default_keepalive_is_off
|
95
95
|
assert ! @parser.keepalive?
|
96
96
|
assert ! @parser.next?
|
97
|
-
|
98
|
-
|
99
|
-
@parser.parse
|
100
|
-
end
|
97
|
+
@parser.buf << "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
98
|
+
@parser.parse
|
101
99
|
assert @parser.keepalive?
|
102
100
|
@parser.clear
|
103
101
|
assert ! @parser.keepalive?
|
@@ -419,7 +417,7 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
419
417
|
req = @parser.env
|
420
418
|
assert_equal req, @parser.parse
|
421
419
|
assert_nil @parser.content_length
|
422
|
-
|
420
|
+
@parser.filter_body('', str)
|
423
421
|
assert ! @parser.keepalive?
|
424
422
|
end
|
425
423
|
|
@@ -427,7 +425,7 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
427
425
|
n = HttpParser::LENGTH_MAX
|
428
426
|
@parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
|
429
427
|
req = @parser.env
|
430
|
-
|
428
|
+
@parser.headers(req, @parser.buf)
|
431
429
|
assert_equal n, req['CONTENT_LENGTH'].to_i
|
432
430
|
assert ! @parser.keepalive?
|
433
431
|
end
|
data/test/unit/test_request.rb
CHANGED
@@ -30,47 +30,43 @@ class RequestTest < Test::Unit::TestCase
|
|
30
30
|
def test_options
|
31
31
|
client = MockRequest.new("OPTIONS * HTTP/1.1\r\n" \
|
32
32
|
"Host: foo\r\n\r\n")
|
33
|
-
|
34
|
-
assert_nothing_raised { env = @request.read(client) }
|
33
|
+
env = @request.read(client)
|
35
34
|
assert_equal '', env['REQUEST_PATH']
|
36
35
|
assert_equal '', env['PATH_INFO']
|
37
36
|
assert_equal '*', env['REQUEST_URI']
|
38
|
-
|
37
|
+
res = @lint.call(env)
|
39
38
|
end
|
40
39
|
|
41
40
|
def test_absolute_uri_with_query
|
42
41
|
client = MockRequest.new("GET http://e:3/x?y=z HTTP/1.1\r\n" \
|
43
42
|
"Host: foo\r\n\r\n")
|
44
|
-
|
45
|
-
assert_nothing_raised { env = @request.read(client) }
|
43
|
+
env = @request.read(client)
|
46
44
|
assert_equal '/x', env['REQUEST_PATH']
|
47
45
|
assert_equal '/x', env['PATH_INFO']
|
48
46
|
assert_equal 'y=z', env['QUERY_STRING']
|
49
|
-
|
47
|
+
res = @lint.call(env)
|
50
48
|
end
|
51
49
|
|
52
50
|
def test_absolute_uri_with_fragment
|
53
51
|
client = MockRequest.new("GET http://e:3/x#frag HTTP/1.1\r\n" \
|
54
52
|
"Host: foo\r\n\r\n")
|
55
|
-
|
56
|
-
assert_nothing_raised { env = @request.read(client) }
|
53
|
+
env = @request.read(client)
|
57
54
|
assert_equal '/x', env['REQUEST_PATH']
|
58
55
|
assert_equal '/x', env['PATH_INFO']
|
59
56
|
assert_equal '', env['QUERY_STRING']
|
60
57
|
assert_equal 'frag', env['FRAGMENT']
|
61
|
-
|
58
|
+
res = @lint.call(env)
|
62
59
|
end
|
63
60
|
|
64
61
|
def test_absolute_uri_with_query_and_fragment
|
65
62
|
client = MockRequest.new("GET http://e:3/x?a=b#frag HTTP/1.1\r\n" \
|
66
63
|
"Host: foo\r\n\r\n")
|
67
|
-
|
68
|
-
assert_nothing_raised { env = @request.read(client) }
|
64
|
+
env = @request.read(client)
|
69
65
|
assert_equal '/x', env['REQUEST_PATH']
|
70
66
|
assert_equal '/x', env['PATH_INFO']
|
71
67
|
assert_equal 'a=b', env['QUERY_STRING']
|
72
68
|
assert_equal 'frag', env['FRAGMENT']
|
73
|
-
|
69
|
+
res = @lint.call(env)
|
74
70
|
end
|
75
71
|
|
76
72
|
def test_absolute_uri_unsupported_schemes
|
@@ -82,48 +78,43 @@ class RequestTest < Test::Unit::TestCase
|
|
82
78
|
end
|
83
79
|
|
84
80
|
def test_x_forwarded_proto_https
|
85
|
-
res = env = nil
|
86
81
|
client = MockRequest.new("GET / HTTP/1.1\r\n" \
|
87
82
|
"X-Forwarded-Proto: https\r\n" \
|
88
83
|
"Host: foo\r\n\r\n")
|
89
|
-
|
84
|
+
env = @request.read(client)
|
90
85
|
assert_equal "https", env['rack.url_scheme']
|
91
|
-
|
86
|
+
res = @lint.call(env)
|
92
87
|
end
|
93
88
|
|
94
89
|
def test_x_forwarded_proto_http
|
95
|
-
res = env = nil
|
96
90
|
client = MockRequest.new("GET / HTTP/1.1\r\n" \
|
97
91
|
"X-Forwarded-Proto: http\r\n" \
|
98
92
|
"Host: foo\r\n\r\n")
|
99
|
-
|
93
|
+
env = @request.read(client)
|
100
94
|
assert_equal "http", env['rack.url_scheme']
|
101
|
-
|
95
|
+
res = @lint.call(env)
|
102
96
|
end
|
103
97
|
|
104
98
|
def test_x_forwarded_proto_invalid
|
105
|
-
res = env = nil
|
106
99
|
client = MockRequest.new("GET / HTTP/1.1\r\n" \
|
107
100
|
"X-Forwarded-Proto: ftp\r\n" \
|
108
101
|
"Host: foo\r\n\r\n")
|
109
|
-
|
102
|
+
env = @request.read(client)
|
110
103
|
assert_equal "http", env['rack.url_scheme']
|
111
|
-
|
104
|
+
res = @lint.call(env)
|
112
105
|
end
|
113
106
|
|
114
107
|
def test_rack_lint_get
|
115
108
|
client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
|
116
|
-
|
117
|
-
assert_nothing_raised { env = @request.read(client) }
|
109
|
+
env = @request.read(client)
|
118
110
|
assert_equal "http", env['rack.url_scheme']
|
119
111
|
assert_equal '127.0.0.1', env['REMOTE_ADDR']
|
120
|
-
|
112
|
+
res = @lint.call(env)
|
121
113
|
end
|
122
114
|
|
123
115
|
def test_no_content_stringio
|
124
116
|
client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
|
125
|
-
env =
|
126
|
-
assert_nothing_raised { env = @request.read(client) }
|
117
|
+
env = @request.read(client)
|
127
118
|
assert_equal StringIO, env['rack.input'].class
|
128
119
|
end
|
129
120
|
|
@@ -131,8 +122,7 @@ class RequestTest < Test::Unit::TestCase
|
|
131
122
|
client = MockRequest.new("PUT / HTTP/1.1\r\n" \
|
132
123
|
"Content-Length: 0\r\n" \
|
133
124
|
"Host: foo\r\n\r\n")
|
134
|
-
env =
|
135
|
-
assert_nothing_raised { env = @request.read(client) }
|
125
|
+
env = @request.read(client)
|
136
126
|
assert_equal StringIO, env['rack.input'].class
|
137
127
|
end
|
138
128
|
|
@@ -140,8 +130,7 @@ class RequestTest < Test::Unit::TestCase
|
|
140
130
|
client = MockRequest.new("PUT / HTTP/1.1\r\n" \
|
141
131
|
"Content-Length: 1\r\n" \
|
142
132
|
"Host: foo\r\n\r\n")
|
143
|
-
env =
|
144
|
-
assert_nothing_raised { env = @request.read(client) }
|
133
|
+
env = @request.read(client)
|
145
134
|
assert_equal Unicorn::TeeInput, env['rack.input'].class
|
146
135
|
end
|
147
136
|
|
@@ -152,10 +141,9 @@ class RequestTest < Test::Unit::TestCase
|
|
152
141
|
"Content-Length: 5\r\n" \
|
153
142
|
"\r\n" \
|
154
143
|
"abcde")
|
155
|
-
|
156
|
-
assert_nothing_raised { env = @request.read(client) }
|
144
|
+
env = @request.read(client)
|
157
145
|
assert ! env.include?(:http_body)
|
158
|
-
|
146
|
+
res = @lint.call(env)
|
159
147
|
end
|
160
148
|
|
161
149
|
def test_rack_lint_big_put
|
@@ -179,8 +167,7 @@ class RequestTest < Test::Unit::TestCase
|
|
179
167
|
"\r\n")
|
180
168
|
count.times { assert_equal bs, client.syswrite(buf) }
|
181
169
|
assert_equal 0, client.sysseek(0)
|
182
|
-
|
183
|
-
assert_nothing_raised { env = @request.read(client) }
|
170
|
+
env = @request.read(client)
|
184
171
|
assert ! env.include?(:http_body)
|
185
172
|
assert_equal length, env['rack.input'].size
|
186
173
|
count.times {
|
@@ -189,9 +176,7 @@ class RequestTest < Test::Unit::TestCase
|
|
189
176
|
assert_equal buf, tmp
|
190
177
|
}
|
191
178
|
assert_nil env['rack.input'].read(bs)
|
192
|
-
|
193
|
-
|
179
|
+
env['rack.input'].rewind
|
180
|
+
res = @lint.call(env)
|
194
181
|
end
|
195
|
-
|
196
182
|
end
|
197
|
-
|
data/test/unit/test_server.rb
CHANGED
@@ -92,14 +92,10 @@ class WebServerTest < Test::Unit::TestCase
|
|
92
92
|
@server = HttpServer.new(app, :listeners => [ "127.0.0.1:#@port"] )
|
93
93
|
@server.start
|
94
94
|
end
|
95
|
-
sock =
|
96
|
-
|
97
|
-
sock = TCPSocket.new('127.0.0.1', @port)
|
98
|
-
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
99
|
-
end
|
100
|
-
|
95
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
96
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
101
97
|
assert_match %r{\AHTTP/1.[01] 500\b}, sock.sysread(4096)
|
102
|
-
|
98
|
+
assert_nil sock.close
|
103
99
|
end
|
104
100
|
|
105
101
|
def test_simple_server
|
@@ -108,56 +104,48 @@ class WebServerTest < Test::Unit::TestCase
|
|
108
104
|
end
|
109
105
|
|
110
106
|
def test_client_shutdown_writes
|
111
|
-
sock = nil
|
112
|
-
buf = nil
|
113
107
|
bs = 15609315 * rand
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
sleep 0.05
|
127
|
-
end
|
128
|
-
# we wrote the entire request before shutting down, server should
|
129
|
-
# continue to process our request and never hit EOFError on our sock
|
130
|
-
sock.shutdown(Socket::SHUT_WR)
|
131
|
-
buf = sock.read
|
108
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
109
|
+
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
110
|
+
sock.syswrite("Host: example.com\r\n")
|
111
|
+
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
112
|
+
sock.syswrite("Trailer: X-Foo\r\n")
|
113
|
+
sock.syswrite("\r\n")
|
114
|
+
sock.syswrite("%x\r\n" % [ bs ])
|
115
|
+
sock.syswrite("F" * bs)
|
116
|
+
sock.syswrite("\r\n0\r\nX-")
|
117
|
+
"Foo: bar\r\n\r\n".each_byte do |x|
|
118
|
+
sock.syswrite x.chr
|
119
|
+
sleep 0.05
|
132
120
|
end
|
121
|
+
# we wrote the entire request before shutting down, server should
|
122
|
+
# continue to process our request and never hit EOFError on our sock
|
123
|
+
sock.shutdown(Socket::SHUT_WR)
|
124
|
+
buf = sock.read
|
133
125
|
assert_equal 'hello!\n', buf.split(/\r\n\r\n/).last
|
134
126
|
next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/"))
|
135
127
|
assert_equal 'hello!\n', next_client
|
136
128
|
lines = File.readlines("test_stderr.#$$.log")
|
137
129
|
assert lines.grep(/^Unicorn::ClientShutdown: /).empty?
|
138
|
-
|
130
|
+
assert_nil sock.close
|
139
131
|
end
|
140
132
|
|
141
133
|
def test_client_shutdown_write_truncates
|
142
|
-
sock = nil
|
143
|
-
buf = nil
|
144
134
|
bs = 15609315 * rand
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
sock.syswrite("F" * (bs / 2.0))
|
135
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
136
|
+
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
137
|
+
sock.syswrite("Host: example.com\r\n")
|
138
|
+
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
139
|
+
sock.syswrite("Trailer: X-Foo\r\n")
|
140
|
+
sock.syswrite("\r\n")
|
141
|
+
sock.syswrite("%x\r\n" % [ bs ])
|
142
|
+
sock.syswrite("F" * (bs / 2.0))
|
154
143
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
144
|
+
# shutdown prematurely, this will force the server to abort
|
145
|
+
# processing on us even during app dispatch
|
146
|
+
sock.shutdown(Socket::SHUT_WR)
|
147
|
+
IO.select([sock], nil, nil, 60) or raise "Timed out"
|
148
|
+
buf = sock.read
|
161
149
|
assert_equal "", buf
|
162
150
|
next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/"))
|
163
151
|
assert_equal 'hello!\n', next_client
|
@@ -165,27 +153,24 @@ class WebServerTest < Test::Unit::TestCase
|
|
165
153
|
lines = lines.grep(/^Unicorn::ClientShutdown: bytes_read=\d+/)
|
166
154
|
assert_equal 1, lines.size
|
167
155
|
assert_match %r{\AUnicorn::ClientShutdown: bytes_read=\d+ true$}, lines[0]
|
168
|
-
|
156
|
+
assert_nil sock.close
|
169
157
|
end
|
170
158
|
|
171
159
|
def test_client_malformed_body
|
172
|
-
sock = nil
|
173
160
|
bs = 15653984
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
sock.syswrite("F" * bs)
|
183
|
-
end
|
161
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
162
|
+
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
163
|
+
sock.syswrite("Host: example.com\r\n")
|
164
|
+
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
165
|
+
sock.syswrite("Trailer: X-Foo\r\n")
|
166
|
+
sock.syswrite("\r\n")
|
167
|
+
sock.syswrite("%x\r\n" % [ bs ])
|
168
|
+
sock.syswrite("F" * bs)
|
184
169
|
begin
|
185
170
|
File.open("/dev/urandom", "rb") { |fp| sock.syswrite(fp.sysread(16384)) }
|
186
171
|
rescue
|
187
172
|
end
|
188
|
-
|
173
|
+
assert_nil sock.close
|
189
174
|
next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/"))
|
190
175
|
assert_equal 'hello!\n', next_client
|
191
176
|
lines = File.readlines("test_stderr.#$$.log")
|
@@ -240,23 +225,17 @@ class WebServerTest < Test::Unit::TestCase
|
|
240
225
|
end
|
241
226
|
|
242
227
|
def test_bad_client_400
|
243
|
-
sock =
|
244
|
-
|
245
|
-
sock = TCPSocket.new('127.0.0.1', @port)
|
246
|
-
sock.syswrite("GET / HTTP/1.0\r\nHost: foo\rbar\r\n\r\n")
|
247
|
-
end
|
228
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
229
|
+
sock.syswrite("GET / HTTP/1.0\r\nHost: foo\rbar\r\n\r\n")
|
248
230
|
assert_match %r{\AHTTP/1.[01] 400\b}, sock.sysread(4096)
|
249
|
-
|
231
|
+
assert_nil sock.close
|
250
232
|
end
|
251
233
|
|
252
234
|
def test_http_0_9
|
253
|
-
sock =
|
254
|
-
|
255
|
-
sock = TCPSocket.new('127.0.0.1', @port)
|
256
|
-
sock.syswrite("GET /hello\r\n")
|
257
|
-
end
|
235
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
236
|
+
sock.syswrite("GET /hello\r\n")
|
258
237
|
assert_match 'hello!\n', sock.sysread(4096)
|
259
|
-
|
238
|
+
assert_nil sock.close
|
260
239
|
end
|
261
240
|
|
262
241
|
def test_header_is_too_long
|
data/test/unit/test_signals.rb
CHANGED
@@ -51,22 +51,19 @@ class SignalsTest < Test::Unit::TestCase
|
|
51
51
|
opts = @server_opts.merge(:timeout => 3)
|
52
52
|
redirect_test_io { HttpServer.new(app, opts).start.join }
|
53
53
|
}
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
File.unlink("test_stderr.#{pid}.log", "test_stdout.#{pid}.log")
|
68
|
-
t0 = Time.now
|
69
|
-
end
|
54
|
+
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
55
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
56
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
57
|
+
buf = sock.readpartial(4096)
|
58
|
+
assert_nil sock.close
|
59
|
+
buf =~ /\bX-Pid: (\d+)\b/ or raise Exception
|
60
|
+
child = $1.to_i
|
61
|
+
wait_master_ready("test_stderr.#{pid}.log")
|
62
|
+
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
63
|
+
Process.kill(:KILL, pid)
|
64
|
+
Process.waitpid(pid)
|
65
|
+
File.unlink("test_stderr.#{pid}.log", "test_stdout.#{pid}.log")
|
66
|
+
t0 = Time.now
|
70
67
|
assert child
|
71
68
|
assert t0
|
72
69
|
assert_raises(Errno::ESRCH) { loop { Process.kill(0, child); sleep 0.2 } }
|
@@ -80,17 +77,14 @@ class SignalsTest < Test::Unit::TestCase
|
|
80
77
|
app = lambda { |env| wr.syswrite('.'); sleep; [ 200, {}, [] ] }
|
81
78
|
redirect_test_io { HttpServer.new(app, @server_opts).start.join }
|
82
79
|
}
|
83
|
-
sock = buf = nil
|
84
80
|
wr.close
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
Process.waitpid(pid)
|
93
|
-
end
|
81
|
+
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
82
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
83
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
84
|
+
buf = rd.readpartial(1)
|
85
|
+
wait_master_ready("test_stderr.#{pid}.log")
|
86
|
+
Process.kill(:INT, pid)
|
87
|
+
Process.waitpid(pid)
|
94
88
|
assert_equal '.', buf
|
95
89
|
buf = nil
|
96
90
|
assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
|
@@ -98,7 +92,6 @@ class SignalsTest < Test::Unit::TestCase
|
|
98
92
|
buf = sock.sysread(4096)
|
99
93
|
end
|
100
94
|
assert_nil buf
|
101
|
-
ensure
|
102
95
|
end
|
103
96
|
|
104
97
|
def test_timeout_slow_response
|
@@ -108,12 +101,9 @@ class SignalsTest < Test::Unit::TestCase
|
|
108
101
|
redirect_test_io { HttpServer.new(app, opts).start.join }
|
109
102
|
}
|
110
103
|
t0 = Time.now
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
sock = TCPSocket.new('127.0.0.1', @port)
|
115
|
-
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
116
|
-
end
|
104
|
+
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
105
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
106
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
117
107
|
|
118
108
|
buf = nil
|
119
109
|
assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
|
@@ -134,19 +124,14 @@ class SignalsTest < Test::Unit::TestCase
|
|
134
124
|
Dd.new(@bs, @count) ]
|
135
125
|
}
|
136
126
|
redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
sock = TCPSocket.new('127.0.0.1', @port)
|
141
|
-
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
142
|
-
end
|
127
|
+
wait_workers_ready("test_stderr.#{$$}.log", 1)
|
128
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
129
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
143
130
|
buf = ''
|
144
131
|
header_len = pid = nil
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
header_len = buf[/\A(.+?\r\n\r\n)/m, 1].size
|
149
|
-
end
|
132
|
+
buf = sock.sysread(16384, buf)
|
133
|
+
pid = buf[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
|
134
|
+
header_len = buf[/\A(.+?\r\n\r\n)/m, 1].size
|
150
135
|
assert pid > 0, "pid not positive: #{pid.inspect}"
|
151
136
|
read = buf.size
|
152
137
|
size_before = @tmp.stat.size
|
@@ -166,7 +151,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
166
151
|
got = read - header_len
|
167
152
|
expect = @bs * @count
|
168
153
|
assert_equal(expect, got, "expect=#{expect} got=#{got}")
|
169
|
-
|
154
|
+
assert_nil sock.close
|
170
155
|
end
|
171
156
|
|
172
157
|
def test_request_read
|
@@ -176,15 +161,12 @@ class SignalsTest < Test::Unit::TestCase
|
|
176
161
|
[ 200, {'Content-Type'=>'text/plain', 'X-Pid'=>Process.pid.to_s}, [] ]
|
177
162
|
}
|
178
163
|
redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
|
186
|
-
sock.close
|
187
|
-
end
|
164
|
+
|
165
|
+
wait_workers_ready("test_stderr.#{$$}.log", 1)
|
166
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
167
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
168
|
+
pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
|
169
|
+
assert_nil sock.close
|
188
170
|
|
189
171
|
assert pid > 0, "pid not positive: #{pid.inspect}"
|
190
172
|
sock = TCPSocket.new('127.0.0.1', @port)
|
@@ -201,7 +183,6 @@ class SignalsTest < Test::Unit::TestCase
|
|
201
183
|
# can't check for == since pending signals get merged
|
202
184
|
assert size_before < @tmp.stat.size
|
203
185
|
assert_equal pid, sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
|
204
|
-
sock.close
|
186
|
+
assert_nil sock.close
|
205
187
|
end
|
206
|
-
|
207
188
|
end
|
@@ -37,16 +37,13 @@ class TestSocketHelper < Test::Unit::TestCase
|
|
37
37
|
[ { :backlog => 5 }, { :sndbuf => 4096 }, { :rcvbuf => 4096 },
|
38
38
|
{ :backlog => 16, :rcvbuf => 4096, :sndbuf => 4096 }
|
39
39
|
].each do |opts|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
unix_listener.close
|
47
|
-
end
|
40
|
+
tcp_listener = bind_listen(tcp_listener_name, opts)
|
41
|
+
assert TCPServer === tcp_listener
|
42
|
+
tcp_listener.close
|
43
|
+
unix_listener = bind_listen(unix_listener_name, opts)
|
44
|
+
assert UNIXServer === unix_listener
|
45
|
+
unix_listener.close
|
48
46
|
end
|
49
|
-
#system('cat', @log_tmp.path)
|
50
47
|
end
|
51
48
|
|
52
49
|
def test_bind_listen_unix
|
@@ -106,10 +103,10 @@ class TestSocketHelper < Test::Unit::TestCase
|
|
106
103
|
assert_raises(Errno::EADDRINUSE) do
|
107
104
|
new_listener = bind_listen(@unix_listener_path)
|
108
105
|
end
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
106
|
+
|
107
|
+
File.unlink(@unix_listener_path)
|
108
|
+
new_listener = bind_listen(@unix_listener_path)
|
109
|
+
|
113
110
|
assert UNIXServer === new_listener
|
114
111
|
assert new_listener.fileno != @unix_listener.fileno
|
115
112
|
assert_equal sock_name(new_listener), sock_name(@unix_listener)
|
@@ -127,10 +124,8 @@ class TestSocketHelper < Test::Unit::TestCase
|
|
127
124
|
end
|
128
125
|
|
129
126
|
def test_server_cast
|
130
|
-
|
131
|
-
|
132
|
-
test_bind_listen_tcp
|
133
|
-
end
|
127
|
+
test_bind_listen_unix
|
128
|
+
test_bind_listen_tcp
|
134
129
|
unix_listener_socket = Socket.for_fd(@unix_listener.fileno)
|
135
130
|
assert Socket === unix_listener_socket
|
136
131
|
@unix_server = server_cast(unix_listener_socket)
|
@@ -85,8 +85,7 @@ class TestStreamInput < Test::Unit::TestCase
|
|
85
85
|
assert_equal '....', si.read(4), "nr=#{x}"
|
86
86
|
}
|
87
87
|
assert_nil si.read(1)
|
88
|
-
status =
|
89
|
-
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
88
|
+
pid, status = Process.waitpid2(pid)
|
90
89
|
assert status.success?
|
91
90
|
end
|
92
91
|
|
@@ -101,13 +100,13 @@ class TestStreamInput < Test::Unit::TestCase
|
|
101
100
|
@wr.close
|
102
101
|
}
|
103
102
|
@wr.close
|
104
|
-
|
103
|
+
line = si.gets
|
105
104
|
assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
|
106
105
|
assert_equal("hello" << ("ffff" * 4096 * 3) << "#$/", line)
|
107
|
-
|
106
|
+
line = si.gets
|
108
107
|
assert_equal "foo#$/", line
|
109
108
|
assert_nil si.gets
|
110
|
-
|
109
|
+
pid, status = Process.waitpid2(pid)
|
111
110
|
assert status.success?
|
112
111
|
end
|
113
112
|
|
data/test/unit/test_tee_input.rb
CHANGED
@@ -40,13 +40,13 @@ class TestTeeInput < Test::Unit::TestCase
|
|
40
40
|
@wr.close
|
41
41
|
}
|
42
42
|
@wr.close
|
43
|
-
|
43
|
+
line = ti.gets
|
44
44
|
assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
|
45
45
|
assert_equal("hello" << ("ffff" * 4096 * 3) << "#$/", line)
|
46
|
-
|
46
|
+
line = ti.gets
|
47
47
|
assert_equal "foo#$/", line
|
48
48
|
assert_nil ti.gets
|
49
|
-
|
49
|
+
pid, status = Process.waitpid2(pid)
|
50
50
|
assert status.success?
|
51
51
|
end
|
52
52
|
|
@@ -60,12 +60,12 @@ class TestTeeInput < Test::Unit::TestCase
|
|
60
60
|
@wr.close
|
61
61
|
}
|
62
62
|
@wr.close
|
63
|
-
|
63
|
+
line = ti.gets
|
64
64
|
assert_equal("hello#$/", line)
|
65
|
-
|
65
|
+
line = ti.gets
|
66
66
|
assert_equal "foo", line
|
67
67
|
assert_nil ti.gets
|
68
|
-
|
68
|
+
pid, status = Process.waitpid2(pid)
|
69
69
|
assert status.success?
|
70
70
|
end
|
71
71
|
|
@@ -146,8 +146,7 @@ class TestTeeInput < Test::Unit::TestCase
|
|
146
146
|
assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
|
147
147
|
}
|
148
148
|
assert_nil ti.read(1)
|
149
|
-
status =
|
150
|
-
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
149
|
+
pid, status = Process.waitpid2(pid)
|
151
150
|
assert status.success?
|
152
151
|
end
|
153
152
|
|
@@ -174,15 +173,15 @@ class TestTeeInput < Test::Unit::TestCase
|
|
174
173
|
assert @parser.body_eof?
|
175
174
|
assert_equal 25, ti.len
|
176
175
|
assert_equal 0, ti.tmp.pos
|
177
|
-
|
176
|
+
ti.rewind
|
178
177
|
assert_equal 0, ti.tmp.pos
|
179
178
|
assert_equal 'abcdeabcdeabcdeabcde', ti.read(20)
|
180
179
|
assert_equal 20, ti.tmp.pos
|
181
|
-
|
180
|
+
ti.rewind
|
182
181
|
assert_equal 0, ti.tmp.pos
|
183
182
|
assert_kind_of File, ti.tmp
|
184
183
|
status = nil
|
185
|
-
|
184
|
+
pid, status = Process.waitpid2(pid)
|
186
185
|
assert status.success?
|
187
186
|
end
|
188
187
|
|
@@ -241,8 +240,7 @@ class TestTeeInput < Test::Unit::TestCase
|
|
241
240
|
assert ! @parser.body_eof?
|
242
241
|
assert_equal 25, ti.size
|
243
242
|
assert_equal "World", @parser.env['HTTP_HELLO']
|
244
|
-
status =
|
245
|
-
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
243
|
+
pid, status = Process.waitpid2(pid)
|
246
244
|
assert status.success?
|
247
245
|
end
|
248
246
|
|
data/test/unit/test_upload.rb
CHANGED
@@ -163,7 +163,7 @@ class UploadTest < Test::Unit::TestCase
|
|
163
163
|
assert_raise(Errno::ECONNRESET, Errno::EPIPE) do
|
164
164
|
::Unicorn::Const::CHUNK_SIZE.times { sock.syswrite(buf) }
|
165
165
|
end
|
166
|
-
|
166
|
+
sock.gets
|
167
167
|
tmp.rewind
|
168
168
|
assert_equal length, tmp.read.to_i
|
169
169
|
end
|
metadata
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: -223651717
|
5
|
+
prerelease: 5
|
6
6
|
segments:
|
7
7
|
- 4
|
8
|
-
-
|
8
|
+
- 5
|
9
9
|
- 0
|
10
|
-
|
10
|
+
- pre
|
11
|
+
- 1
|
12
|
+
version: 4.5.0pre1
|
11
13
|
platform: ruby
|
12
14
|
authors:
|
13
15
|
- Unicorn hackers
|
@@ -15,7 +17,7 @@ autorequire:
|
|
15
17
|
bindir: bin
|
16
18
|
cert_chain: []
|
17
19
|
|
18
|
-
date: 2012-
|
20
|
+
date: 2012-11-29 00:00:00 Z
|
19
21
|
dependencies:
|
20
22
|
- !ruby/object:Gem::Dependency
|
21
23
|
name: rack
|
@@ -318,12 +320,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
318
320
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
319
321
|
none: false
|
320
322
|
requirements:
|
321
|
-
- - "
|
323
|
+
- - ">"
|
322
324
|
- !ruby/object:Gem::Version
|
323
|
-
hash:
|
325
|
+
hash: 25
|
324
326
|
segments:
|
325
|
-
-
|
326
|
-
|
327
|
+
- 1
|
328
|
+
- 3
|
329
|
+
- 1
|
330
|
+
version: 1.3.1
|
327
331
|
requirements: []
|
328
332
|
|
329
333
|
rubyforge_project: mongrel
|