yahns 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/GIT-VERSION-GEN +1 -1
- data/lib/yahns/http_client.rb +10 -5
- data/lib/yahns/http_response.rb +7 -7
- data/lib/yahns/proxy_http_response.rb +9 -0
- data/lib/yahns/proxy_pass.rb +11 -2
- data/lib/yahns/queue_epoll.rb +1 -1
- data/lib/yahns/server_mp.rb +3 -3
- data/lib/yahns/sigevent_efd.rb +1 -1
- data/test/helper.rb +2 -1
- data/test/test_extras_proxy_pass.rb +5 -0
- data/test/test_proxy_pass.rb +5 -0
- data/test/test_rack_hijack.rb +5 -0
- data/test/test_ssl.rb +4 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0bea696fff5f5d58044ab6f62df4b89d0b03a4d5
|
4
|
+
data.tar.gz: 7936ab7581a763fac0e466a98f5d88e8ff1e8a1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8ead2093e262430942ffbd4fc438d1160903b0fbb5c61f32f2339022d46488d9e7ad370714494f2232e09a1759e3c2d4b8216e1dad68f5b0abcf82cccf3abf1
|
7
|
+
data.tar.gz: cd8b7d89b7341c7aa1da9681ccadff2d9d2a828107d8b1ca37354daffeee8b46039b752babd326ef5e51ecb1b97720e3a75d2c7ea55e687758b17aa87e3ee6b8
|
data/GIT-VERSION-GEN
CHANGED
data/lib/yahns/http_client.rb
CHANGED
@@ -20,7 +20,6 @@ class Yahns::HttpClient < Kgio::Socket # :nodoc:
|
|
20
20
|
# called from acceptor thread
|
21
21
|
def yahns_init
|
22
22
|
@hs = Unicorn::HttpRequest.new
|
23
|
-
@response_start_sent = false
|
24
23
|
@state = :headers # :body, :trailers, :pipelined, Wbuf, StreamFile
|
25
24
|
@input = nil
|
26
25
|
end
|
@@ -194,9 +193,9 @@ class Yahns::HttpClient < Kgio::Socket # :nodoc:
|
|
194
193
|
mkinput_preread # keep looping (@state == :body)
|
195
194
|
true
|
196
195
|
else # :lazy, false
|
197
|
-
|
198
|
-
return :ignore if
|
199
|
-
http_response_write(
|
196
|
+
status, headers, body = k.app.call(env = @hs.env)
|
197
|
+
return :ignore if app_hijacked?(env, body)
|
198
|
+
http_response_write(status, headers, body)
|
200
199
|
end
|
201
200
|
end
|
202
201
|
|
@@ -218,7 +217,7 @@ class Yahns::HttpClient < Kgio::Socket # :nodoc:
|
|
218
217
|
|
219
218
|
# run the rack app
|
220
219
|
status, headers, body = k.app.call(env.merge!(k.app_defaults))
|
221
|
-
return :ignore if
|
220
|
+
return :ignore if app_hijacked?(env, body)
|
222
221
|
if status.to_i == 100
|
223
222
|
rv = http_100_response(env) and return rv
|
224
223
|
status, headers, body = k.app.call(env)
|
@@ -299,4 +298,10 @@ class Yahns::HttpClient < Kgio::Socket # :nodoc:
|
|
299
298
|
shutdown rescue nil
|
300
299
|
return # always drop the connection on uncaught errors
|
301
300
|
end
|
301
|
+
|
302
|
+
def app_hijacked?(env, body)
|
303
|
+
return false unless env.include?(RACK_HIJACK_IO)
|
304
|
+
body.close if body.respond_to?(:close)
|
305
|
+
true
|
306
|
+
end
|
302
307
|
end
|
data/lib/yahns/http_response.rb
CHANGED
@@ -46,7 +46,7 @@ module Yahns::HttpResponse # :nodoc:
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def response_start
|
49
|
-
@response_start_sent ? Z : RESPONSE_START
|
49
|
+
@hs.response_start_sent ? Z : RESPONSE_START
|
50
50
|
end
|
51
51
|
|
52
52
|
def response_wait_write(rv)
|
@@ -69,7 +69,9 @@ module Yahns::HttpResponse # :nodoc:
|
|
69
69
|
end
|
70
70
|
wbuf = Yahns::Wbuf.new(body, alive, self.class.output_buffer_tmpdir, ret)
|
71
71
|
rv = wbuf.wbuf_write(self, header)
|
72
|
-
body
|
72
|
+
if body && ! alive.respond_to?(:call) # skip body.each if hijacked
|
73
|
+
body.each { |chunk| rv = wbuf.wbuf_write(self, chunk) }
|
74
|
+
end
|
73
75
|
wbuf_maybe(wbuf, rv)
|
74
76
|
end
|
75
77
|
|
@@ -94,7 +96,6 @@ module Yahns::HttpResponse # :nodoc:
|
|
94
96
|
def http_response_done(alive)
|
95
97
|
@input = @input.close if @input
|
96
98
|
if alive
|
97
|
-
@response_start_sent = false
|
98
99
|
# @hs.buf will have data if the client pipelined
|
99
100
|
if @hs.buf.empty?
|
100
101
|
@state = :headers
|
@@ -156,7 +157,6 @@ module Yahns::HttpResponse # :nodoc:
|
|
156
157
|
buf << kv_str(key, value)
|
157
158
|
when "rack.hijack"
|
158
159
|
hijack = value
|
159
|
-
body = nil # ensure we do not close body
|
160
160
|
else
|
161
161
|
buf << kv_str(key, value)
|
162
162
|
end
|
@@ -224,7 +224,7 @@ module Yahns::HttpResponse # :nodoc:
|
|
224
224
|
# returns nil on success
|
225
225
|
# :wait_readable/:wait_writable/:close for epoll
|
226
226
|
def do_ccc
|
227
|
-
@response_start_sent = true
|
227
|
+
@hs.response_start_sent = true
|
228
228
|
wbuf = nil
|
229
229
|
rv = nil
|
230
230
|
CCC_RESPONSE_START.each do |buf|
|
@@ -256,8 +256,8 @@ module Yahns::HttpResponse # :nodoc:
|
|
256
256
|
# returns :close, :wait_writable, or :wait_readable
|
257
257
|
def http_100_response(env)
|
258
258
|
env.delete("HTTP_EXPECT") =~ /\A100-continue\z/i or return
|
259
|
-
buf = @response_start_sent ? "100 Continue\r\n\r\nHTTP/1.1 ".freeze
|
260
|
-
|
259
|
+
buf = @hs.response_start_sent ? "100 Continue\r\n\r\nHTTP/1.1 ".freeze
|
260
|
+
: "HTTP/1.1 100 Continue\r\n\r\n".freeze
|
261
261
|
|
262
262
|
case rv = kgio_trywrite(buf)
|
263
263
|
when String
|
@@ -66,6 +66,7 @@ module Yahns::HttpResponse # :nodoc:
|
|
66
66
|
env[REQUEST_METHOD] != HEAD
|
67
67
|
flags = MSG_DONTWAIT
|
68
68
|
alive = @hs.next? && self.class.persistent_connections
|
69
|
+
response_headers = env['yahns.proxy_pass.response_headers']
|
69
70
|
|
70
71
|
res = "HTTP/1.1 #{status}\r\n"
|
71
72
|
headers.each do |key,value| # n.b.: headers is an Array of 2-element Arrays
|
@@ -76,6 +77,14 @@ module Yahns::HttpResponse # :nodoc:
|
|
76
77
|
flags |= MSG_MORE if have_body && value.to_i > 0
|
77
78
|
end
|
78
79
|
|
80
|
+
# response header mapping
|
81
|
+
case val = response_headers[key]
|
82
|
+
when :ignore
|
83
|
+
next
|
84
|
+
when String
|
85
|
+
value = val
|
86
|
+
end
|
87
|
+
|
79
88
|
res << "#{key}: #{value}\r\n"
|
80
89
|
end
|
81
90
|
|
data/lib/yahns/proxy_pass.rb
CHANGED
@@ -149,8 +149,10 @@ class Yahns::ProxyPass # :nodoc:
|
|
149
149
|
end
|
150
150
|
|
151
151
|
def close_req_body(input)
|
152
|
+
# we cannot use respond_to?(:close) here since Rack::Lint::InputWrapper
|
153
|
+
# tries to prevent that (and hijack means all Rack specs go out the door)
|
152
154
|
case input
|
153
|
-
when Yahns::TeeInput, IO
|
155
|
+
when Yahns::TeeInput, IO
|
154
156
|
input.close
|
155
157
|
end
|
156
158
|
end
|
@@ -170,7 +172,7 @@ class Yahns::ProxyPass # :nodoc:
|
|
170
172
|
end
|
171
173
|
end # class ReqRes
|
172
174
|
|
173
|
-
def initialize(dest)
|
175
|
+
def initialize(dest, opts = {})
|
174
176
|
case dest
|
175
177
|
when %r{\Aunix:([^:]+)(?::(/.*))?\z}
|
176
178
|
path = $2
|
@@ -182,6 +184,12 @@ class Yahns::ProxyPass # :nodoc:
|
|
182
184
|
else
|
183
185
|
raise ArgumentError, "destination must be an HTTP URL or unix: path"
|
184
186
|
end
|
187
|
+
@response_headers = opts[:response_headers] || {}
|
188
|
+
|
189
|
+
# It's wrong to send the backend Server tag through. Let users say
|
190
|
+
# { "Server => "yahns" } if they want to advertise for us, but don't
|
191
|
+
# advertise by default (for security)
|
192
|
+
@response_headers['Server'] ||= :ignore
|
185
193
|
init_path_vars(path)
|
186
194
|
end
|
187
195
|
|
@@ -235,6 +243,7 @@ class Yahns::ProxyPass # :nodoc:
|
|
235
243
|
ctype = env["CONTENT_TYPE"] and req << "Content-Type: #{ctype}\r\n"
|
236
244
|
clen = env["CONTENT_LENGTH"] and req << "Content-Length: #{clen}\r\n"
|
237
245
|
input = chunked || (clen && clen.to_i > 0) ? env['rack.input'] : nil
|
246
|
+
env['yahns.proxy_pass.response_headers'] = @response_headers
|
238
247
|
|
239
248
|
# finally, prepare to emit the headers
|
240
249
|
rr.req_start(c, req << "\r\n".freeze, input, chunked)
|
data/lib/yahns/queue_epoll.rb
CHANGED
@@ -15,7 +15,7 @@ class Yahns::Queue < SleepyPenguin::Epoll::IO # :nodoc:
|
|
15
15
|
QEV_WR = Epoll::OUT | Epoll::ONESHOT
|
16
16
|
|
17
17
|
def self.new
|
18
|
-
super(
|
18
|
+
super(Epoll::CLOEXEC)
|
19
19
|
end
|
20
20
|
|
21
21
|
# for HTTP and HTTPS servers, we rely on the io writing to us, first
|
data/lib/yahns/server_mp.rb
CHANGED
@@ -103,13 +103,13 @@ module Yahns::ServerMP # :nodoc:
|
|
103
103
|
when :USR2 # exec binary, stay alive in case something went wrong
|
104
104
|
reexec
|
105
105
|
when :WINCH
|
106
|
-
if
|
106
|
+
if $stdin.tty?
|
107
|
+
@logger.info "SIGWINCH ignored because we're not daemonized"
|
108
|
+
else
|
107
109
|
state = :WINCH
|
108
110
|
@logger.info "gracefully stopping all workers"
|
109
111
|
soft_kill_each_worker("QUIT")
|
110
112
|
@worker_processes = 0
|
111
|
-
else
|
112
|
-
@logger.info "SIGWINCH ignored because we're not daemonized"
|
113
113
|
end
|
114
114
|
when :TTIN
|
115
115
|
state = :respawn unless state == :QUIT
|
data/lib/yahns/sigevent_efd.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -134,12 +134,13 @@ def require_exec(cmd)
|
|
134
134
|
end
|
135
135
|
|
136
136
|
class DieIfUsed
|
137
|
+
@@n = 0
|
137
138
|
def each
|
138
139
|
abort "body.each called after response hijack\n"
|
139
140
|
end
|
140
141
|
|
141
142
|
def close
|
142
|
-
|
143
|
+
warn "INFO #$$ closed DieIfUsed #{@@n += 1}"
|
143
144
|
end
|
144
145
|
end
|
145
146
|
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# Copyright (C) 2015 all contributors <yahns-public@yhbt.net>
|
2
2
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
3
|
require_relative 'server_helper'
|
4
|
+
begin
|
5
|
+
require 'kcar'
|
6
|
+
rescue LoadError
|
7
|
+
end
|
4
8
|
|
5
9
|
class TestExtrasProxyPass < Testcase
|
6
10
|
ENV["N"].to_i > 1 and parallelize_me!
|
@@ -27,6 +31,7 @@ class TestExtrasProxyPass < Testcase
|
|
27
31
|
def setup
|
28
32
|
@srv2 = TCPServer.new(ENV["TEST_HOST"] || "127.0.0.1", 0)
|
29
33
|
server_helper_setup
|
34
|
+
skip "kcar missing for extras/proxy_pass" unless defined?(Kcar)
|
30
35
|
end
|
31
36
|
|
32
37
|
def teardown
|
data/test/test_proxy_pass.rb
CHANGED
@@ -3,6 +3,10 @@
|
|
3
3
|
require_relative 'server_helper'
|
4
4
|
require 'json'
|
5
5
|
require 'digest'
|
6
|
+
begin
|
7
|
+
require 'kcar'
|
8
|
+
rescue LoadError
|
9
|
+
end
|
6
10
|
|
7
11
|
class TestProxyPass < Testcase
|
8
12
|
ENV["N"].to_i > 1 and parallelize_me!
|
@@ -163,6 +167,7 @@ class TestProxyPass < Testcase
|
|
163
167
|
def setup
|
164
168
|
@srv2 = TCPServer.new(ENV["TEST_HOST"] || "127.0.0.1", 0)
|
165
169
|
server_helper_setup
|
170
|
+
skip "kcar missing yahns/proxy_pass" unless defined?(Kcar)
|
166
171
|
end
|
167
172
|
|
168
173
|
def teardown
|
data/test/test_rack_hijack.rb
CHANGED
@@ -48,6 +48,7 @@ class TestRackHijack < Testcase
|
|
48
48
|
cfg.instance_eval do
|
49
49
|
GTL.synchronize { app(:rack, HIJACK_APP) { listen "#{host}:#{port}" } }
|
50
50
|
logger(Logger.new(err.path))
|
51
|
+
stderr_path err.path
|
51
52
|
end
|
52
53
|
pid = mkserver(cfg)
|
53
54
|
res = Net::HTTP.start(host, port) { |h| h.get("/hijack_req") }
|
@@ -61,6 +62,10 @@ class TestRackHijack < Testcase
|
|
61
62
|
assert_equal "zzz", res["X-Test"]
|
62
63
|
assert_equal "1.1", res.http_version
|
63
64
|
|
65
|
+
errs = File.readlines(err.path).grep(/DieIfUsed/)
|
66
|
+
assert_equal([ "INFO #{pid} closed DieIfUsed 1\n",
|
67
|
+
"INFO #{pid} closed DieIfUsed 2\n" ], errs)
|
68
|
+
|
64
69
|
res = Net::HTTP.start(host, port) do |h|
|
65
70
|
hdr = { "Content-Type" => 'application/octet-stream' }
|
66
71
|
h.put("/hijack_input", "BLAH", hdr)
|
data/test/test_ssl.rb
CHANGED
@@ -124,10 +124,11 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
|
|
124
124
|
p [ :ERR, req ]
|
125
125
|
end until s.closed?
|
126
126
|
end
|
127
|
-
[ 200, DieIfUsed, DieIfUsed ]
|
127
|
+
[ 200, DieIfUsed.new, DieIfUsed.new ]
|
128
128
|
end
|
129
129
|
app(:rack, ru) { listen "#{host}:#{port}", ssl_ctx: ctx }
|
130
130
|
logger(Logger.new(err.path))
|
131
|
+
stderr_path err.path
|
131
132
|
end
|
132
133
|
end
|
133
134
|
client = ssl_client(host, port)
|
@@ -147,6 +148,8 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
|
|
147
148
|
%w(a b c d).each { |x| client.puts(x) }
|
148
149
|
assert_equal "abcd", client.gets.strip
|
149
150
|
end
|
151
|
+
errs = File.readlines(err.path).grep(/DieIfUsed/)
|
152
|
+
assert_equal([ "INFO #{pid} closed DieIfUsed 1\n" ], errs)
|
150
153
|
ensure
|
151
154
|
client.close if client
|
152
155
|
quit_wait(pid)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yahns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yahns hackers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kgio
|