unicorn 3.3.1 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  ---
2
- cgit_url: http://git.bogomips.org/cgit/unicorn.git
3
- git_url: git://git.bogomips.org/unicorn.git
2
+ cgit_url: http://bogomips.org/unicorn.git
3
+ git_url: git://bogomips.org/unicorn.git
4
4
  rdoc_url: http://unicorn.bogomips.org/
5
5
  changelog_start: v1.1.5
6
6
  merge_html:
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v3.3.1.GIT
4
+ DEF_VER=v3.4.0.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -3,16 +3,17 @@
3
3
  Occasionally odd {issues}[link:ISSUES.html] arise without a transparent or
4
4
  acceptable solution. Those issues are documented here.
5
5
 
6
+ * Under some versions of Ruby 1.8, it is necessary to call +srand+ in an
7
+ after_fork hook to get correct random number generation.
8
+
9
+ See http://redmine.ruby-lang.org/issues/show/4338
10
+
6
11
  * For notes on sandboxing tools such as Bundler or Isolate,
7
12
  see the {Sandbox}[link:Sandbox.html] page.
8
13
 
9
- * Under Ruby 1.9.1, methods like Array#shuffle and Array#sample will
10
- segfault if called after forking. This is fixed in trunk (r26936) and
11
- should be backported to the next 1.9.1 stable release (after p378).
12
- Until then, it is advisable to call "Kernel.rand" in your after_fork
13
- hook to reinitialize the random number generator.
14
-
15
- See http://redmine.ruby-lang.org/issues/show/2962 for more details
14
+ * nginx with "sendfile on" under FreeBSD 8 is broken when
15
+ uploads are buffered to disk. Disabling sendfile is required to
16
+ work around this bug which should be fixed in newer versions of FreeBSD.
16
17
 
17
18
  * When using "preload_app true", with apps using background threads
18
19
  need to restart them in the after_fork hook because threads are never
@@ -22,6 +23,15 @@ acceptable solution. Those issues are documented here.
22
23
  deadlocks. The core Ruby Logger class needlessly uses a MonitorMutex
23
24
  which can be disabled with a {monkey patch}[link:examples/logger_mp_safe.rb]
24
25
 
26
+ == Known Issues (Old)
27
+
28
+ * Under Ruby 1.9.1, methods like Array#shuffle and Array#sample will
29
+ segfault if called after forking. Upgrade to Ruby 1.9.2 or call
30
+ "Kernel.rand" in your after_fork hook to reinitialize the random
31
+ number generator.
32
+
33
+ See http://redmine.ruby-lang.org/issues/show/2962 for more details
34
+
25
35
  * Rails 2.3.2 bundles its own version of Rack. This may cause subtle
26
36
  bugs when simultaneously loaded with the system-wide Rack Rubygem
27
37
  which Unicorn depends on. Upgrading to Rails 2.3.4 (or later) is
data/README CHANGED
@@ -85,13 +85,13 @@ You may also install it via RubyGems on Gemcutter:
85
85
  You can get the latest source via git from the following locations
86
86
  (these versions may not be stable):
87
87
 
88
- git://git.bogomips.org/unicorn.git
88
+ git://bogomips.org/unicorn.git
89
89
  git://repo.or.cz/unicorn.git (mirror)
90
90
 
91
91
  You may browse the code from the web and download the latest snapshot
92
92
  tarballs here:
93
93
 
94
- * http://git.bogomips.org/cgit/unicorn.git (cgit)
94
+ * http://bogomips.org/unicorn.git (cgit)
95
95
  * http://repo.or.cz/w/unicorn.git (gitweb)
96
96
 
97
97
  See the HACKING guide on how to contribute and build prerelease gems
data/Rakefile CHANGED
@@ -1,8 +1,9 @@
1
1
  # -*- encoding: binary -*-
2
2
  autoload :Gem, 'rubygems'
3
+ require 'wrongdoc'
3
4
 
