unicorn 4.5.0 → 4.6.0pre1

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.
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v4.5.0
4
+ DEF_VER=v4.6.0.pre1
5
5
 
6
6
  LF='
7
7
  '
@@ -58,6 +58,11 @@ op = OptionParser.new("", 24, ' ') do |opts|
58
58
  ENV["RACK_ENV"] = e
59
59
  end
60
60
 
61
+ opts.on("-N", "--no-default-middleware",
62
+ "do not load middleware implied by RACK_ENV") do |e|
63
+ rackup_opts[:no_default_middleware] = true
64
+ end
65
+
61
66
  opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
62
67
  rackup_opts[:daemonize] = !!d
63
68
  end
@@ -49,6 +49,8 @@ module Unicorn
49
49
 
50
50
  pp({ :inner_app => inner_app }) if $DEBUG
51
51
 
52
+ return inner_app if op[:no_default_middleware]
53
+
52
54
  # return value, matches rackup defaults based on env
53
55
  # Unicorn does not support persistent connections, but Rainbows!
54
56
  # and Zbatery both do. Users accustomed to the Rack::Server default
@@ -91,6 +91,35 @@ class Unicorn::HttpParser
91
91
 
92
92
  e[RACK_INPUT] = 0 == content_length ?
93
93
  NULL_IO : @@input_class.new(socket, self)
94
+ hijack_setup(e, socket)
94
95
  e.merge!(DEFAULTS)
95
96
  end
97
+
98
+ # Rack 1.5.0 (protocol version 1.2) adds hijack request support
99
+ if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
100
+ DEFAULTS["rack.hijack?"] = true
101
+
102
+ # FIXME: asking for clarification about this in
103
+ # http://mid.gmane.org/20130122100802.GA28585@dcvr.yhbt.net
104
+ DEFAULTS["rack.version"] = [1, 2]
105
+
106
+ RACK_HIJACK = "rack.hijack".freeze
107
+ RACK_HIJACK_IO = "rack.hijack_io".freeze
108
+
109
+ def hijacked?
110
+ env.include?(RACK_HIJACK_IO)
111
+ end
112
+
113
+ def hijack_setup(e, socket)
114
+ e[RACK_HIJACK] = proc { e[RACK_HIJACK_IO] ||= socket }
115
+ end
116
+ else
117
+ # old Rack, do nothing.
118
+ def hijack_setup(e, _)
119
+ end
120
+
121
+ def hijacked?
122
+ false
123
+ end
124
+ end
96
125
  end
@@ -25,6 +25,7 @@ module Unicorn::HttpResponse
25
25
  def http_response_write(socket, status, headers, body,
26
26
  response_start_sent=false)
27
27
  status = CODES[status.to_i] || status
28
+ hijack = nil
28
29
 
29
30
  http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
30
31
  if headers
@@ -33,19 +34,42 @@ module Unicorn::HttpResponse
33
34
  "Status: #{status}\r\n" \
34
35
  "Connection: close\r\n"
35
36
  headers.each do |key, value|
36
- next if %r{\A(?:Date\z|Connection\z)}i =~ key
37
- if value =~ /\n/
38
- # avoiding blank, key-only cookies with /\n+/
39
- buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
37
+ case key
38
+ when %r{\A(?:Date\z|Connection\z)}i
39
+ next
40
+ when "rack.hijack"
41
+ # this was an illegal key in Rack < 1.5, so it should be
42
+ # OK to silently discard it for those older versions
43
+ hijack = hijack_prepare(value)
40
44
  else
41
- buf << "#{key}: #{value}\r\n"
45
+ if value =~ /\n/
46
+ # avoiding blank, key-only cookies with /\n+/
47
+ buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
48
+ else
49
+ buf << "#{key}: #{value}\r\n"
50
+ end
42
51
  end
43
52
  end
44
53
  socket.write(buf << CRLF)
45
54
  end
46
55
 
47
- body.each { |chunk| socket.write(chunk) }
48
- ensure
49
- body.respond_to?(:close) and body.close
56
+ if hijack
57
+ body = nil # ensure we do not close body
58
+ hijack.call(socket)
59
+ else
60
+ body.each { |chunk| socket.write(chunk) }
61
+ end
62
+ ensure
63
+ body.respond_to?(:close) and body.close
64
+ end
65
+
66
+ # Rack 1.5.0 (protocol version 1.2) adds response hijacking support
67
+ if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
68
+ def hijack_prepare(value)
69
+ value
70
+ end
71
+ else
72
+ def hijack_prepare(_)
73
+ end
50
74
  end
51
75
  end
@@ -550,17 +550,21 @@ class Unicorn::HttpServer
550
550
  # in 3 easy steps: read request, call app, write app response
551
551
  def process_client(client)
552
552
  status, headers, body = @app.call(env = @request.read(client))
553
+ return if @request.hijacked?
553
554
 
554
555
  if 100 == status.to_i
555
556
  client.write(expect_100_response)
556
557
  env.delete(Unicorn::Const::HTTP_EXPECT)
557
558
  status, headers, body = @app.call(env)
559
+ return if @request.hijacked?
558
560
  end
559
561
  @request.headers? or headers = nil
560
562
  http_response_write(client, status, headers, body,
561
563
  @request.response_start_sent)
562
- client.shutdown # in case of fork() in Rack app
563
- client.close # flush and uncork socket immediately, no keepalive
564
+ unless client.closed? # rack.hijack may've close this for us
565
+ client.shutdown # in case of fork() in Rack app
566
+ client.close # flush and uncork socket immediately, no keepalive
567
+ end
564
568
  rescue => e
565
569
  handle_error(client, e)
566
570
  end
