unicorn 5.3.1 → 5.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.manifest +2 -0
- data/.olddoc.yml +1 -1
- data/Application_Timeouts +4 -4
- data/Documentation/unicorn.1.txt +1 -1
- data/Documentation/unicorn_rails.1.txt +6 -8
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +6 -1
- data/ISSUES +10 -10
- data/LATEST +22 -102
- data/LICENSE +2 -2
- data/Links +9 -7
- data/NEWS +107 -0
- data/README +13 -6
- data/Sandbox +2 -2
- data/bin/unicorn +3 -1
- data/bin/unicorn_rails +2 -2
- data/examples/logrotate.conf +1 -1
- data/examples/nginx.conf +3 -2
- data/ext/unicorn_http/common_field_optimization.h +24 -6
- data/ext/unicorn_http/extconf.rb +30 -0
- data/ext/unicorn_http/global_variables.h +2 -2
- data/ext/unicorn_http/httpdate.c +2 -2
- data/ext/unicorn_http/unicorn_http.c +229 -219
- data/ext/unicorn_http/unicorn_http.rl +19 -9
- data/lib/unicorn/configurator.rb +13 -2
- data/lib/unicorn/http_request.rb +2 -2
- data/lib/unicorn/http_response.rb +3 -2
- data/lib/unicorn/http_server.rb +21 -23
- data/lib/unicorn/launcher.rb +1 -1
- data/lib/unicorn/socket_helper.rb +4 -3
- data/lib/unicorn/util.rb +3 -3
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +16 -2
- data/lib/unicorn.rb +26 -9
- data/man/man1/unicorn.1 +7 -5
- data/man/man1/unicorn_rails.1 +11 -11
- data/t/README +4 -4
- data/t/hijack.ru +12 -0
- data/t/t0200-rack-hijack.sh +22 -1
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +13 -0
- data/test/exec/test_exec.rb +6 -7
- data/test/unit/test_ccc.rb +1 -1
- data/test/unit/test_droplet.rb +1 -1
- data/test/unit/test_http_parser.rb +16 -0
- data/test/unit/test_request.rb +10 -10
- data/test/unit/test_server.rb +5 -5
- data/test/unit/test_signals.rb +2 -2
- data/test/unit/test_socket_helper.rb +4 -4
- data/test/unit/test_util.rb +25 -0
- data/unicorn.gemspec +1 -1
- metadata +5 -4
@@ -13,7 +13,7 @@
|
|
13
13
|
#include "global_variables.h"
|
14
14
|
#include "c_util.h"
|
15
15
|
|
16
|
-
void init_unicorn_httpdate(
|
16
|
+
void init_unicorn_httpdate(void);
|
17
17
|
|
18
18
|
#define UH_FL_CHUNKED 0x1
|
19
19
|
#define UH_FL_HASBODY 0x2
|
@@ -26,6 +26,7 @@ void init_unicorn_httpdate(VALUE mark_ary);
|
|
26
26
|
#define UH_FL_HASHEADER 0x100
|
27
27
|
#define UH_FL_TO_CLEAR 0x200
|
28
28
|
#define UH_FL_RESSTART 0x400 /* for check_client_connection */
|
29
|
+
#define UH_FL_HIJACK 0x800
|
29
30
|
|
30
31
|
/* all of these flags need to be set for keepalive to be supported */
|
31
32
|
#define UH_FL_KEEPALIVE (UH_FL_KAVERSION | UH_FL_REQEOF | UH_FL_HASHEADER)
|
@@ -607,6 +608,10 @@ static VALUE HttpParser_clear(VALUE self)
|
|
607
608
|
{
|
608
609
|
struct http_parser *hp = data_get(self);
|
609
610
|
|
611
|
+
/* we can't safely reuse .buf and .env if hijacked */
|
612
|
+
if (HP_FL_TEST(hp, HIJACK))
|
613
|
+
return HttpParser_init(self);
|
614
|
+
|
610
615
|
http_parser_init(hp);
|
611
616
|
my_hash_clear(hp->env);
|
612
617
|
|
@@ -813,6 +818,15 @@ static VALUE HttpParser_env(VALUE self)
|
|
813
818
|
return data_get(self)->env;
|
814
819
|
}
|
815
820
|
|
821
|
+
static VALUE HttpParser_hijacked_bang(VALUE self)
|
822
|
+
{
|
823
|
+
struct http_parser *hp = data_get(self);
|
824
|
+
|
825
|
+
HP_FL_SET(hp, HIJACK);
|
826
|
+
|
827
|
+
return self;
|
828
|
+
}
|
829
|
+
|
816
830
|
/**
|
817
831
|
* call-seq:
|
818
832
|
* parser.filter_body(dst, src) => nil/src
|
@@ -917,11 +931,8 @@ static VALUE HttpParser_rssget(VALUE self)
|
|
917
931
|
|
918
932
|
void Init_unicorn_http(void)
|
919
933
|
{
|
920
|
-
static VALUE mark_ary;
|
921
934
|
VALUE mUnicorn, cHttpParser;
|
922
935
|
|
923
|
-
mark_ary = rb_ary_new();
|
924
|
-
rb_global_variable(&mark_ary);
|
925
936
|
mUnicorn = rb_define_module("Unicorn");
|
926
937
|
cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
|
927
938
|
eHttpParserError =
|
@@ -931,7 +942,7 @@ void Init_unicorn_http(void)
|
|
931
942
|
e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
|
932
943
|
eHttpParserError);
|
933
944
|
|
934
|
-
init_globals(
|
945
|
+
init_globals();
|
935
946
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
936
947
|
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
937
948
|
rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
|
@@ -947,6 +958,7 @@ void Init_unicorn_http(void)
|
|
947
958
|
rb_define_method(cHttpParser, "next?", HttpParser_next, 0);
|
948
959
|
rb_define_method(cHttpParser, "buf", HttpParser_buf, 0);
|
949
960
|
rb_define_method(cHttpParser, "env", HttpParser_env, 0);
|
961
|
+
rb_define_method(cHttpParser, "hijacked!", HttpParser_hijacked_bang, 0);
|
950
962
|
rb_define_method(cHttpParser, "response_start_sent=", HttpParser_rssset, 1);
|
951
963
|
rb_define_method(cHttpParser, "response_start_sent", HttpParser_rssget, 0);
|
952
964
|
|
@@ -967,16 +979,14 @@ void Init_unicorn_http(void)
|
|
967
979
|
|
968
980
|
rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
|
969
981
|
|
970
|
-
init_common_fields(
|
982
|
+
init_common_fields();
|
971
983
|
SET_GLOBAL(g_http_host, "HOST");
|
972
984
|
SET_GLOBAL(g_http_trailer, "TRAILER");
|
973
985
|
SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
|
974
986
|
SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
|
975
987
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
976
988
|
id_set_backtrace = rb_intern("set_backtrace");
|
977
|
-
init_unicorn_httpdate(
|
978
|
-
|
979
|
-
OBJ_FREEZE(mark_ary);
|
989
|
+
init_unicorn_httpdate();
|
980
990
|
|
981
991
|
#ifndef HAVE_RB_HASH_CLEAR
|
982
992
|
id_clear = rb_intern("clear");
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -88,6 +88,9 @@ class Unicorn::Configurator
|
|
88
88
|
RACKUP[:set_listener] and
|
89
89
|
set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
|
90
90
|
|
91
|
+
RACKUP[:no_default_middleware] and
|
92
|
+
set[:default_middleware] = false
|
93
|
+
|
91
94
|
# unicorn_rails creates dirs here after working_directory is bound
|
92
95
|
after_reload.call if after_reload
|
93
96
|
|
@@ -235,7 +238,7 @@ class Unicorn::Configurator
|
|
235
238
|
# server 192.168.0.9:8080 fail_timeout=0;
|
236
239
|
# }
|
237
240
|
#
|
238
|
-
# See
|
241
|
+
# See https://nginx.org/en/docs/http/ngx_http_upstream_module.html
|
239
242
|
# for more details on nginx upstream configuration.
|
240
243
|
def timeout(seconds)
|
241
244
|
set_int(:timeout, seconds, 3)
|
@@ -265,6 +268,14 @@ class Unicorn::Configurator
|
|
265
268
|
set_int(:worker_processes, nr, 1)
|
266
269
|
end
|
267
270
|
|
271
|
+
# sets whether to add default middleware in the development and
|
272
|
+
# deployment RACK_ENVs.
|
273
|
+
#
|
274
|
+
# default_middleware is only available in unicorn 5.5.0+
|
275
|
+
def default_middleware(bool)
|
276
|
+
set_bool(:default_middleware, bool)
|
277
|
+
end
|
278
|
+
|
268
279
|
# sets listeners to the given +addresses+, replacing or augmenting the
|
269
280
|
# current set. This is for the global listener pool shared by all
|
270
281
|
# worker processes. For per-worker listeners, see the after_fork example
|
@@ -587,7 +598,7 @@ class Unicorn::Configurator
|
|
587
598
|
# just let chdir raise errors
|
588
599
|
path = File.expand_path(path)
|
589
600
|
if config_file &&
|
590
|
-
config_file
|
601
|
+
! config_file.start_with?('/') &&
|
591
602
|
! File.readable?("#{path}/#{config_file}")
|
592
603
|
raise ArgumentError,
|
593
604
|
"config_file=#{config_file} would not be accessible in" \
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
# no stable API here
|
4
4
|
require 'unicorn_http'
|
5
|
-
require 'raindrops'
|
6
5
|
|
7
6
|
# TODO: remove redundant names
|
8
7
|
Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
|
@@ -66,7 +65,7 @@ class Unicorn::HttpParser
|
|
66
65
|
clear
|
67
66
|
e = env
|
68
67
|
|
69
|
-
# From
|
68
|
+
# From https://www.ietf.org/rfc/rfc3875:
|
70
69
|
# "Script authors should be aware that the REMOTE_ADDR and
|
71
70
|
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
72
71
|
# may not identify the ultimate source of the request. They
|
@@ -98,6 +97,7 @@ class Unicorn::HttpParser
|
|
98
97
|
# for rack.hijack, we respond to this method so no extra allocation
|
99
98
|
# of a proc object
|
100
99
|
def call
|
100
|
+
hijacked!
|
101
101
|
env['rack.hijack_io'] = env['unicorn.socket']
|
102
102
|
end
|
103
103
|
|
@@ -21,13 +21,13 @@ module Unicorn::HttpResponse
|
|
21
21
|
|
22
22
|
# writes the rack_response to socket as an HTTP response
|
23
23
|
def http_response_write(socket, status, headers, body,
|
24
|
-
|
24
|
+
req = Unicorn::HttpRequest.new)
|
25
25
|
hijack = nil
|
26
26
|
|
27
27
|
if headers
|
28
28
|
code = status.to_i
|
29
29
|
msg = STATUS_CODES[code]
|
30
|
-
start = response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze
|
30
|
+
start = req.response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze
|
31
31
|
buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \
|
32
32
|
"Date: #{httpdate}\r\n" \
|
33
33
|
"Connection: close\r\n"
|
@@ -52,6 +52,7 @@ module Unicorn::HttpResponse
|
|
52
52
|
end
|
53
53
|
|
54
54
|
if hijack
|
55
|
+
req.hijacked!
|
55
56
|
hijack.call(socket)
|
56
57
|
else
|
57
58
|
body.each { |chunk| socket.write(chunk) }
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -14,7 +14,8 @@ class Unicorn::HttpServer
|
|
14
14
|
attr_accessor :app, :timeout, :worker_processes,
|
15
15
|
:before_fork, :after_fork, :before_exec,
|
16
16
|
:listener_opts, :preload_app,
|
17
|
-
:orig_app, :config, :ready_pipe, :user
|
17
|
+
:orig_app, :config, :ready_pipe, :user,
|
18
|
+
:default_middleware
|
18
19
|
attr_writer :after_worker_exit, :after_worker_ready, :worker_exec
|
19
20
|
|
20
21
|
attr_reader :pid, :logger
|
@@ -70,6 +71,7 @@ class Unicorn::HttpServer
|
|
70
71
|
@app = app
|
71
72
|
@request = Unicorn::HttpRequest.new
|
72
73
|
@reexec_pid = 0
|
74
|
+
@default_middleware = true
|
73
75
|
options = options.dup
|
74
76
|
@ready_pipe = options.delete(:ready_pipe)
|
75
77
|
@init_listeners = options[:listeners] ? options[:listeners].dup : []
|
@@ -82,7 +84,7 @@ class Unicorn::HttpServer
|
|
82
84
|
# * The master process never closes or reinitializes this once
|
83
85
|
# initialized. Signal handlers in the master process will write to
|
84
86
|
# it to wake up the master from IO.select in exactly the same manner
|
85
|
-
# djb describes in
|
87
|
+
# djb describes in https://cr.yp.to/docs/selfpipe.html
|
86
88
|
#
|
87
89
|
# * The workers immediately close the pipe they inherit. See the
|
88
90
|
# Unicorn::Worker class for the pipe workers use.
|
@@ -148,7 +150,7 @@ class Unicorn::HttpServer
|
|
148
150
|
def listeners=(listeners)
|
149
151
|
cur_names, dead_names = [], []
|
150
152
|
listener_names.each do |name|
|
151
|
-
if
|
153
|
+
if name.start_with?('/')
|
152
154
|
# mark unlinked sockets as dead so we can rebind them
|
153
155
|
(File.socket?(name) ? cur_names : dead_names) << name
|
154
156
|
else
|
@@ -380,7 +382,7 @@ class Unicorn::HttpServer
|
|
380
382
|
|
381
383
|
# wait for a signal hander to wake us up and then consume the pipe
|
382
384
|
def master_sleep(sec)
|
383
|
-
@self_pipe[0].
|
385
|
+
@self_pipe[0].wait(sec) or return
|
384
386
|
# 11 bytes is the maximum string length which can be embedded within
|
385
387
|
# the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
|
386
388
|
# Most reads are only one byte here and uncommon, so it's not worth a
|
@@ -520,9 +522,6 @@ class Unicorn::HttpServer
|
|
520
522
|
Unicorn::Configurator::RACKUP.clear
|
521
523
|
@ready_pipe = @init_listeners = @before_exec = @before_fork = nil
|
522
524
|
|
523
|
-
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/36450
|
524
|
-
srand # remove in unicorn 6
|
525
|
-
|
526
525
|
# The OpenSSL PRNG is seeded with only the pid, and apps with frequently
|
527
526
|
# dying workers can recycle pids
|
528
527
|
OpenSSL::Random.seed(rand.to_s) if defined?(OpenSSL::Random)
|
@@ -553,9 +552,9 @@ class Unicorn::HttpServer
|
|
553
552
|
@workers[pid] = worker
|
554
553
|
worker.atfork_parent
|
555
554
|
end
|
556
|
-
|
557
|
-
|
558
|
-
|
555
|
+
rescue => e
|
556
|
+
@logger.error(e) rescue nil
|
557
|
+
exit!
|
559
558
|
end
|
560
559
|
|
561
560
|
def maintain_worker_count
|
@@ -586,7 +585,7 @@ class Unicorn::HttpServer
|
|
586
585
|
client.kgio_trywrite(err_response(code, @request.response_start_sent))
|
587
586
|
end
|
588
587
|
client.close
|
589
|
-
|
588
|
+
rescue
|
590
589
|
end
|
591
590
|
|
592
591
|
def e100_response_write(client, env)
|
@@ -614,8 +613,7 @@ class Unicorn::HttpServer
|
|
614
613
|
return if @request.hijacked?
|
615
614
|
end
|
616
615
|
@request.headers? or headers = nil
|
617
|
-
http_response_write(client, status, headers, body,
|
618
|
-
@request.response_start_sent)
|
616
|
+
http_response_write(client, status, headers, body, @request)
|
619
617
|
ensure
|
620
618
|
body.respond_to?(:close) and body.close
|
621
619
|
end
|
@@ -670,9 +668,9 @@ class Unicorn::HttpServer
|
|
670
668
|
logger.info "worker=#{worker_nr} reopening logs..."
|
671
669
|
Unicorn::Util.reopen_logs
|
672
670
|
logger.info "worker=#{worker_nr} done reopening logs"
|
673
|
-
|
674
|
-
|
675
|
-
|
671
|
+
rescue => e
|
672
|
+
logger.error(e) rescue nil
|
673
|
+
exit!(77) # EX_NOPERM in sysexits.h
|
676
674
|
end
|
677
675
|
|
678
676
|
# runs inside each forked worker, this sits around and waits
|
@@ -758,11 +756,11 @@ class Unicorn::HttpServer
|
|
758
756
|
wpid <= 0 and return
|
759
757
|
Process.kill(0, wpid)
|
760
758
|
wpid
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
759
|
+
rescue Errno::EPERM
|
760
|
+
logger.info "pid=#{path} possibly stale, got EPERM signalling PID:#{wpid}"
|
761
|
+
nil
|
762
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
763
|
+
# don't unlink stale pid files, racy without non-portable locking...
|
766
764
|
end
|
767
765
|
|
768
766
|
def load_config!
|
@@ -788,12 +786,12 @@ class Unicorn::HttpServer
|
|
788
786
|
end
|
789
787
|
|
790
788
|
def build_app!
|
791
|
-
if app.respond_to?(:arity) && app.arity == 0
|
789
|
+
if app.respond_to?(:arity) && (app.arity == 0 || app.arity == 2)
|
792
790
|
if defined?(Gem) && Gem.respond_to?(:refresh)
|
793
791
|
logger.info "Refreshing Gem list"
|
794
792
|
Gem.refresh
|
795
793
|
end
|
796
|
-
self.app = app.call
|
794
|
+
self.app = app.arity == 0 ? app.call : app.call(nil, self)
|
797
795
|
end
|
798
796
|
end
|
799
797
|
|
data/lib/unicorn/launcher.rb
CHANGED
@@ -83,6 +83,7 @@ module Unicorn
|
|
83
83
|
rescue => e
|
84
84
|
logger.error("#{sock_name(sock)} " \
|
85
85
|
"failed to set accept_filter=#{name} (#{e.inspect})")
|
86
|
+
logger.error("perhaps accf_http(9) needs to be loaded".freeze)
|
86
87
|
end if arg != got
|
87
88
|
end
|
88
89
|
end
|
@@ -100,8 +101,8 @@ module Unicorn
|
|
100
101
|
log_buffer_sizes(sock, " after: ")
|
101
102
|
end
|
102
103
|
sock.listen(opt[:backlog])
|
103
|
-
|
104
|
-
|
104
|
+
rescue => e
|
105
|
+
Unicorn.log_error(logger, "#{sock_name(sock)} #{opt.inspect}", e)
|
105
106
|
end
|
106
107
|
|
107
108
|
def log_buffer_sizes(sock, pfx = '')
|
@@ -116,7 +117,7 @@ module Unicorn
|
|
116
117
|
def bind_listen(address = '0.0.0.0:8080', opt = {})
|
117
118
|
return address unless String === address
|
118
119
|
|
119
|
-
sock = if address
|
120
|
+
sock = if address.start_with?('/')
|
120
121
|
if File.exist?(address)
|
121
122
|
if File.socket?(address)
|
122
123
|
begin
|
data/lib/unicorn/util.rb
CHANGED
@@ -11,8 +11,8 @@ module Unicorn::Util # :nodoc:
|
|
11
11
|
fp.stat.file? &&
|
12
12
|
fp.sync &&
|
13
13
|
(fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
|
14
|
-
|
15
|
-
|
14
|
+
rescue IOError, Errno::EBADF
|
15
|
+
false
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.chown_logs(uid, gid)
|
@@ -64,7 +64,7 @@ module Unicorn::Util # :nodoc:
|
|
64
64
|
fp.reopen(fp.path, "a")
|
65
65
|
else
|
66
66
|
# We should not need this workaround, Ruby can be fixed:
|
67
|
-
#
|
67
|
+
# https://bugs.ruby-lang.org/issues/9036
|
68
68
|
# MRI will not call call fclose(3) or freopen(3) here
|
69
69
|
# since there's no associated std{in,out,err} FILE * pointer
|
70
70
|
# This should atomically use dup3(2) (or dup2(2)) syscall
|
data/lib/unicorn/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Unicorn::Const::UNICORN_VERSION = '5.
|
1
|
+
Unicorn::Const::UNICORN_VERSION = '5.5.1'
|
data/lib/unicorn/worker.rb
CHANGED
@@ -122,6 +122,11 @@ class Unicorn::Worker
|
|
122
122
|
# the +after_fork+ hook after any privileged functions need to be
|
123
123
|
# run (e.g. to set per-worker CPU affinity, niceness, etc)
|
124
124
|
#
|
125
|
+
# +group+ can be specified as a string, or as an array of two
|
126
|
+
# strings. If an array of two strings is given, the first string
|
127
|
+
# is used as the primary group of the process, and the second is
|
128
|
+
# used as the group of the log files.
|
129
|
+
#
|
125
130
|
# Any and all errors raised within this method will be propagated
|
126
131
|
# directly back to the caller (usually the +after_fork+ hook.
|
127
132
|
# These errors commonly include ArgumentError for specifying an
|
@@ -134,8 +139,17 @@ class Unicorn::Worker
|
|
134
139
|
# insufficient because modern systems have fine-grained
|
135
140
|
# capabilities. Let the caller handle any and all errors.
|
136
141
|
uid = Etc.getpwnam(user).uid
|
137
|
-
|
138
|
-
|
142
|
+
|
143
|
+
if group
|
144
|
+
if group.is_a?(Array)
|
145
|
+
group, log_group = group
|
146
|
+
log_gid = Etc.getgrnam(log_group).gid
|
147
|
+
end
|
148
|
+
gid = Etc.getgrnam(group).gid
|
149
|
+
log_gid ||= gid
|
150
|
+
end
|
151
|
+
|
152
|
+
Unicorn::Util.chown_logs(uid, log_gid)
|
139
153
|
if gid && Process.egid != gid
|
140
154
|
Process.initgroups(user, gid)
|
141
155
|
Process::GID.change_privilege(gid)
|
data/lib/unicorn.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
require 'etc'
|
3
3
|
require 'stringio'
|
4
4
|
require 'kgio'
|
5
|
+
require 'raindrops'
|
6
|
+
require 'io/wait'
|
5
7
|
|
6
8
|
begin
|
7
9
|
require 'rack'
|
@@ -43,12 +45,8 @@ module Unicorn
|
|
43
45
|
abort "rack and Rack::Builder must be available for processing #{ru}"
|
44
46
|
end
|
45
47
|
|
46
|
-
# Op is going to get cleared before the returned lambda is called, so
|
47
|
-
# save this value so that it's still there when we need it:
|
48
|
-
no_default_middleware = op[:no_default_middleware]
|
49
|
-
|
50
48
|
# always called after config file parsing, may be called after forking
|
51
|
-
lambda do
|
49
|
+
lambda do |_, server|
|
52
50
|
inner_app = case ru
|
53
51
|
when /\.ru$/
|
54
52
|
raw = File.read(ru)
|
@@ -59,9 +57,12 @@ module Unicorn
|
|
59
57
|
Object.const_get(File.basename(ru, '.rb').capitalize)
|
60
58
|
end
|
61
59
|
|
62
|
-
|
60
|
+
if $DEBUG
|
61
|
+
require 'pp'
|
62
|
+
pp({ :inner_app => inner_app })
|
63
|
+
end
|
63
64
|
|
64
|
-
return inner_app
|
65
|
+
return inner_app unless server.default_middleware
|
65
66
|
|
66
67
|
middleware = { # order matters
|
67
68
|
ContentLength: nil,
|
@@ -109,9 +110,25 @@ module Unicorn
|
|
109
110
|
exc.backtrace.each { |line| logger.error(line) }
|
110
111
|
end
|
111
112
|
|
112
|
-
|
113
|
+
F_SETPIPE_SZ = 1031 if RUBY_PLATFORM =~ /linux/
|
114
|
+
|
113
115
|
def self.pipe # :nodoc:
|
114
|
-
Kgio::Pipe.new.each
|
116
|
+
Kgio::Pipe.new.each do |io|
|
117
|
+
io.close_on_exec = true # remove this when we only support Ruby >= 2.0
|
118
|
+
|
119
|
+
# shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
|
120
|
+
# limits.
|
121
|
+
if defined?(F_SETPIPE_SZ)
|
122
|
+
begin
|
123
|
+
io.fcntl(F_SETPIPE_SZ, Raindrops::PAGE_SIZE)
|
124
|
+
rescue Errno::EINVAL
|
125
|
+
# old kernel
|
126
|
+
rescue Errno::EPERM
|
127
|
+
# resizes fail if Linux is close to the pipe limit for the user
|
128
|
+
# or if the user does not have permissions to resize
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
115
132
|
end
|
116
133
|
# :startdoc:
|
117
134
|
end
|
data/man/man1/unicorn.1
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
.\" Automatically generated by Pandoc 1.17.2
|
2
|
+
.\"
|
1
3
|
.TH "UNICORN" "1" "September 15, 2009" "Unicorn User Manual" ""
|
4
|
+
.hy
|
2
5
|
.SH NAME
|
3
6
|
.PP
|
4
7
|
unicorn \- a rackup\-like command to launch the Unicorn HTTP server
|
@@ -61,10 +64,9 @@ socket.
|
|
61
64
|
Defaults to "0.0.0.0:8080" (all addresses on TCP port 8080) For
|
62
65
|
production deployments, specifying the "listen" directive in CONFIG_FILE
|
63
66
|
is recommended as it allows fine\-tuning of socket options.
|
67
|
+
\-N, \-\-no\-default\-middleware
|
64
68
|
.RS
|
65
69
|
.RE
|
66
|
-
.TP
|
67
|
-
.B \-N, \-\-no\-default\-middleware
|
68
70
|
Disables loading middleware implied by RACK_ENV.
|
69
71
|
This bypasses the configuration documented in the RACK ENVIRONMENT
|
70
72
|
section, but still allows RACK_ENV to be used for
|
@@ -116,8 +118,8 @@ Turn on verbose warnings, the $VERBOSE variable is set to true.
|
|
116
118
|
.RE
|
117
119
|
.TP
|
118
120
|
.B \-I, \-\-include PATH
|
119
|
-
specify
|
120
|
-
|
121
|
+
specify $LOAD_PATH.
|
122
|
+
PATH will be prepended to $LOAD_PATH.
|
121
123
|
The \[aq]:\[aq] character may be used to delimit multiple directories.
|
122
124
|
This directive may be used more than once.
|
123
125
|
Modifications to $LOAD_PATH take place immediately and in the order they
|
@@ -212,7 +214,7 @@ the unicorn config file.
|
|
212
214
|
.IP \[bu] 2
|
213
215
|
Unicorn RDoc (https://bogomips.org/unicorn/)
|
214
216
|
.IP \[bu] 2
|
215
|
-
Rack RDoc (
|
217
|
+
Rack RDoc (https://www.rubydoc.info/github/rack/rack/)
|
216
218
|
.IP \[bu] 2
|
217
219
|
Rackup HowTo (https://github.com/rack/rack/wiki/tutorial-rackup-howto)
|
218
220
|
.SH AUTHORS
|
data/man/man1/unicorn_rails.1
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
.\" Automatically generated by Pandoc 1.17.2
|
2
|
+
.\"
|
1
3
|
.TH "UNICORN_RAILS" "1" "September 17, 2009" "Unicorn User Manual" ""
|
4
|
+
.hy
|
2
5
|
.SH NAME
|
3
6
|
.PP
|
4
7
|
unicorn_rails \- unicorn launcher for Rails 1.x and 2.x users
|
@@ -7,18 +10,15 @@ unicorn_rails \- unicorn launcher for Rails 1.x and 2.x users
|
|
7
10
|
unicorn_rails [\-c CONFIG_FILE] [\-E RAILS_ENV] [\-D] [RACKUP_FILE]
|
8
11
|
.SH DESCRIPTION
|
9
12
|
.PP
|
10
|
-
A rackup(1)\-like command to launch Rails
|
13
|
+
A rackup(1)\-like command to launch ancient Rails (2.x and earlier)
|
14
|
+
applications using Unicorn.
|
15
|
+
Rails 3 (and later) support Rack natively, so users are encouraged to
|
16
|
+
use unicorn(1) instead of unicorn_rails(1).
|
17
|
+
.PP
|
11
18
|
It is expected to be started in your Rails application root
|
12
19
|
(RAILS_ROOT), but the "working_directory" directive may be used in the
|
13
20
|
CONFIG_FILE.
|
14
21
|
.PP
|
15
|
-
It is designed to help Rails 1.x and 2.y users transition to Rack, but
|
16
|
-
it is NOT needed for Rails 3 applications.
|
17
|
-
Rails 3 users are encouraged to use unicorn(1) instead of
|
18
|
-
unicorn_rails(1).
|
19
|
-
Users of Rails 1.x/2.y may also use unicorn(1) instead of
|
20
|
-
unicorn_rails(1).
|
21
|
-
.PP
|
22
22
|
The outward interface resembles rackup(1), the internals and default
|
23
23
|
middleware loading is designed like the \f[C]script/server\f[] command
|
24
24
|
distributed with Rails.
|
@@ -125,8 +125,8 @@ Turn on verbose warnings, the $VERBOSE variable is set to true.
|
|
125
125
|
.RE
|
126
126
|
.TP
|
127
127
|
.B \-I, \-\-include PATH
|
128
|
-
specify
|
129
|
-
|
128
|
+
specify $LOAD_PATH.
|
129
|
+
PATH will be prepended to $LOAD_PATH.
|
130
130
|
The \[aq]:\[aq] character may be used to delimit multiple directories.
|
131
131
|
This directive may be used more than once.
|
132
132
|
Modifications to $LOAD_PATH take place immediately and in the order they
|
@@ -202,7 +202,7 @@ unicorn(1)
|
|
202
202
|
.IP \[bu] 2
|
203
203
|
Unicorn RDoc (https://bogomips.org/unicorn/)
|
204
204
|
.IP \[bu] 2
|
205
|
-
Rack RDoc (
|
205
|
+
Rack RDoc (https://www.rubydoc.info/github/rack/rack/)
|
206
206
|
.IP \[bu] 2
|
207
207
|
Rackup HowTo (https://github.com/rack/rack/wiki/tutorial-rackup-howto)
|
208
208
|
.SH AUTHORS
|
data/t/README
CHANGED
@@ -10,17 +10,17 @@ comfortable writing integration tests with.
|
|
10
10
|
|
11
11
|
== Requirements
|
12
12
|
|
13
|
-
* {Ruby 1.9.3+}[https://www.ruby-lang.org/] (duh!)
|
14
|
-
* {GNU make}[
|
13
|
+
* {Ruby 1.9.3+}[https://www.ruby-lang.org/en/] (duh!)
|
14
|
+
* {GNU make}[https://www.gnu.org/software/make/]
|
15
15
|
* {socat}[http://www.dest-unreach.org/socat/]
|
16
|
-
* {curl}[
|
16
|
+
* {curl}[https://curl.haxx.se/]
|
17
17
|
* standard UNIX shell utilities (Bourne sh, awk, sed, grep, ...)
|
18
18
|
|
19
19
|
We do not use bashisms or any non-portable, non-POSIX constructs
|
20
20
|
in our shell code. We use the "pipefail" option if available and
|
21
21
|
mainly test with {ksh}[http://kornshell.com/], but occasionally
|
22
22
|
with {dash}[http://gondor.apana.org.au/~herbert/dash/] and
|
23
|
-
{bash}[
|
23
|
+
{bash}[https://www.gnu.org/software/bash/], too.
|
24
24
|
|
25
25
|
== Running Tests
|
26
26
|
|
data/t/hijack.ru
CHANGED
@@ -11,11 +11,15 @@ class DieIfUsed
|
|
11
11
|
warn "closed DieIfUsed #{@@n += 1}\n"
|
12
12
|
end
|
13
13
|
end
|
14
|
+
|
15
|
+
envs = []
|
16
|
+
|
14
17
|
run lambda { |env|
|
15
18
|
case env["PATH_INFO"]
|
16
19
|
when "/hijack_req"
|
17
20
|
if env["rack.hijack?"]
|
18
21
|
io = env["rack.hijack"].call
|
22
|
+
envs << env
|
19
23
|
if io.respond_to?(:read_nonblock) &&
|
20
24
|
env["rack.hijack_io"].respond_to?(:read_nonblock)
|
21
25
|
|
@@ -33,11 +37,19 @@ run lambda { |env|
|
|
33
37
|
{
|
34
38
|
"Content-Length" => r.bytesize.to_s,
|
35
39
|
"rack.hijack" => proc do |io|
|
40
|
+
envs << env
|
36
41
|
io.write(r)
|
37
42
|
io.close
|
38
43
|
end
|
39
44
|
},
|
40
45
|
DieIfUsed.new
|
41
46
|
]
|
47
|
+
when "/normal_env_id"
|
48
|
+
b = "#{env.object_id}\n"
|
49
|
+
h = {
|
50
|
+
'Content-Type' => 'text/plain',
|
51
|
+
'Content-Length' => b.bytesize.to_s,
|
52
|
+
}
|
53
|
+
[ 200, h, [ b ] ]
|
42
54
|
end
|
43
55
|
}
|
data/t/t0200-rack-hijack.sh
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/bin/sh
|
2
2
|
. ./test-lib.sh
|
3
|
-
t_plan
|
3
|
+
t_plan 9 "rack.hijack tests (Rack 1.5+ (Rack::VERSION >= [ 1,2]))"
|
4
4
|
|
5
5
|
t_begin "setup and start" && {
|
6
6
|
unicorn_setup
|
@@ -8,14 +8,35 @@ t_begin "setup and start" && {
|
|
8
8
|
unicorn_wait_start
|
9
9
|
}
|
10
10
|
|
11
|
+
t_begin "normal env reused between requests" && {
|
12
|
+
env_a="$(curl -sSf http://$listen/normal_env_id)"
|
13
|
+
b="$(curl -sSf http://$listen/normal_env_id)"
|
14
|
+
test x"$env_a" = x"$b"
|
15
|
+
}
|
16
|
+
|
11
17
|
t_begin "check request hijack" && {
|
12
18
|
test "xrequest.hijacked" = x"$(curl -sSfv http://$listen/hijack_req)"
|
13
19
|
}
|
14
20
|
|
21
|
+
t_begin "env changed after request hijack" && {
|
22
|
+
env_b="$(curl -sSf http://$listen/normal_env_id)"
|
23
|
+
test x"$env_a" != x"$env_b"
|
24
|
+
}
|
25
|
+
|
15
26
|
t_begin "check response hijack" && {
|
16
27
|
test "xresponse.hijacked" = x"$(curl -sSfv http://$listen/hijack_res)"
|
17
28
|
}
|
18
29
|
|
30
|
+
t_begin "env changed after response hijack" && {
|
31
|
+
env_c="$(curl -sSf http://$listen/normal_env_id)"
|
32
|
+
test x"$env_b" != x"$env_c"
|
33
|
+
}
|
34
|
+
|
35
|
+
t_begin "env continues to be reused between requests" && {
|
36
|
+
b="$(curl -sSf http://$listen/normal_env_id)"
|
37
|
+
test x"$env_c" = x"$b"
|
38
|
+
}
|
39
|
+
|
19
40
|
t_begin "killing succeeds after hijack" && {
|
20
41
|
kill $unicorn_pid
|
21
42
|
}
|