unicorn 3.3.1 → 3.4.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.
@@ -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