Skip to content

Commit cdef93b

Browse files
authored
Merge pull request #123 from Shopify/refactor-check_client_connection
Stop using Raindrops for `check_client_connection`
2 parents e19b97e + b8a38a6 commit cdef93b

File tree

1 file changed

+11
-66
lines changed

1 file changed

+11
-66
lines changed

lib/pitchfork/http_parser.rb

Lines changed: 11 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class HttpParser
2727
EMPTY_ARRAY = [].freeze
2828
@@input_class = Pitchfork::TeeInput
2929
@@check_client_connection = false
30-
@@tcpi_inspect_ok = Socket.const_defined?(:TCP_INFO)
3130

3231
def self.input_class
3332
@@input_class
@@ -108,80 +107,26 @@ def hijacked?
108107
env.include?('rack.hijack_io')
109108
end
110109

111-
if Raindrops.const_defined?(:TCP_Info)
112-
TCPI = Raindrops::TCP_Info.allocate
113-
110+
if Socket.const_defined?(:TCP_INFO) # Linux
114111
def check_client_connection(socket) # :nodoc:
115112
if TCPSocket === socket
116-
# Raindrops::TCP_Info#get!, #state (reads struct tcp_info#tcpi_state)
117-
raise Errno::EPIPE, "client closed connection",
118-
EMPTY_ARRAY if closed_state?(TCPI.get!(socket).state)
119-
else
120-
write_http_header(socket)
121-
end
122-
end
123-
124-
if Raindrops.const_defined?(:TCP)
125-
# raindrops 0.18.0+ supports FreeBSD + Linux using the same names
126-
# Evaluate these hash lookups at load time so we can
127-
# generate an opt_case_dispatch instruction
128-
eval <<-EOS
129-
def closed_state?(state) # :nodoc:
130-
case state
131-
when #{Raindrops::TCP[:ESTABLISHED]}
132-
false
133-
when #{Raindrops::TCP.values_at(
134-
:CLOSE_WAIT, :TIME_WAIT, :CLOSE, :LAST_ACK, :CLOSING).join(',')}
135-
true
136-
else
137-
false
138-
end
139-
end
140-
EOS
141-
else
142-
# raindrops before 0.18 only supported TCP_INFO under Linux
143-
def closed_state?(state) # :nodoc:
144-
case state
145-
when 1 # ESTABLISHED
146-
false
147-
when 8, 6, 7, 9, 11 # CLOSE_WAIT, TIME_WAIT, CLOSE, LAST_ACK, CLOSING
148-
true
149-
else
150-
false
113+
begin
114+
tcp_info = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
115+
rescue IOError, SystemCallError
116+
return write_http_header(socket)
151117
end
152-
end
153-
end
154-
else
155118

156-
# Ruby 2.2+ can show struct tcp_info as a string Socket::Option#inspect.
157-
# Not that efficient, but probably still better than doing unnecessary
158-
# work after a client gives up.
159-
def check_client_connection(socket) # :nodoc:
160-
if TCPSocket === socket && @@tcpi_inspect_ok
161-
opt = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO).inspect
162-
if opt =~ /\bstate=(\S+)/
163-
raise Errno::EPIPE, "client closed connection",
164-
EMPTY_ARRAY if closed_state_str?($1)
165-
else
166-
@@tcpi_inspect_ok = false
167-
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
168122
end
169-
opt.clear
170123
else
171124
write_http_header(socket)
172125
end
173126
end
174-
175-
def closed_state_str?(state)
176-
case state
177-
when 'ESTABLISHED'
178-
false
179-
# not a typo, ruby maps TCP_CLOSE (no 'D') to state=CLOSED (w/ 'D')
180-
when 'CLOSE_WAIT', 'TIME_WAIT', 'CLOSED', 'LAST_ACK', 'CLOSING'
181-
true
182-
else
183-
false
184-
end
127+
else
128+
def check_client_connection(socket) # :nodoc:
129+
write_http_header(socket)
185130
end
186131
end
187132

0 commit comments

Comments
 (0)