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