@@ -16,10 +16,10 @@ opts = {
16
16
 
17
17
  pid = fork do
18
18
  Isolate.now!(opts) do
19
- gem 'raindrops', '0.8.0'
19
+ gem 'raindrops', '0.10.0'
20
20
  gem 'kgio-monkey', '0.4.0'
21
- gem 'kgio', '2.7.4'
22
- gem 'rack', '1.4.1'
21
+ gem 'kgio', '2.8.0'
22
+ gem 'rack', '1.5.1'
23
23
  end
24
24
  end
25
25
  _, status = Process.waitpid2(pid)
@@ -0,0 +1,42 @@
1
+ use Rack::Lint
2
+ use Rack::ContentLength
3
+ use Rack::ContentType, "text/plain"
4
+ class DieIfUsed
5
+ def each
6
+ abort "body.each called after response hijack\n"
7
+ end
8
+
9
+ def close
10
+ abort "body.close called after response hijack\n"
11
+ end
12
+ end
13
+ run lambda { |env|
14
+ case env["PATH_INFO"]
15
+ when "/hijack_req"
16
+ if env["rack.hijack?"]
17
+ io = env["rack.hijack"].call
18
+ if io.respond_to?(:read_nonblock) &&
19
+ env["rack.hijack_io"].respond_to?(:read_nonblock)
20
+
21
+ # exercise both, since we Rack::Lint may use different objects
22
+ env["rack.hijack_io"].write("HTTP/1.0 200 OK\r\n\r\n")
23
+ io.write("request.hijacked")
24
+ io.close
25
+ return [ 500, {}, DieIfUsed.new ]
26
+ end
27
+ end
28
+ [ 500, {}, [ "hijack BAD\n" ] ]
29
+ when "/hijack_res"
30
+ r = "response.hijacked"
31
+ [ 200,
32
+ {
33
+ "Content-Length" => r.bytesize.to_s,
34
+ "rack.hijack" => proc do |io|
35
+ io.write(r)
36
+ io.close
37
+ end
38
+ },
39
+ DieIfUsed.new
40
+ ]
41
+ end
42
+ }
@@ -11,7 +11,10 @@ t_begin "setup and start" && {
11
11
  cat > $t_pfx.app/fooapp.rb <<\EOF
12
12
  class Fooapp
13
13
  def self.call(env)
14
- [ 200, [%w(Content-Type text/plain), %w(Content-Length 2)], %w(HI) ]
14
+ # Rack::Lint in 1.5.0 requires headers to be a hash
15
+ h = [%w(Content-Type text/plain), %w(Content-Length 2)]
16
+ h = Rack::Utils::HeaderHash.new(h)
17
+ [ 200, h, %w(HI) ]
15
18
  end
16
19
  end
17
20
  EOF
@@ -0,0 +1,27 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ t_plan 5 "rack.hijack tests (Rack 1.5+ (Rack::VERSION >= [ 1,2]))"
4
+
5
+ t_begin "setup and start" && {
6
+ unicorn_setup
7
+ unicorn -D -c $unicorn_config hijack.ru
8
+ unicorn_wait_start
9
+ }
10
+
11
+ t_begin "check request hijack" && {
12
+ test "xrequest.hijacked" = x"$(curl -sSfv http://$listen/hijack_req)"
13
+ }
14
+
15
+ t_begin "check response hijack" && {
16
+ test "xresponse.hijacked" = x"$(curl -sSfv http://$listen/hijack_res)"
17
+ }
18
+
19
+ t_begin "killing succeeds" && {
20
+ kill $unicorn_pid
21
+ }
22
+
23
+ t_begin "check stderr" && {
24
+ check_stderr
25
+ }
26
+
27
+ t_done
@@ -323,6 +323,7 @@ EOF
323
323
  # mobile phone or netbook on a slow connection :)
324
324
  assert lines.size <= 24, "help height fits in an ANSI terminal window"
325
325
  lines.each do |line|
326
+ line.chomp!
326
327
  assert line.size <= 80, "help width fits in an ANSI terminal window"
327
328
  end
328
329
  end
metadata CHANGED
@@ -2,15 +2,15 @@
2
2
  name: !binary |-
3
3
  dW5pY29ybg==
4
4
  version: !ruby/object:Gem::Version
5
- version: 4.5.0
6
- prerelease:
5
+ version: 4.6.0pre1
6
+ prerelease: 5
7
7
  platform: ruby
8
8
  authors:
9
9
  - Unicorn hackers
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-12-08 00:00:00.000000000 Z
13
+ date: 2013-01-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: !binary |-
@@ -255,6 +255,7 @@ files:
255
255
  - t/detach.ru
256
256
  - t/env.ru
257
257
  - t/heartbeat-timeout.ru
258
+ - t/hijack.ru
258
259
  - t/listener_names.ru
259
260
  - t/my-tap-lib.sh
260
261
  - t/oob_gc.ru
@@ -295,6 +296,7 @@ files:
295
296
  - t/t0100-rack-input-tests.sh
296
297
  - t/t0116-client_body_buffer_size.sh
297
298
  - t/t0116.ru
299
+ - t/t0200-rack-hijack.sh
298
300
  - t/t0600-https-server-basic.sh
299
301
  - t/t9000-preread-input.sh
300
302
  - t/t9001-oob_gc.sh
@@ -349,9 +351,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
349
351
  required_rubygems_version: !ruby/object:Gem::Requirement
350
352
  none: false
351
353
  requirements:
352
- - - ! '>='
354
+ - - ! '>'
353
355
  - !ruby/object:Gem::Version
354
- version: '0'
356
+ version: 1.3.1
355
357
  requirements: []
356
358
  rubyforge_project: !binary |-
357
359
  bW9uZ3JlbA==