unicorn-maintained 6.2.0

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