4
- cgit_url = "http://git.bogomips.org/cgit/unicorn.git"
5
- git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/unicorn.git'
5
+ cgit_url = Wrongdoc.config[:cgit_url]
6
+ git_url = Wrongdoc.config[:git_url]
6
7
 
7
8
  desc "post to RAA"
8
9
  task :raa_update do
@@ -83,9 +83,9 @@ http {
83
83
  }
84
84
 
85
85
  server {
86
+ # enable one of the following if you're on Linux or FreeBSD
86
87
  # listen 80 default deferred; # for Linux
87
88
  # listen 80 default accept_filter=httpready; # for FreeBSD
88
- listen 80 default;
89
89
 
90
90
  client_max_body_size 4G;
91
91
  server_name _;
@@ -98,7 +98,16 @@ http {
98
98
  # path for static files
99
99
  root /path/to/app/current/public;
100
100
 
101
- location / {
101
+ # Prefer to serve static files directly from nginx to avoid unnecessary
102
+ # data copies from the application server.
103
+ #
104
+ # try_files directive appeared in in nginx 0.7.27 and has stabilized
105
+ # over time. Older versions of nginx (e.g. 0.6.x) requires
106
+ # "if (!-f $request_filename)" which was less efficient:
107
+ # http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
108
+ try_files $uri/index.html $uri.html $uri @app;
109
+
110
+ location @app {
102
111
  # an HTTP header important enough to have its own Wikipedia entry:
103
112
  # http://en.wikipedia.org/wiki/X-Forwarded-For
104
113
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -122,12 +131,7 @@ http {
122
131
  # clients, really.
123
132
  # proxy_buffering off;
124
133
 
125
- # Try to serve static files from nginx, no point in making an
126
- # *application* server like Unicorn/Rainbows! serve static files.
127
- if (!-f $request_filename) {
128
- proxy_pass http://app_server;
129
- break;
130
- }
134
+ proxy_pass http://app_server;
131
135
  }
132
136
 
133
137
  # Rails error pages
@@ -505,7 +505,18 @@ static void set_server_vars(VALUE env, VALUE *server_port)
505
505
  if (!NIL_P(host)) {
506
506
  char *host_ptr = RSTRING_PTR(host);
507
507
  long host_len = RSTRING_LEN(host);
508
- char *colon = memchr(host_ptr, ':', host_len);
508
+ char *colon;
509
+
510
+ if (*host_ptr == '[') { /* ipv6 address format */
511
+ char *rbracket = memchr(host_ptr + 1, ']', host_len - 1);
512
+
513
+ if (rbracket)
514
+ colon = (rbracket[1] == ':') ? rbracket + 1 : NULL;
515
+ else
516
+ colon = memchr(host_ptr + 1, ':', host_len - 1);
517
+ } else {
518
+ colon = memchr(host_ptr, ':', host_len);
519
+ }
509
520
 
510
521
  if (colon) {
511
522
  long port_start = colon - host_ptr + 1;
@@ -26,7 +26,7 @@
26
26
 
27
27
  # URI schemes and absolute paths
28
28
  scheme = ( "http"i ("s"i)? ) $downcase_char >mark %scheme;
29
- hostname = (alnum | "-" | "." | "_")+;
29
+ hostname = ((alnum | "-" | "." | "_")+ | ("[" (":" | xdigit)+ "]"));
30
30
  host_with_port = (hostname (":" digit*)?) >mark %host;
31
31
  userinfo = ((unreserved | escape | ";" | ":" | "&" | "=" | "+")+ "@")*;
32
32
 
@@ -28,6 +28,24 @@ module Unicorn::App
28
28
  SERVER_SOFTWARE
29
29
  ).map { |x| x.freeze } # frozen strings are faster for Hash assignments
30
30
 
31
+ class Body < Unicorn::TmpIO
32
+ def body_offset=(n)
33
+ sysseek(@body_offset = n)
34
+ end
35
+
36
+ def each(&block)
37
+ sysseek @body_offset
38
+ # don't use a preallocated buffer for sysread since we can't
39
+ # guarantee an actual socket is consuming the yielded string
40
+ # (or if somebody is pushing to an array for eventual concatenation
41
+ begin
42
+ yield sysread(CHUNK_SIZE)
43
+ rescue EOFError
44
+ break
45
+ end while true
46
+ end
47
+ end
48
+
31
49
  # Intializes the app, example of usage in a config.ru
32
50
  # map "/cgit" do
33
51
  # run Unicorn::App::ExecCgi.new("/path/to/cgit.cgi")
@@ -43,7 +61,7 @@ module Unicorn::App
43
61
 
44
62
  # Calls the app
45
63
  def call(env)
46
- out, err = Unicorn::TmpIO.new, Unicorn::TmpIO.new
64
+ out, err = Body.new, Unicorn::TmpIO.new
47
65
  inp = force_file_input(env)
48
66
  pid = fork { run_child(inp, out, err, env) }
49
67
  inp.close
@@ -67,9 +85,9 @@ module Unicorn::App
67
85
  ENV['GATEWAY_INTERFACE'] = 'CGI/1.1'
68
86
  env.keys.grep(/^HTTP_/) { |key| ENV[key] = env[key] }
69
87
 
70
- a = IO.new(0).reopen(inp)
71
- b = IO.new(1).reopen(out)
72
- c = IO.new(2).reopen(err)
88
+ $stdin.reopen(inp)
89
+ $stdout.reopen(out)
90
+ $stderr.reopen(err)
73
91
  exec(*args)
74
92
  end
75
93
 
@@ -87,23 +105,7 @@ module Unicorn::App
87
105
  offset = 4
88
106
  end
89
107
  offset += head.length
90
-
91
- # Allows +out+ to be used as a Rack body.
92
- out.instance_eval { class << self; self; end }.instance_eval {
93
- define_method(:each) { |&blk|
94
- sysseek(offset)
95
-
96
- # don't use a preallocated buffer for sysread since we can't
97
- # guarantee an actual socket is consuming the yielded string
98
- # (or if somebody is pushing to an array for eventual concatenation
99
- begin
100
- blk.call(sysread(CHUNK_SIZE))
101
- rescue EOFError
102
- break
103
- end while true
104
- }
105
- }
106
-
108
+ out.body_offset = offset
107
109
  size -= offset
108
110
  prev = nil
109
111
  headers = Rack::Utils::HeaderHash.new
@@ -113,8 +115,9 @@ module Unicorn::App
113
115
  when /^[ \t]/ then headers[prev] << "\n#{line}" if prev
114
116
  end
115
117
  end
118
+ status = headers.delete("Status") || 200
116
119
  headers['Content-Length'] = size.to_s
117
- [ 200, headers, out ]
120
+ [ status, headers, out ]
118
121
  end
119
122
 
120
123
  # ensures rack.input is a file handle that we can redirect stdin to
@@ -247,13 +247,13 @@ class Unicorn::Configurator
247
247
  #
248
248
  # Default: operating system defaults (usually Nagle's algorithm enabled)
249
249
  #
250
- # +:tcp_nopush+: enables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
250
+ # +:tcp_nopush+: enables/disables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
251
251
  #
252
- # This will prevent partial TCP frames from being sent out.
253
- # Enabling +tcp_nopush+ is generally not needed or recommended as
254
- # controlling +tcp_nodelay+ already provides sufficient latency
255
- # reduction whereas Unicorn does not know when the best times are
256
- # for flushing corked sockets.
252
+ # This is enabled by default as of Unicorn 3.4. This prevents partial
253
+ # TCP frames from being sent out and reduces wakeups in nginx if it is
254
+ # on a different machine. Since Unicorn is only designed for applications
255
+ # that send the response body quickly without keepalive, sockets will
256
+ # always be flushed on close to prevent delays.
257
257
  #
258
258
  # This has no effect on UNIX sockets.
259
259
  #
@@ -455,7 +455,7 @@ class Unicorn::Configurator
455
455
  # Rainbows!/Zbatery installations facing untrusted clients directly
456
456
  # should set this to +false+. This is +true+ by default as Unicorn
457
457
  # is designed to only sit behind trusted nginx proxies.
458
- def trust_x_forwarded(bool)
458
+ def trust_x_forwarded(bool) # :nodoc:
459
459
  set_bool(:trust_x_forwarded, bool)
460
460
  end
461
461
 
@@ -473,10 +473,10 @@ class Unicorn::Configurator
473
473
  File.expand_path(address)
474
474
  when %r{\A(?:\*:)?(\d+)\z}
475
475
  "0.0.0.0:#$1"
476
- when %r{\A(.*):(\d+)\z}
477
- # canonicalize the name
478
- packed = Socket.pack_sockaddr_in($2.to_i, $1)
479
- Socket.unpack_sockaddr_in(packed).reverse!.join(':')
476
+ when %r{\A\[([a-fA-F0-9:]+)\]\z}, %r/\A((?:\d+\.){3}\d+)\z/
477
+ canonicalize_tcp($1, 80)
478
+ when %r{\A\[([a-fA-F0-9:]+)\]:(\d+)\z}, %r{\A(.*):(\d+)\z}
479
+ canonicalize_tcp($1, $2.to_i)
480
480
  else
481
481
  address
482
482
  end
@@ -489,6 +489,12 @@ private
489
489
  set[var] = n
490
490
  end
491
491
 
492
+ def canonicalize_tcp(addr, port)
493
+ packed = Socket.pack_sockaddr_in(port, addr)
494
+ port, addr = Socket.unpack_sockaddr_in(packed)
495
+ /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
496
+ end
497
+
492
498
  def set_path(var, path) #:nodoc:
493
499
  case path
494
500
  when NilClass, String
@@ -7,8 +7,8 @@
7
7
  # improve things much compared to constants.
8
8
  module Unicorn::Const
9
9
 
10
- # The current version of Unicorn, currently 3.3.1
11
- UNICORN_VERSION = "3.3.1"
10
+ # The current version of Unicorn, currently 3.4.0
11
+ UNICORN_VERSION = "3.4.0"
12
12
 
13
13
  # default TCP listen host address (0.0.0.0, all interfaces)
14
14
  DEFAULT_HOST = "0.0.0.0"
@@ -26,7 +26,7 @@ module Unicorn::HttpResponse
26
26
  "Status: #{status}\r\n" \
27
27
  "Connection: close\r\n"
28
28
  headers.each do |key, value|
29
- next if %r{\A(?:Date\z|Status\z|Connection\z)}i =~ key
29
+ next if %r{\A(?:Date\z|Connection\z)}i =~ key
30
30
  if value =~ /\n/
31
31
  # avoiding blank, key-only cookies with /\n+/
32
32
  buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
@@ -424,7 +424,6 @@ class Unicorn::HttpServer
424
424
 
425
425
  if pid
426
426
  old_pid = "#{pid}.oldbin"
427
- prev_pid = pid.dup
428
427
  begin
429
428
  self.pid = old_pid # clear the path for a new pid file
430
429
  rescue ArgumentError
@@ -23,6 +23,10 @@ module Unicorn
23
23
 
24
24
  # same default value as Mongrel
25
25
  :backlog => 1024,
26
+
27
+ # since we don't do keepalive, we'll always flush-on-close and
28
+ # this saves packets for everyone.
29
+ :tcp_nopush => true,
26
30
  }
27
31
  #:startdoc:
28
32
 
@@ -45,17 +49,19 @@ module Unicorn
45
49
 
46
50
  def set_tcp_sockopt(sock, opt)
47
51
  # highly portable, but off by default because we don't do keepalive
48
- if defined?(TCP_NODELAY) && ! (val = opt[:tcp_nodelay]).nil?
52
+ if defined?(TCP_NODELAY)
53
+ val = opt[:tcp_nodelay]
54
+ val = DEFAULTS[:tcp_nodelay] if nil == val
49
55
  sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
50
56
  end
51
57
 
52
- unless (val = opt[:tcp_nopush]).nil?
53
- val = val ? 1 : 0
54
- if defined?(TCP_CORK) # Linux
55
- sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
56
- elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
57
- sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
58
- end
58
+ val = opt[:tcp_nopush]
59
+ val = DEFAULTS[:tcp_nopush] if nil == val
60
+ val = val ? 1 : 0
61
+ if defined?(TCP_CORK) # Linux
62
+ sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
63
+ elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
64
+ sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
59
65
  end
60
66
 
61
67
  # No good reason to ever have deferred accepts off
@@ -64,17 +70,17 @@ module Unicorn
64
70
  # this differs from nginx, since nginx doesn't allow us to
65
71
  # configure the the timeout...
66
72
  seconds = opt[:tcp_defer_accept]
67
- seconds = DEFAULTS[:tcp_defer_accept] if seconds == true
73
+ seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
68
74
  seconds = 0 unless seconds # nil/false means disable this
69
75
  sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
70
76
  elsif respond_to?(:accf_arg)
71
- if name = opt[:accept_filter]
72
- begin
73
- sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
74
- rescue => e
75
- logger.error("#{sock_name(sock)} " \
76
- "failed to set accept_filter=#{name} (#{e.inspect})")
77
- end
77
+ name = opt[:accept_filter]
78
+ name = DEFAULTS[:accept_filter] if nil == name
79
+ begin
80
+ sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
81
+ rescue => e
82
+ logger.error("#{sock_name(sock)} " \
83
+ "failed to set accept_filter=#{name} (#{e.inspect})")
78
84
  end
79
85
  end
80
86
  end
@@ -130,7 +136,8 @@ module Unicorn
130
136
  ensure
131
137
  File.umask(old_umask)
132
138
  end
133
- elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/
139
+ elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address ||
140
+ /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address
134
141
  Kgio::TCPServer.new($1, $2.to_i)
135
142
  else
136
143
  raise ArgumentError, "Don't know how to bind: #{address}"
@@ -139,6 +146,13 @@ module Unicorn
139
146
  sock
140
147
  end
141
148
 
149
+ # returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
150
+ def tcp_name(sock)
151
+ port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
152
+ /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
153
+ end
154
+ module_function :tcp_name
155
+
142
156
  # Returns the configuration name of a socket as a string. sock may
143
157
  # be a string value, in which case it is returned as-is
144
158
  # Warning: TCP sockets may not always return the name given to it.
@@ -148,10 +162,10 @@ module Unicorn
148
162
  when UNIXServer
149
163
  Socket.unpack_sockaddr_un(sock.getsockname)
150
164
  when TCPServer
151
- Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
165
+ tcp_name(sock)
152
166
  when Socket
153
167
  begin
154
- Socket.unpack_sockaddr_in(sock.getsockname).reverse!.join(':')
168
+ tcp_name(sock)
155
169
  rescue ArgumentError
156
170
  Socket.unpack_sockaddr_un(sock.getsockname)
157
171
  end
@@ -118,7 +118,7 @@ private
118
118
  end
119
119
 
120
120
  def tee(buffer)
121
- if buffer && (n = buffer.size) > 0
121
+ if buffer && buffer.size > 0
122
122
  @tmp.write(buffer)
123
123
  @tmp.seek(0, IO::SEEK_END) # workaround FreeBSD/OSX + MRI 1.8.x bug
124
124
  end
@@ -17,7 +17,7 @@ opts = {
17
17
  pid = fork do
18
18
  Isolate.now!(opts) do
19
19
  gem 'sqlite3-ruby', '1.2.5'
20
- gem 'kgio', '2.1.1'
20
+ gem 'kgio', '2.2.0'
21
21
  gem 'rack', '1.1.0'
22
22
  end
23
23
  end