unicorn-rupcio 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.CHANGELOG.old +25 -0
- data/.document +28 -0
- data/.gitattributes +5 -0
- data/.gitignore +25 -0
- data/.mailmap +26 -0
- data/.manifest +144 -0
- data/.olddoc.yml +25 -0
- data/Application_Timeouts +77 -0
- data/CONTRIBUTORS +39 -0
- data/COPYING +674 -0
- data/DESIGN +99 -0
- data/Documentation/.gitignore +3 -0
- data/Documentation/unicorn.1 +222 -0
- data/Documentation/unicorn_rails.1 +207 -0
- data/FAQ +70 -0
- data/GIT-VERSION-FILE +1 -0
- data/GIT-VERSION-GEN +39 -0
- data/GNUmakefile +318 -0
- data/HACKING +117 -0
- data/ISSUES +102 -0
- data/KNOWN_ISSUES +79 -0
- data/LICENSE +67 -0
- data/Links +58 -0
- data/PHILOSOPHY +139 -0
- data/README +165 -0
- data/Rakefile +17 -0
- data/SIGNALS +123 -0
- data/Sandbox +104 -0
- data/TODO +1 -0
- data/TUNING +119 -0
- data/archive/.gitignore +3 -0
- data/archive/slrnpull.conf +4 -0
- data/bin/unicorn +129 -0
- data/bin/unicorn_rails +210 -0
- data/examples/big_app_gc.rb +3 -0
- data/examples/echo.ru +27 -0
- data/examples/init.sh +102 -0
- data/examples/logger_mp_safe.rb +26 -0
- data/examples/logrotate.conf +44 -0
- data/examples/nginx.conf +156 -0
- data/examples/unicorn.conf.minimal.rb +14 -0
- data/examples/unicorn.conf.rb +111 -0
- data/examples/unicorn.socket +11 -0
- data/examples/unicorn@.service +40 -0
- data/ext/unicorn_http/CFLAGS +13 -0
- data/ext/unicorn_http/c_util.h +115 -0
- data/ext/unicorn_http/common_field_optimization.h +128 -0
- data/ext/unicorn_http/epollexclusive.h +128 -0
- data/ext/unicorn_http/ext_help.h +38 -0
- data/ext/unicorn_http/extconf.rb +40 -0
- data/ext/unicorn_http/global_variables.h +97 -0
- data/ext/unicorn_http/httpdate.c +91 -0
- data/ext/unicorn_http/unicorn_http.c +4348 -0
- data/ext/unicorn_http/unicorn_http.rl +1054 -0
- data/ext/unicorn_http/unicorn_http_common.rl +76 -0
- data/lib/unicorn/app/old_rails/static.rb +60 -0
- data/lib/unicorn/app/old_rails.rb +36 -0
- data/lib/unicorn/cgi_wrapper.rb +148 -0
- data/lib/unicorn/configurator.rb +749 -0
- data/lib/unicorn/const.rb +22 -0
- data/lib/unicorn/http_request.rb +180 -0
- data/lib/unicorn/http_response.rb +95 -0
- data/lib/unicorn/http_server.rb +860 -0
- data/lib/unicorn/launcher.rb +63 -0
- data/lib/unicorn/oob_gc.rb +82 -0
- data/lib/unicorn/preread_input.rb +34 -0
- data/lib/unicorn/select_waiter.rb +7 -0
- data/lib/unicorn/socket_helper.rb +186 -0
- data/lib/unicorn/stream_input.rb +152 -0
- data/lib/unicorn/tee_input.rb +132 -0
- data/lib/unicorn/tmpio.rb +34 -0
- data/lib/unicorn/util.rb +91 -0
- data/lib/unicorn/version.rb +1 -0
- data/lib/unicorn/worker.rb +166 -0
- data/lib/unicorn.rb +137 -0
- data/man/man1/unicorn.1 +222 -0
- data/man/man1/unicorn_rails.1 +207 -0
- data/setup.rb +1587 -0
- data/t/.gitignore +4 -0
- data/t/GNUmakefile +5 -0
- data/t/README +49 -0
- data/t/active-unix-socket.t +110 -0
- data/t/back-out-of-upgrade.t +44 -0
- data/t/bin/unused_listen +40 -0
- data/t/client_body_buffer_size.ru +15 -0
- data/t/client_body_buffer_size.t +79 -0
- data/t/detach.ru +12 -0
- data/t/env.ru +4 -0
- data/t/fails-rack-lint.ru +6 -0
- data/t/heartbeat-timeout.ru +13 -0
- data/t/heartbeat-timeout.t +60 -0
- data/t/integration.ru +129 -0
- data/t/integration.t +509 -0
- data/t/lib.perl +309 -0
- data/t/listener_names.ru +5 -0
- data/t/my-tap-lib.sh +201 -0
- data/t/oob_gc.ru +18 -0
- data/t/oob_gc_path.ru +18 -0
- data/t/pid.ru +4 -0
- data/t/preread_input.ru +23 -0
- data/t/reload-bad-config.t +49 -0
- data/t/reopen-logs.ru +14 -0
- data/t/reopen-logs.t +36 -0
- data/t/t0010-reap-logging.sh +55 -0
- data/t/t0012-reload-empty-config.sh +86 -0
- data/t/t0013-rewindable-input-false.sh +24 -0
- data/t/t0013.ru +13 -0
- data/t/t0014-rewindable-input-true.sh +24 -0
- data/t/t0014.ru +13 -0
- data/t/t0015-configurator-internals.sh +25 -0
- data/t/t0020-at_exit-handler.sh +49 -0
- data/t/t0021-process_detach.sh +29 -0
- data/t/t0022-listener_names-preload_app.sh +32 -0
- data/t/t0300-no-default-middleware.sh +20 -0
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +14 -0
- data/t/t9001-oob_gc.sh +47 -0
- data/t/t9002-oob_gc-path.sh +75 -0
- data/t/test-lib.sh +125 -0
- data/t/winch_ttin.t +64 -0
- data/t/working_directory.t +86 -0
- data/test/aggregate.rb +16 -0
- data/test/benchmark/README +60 -0
- data/test/benchmark/dd.ru +19 -0
- data/test/benchmark/ddstream.ru +51 -0
- data/test/benchmark/readinput.ru +41 -0
- data/test/benchmark/stack.ru +9 -0
- data/test/benchmark/uconnect.perl +66 -0
- data/test/exec/README +5 -0
- data/test/exec/test_exec.rb +1030 -0
- data/test/test_helper.rb +307 -0
- data/test/unit/test_configurator.rb +176 -0
- data/test/unit/test_droplet.rb +29 -0
- data/test/unit/test_http_parser.rb +885 -0
- data/test/unit/test_http_parser_ng.rb +715 -0
- data/test/unit/test_server.rb +245 -0
- data/test/unit/test_signals.rb +189 -0
- data/test/unit/test_socket_helper.rb +160 -0
- data/test/unit/test_stream_input.rb +211 -0
- data/test/unit/test_tee_input.rb +304 -0
- data/test/unit/test_util.rb +132 -0
- data/test/unit/test_waiter.rb +35 -0
- data/unicorn.gemspec +49 -0
- metadata +266 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
module Unicorn::Const # :nodoc:
|
5
|
+
# default TCP listen host address (0.0.0.0, all interfaces)
|
6
|
+
DEFAULT_HOST = "0.0.0.0"
|
7
|
+
|
8
|
+
# default TCP listen port (8080)
|
9
|
+
DEFAULT_PORT = 8080
|
10
|
+
|
11
|
+
# default TCP listen address and port (0.0.0.0:8080)
|
12
|
+
DEFAULT_LISTEN = "#{DEFAULT_HOST}:#{DEFAULT_PORT}"
|
13
|
+
|
14
|
+
# The basic request body size we'll try to read at once (16 kilobytes).
|
15
|
+
CHUNK_SIZE = 16 * 1024
|
16
|
+
|
17
|
+
# Maximum request body size before it is moved out of memory and into a
|
18
|
+
# temporary file for reading (112 kilobytes). This is the default
|
19
|
+
# value of client_body_buffer_size.
|
20
|
+
MAX_BODY = 1024 * 112
|
21
|
+
end
|
22
|
+
require_relative 'version'
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: false
|
3
|
+
# :enddoc:
|
4
|
+
# no stable API here
|
5
|
+
require 'unicorn_http'
|
6
|
+
|
7
|
+
# TODO: remove redundant names
|
8
|
+
Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
|
9
|
+
class Unicorn::HttpParser
|
10
|
+
|
11
|
+
# default parameters we merge into the request env for Rack handlers
|
12
|
+
DEFAULTS = {
|
13
|
+
"rack.errors" => $stderr,
|
14
|
+
"rack.multiprocess" => true,
|
15
|
+
"rack.multithread" => false,
|
16
|
+
"rack.run_once" => false,
|
17
|
+
"rack.version" => [1, 2],
|
18
|
+
"rack.hijack?" => true,
|
19
|
+
"SCRIPT_NAME" => "",
|
20
|
+
|
21
|
+
# this is not in the Rack spec, but some apps may rely on it
|
22
|
+
"SERVER_SOFTWARE" => "Unicorn #{Unicorn::Const::UNICORN_VERSION}"
|
23
|
+
}
|
24
|
+
|
25
|
+
NULL_IO = StringIO.new("")
|
26
|
+
|
27
|
+
# :stopdoc:
|
28
|
+
HTTP_RESPONSE_START = [ 'HTTP'.freeze, '/1.1 '.freeze ]
|
29
|
+
EMPTY_ARRAY = [].freeze
|
30
|
+
@@input_class = Unicorn::TeeInput
|
31
|
+
@@check_client_connection = false
|
32
|
+
@@tcpi_inspect_ok = Socket.const_defined?(:TCP_INFO)
|
33
|
+
|
34
|
+
def self.input_class
|
35
|
+
@@input_class
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.input_class=(klass)
|
39
|
+
@@input_class = klass
|
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
|
+
|
50
|
+
# :startdoc:
|
51
|
+
|
52
|
+
# Does the majority of the IO processing. It has been written in
|
53
|
+
# Ruby using about 8 different IO processing strategies.
|
54
|
+
#
|
55
|
+
# It is currently carefully constructed to make sure that it gets
|
56
|
+
# the best possible performance for the common case: GET requests
|
57
|
+
# that are fully complete after a single read(2)
|
58
|
+
#
|
59
|
+
# Anyone who thinks they can make it faster is more than welcome to
|
60
|
+
# take a crack at it.
|
61
|
+
#
|
62
|
+
# returns an environment hash suitable for Rack if successful
|
63
|
+
# This does minimal exception trapping and it is up to the caller
|
64
|
+
# to handle any socket errors (e.g. user aborted upload).
|
65
|
+
def read_headers(socket, ai)
|
66
|
+
e = env
|
67
|
+
|
68
|
+
# From https://www.ietf.org/rfc/rfc3875:
|
69
|
+
# "Script authors should be aware that the REMOTE_ADDR and
|
70
|
+
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
71
|
+
# may not identify the ultimate source of the request. They
|
72
|
+
# identify the client for the immediate request to the server;
|
73
|
+
# that client may be a proxy, gateway, or other intermediary
|
74
|
+
# acting on behalf of the actual source client."
|
75
|
+
e['REMOTE_ADDR'] = ai.unix? ? '127.0.0.1' : ai.ip_address
|
76
|
+
|
77
|
+
# short circuit the common case with small GET requests first
|
78
|
+
socket.readpartial(16384, buf)
|
79
|
+
if parse.nil?
|
80
|
+
# Parser is not done, queue up more data to read and continue parsing
|
81
|
+
# an Exception thrown from the parser will throw us out of the loop
|
82
|
+
false until add_parse(socket.readpartial(16384))
|
83
|
+
end
|
84
|
+
|
85
|
+
check_client_connection(socket, ai) if @@check_client_connection
|
86
|
+
|
87
|
+
e['rack.input'] = 0 == content_length ?
|
88
|
+
NULL_IO : @@input_class.new(socket, self)
|
89
|
+
|
90
|
+
# for Rack hijacking in Rack 1.5 and later
|
91
|
+
e['unicorn.socket'] = socket
|
92
|
+
e['rack.hijack'] = self
|
93
|
+
|
94
|
+
e.merge!(DEFAULTS)
|
95
|
+
end
|
96
|
+
|
97
|
+
# for rack.hijack, we respond to this method so no extra allocation
|
98
|
+
# of a proc object
|
99
|
+
def call
|
100
|
+
hijacked!
|
101
|
+
env['rack.hijack_io'] = env['unicorn.socket']
|
102
|
+
end
|
103
|
+
|
104
|
+
def hijacked?
|
105
|
+
env.include?('rack.hijack_io'.freeze)
|
106
|
+
end
|
107
|
+
|
108
|
+
if Raindrops.const_defined?(:TCP_Info)
|
109
|
+
TCPI = Raindrops::TCP_Info.allocate
|
110
|
+
|
111
|
+
def check_client_connection(socket, ai) # :nodoc:
|
112
|
+
if ai.ip?
|
113
|
+
# Raindrops::TCP_Info#get!, #state (reads struct tcp_info#tcpi_state)
|
114
|
+
raise Errno::EPIPE, "client closed connection".freeze,
|
115
|
+
EMPTY_ARRAY if closed_state?(TCPI.get!(socket).state)
|
116
|
+
else
|
117
|
+
write_http_header(socket)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
if Raindrops.const_defined?(:TCP)
|
122
|
+
# raindrops 0.18.0+ supports FreeBSD + Linux using the same names
|
123
|
+
# Evaluate these hash lookups at load time so we can
|
124
|
+
# generate an opt_case_dispatch instruction
|
125
|
+
eval <<-EOS
|
126
|
+
def closed_state?(state) # :nodoc:
|
127
|
+
state == #{Raindrops::TCP[:ESTABLISHED]} ? false : true
|
128
|
+
end
|
129
|
+
EOS
|
130
|
+
else
|
131
|
+
# raindrops before 0.18 only supported TCP_INFO under Linux
|
132
|
+
def closed_state?(state) # :nodoc:
|
133
|
+
# TCP_ESTABLISHED == 1 on Linux
|
134
|
+
state == 1 ? false : true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
else
|
138
|
+
|
139
|
+
# Ruby 2.2+ can show struct tcp_info as a string Socket::Option#inspect.
|
140
|
+
# Not that efficient, but probably still better than doing unnecessary
|
141
|
+
# work after a client gives up.
|
142
|
+
def check_client_connection(socket, ai) # :nodoc:
|
143
|
+
if @@tcpi_inspect_ok && ai.ip?
|
144
|
+
opt = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO).inspect
|
145
|
+
if opt =~ /\bstate=(\S+)/
|
146
|
+
raise Errno::EPIPE, "client closed connection".freeze,
|
147
|
+
EMPTY_ARRAY if closed_state_str?($1)
|
148
|
+
else
|
149
|
+
@@tcpi_inspect_ok = false
|
150
|
+
write_http_header(socket)
|
151
|
+
end
|
152
|
+
opt.clear
|
153
|
+
else
|
154
|
+
write_http_header(socket)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def closed_state_str?(state)
|
159
|
+
state == 'ESTABLISHED' ? false : true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def write_http_header(socket) # :nodoc:
|
164
|
+
if headers?
|
165
|
+
self.response_start_sent = true
|
166
|
+
HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# called by ext/unicorn_http/unicorn_http.rl via rb_funcall
|
171
|
+
def self.is_chunked?(v) # :nodoc:
|
172
|
+
vals = v.split(/[ \t]*,[ \t]*/).map!(&:downcase)
|
173
|
+
if vals.pop == 'chunked'.freeze
|
174
|
+
return true unless vals.include?('chunked'.freeze)
|
175
|
+
raise Unicorn::HttpParserError, 'double chunked', []
|
176
|
+
end
|
177
|
+
return false unless vals.include?('chunked'.freeze)
|
178
|
+
raise Unicorn::HttpParserError, 'chunked not last', []
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: false
|
3
|
+
# :enddoc:
|
4
|
+
# Writes a Rack response to your client using the HTTP/1.1 specification.
|
5
|
+
# You use it by simply doing:
|
6
|
+
#
|
7
|
+
# status, headers, body = rack_app.call(env)
|
8
|
+
# http_response_write(socket, status, headers, body)
|
9
|
+
#
|
10
|
+
# Most header correctness (including Content-Length and Content-Type)
|
11
|
+
# is the job of Rack, with the exception of the "Date" and "Status" header.
|
12
|
+
module Unicorn::HttpResponse
|
13
|
+
|
14
|
+
STATUS_CODES = defined?(Rack::Utils::HTTP_STATUS_CODES) ?
|
15
|
+
Rack::Utils::HTTP_STATUS_CODES : {}
|
16
|
+
STATUS_WITH_NO_ENTITY_BODY = defined?(
|
17
|
+
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY) ?
|
18
|
+
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY : begin
|
19
|
+
warn 'Rack::Utils::STATUS_WITH_NO_ENTITY_BODY missing'
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
|
23
|
+
# internal API, code will always be common-enough-for-even-old-Rack
|
24
|
+
def err_response(code, response_start_sent)
|
25
|
+
"#{response_start_sent ? '' : 'HTTP/1.1 '}" \
|
26
|
+
"#{code} #{STATUS_CODES[code]}\r\n\r\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
def append_header(buf, key, value)
|
30
|
+
case value
|
31
|
+
when Array # Rack 3
|
32
|
+
value.each { |v| buf << "#{key}: #{v}\r\n" }
|
33
|
+
when /\n/ # Rack 2
|
34
|
+
# avoiding blank, key-only cookies with /\n+/
|
35
|
+
value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
|
36
|
+
else
|
37
|
+
buf << "#{key}: #{value}\r\n"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# writes the rack_response to socket as an HTTP response
|
42
|
+
def http_response_write(socket, status, headers, body,
|
43
|
+
req = Unicorn::HttpRequest.new)
|
44
|
+
hijack = nil
|
45
|
+
do_chunk = false
|
46
|
+
if headers
|
47
|
+
code = status.to_i
|
48
|
+
msg = STATUS_CODES[code]
|
49
|
+
start = req.response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze
|
50
|
+
term = STATUS_WITH_NO_ENTITY_BODY.include?(code) || false
|
51
|
+
buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \
|
52
|
+
"Date: #{httpdate}\r\n" \
|
53
|
+
"Connection: close\r\n"
|
54
|
+
headers.each do |key, value|
|
55
|
+
case key
|
56
|
+
when %r{\A(?:Date|Connection)\z}i
|
57
|
+
next
|
58
|
+
when %r{\AContent-Length\z}i
|
59
|
+
append_header(buf, key, value)
|
60
|
+
term = true
|
61
|
+
when %r{\ATransfer-Encoding\z}i
|
62
|
+
append_header(buf, key, value)
|
63
|
+
term = true if /\bchunked\b/i === value # value may be Array :x
|
64
|
+
when "rack.hijack"
|
65
|
+
# This should only be hit under Rack >= 1.5, as this was an illegal
|
66
|
+
# key in Rack < 1.5
|
67
|
+
hijack = value
|
68
|
+
else
|
69
|
+
append_header(buf, key, value)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
if !hijack && !term && req.chunkable_response?
|
73
|
+
do_chunk = true
|
74
|
+
buf << "Transfer-Encoding: chunked\r\n".freeze
|
75
|
+
end
|
76
|
+
socket.write(buf << "\r\n".freeze)
|
77
|
+
buf.clear # remove this line if C Ruby gets escape analysis
|
78
|
+
end
|
79
|
+
|
80
|
+
if hijack
|
81
|
+
req.hijacked!
|
82
|
+
hijack.call(socket)
|
83
|
+
elsif do_chunk
|
84
|
+
begin
|
85
|
+
body.each do |b|
|
86
|
+
socket.write("#{b.bytesize.to_s(16)}\r\n", b, "\r\n".freeze)
|
87
|
+
end
|
88
|
+
ensure
|
89
|
+
socket.write("0\r\n\r\n".freeze)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
body.each { |chunk| socket.write(chunk) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|