unicorn 0.93.1 → 0.93.2

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=v0.93.1.GIT
4
+ DEF_VER=v0.93.2.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -169,6 +169,7 @@ atom = <link rel="alternate" title="Atom feed" href="$(1)" \
169
169
  doc: .document $(ext)/unicorn_http.c NEWS ChangeLog
170
170
  for i in $(man1_bins); do > $$i; done
171
171
  rdoc -Na -t "$(shell sed -ne '1s/^= //p' README)"
172
+ install -m644 COPYING doc/COPYING
172
173
  install -m644 $(shell grep '^[A-Z]' .document) doc/
173
174
  $(MAKE) -C Documentation install-html install-man
174
175
  install -m644 $(man1_paths) doc/
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
- Unicorn is copyrighted free software by Eric Wong
2
- (mailto:normalperson@yhbt.net) and contributors. You can redistribute it
3
- and/or modify it under either the terms of the
1
+ Unicorn is copyrighted free software by all contributors, see logs in
2
+ revision control for names and email addresses of all of them. You can
3
+ redistribute it and/or modify it under either the terms of the
4
4
  {GPL2}[http://www.gnu.org/licenses/gpl-2.0.txt] (see link:COPYING) or
5
5
  the conditions below:
6
6
 
data/README CHANGED
@@ -59,7 +59,7 @@ both the the request and response in between Unicorn and slow clients.
59
59
 
60
60
  == License
61
61
 
62
- Unicorn is copyright 2009 Eric Wong and contributors.
62
+ Unicorn is copyright 2009 by all contributors (see logs in git).
63
63
  It is based on Mongrel and carries the same license.
64
64
 
65
65
  Mongrel is copyright 2007 Zed A. Shaw and contributors. It is licensed
data/Rakefile CHANGED
@@ -73,7 +73,7 @@ task :news_rdoc do
73
73
  puts ""
74
74
 
75
75
  body = tag[:body]
76
- puts tag[:body].gsub(/^/sm, " ").gsub!(/[ \t]+$/sm, "")
76
+ puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
77
77
  puts ""
78
78
  end
79
79
  end
data/TODO CHANGED
@@ -1,5 +1,3 @@
1
1
  * Documentation improvements
2
2
 
3
3
  * launchers - add --kill switch to allow DRY pid file path definition
4
-
5
- * Rainbows! (http://article.gmane.org/gmane.comp.lang.ruby.unicorn.general/22)
@@ -53,6 +53,27 @@ module Unicorn
53
53
 
54
54
  # We populate this at startup so we can figure out how to reexecute
55
55
  # and upgrade the currently running instance of Unicorn
56
+ # This Hash is considered a stable interface and changing its contents
57
+ # will allow you to switch between different installations of Unicorn
58
+ # or even different installations of the same applications without
59
+ # downtime. Keys of this constant Hash are described as follows:
60
+ #
61
+ # * 0 - the path to the unicorn/unicorn_rails executable
62
+ # * :argv - a deep copy of the ARGV array the executable originally saw
63
+ # * :cwd - the working directory of the application, this is where
64
+ # you originally started Unicorn.
65
+ #
66
+ # The following example may be used in your Unicorn config file to
67
+ # change your working directory during a config reload (HUP) without
68
+ # upgrading or restarting:
69
+ #
70
+ # Dir.chdir(Unicorn::HttpServer::START_CTX[:cwd] = path)
71
+ #
72
+ # To change your unicorn executable to a different path without downtime,
73
+ # you can set the following in your Unicorn config file, HUP and then
74
+ # continue with the traditional USR2 + QUIT upgrade steps:
75
+ #
76
+ # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/1.9.2/bin/unicorn"
56
77
  START_CTX = {
57
78
  :argv => ARGV.map { |arg| arg.dup },
58
79
  # don't rely on Dir.pwd here since it's not symlink-aware, and
@@ -7,7 +7,7 @@ module Unicorn
7
7
  # gave about a 3% to 10% performance improvement over using the strings directly.
8
8
  # Symbols did not really improve things much compared to constants.
9
9
  module Const
10
- UNICORN_VERSION="0.93.1"
10
+ UNICORN_VERSION="0.93.2"
11
11
 
12
12
  DEFAULT_HOST = "0.0.0.0" # default TCP listen host address
13
13
  DEFAULT_PORT = 8080 # default TCP listen port
@@ -41,6 +41,26 @@ module Unicorn
41
41
  @size = tmp_size
42
42
  end
43
43
 
44
+ # call-seq:
45
+ # ios = env['rack.input']
46
+ # ios.read([length [, buffer ]]) => string, buffer, or nil
47
+ #
48
+ # Reads at most length bytes from the I/O stream, or to the end of
49
+ # file if length is omitted or is nil. length must be a non-negative
50
+ # integer or nil. If the optional buffer argument is present, it
51
+ # must reference a String, which will receive the data.
52
+ #
53
+ # At end of file, it returns nil or "" depend on length.
54
+ # ios.read() and ios.read(nil) returns "".
55
+ # ios.read(length [, buffer]) returns nil.
56
+ #
57
+ # If the Content-Length of the HTTP request is known (as is the common
58
+ # case for POST requests), then ios.read(length [, buffer]) will block
59
+ # until the specified length is read (or it is the last chunk).
60
+ # Otherwise, for uncommon "Transfer-Encoding: chunked" requests,
61
+ # ios.read(length [, buffer]) will return immediately if there is
62
+ # any data and only block when nothing is available (providing
63
+ # IO#readpartial semantics).
44
64
  def read(*args)
45
65
  socket or return @tmp.read(*args)
46
66
 
@@ -55,9 +75,9 @@ module Unicorn
55
75
  rv = args.shift || @buf2.dup
56
76
  diff = tmp_size - @tmp.pos
57
77
  if 0 == diff
58
- tee(length, rv)
78
+ ensure_length(tee(length, rv), length)
59
79
  else
60
- @tmp.read(diff > length ? length : diff, rv)
80
+ ensure_length(@tmp.read(diff > length ? length : diff, rv), length)
61
81
  end
62
82
  end
63
83
  end
@@ -130,5 +150,24 @@ module Unicorn
130
150
  StringIO === @tmp ? @tmp.size : @tmp.stat.size
131
151
  end
132
152
 
153
+ # tee()s into +buf+ until it is of +length+ bytes (or until
154
+ # we've reached the Content-Length of the request body).
155
+ # Returns +buf+ (the exact object, not a duplicate)
156
+ # To continue supporting applications that need near-real-time
157
+ # streaming input bodies, this is a no-op for
158
+ # "Transfer-Encoding: chunked" requests.
159
+ def ensure_length(buf, length)
160
+ # @size is nil for chunked bodies, so we can't ensure length for those
161
+ # since they could be streaming bidirectionally and we don't want to
162
+ # block the caller in that case.
163
+ return buf if buf.nil? || @size.nil?
164
+
165
+ while buf.size < length && @size != @tmp.pos
166
+ buf << tee(length - buf.size, @buf2)
167
+ end
168
+
169
+ buf
170
+ end
171
+
133
172
  end
134
173
  end
@@ -104,6 +104,22 @@ class TestTeeInput < Test::Unit::TestCase
104
104
  assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
105
105
  end
106
106
 
107
+ def test_read_in_full_if_content_length
108
+ a, b = 300, 3
109
+ init_parser('.' * b, 300)
110
+ assert_equal 300, @parser.content_length
111
+ ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
112
+ pid = fork {
113
+ @wr.write('.' * 197)
114
+ sleep 1 # still a *potential* race here that would make the test moot...
115
+ @wr.write('.' * 100)
116
+ }
117
+ assert_equal a, ti.read(a).size
118
+ _, status = Process.waitpid2(pid)
119
+ assert status.success?
120
+ @wr.close
121
+ end
122
+
107
123
  def test_big_body_multi
108
124
  init_parser('.', Unicorn::Const::MAX_BODY + 1)
109
125
  ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
@@ -167,6 +183,36 @@ class TestTeeInput < Test::Unit::TestCase
167
183
  assert status.success?
168
184
  end
169
185
 
186
+ def test_chunked_ping_pong
187
+ @parser = Unicorn::HttpParser.new
188
+ @buf = "POST / HTTP/1.1\r\n" \
189
+ "Host: localhost\r\n" \
190
+ "Transfer-Encoding: chunked\r\n" \
191
+ "\r\n"
192
+ assert_equal @env, @parser.headers(@env, @buf)
193
+ assert_equal "", @buf
194
+ chunks = %w(aa bbb cccc dddd eeee)
195
+ rd, wr = IO.pipe
196
+
197
+ pid = fork {
198
+ chunks.each do |chunk|
199
+ rd.read(1) == "." and
200
+ @wr.write("#{'%x' % [ chunk.size]}\r\n#{chunk}\r\n")
201
+ end
202
+ @wr.write("0\r\n")
203
+ }
204
+ ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
205
+ assert_nil @parser.content_length
206
+ assert_nil ti.instance_eval { @size }
207
+ assert ! @parser.body_eof?
208
+ chunks.each do |chunk|
209
+ wr.write('.')
210
+ assert_equal chunk, ti.read(16384)
211
+ end
212
+ _, status = Process.waitpid2(pid)
213
+ assert status.success?
214
+ end
215
+
170
216
  private
171
217
 
172
218
  def init_parser(body, size = nil)
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.name = %q{unicorn}
15
15
  s.version = ENV["VERSION"]
16
16
 
17
- s.authors = ["Eric Wong"]
17
+ s.authors = ["Unicorn developers"]
18
18
  s.date = Time.now.utc.strftime('%Y-%m-%d')
19
19
  s.description = File.read("README").split(/\n\n/)[1]
20
20
  s.email = %q{mongrel-unicorn@rubyforge.org}
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unicorn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.93.1
4
+ version: 0.93.2
5
5
  platform: ruby
6
6
  authors:
7
- - Eric Wong
7
+ - Unicorn developers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-03 00:00:00 -07:00
12
+ date: 2009-10-07 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency