pitchfork 0.12.0 → 0.14.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 +4 -4
- data/.github/workflows/ci.yml +7 -3
- data/.ruby-version +1 -0
- data/CHANGELOG.md +12 -0
- data/Dockerfile +1 -1
- data/Gemfile.lock +3 -5
- data/benchmark/README.md +1 -1
- data/benchmark/cow_benchmark.rb +1 -0
- data/docs/CONFIGURATION.md +39 -1
- data/docs/MIGRATING_FROM_UNICORN.md +34 -0
- data/docs/WHY_MIGRATE.md +5 -0
- data/examples/constant_caches.ru +1 -0
- data/examples/echo.ru +1 -0
- data/examples/hello.ru +1 -0
- data/examples/pitchfork.conf.minimal.rb +1 -0
- data/examples/pitchfork.conf.rb +1 -0
- data/examples/pitchfork.conf.service.rb +27 -0
- data/exe/pitchfork +5 -4
- data/ext/pitchfork_http/epollexclusive.h +2 -2
- data/ext/pitchfork_http/extconf.rb +3 -0
- data/ext/pitchfork_http/memory_page.c +223 -0
- data/ext/pitchfork_http/pitchfork_http.c +213 -211
- data/ext/pitchfork_http/pitchfork_http.rl +3 -1
- data/lib/pitchfork/children.rb +21 -15
- data/lib/pitchfork/configurator.rb +13 -0
- data/lib/pitchfork/const.rb +1 -0
- data/lib/pitchfork/flock.rb +1 -0
- data/lib/pitchfork/http_parser.rb +18 -72
- data/lib/pitchfork/http_response.rb +4 -3
- data/lib/pitchfork/http_server.rb +181 -62
- data/lib/pitchfork/launcher.rb +1 -0
- data/lib/pitchfork/message.rb +11 -6
- data/lib/pitchfork/select_waiter.rb +1 -0
- data/lib/pitchfork/shared_memory.rb +16 -14
- data/lib/pitchfork/socket_helper.rb +2 -1
- data/lib/pitchfork/stream_input.rb +6 -5
- data/lib/pitchfork/tee_input.rb +3 -2
- data/lib/pitchfork/tmpio.rb +1 -0
- data/lib/pitchfork/version.rb +1 -1
- data/lib/pitchfork/worker.rb +44 -15
- data/lib/pitchfork.rb +1 -20
- data/pitchfork.gemspec +0 -1
- metadata +7 -18
- data/lib/pitchfork/app/old_rails/static.rb +0 -59
@@ -16,6 +16,7 @@
|
|
16
16
|
#include "child_subreaper.h"
|
17
17
|
|
18
18
|
void init_pitchfork_httpdate(void);
|
19
|
+
void init_pitchfork_memory_page(VALUE);
|
19
20
|
|
20
21
|
#define UH_FL_CHUNKED 0x1
|
21
22
|
#define UH_FL_HASBODY 0x2
|
@@ -960,7 +961,7 @@ static VALUE HttpParser_rssget(VALUE self)
|
|
960
961
|
assert(!NIL_P(var) && "missed global field"); \
|
961
962
|
} while (0)
|
962
963
|
|
963
|
-
void Init_pitchfork_http(void)
|
964
|
+
RUBY_FUNC_EXPORTED void Init_pitchfork_http(void)
|
964
965
|
{
|
965
966
|
VALUE mPitchfork;
|
966
967
|
|
@@ -1024,5 +1025,6 @@ void Init_pitchfork_http(void)
|
|
1024
1025
|
|
1025
1026
|
init_epollexclusive(mPitchfork);
|
1026
1027
|
init_child_subreaper(mPitchfork);
|
1028
|
+
init_pitchfork_memory_page(mPitchfork);
|
1027
1029
|
}
|
1028
1030
|
#undef SET_GLOBAL
|
data/lib/pitchfork/children.rb
CHANGED
@@ -1,31 +1,35 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Pitchfork
|
4
5
|
# This class keep tracks of the state of all the master children.
|
5
6
|
class Children
|
6
|
-
attr_reader :mold
|
7
|
+
attr_reader :mold, :service
|
7
8
|
attr_accessor :last_generation
|
8
9
|
|
9
10
|
def initialize
|
10
11
|
@last_generation = 0
|
11
|
-
@children = {} # All children, including molds, indexed by PID.
|
12
|
+
@children = {} # All children, including molds and services, indexed by PID.
|
12
13
|
@workers = {} # Workers indexed by their `nr`.
|
13
14
|
@molds = {} # Molds, index by PID.
|
14
15
|
@mold = nil # The latest mold, if any.
|
16
|
+
@service = nil
|
15
17
|
@pending_workers = {} # Pending workers indexed by their `nr`.
|
16
18
|
@pending_molds = {} # Worker promoted to mold, not yet acknowledged
|
17
19
|
end
|
18
20
|
|
19
|
-
def refresh
|
20
|
-
@workers.each_value(&:refresh)
|
21
|
-
@molds.each_value(&:refresh)
|
22
|
-
end
|
23
|
-
|
24
21
|
def register(child)
|
25
22
|
# Children always start as workers, never molds, so we know they have a `#nr`.
|
23
|
+
unless child.nr
|
24
|
+
raise "[BUG] Trying to register a child without an `nr`: #{child.inspect}"
|
25
|
+
end
|
26
26
|
@pending_workers[child.nr] = @workers[child.nr] = child
|
27
27
|
end
|
28
28
|
|
29
|
+
def register_service(service)
|
30
|
+
@service = service
|
31
|
+
end
|
32
|
+
|
29
33
|
def register_mold(mold)
|
30
34
|
@pending_molds[mold.pid] = mold
|
31
35
|
@children[mold.pid] = mold
|
@@ -43,6 +47,11 @@ module Pitchfork
|
|
43
47
|
@pending_molds[mold.pid] = mold
|
44
48
|
@children[mold.pid] = mold
|
45
49
|
return mold
|
50
|
+
when Message::ServiceSpawned
|
51
|
+
service = @service
|
52
|
+
service.update(message)
|
53
|
+
@children[service.pid] = service
|
54
|
+
return service
|
46
55
|
end
|
47
56
|
|
48
57
|
child = @children[message.pid] || (message.nr && @workers[message.nr])
|
@@ -77,12 +86,17 @@ module Pitchfork
|
|
77
86
|
@pending_molds.delete(child.pid)
|
78
87
|
@molds.delete(child.pid)
|
79
88
|
@workers.delete(child.nr)
|
89
|
+
|
80
90
|
if @mold == child
|
81
91
|
@pending_workers.reject! do |nr, worker|
|
82
92
|
worker.generation == @mold.generation
|
83
93
|
end
|
84
94
|
@mold = nil
|
85
95
|
end
|
96
|
+
|
97
|
+
if @service == child
|
98
|
+
@service = nil
|
99
|
+
end
|
86
100
|
end
|
87
101
|
child
|
88
102
|
end
|
@@ -153,13 +167,5 @@ module Pitchfork
|
|
153
167
|
def workers_count
|
154
168
|
@workers.size
|
155
169
|
end
|
156
|
-
|
157
|
-
def total_pss
|
158
|
-
total_pss = MemInfo.new(Process.pid).pss
|
159
|
-
@children.each do |_, worker|
|
160
|
-
total_pss += worker.meminfo.pss if worker.meminfo
|
161
|
-
end
|
162
|
-
total_pss
|
163
|
-
end
|
164
170
|
end
|
165
171
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'logger'
|
3
4
|
|
4
5
|
module Pitchfork
|
@@ -51,6 +52,8 @@ module Pitchfork
|
|
51
52
|
"repead unknown process (#{status.inspect})"
|
52
53
|
elsif worker.mold?
|
53
54
|
"mold pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
|
55
|
+
elsif worker.service?
|
56
|
+
"service pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
|
54
57
|
else
|
55
58
|
"worker=#{worker.nr rescue 'unknown'} pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
|
56
59
|
end
|
@@ -74,6 +77,8 @@ module Pitchfork
|
|
74
77
|
:check_client_connection => false,
|
75
78
|
:rewindable_input => true,
|
76
79
|
:client_body_buffer_size => Pitchfork::Const::MAX_BODY,
|
80
|
+
:before_service_worker_ready => nil,
|
81
|
+
:before_service_worker_exit => nil,
|
77
82
|
}
|
78
83
|
#:startdoc:
|
79
84
|
|
@@ -175,6 +180,14 @@ module Pitchfork
|
|
175
180
|
set_hook(:after_request_complete, block_given? ? block : args[0], 3)
|
176
181
|
end
|
177
182
|
|
183
|
+
def before_service_worker_ready(&block)
|
184
|
+
set_hook(:before_service_worker_ready, block, 2)
|
185
|
+
end
|
186
|
+
|
187
|
+
def before_service_worker_exit(&block)
|
188
|
+
set_hook(:before_service_worker_exit, block, 2)
|
189
|
+
end
|
190
|
+
|
178
191
|
def timeout(seconds, cleanup: 2)
|
179
192
|
soft_timeout = set_int(:soft_timeout, seconds, 3)
|
180
193
|
cleanup_timeout = set_int(:cleanup_timeout, cleanup, 2)
|
data/lib/pitchfork/const.rb
CHANGED
data/lib/pitchfork/flock.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
# :enddoc:
|
3
4
|
# no stable API here
|
4
5
|
|
@@ -19,14 +20,13 @@ module Pitchfork
|
|
19
20
|
"SERVER_SOFTWARE" => "Pitchfork #{Pitchfork::Const::UNICORN_VERSION}"
|
20
21
|
}
|
21
22
|
|
22
|
-
NULL_IO = StringIO.new
|
23
|
+
NULL_IO = StringIO.new.binmode
|
23
24
|
|
24
25
|
# :stopdoc:
|
25
|
-
HTTP_RESPONSE_START = [ 'HTTP'
|
26
|
+
HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ' ]
|
26
27
|
EMPTY_ARRAY = [].freeze
|
27
28
|
@@input_class = Pitchfork::TeeInput
|
28
29
|
@@check_client_connection = false
|
29
|
-
@@tcpi_inspect_ok = Socket.const_defined?(:TCP_INFO)
|
30
30
|
|
31
31
|
def self.input_class
|
32
32
|
@@input_class
|
@@ -104,83 +104,29 @@ module Pitchfork
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def hijacked?
|
107
|
-
env.include?('rack.hijack_io'
|
107
|
+
env.include?('rack.hijack_io')
|
108
108
|
end
|
109
109
|
|
110
|
-
if
|
111
|
-
TCPI = Raindrops::TCP_Info.allocate
|
112
|
-
|
110
|
+
if Socket.const_defined?(:TCP_INFO) # Linux
|
113
111
|
def check_client_connection(socket) # :nodoc:
|
114
112
|
if TCPSocket === socket
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
write_http_header(socket)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
if Raindrops.const_defined?(:TCP)
|
124
|
-
# raindrops 0.18.0+ supports FreeBSD + Linux using the same names
|
125
|
-
# Evaluate these hash lookups at load time so we can
|
126
|
-
# generate an opt_case_dispatch instruction
|
127
|
-
eval <<-EOS
|
128
|
-
def closed_state?(state) # :nodoc:
|
129
|
-
case state
|
130
|
-
when #{Raindrops::TCP[:ESTABLISHED]}
|
131
|
-
false
|
132
|
-
when #{Raindrops::TCP.values_at(
|
133
|
-
:CLOSE_WAIT, :TIME_WAIT, :CLOSE, :LAST_ACK, :CLOSING).join(',')}
|
134
|
-
true
|
135
|
-
else
|
136
|
-
false
|
137
|
-
end
|
138
|
-
end
|
139
|
-
EOS
|
140
|
-
else
|
141
|
-
# raindrops before 0.18 only supported TCP_INFO under Linux
|
142
|
-
def closed_state?(state) # :nodoc:
|
143
|
-
case state
|
144
|
-
when 1 # ESTABLISHED
|
145
|
-
false
|
146
|
-
when 8, 6, 7, 9, 11 # CLOSE_WAIT, TIME_WAIT, CLOSE, LAST_ACK, CLOSING
|
147
|
-
true
|
148
|
-
else
|
149
|
-
false
|
113
|
+
begin
|
114
|
+
tcp_info = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
|
115
|
+
rescue IOError, SystemCallError
|
116
|
+
return write_http_header(socket)
|
150
117
|
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
else
|
154
118
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
def check_client_connection(socket) # :nodoc:
|
159
|
-
if TCPSocket === socket && @@tcpi_inspect_ok
|
160
|
-
opt = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO).inspect
|
161
|
-
if opt =~ /\bstate=(\S+)/
|
162
|
-
raise Errno::EPIPE, "client closed connection".freeze,
|
163
|
-
EMPTY_ARRAY if closed_state_str?($1)
|
164
|
-
else
|
165
|
-
@@tcpi_inspect_ok = false
|
166
|
-
write_http_header(socket)
|
119
|
+
case tcp_info.data.unpack1("C")
|
120
|
+
when 6, 7, 8, 9, 11 # TIME_WAIT, CLOSE, CLOSE_WAIT, LAST_ACK, CLOSING
|
121
|
+
raise Errno::EPIPE, "client closed connection", EMPTY_ARRAY
|
167
122
|
end
|
168
|
-
opt.clear
|
169
123
|
else
|
170
124
|
write_http_header(socket)
|
171
125
|
end
|
172
126
|
end
|
173
|
-
|
174
|
-
def
|
175
|
-
|
176
|
-
when 'ESTABLISHED'
|
177
|
-
false
|
178
|
-
# not a typo, ruby maps TCP_CLOSE (no 'D') to state=CLOSED (w/ 'D')
|
179
|
-
when 'CLOSE_WAIT', 'TIME_WAIT', 'CLOSED', 'LAST_ACK', 'CLOSING'
|
180
|
-
true
|
181
|
-
else
|
182
|
-
false
|
183
|
-
end
|
127
|
+
else
|
128
|
+
def check_client_connection(socket) # :nodoc:
|
129
|
+
write_http_header(socket)
|
184
130
|
end
|
185
131
|
end
|
186
132
|
|
@@ -199,11 +145,11 @@ module Pitchfork
|
|
199
145
|
val.downcase!
|
200
146
|
end
|
201
147
|
|
202
|
-
if vals.pop == 'chunked'
|
203
|
-
return true unless vals.include?('chunked'
|
148
|
+
if vals.pop == 'chunked'
|
149
|
+
return true unless vals.include?('chunked')
|
204
150
|
raise Pitchfork::HttpParserError, 'double chunked', []
|
205
151
|
end
|
206
|
-
return false unless vals.include?('chunked'
|
152
|
+
return false unless vals.include?('chunked')
|
207
153
|
raise Pitchfork::HttpParserError, 'chunked not last', []
|
208
154
|
end
|
209
155
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
# :enddoc:
|
3
4
|
|
4
5
|
module Pitchfork
|
@@ -48,10 +49,10 @@ module Pitchfork
|
|
48
49
|
if headers
|
49
50
|
code = status.to_i
|
50
51
|
msg = STATUS_CODES[code]
|
51
|
-
start = req.response_start_sent ? ''
|
52
|
+
start = req.response_start_sent ? '' : 'HTTP/1.1 '
|
52
53
|
buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \
|
53
54
|
"Date: #{httpdate}\r\n" \
|
54
|
-
"Connection: close\r\n"
|
55
|
+
"Connection: close\r\n".b
|
55
56
|
headers.each do |key, value|
|
56
57
|
case key
|
57
58
|
when %r{\A(?:Date|Connection)\z}i
|
@@ -64,7 +65,7 @@ module Pitchfork
|
|
64
65
|
append_header(buf, key, value)
|
65
66
|
end
|
66
67
|
end
|
67
|
-
socket.write(buf << "\r\n"
|
68
|
+
socket.write(buf << "\r\n")
|
68
69
|
end
|
69
70
|
|
70
71
|
if hijack
|