@@ -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 =~ /\b state=(\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