unicorn 4.5.0 → 4.6.0pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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==