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 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