yahns 1.7.0 → 1.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e1ce64c8f47f927355913c766a4c7cc3d007089
4
- data.tar.gz: 29d5d3c75d4b05671d62f30987c71afa544d6950
3
+ metadata.gz: 0bea696fff5f5d58044ab6f62df4b89d0b03a4d5
4
+ data.tar.gz: 7936ab7581a763fac0e466a98f5d88e8ff1e8a1a
5
5
  SHA512:
6
- metadata.gz: 5fdfdb0cf9f3baf831cdf5d8af0f6779f2efc930c6736bdd5f31a18ea4248cf93643476208bfa93e2d43eb9026988b14a084cd708121098489bec599bf55f8d1
7
- data.tar.gz: 6c1f462a5a96ae40eed022c339994e2254ddd3fdb9b95a453ddda67ab04b509badf35868a69840b97a28a66a613fc20407f9104ac6eb769d11ca72a1d553d10d
6
+ metadata.gz: a8ead2093e262430942ffbd4fc438d1160903b0fbb5c61f32f2339022d46488d9e7ad370714494f2232e09a1759e3c2d4b8216e1dad68f5b0abcf82cccf3abf1
7
+ data.tar.gz: cd8b7d89b7341c7aa1da9681ccadff2d9d2a828107d8b1ca37354daffeee8b46039b752babd326ef5e51ecb1b97720e3a75d2c7ea55e687758b17aa87e3ee6b8
data/GIT-VERSION-GEN CHANGED
@@ -4,7 +4,7 @@
4
4
  CONSTANT = "Yahns::VERSION"
5
5
  RVF = "lib/yahns/version.rb"
6
6
  GVF = "GIT-VERSION-FILE"
7
- DEF_VER = "v1.7.0"
7
+ DEF_VER = "v1.8.0"
8
8
  vn = DEF_VER
9
9
 
10
10
  # First see if there is a version file (included in release tarballs),
@@ -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
- r = k.app.call(env = @hs.env)
198
- return :ignore if env.include?(RACK_HIJACK_IO)
199
- http_response_write(*r)
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 env.include?(RACK_HIJACK_IO)
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
@@ -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.each { |chunk| rv = wbuf.wbuf_write(self, chunk) } if 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
- : "HTTP/1.1 100 Continue\r\n\r\n".freeze
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
 
@@ -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, StringIO
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)
@@ -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(SleepyPenguin::Epoll::CLOEXEC)
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
@@ -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 @daemon_pipe
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
@@ -4,7 +4,7 @@
4
4
  class Yahns::Sigevent < SleepyPenguin::EventFD # :nodoc:
5
5
  include Kgio::DefaultWaiters
6
6
  def self.new
7
- super(0, SleepyPenguin::EventFD::CLOEXEC)
7
+ super(0, :CLOEXEC)
8
8
  end
9
9
 
10
10
  def sev_signal
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
- abort "body.close called after response hijack\n"
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
@@ -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
@@ -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.7.0
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-05-11 00:00:00.000000000 Z
11
+ date: 2015-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kgio