unicorn 3.2.1 → 3.3.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.
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v3.2.1.GIT
4
+ DEF_VER=v3.3.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/GNUmakefile CHANGED
@@ -45,7 +45,7 @@ T_r_log := $(subst .r,$(log_suffix),$(T_r))
45
45
  test_prefix = $(CURDIR)/test/$(RUBY_ENGINE)-$(RUBY_VERSION)
46
46
 
47
47
  ext := ext/unicorn_http
48
- c_files := $(ext)/unicorn_http.c $(wildcard $(ext)/*.h)
48
+ c_files := $(ext)/unicorn_http.c $(ext)/httpdate.c $(wildcard $(ext)/*.h)
49
49
  rl_files := $(wildcard $(ext)/*.rl)
50
50
  base_bins := unicorn unicorn_rails
51
51
  bins := $(addprefix bin/, $(base_bins))
data/Rakefile CHANGED
@@ -51,9 +51,12 @@ task :fm_update do
51
51
  uri = URI.parse('http://freshmeat.net/projects/unicorn/releases.json')
52
52
  rc = Net::Netrc.locate('unicorn-fm') or abort "~/.netrc not found"
53
53
  api_token = rc.password
54
- changelog = tags.find { |t| t[:tag] == "v#{version}" }[:body]
54
+ _, subject, body = `git cat-file tag v#{version}`.split(/\n\n/, 3)
55
55
  tmp = Tempfile.new('fm-changelog')
56
- tmp.syswrite(changelog)
56
+ tmp.puts subject
57
+ tmp.puts
58
+ tmp.puts body
59
+ tmp.flush
57
60
  system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?"
58
61
  changelog = File.read(tmp.path).strip
59
62
 
@@ -1,10 +1,9 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'mkmf'
3
3
 
4
- dir_config("unicorn_http")
5
-
6
4
  have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
7
5
  have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
8
6
  have_func("rb_str_set_len", "ruby.h")
7
+ have_func("gmtime_r", "time.h")
9
8
 
10
9
  create_makefile("unicorn_http")
@@ -0,0 +1,82 @@
1
+ #include <ruby.h>
2
+ #include <time.h>
3
+ #include <stdio.h>
4
+
5
+ static const size_t buf_capa = sizeof("Thu, 01 Jan 1970 00:00:00 GMT");
6
+ static VALUE buf;
7
+ static char *buf_ptr;
8
+ static const char *const week[] = {
9
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
10
+ };
11
+ static const char *const months[] = {
12
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
13
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
14
+ };
15
+
16
+ /* for people on wonky systems only */
17
+ #ifndef HAVE_GMTIME_R
18
+ static struct tm * my_gmtime_r(time_t *now, struct tm *tm)
19
+ {
20
+ struct tm *global = gmtime(now);
21
+ if (global)
22
+ *tm = *global;
23
+ return tm;
24
+ }
25
+ # define gmtime_r my_gmtime_r
26
+ #endif
27
+
28
+
29
+ /*
30
+ * Returns a string which represents the time as rfc1123-date of HTTP-date
31
+ * defined by RFC 2616:
32
+ *
33
+ * day-of-week, DD month-name CCYY hh:mm:ss GMT
34
+ *
35
+ * Note that the result is always GMT.
36
+ *
37
+ * This method is identical to Time#httpdate in the Ruby standard library,
38
+ * except it is implemented in C for performance. We always saw
39
+ * Time#httpdate at or near the top of the profiler output so we
40
+ * decided to rewrite this in C.
41
+ *
42
+ * Caveats: it relies on a Ruby implementation with the global VM lock,
43
+ * a thread-safe version will be provided when a Unix-only, GVL-free Ruby
44
+ * implementation becomes viable.
45
+ */
46
+ static VALUE httpdate(VALUE self)
47
+ {
48
+ static time_t last;
49
+ time_t now = time(NULL); /* not a syscall on modern 64-bit systems */
50
+ struct tm tm;
51
+
52
+ if (last == now)
53
+ return buf;
54
+ last = now;
55
+ gmtime_r(&now, &tm);
56
+
57
+ /* we can make this thread-safe later if our Ruby loses the GVL */
58
+ snprintf(buf_ptr, buf_capa,
59
+ "%s, %02d %s %4d %02d:%02d:%02d GMT",
60
+ week[tm.tm_wday],
61
+ tm.tm_mday,
62
+ months[tm.tm_mon],
63
+ tm.tm_year + 1900,
64
+ tm.tm_hour,
65
+ tm.tm_min,
66
+ tm.tm_sec);
67
+
68
+ return buf;
69
+ }
70
+
71
+ void init_unicorn_httpdate(void)
72
+ {
73
+ VALUE mod = rb_const_get(rb_cObject, rb_intern("Unicorn"));
74
+ mod = rb_define_module_under(mod, "HttpResponse");
75
+
76
+ buf = rb_str_new(0, buf_capa - 1);
77
+ rb_global_variable(&buf);
78
+ buf_ptr = RSTRING_PTR(buf);
79
+ httpdate(Qnil);
80
+
81
+ rb_define_method(mod, "httpdate", httpdate, 0);
82
+ }
@@ -12,6 +12,8 @@
12
12
  #include "global_variables.h"
13
13
  #include "c_util.h"
14
14
 
15
+ void init_unicorn_httpdate(void);
16
+
15
17
  #define UH_FL_CHUNKED 0x1
16
18
  #define UH_FL_HASBODY 0x2
17
19
  #define UH_FL_INBODY 0x4
@@ -570,18 +572,39 @@ static VALUE HttpParser_init(VALUE self)
570
572
 
571
573
  /**
572
574
  * call-seq:
573
- * parser.reset => nil
575
+ * parser.clear => parser
574
576
  *
575
577
  * Resets the parser to it's initial state so that you can reuse it
576
578
  * rather than making new ones.
577
579
  */
578
- static VALUE HttpParser_reset(VALUE self)
580
+ static VALUE HttpParser_clear(VALUE self)
579
581
  {
580
582
  struct http_parser *hp = data_get(self);
581
583
 
582
584
  http_parser_init(hp);
583
585
  rb_funcall(hp->env, id_clear, 0);
584
586
 
587
+ return self;
588
+ }
589
+
590
+ /**
591
+ * call-seq:
592
+ * parser.reset => nil
593
+ *
594
+ * Resets the parser to it's initial state so that you can reuse it
595
+ * rather than making new ones.
596
+ *
597
+ * This method is deprecated and to be removed in Unicorn 4.x
598
+ */
599
+ static VALUE HttpParser_reset(VALUE self)
600
+ {
601
+ static int warned;
602
+
603
+ if (!warned) {
604
+ rb_warn("Unicorn::HttpParser#reset is deprecated; "
605
+ "use Unicorn::HttpParser#clear instead");
606
+ }
607
+ HttpParser_clear(self);
585
608
  return Qnil;
586
609
  }
587
610
 
@@ -852,8 +875,9 @@ void Init_unicorn_http(void)
852
875
 
853
876
  init_globals();
854
877
  rb_define_alloc_func(cHttpParser, HttpParser_alloc);
855
- rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
856
- rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
878
+ rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
879
+ rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
880
+ rb_define_method(cHttpParser, "reset", HttpParser_reset, 0);
857
881
  rb_define_method(cHttpParser, "parse", HttpParser_parse, 0);
858
882
  rb_define_method(cHttpParser, "headers", HttpParser_headers, 2);
859
883
  rb_define_method(cHttpParser, "trailers", HttpParser_headers, 2);
@@ -897,5 +921,6 @@ void Init_unicorn_http(void)
897
921
  SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
898
922
  SET_GLOBAL(g_http_connection, "CONNECTION");
899
923
  id_clear = rb_intern("clear");
924
+ init_unicorn_httpdate();
900
925
  }
901
926
  #undef SET_GLOBAL
data/lib/unicorn/const.rb CHANGED
@@ -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.2.1
11
- UNICORN_VERSION = "3.2.1"
10
+ # The current version of Unicorn, currently 3.3.0
11
+ UNICORN_VERSION = "3.3.0"
12
12
 
13
13
  # default TCP listen host address (0.0.0.0, all interfaces)
14
14
  DEFAULT_HOST = "0.0.0.0"
@@ -50,7 +50,7 @@ class Unicorn::HttpParser
50
50
  # This does minimal exception trapping and it is up to the caller
51
51
  # to handle any socket errors (e.g. user aborted upload).
52
52
  def read(socket)
53
- reset
53
+ clear
54
54
  e = env
55
55
 
56
56
  # From http://www.ietf.org/rfc/rfc3875:
@@ -1,11 +1,9 @@
1
1
  # -*- encoding: binary -*-
2
- require 'time'
3
-
4
2
  # Writes a Rack response to your client using the HTTP/1.1 specification.
5
3
  # You use it by simply doing:
6
4
  #
7
5
  # status, headers, body = rack_app.call(env)
8
- # http_response_write(socket, [ status, headers, body ])
6
+ # http_response_write(socket, status, headers, body)
9
7
  #
10
8
  # Most header correctness (including Content-Length and Content-Type)
11
9
  # is the job of Rack, with the exception of the "Date" and "Status" header.
@@ -19,20 +17,19 @@ module Unicorn::HttpResponse
19
17
  CRLF = "\r\n"
20
18
 
21
19
  # writes the rack_response to socket as an HTTP response
22
- def http_response_write(socket, rack_response)
23
- status, headers, body = rack_response
20
+ def http_response_write(socket, status, headers, body)
24
21
  status = CODES[status.to_i] || status
25
22
 
26
23
  if headers
27
24
  buf = "HTTP/1.1 #{status}\r\n" \
28
- "Date: #{Time.now.httpdate}\r\n" \
25
+ "Date: #{httpdate}\r\n" \
29
26
  "Status: #{status}\r\n" \
30
27
  "Connection: close\r\n"
31
28
  headers.each do |key, value|
32
29
  next if %r{\A(?:Date\z|Status\z|Connection\z)}i =~ key
33
30
  if value =~ /\n/
34
31
  # avoiding blank, key-only cookies with /\n+/
35
- buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join('')
32
+ buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
36
33
  else
37
34
  buf << "#{key}: #{value}\r\n"
38
35
  end
@@ -524,22 +524,20 @@ class Unicorn::HttpServer
524
524
  client.kgio_trywrite(msg)
525
525
  client.close
526
526
  rescue
527
- nil
528
527
  end
529
528
 
530
529
  # once a client is accepted, it is processed in its entirety here
531
530
  # in 3 easy steps: read request, call app, write app response
532
531
  def process_client(client)
533
- r = @app.call(env = @request.read(client))
532
+ status, headers, body = @app.call(env = @request.read(client))
534
533
 
535
- if 100 == r[0].to_i
534
+ if 100 == status.to_i
536
535
  client.write(Unicorn::Const::EXPECT_100_RESPONSE)
537
536
  env.delete(Unicorn::Const::HTTP_EXPECT)
538
- r = @app.call(env)
537
+ status, headers, body = @app.call(env)
539
538
  end
540
- # r may be frozen or const, so don't modify it
541
- @request.headers? or r = [ r[0], nil, r[2] ]
542
- http_response_write(client, r)
539
+ @request.headers? or headers = nil
540
+ http_response_write(client, status, headers, body)
543
541
  rescue => e
544
542
  handle_error(client, e)
545
543
  end
@@ -20,6 +20,9 @@ module Unicorn
20
20
  # FreeBSD, we need to override this to 'dataready' when we
21
21
  # eventually get HTTPS support
22
22
  :accept_filter => 'httpready',
23
+
24
+ # same default value as Mongrel
25
+ :backlog => 1024,
23
26
  }
24
27
  #:startdoc:
25
28
 
@@ -41,7 +44,6 @@ module Unicorn
41
44
  end
42
45
 
43
46
  def set_tcp_sockopt(sock, opt)
44
-
45
47
  # highly portable, but off by default because we don't do keepalive
46
48
  if defined?(TCP_NODELAY) && ! (val = opt[:tcp_nodelay]).nil?
47
49
  sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
@@ -61,14 +63,12 @@ module Unicorn
61
63
  if defined?(TCP_DEFER_ACCEPT)
62
64
  # this differs from nginx, since nginx doesn't allow us to
63
65
  # configure the the timeout...
64
- tmp = DEFAULTS.merge(opt)
65
- seconds = tmp[:tcp_defer_accept]
66
+ seconds = opt[:tcp_defer_accept]
66
67
  seconds = DEFAULTS[:tcp_defer_accept] if seconds == true
67
68
  seconds = 0 unless seconds # nil/false means disable this
68
69
  sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
69
70
  elsif respond_to?(:accf_arg)
70
- tmp = DEFAULTS.merge(opt)
71
- if name = tmp[:accept_filter]
71
+ if name = opt[:accept_filter]
72
72
  begin
73
73
  sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
74
74
  rescue => e
@@ -80,7 +80,7 @@ module Unicorn
80
80
  end
81
81
 
82
82
  def set_server_sockopt(sock, opt)
83
- opt ||= {}
83
+ opt = DEFAULTS.merge(opt || {})
84
84
 
85
85
  TCPSocket === sock and set_tcp_sockopt(sock, opt)
86
86
 
@@ -90,7 +90,7 @@ module Unicorn
90
90
  sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
91
91
  log_buffer_sizes(sock, " after: ")
92
92
  end
93
- sock.listen(opt[:backlog] || 1024)
93
+ sock.listen(opt[:backlog])
94
94
  rescue => e
95
95
  logger.error "error setting socket options: #{e.inspect}"
96
96
  logger.error e.backtrace.join("\n")
@@ -28,7 +28,7 @@ class HttpParserTest < Test::Unit::TestCase
28
28
  assert_equal '', req['QUERY_STRING']
29
29
 
30
30
  assert parser.keepalive?
31
- parser.reset
31
+ parser.clear
32
32
  req.clear
33
33
 
34
34
  http = "G"
@@ -326,7 +326,7 @@ class HttpParserTest < Test::Unit::TestCase
326
326
  assert_raises(HttpParserError) { parser.headers(req, bad_http) }
327
327
 
328
328
  # make sure we can recover
329
- parser.reset
329
+ parser.clear
330
330
  req.clear
331
331
  assert_equal req, parser.headers(req, "GET / HTTP/1.0\r\n\r\n")
332
332
  assert ! parser.keepalive?
@@ -569,7 +569,7 @@ class HttpParserTest < Test::Unit::TestCase
569
569
  get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
570
570
  assert_raises Unicorn::HttpParserError do
571
571
  parser.headers({}, get)
572
- parser.reset
572
+ parser.clear
573
573
  end
574
574
  end
575
575
 
@@ -578,7 +578,7 @@ class HttpParserTest < Test::Unit::TestCase
578
578
  get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
579
579
  assert_raises Unicorn::HttpParserError do
580
580
  parser.headers({}, get)
581
- parser.reset
581
+ parser.clear
582
582
  end
583
583
  end
584
584
 
@@ -587,7 +587,7 @@ class HttpParserTest < Test::Unit::TestCase
587
587
  get << "X-Test: test\r\n" * (80 * 1024)
588
588
  assert_raises Unicorn::HttpParserError do
589
589
  parser.headers({}, get)
590
- parser.reset
590
+ parser.clear
591
591
  end
592
592
 
593
593
  # finally just that random garbage gets blocked all the time
@@ -595,7 +595,7 @@ class HttpParserTest < Test::Unit::TestCase
595
595
  get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
596
596
  assert_raises Unicorn::HttpParserError do
597
597
  parser.headers({}, get)
598
- parser.reset
598
+ parser.clear
599
599
  end
600
600
  end
601
601
 
@@ -91,7 +91,7 @@ class HttpParserNgTest < Test::Unit::TestCase
91
91
  @parser.parse
92
92
  end
93
93
  assert @parser.keepalive?
94
- @parser.reset
94
+ @parser.clear
95
95
  assert ! @parser.keepalive?
96
96
  assert ! @parser.next?
97
97
  end
@@ -491,7 +491,7 @@ class HttpParserNgTest < Test::Unit::TestCase
491
491
  }.each do |uri,expect|
492
492
  assert_equal req, @parser.headers(req.clear, str % [ uri ])
493
493
  req = req.dup
494
- @parser.reset
494
+ @parser.clear
495
495
  assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
496
496
  assert_equal expect[qs], req[qs], "#{qs} mismatch"
497
497
  assert_equal expect[pi], req[pi], "#{pi} mismatch"
@@ -516,7 +516,7 @@ class HttpParserNgTest < Test::Unit::TestCase
516
516
  }.each do |uri,expect|
517
517
  assert_equal req, @parser.headers(req.clear, str % [ uri ])
518
518
  req = req.dup
519
- @parser.reset
519
+ @parser.clear
520
520
  assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
521
521
  assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
522
522
  assert_equal expect[qs], req[qs], "#{qs} mismatch"
@@ -608,4 +608,26 @@ class HttpParserNgTest < Test::Unit::TestCase
608
608
  assert_equal expect, env2
609
609
  assert_equal "", @parser.buf
610
610
  end
611
+
612
+ def test_keepalive_requests_disabled
613
+ req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
614
+ expect = {
615
+ "SERVER_NAME" => "example.com",
616
+ "HTTP_HOST" => "example.com",
617
+ "rack.url_scheme" => "http",
618
+ "REQUEST_PATH" => "/",
619
+ "SERVER_PROTOCOL" => "HTTP/1.1",
620
+ "PATH_INFO" => "/",
621
+ "HTTP_VERSION" => "HTTP/1.1",
622
+ "REQUEST_URI" => "/",
623
+ "SERVER_PORT" => "80",
624
+ "REQUEST_METHOD" => "GET",
625
+ "QUERY_STRING" => ""
626
+ }.freeze
627
+ HttpParser.keepalive_requests = 0
628
+ @parser = HttpParser.new
629
+ @parser.buf << req
630
+ assert_equal expect, @parser.parse
631
+ assert ! @parser.next?
632
+ end
611
633
  end
@@ -7,15 +7,26 @@
7
7
  # for more information.
8
8
 
9
9
  require 'test/test_helper'
10
+ require 'time'
10
11
 
11
12
  include Unicorn
12
13
 
13
14
  class ResponseTest < Test::Unit::TestCase
14
15
  include Unicorn::HttpResponse
15
16
 
17
+ def test_httpdate
18
+ before = Time.now.to_i
19
+ str = httpdate
20
+ assert_kind_of(String, str)
21
+ middle = Time.parse(str).to_i
22
+ after = Time.now.to_i
23
+ assert before <= middle
24
+ assert middle <= after
25
+ end
26
+
16
27
  def test_response_headers
17
28
  out = StringIO.new
18
- http_response_write(out,[200, {"X-Whatever" => "stuff"}, ["cool"]])
29
+ http_response_write(out, 200, {"X-Whatever" => "stuff"}, ["cool"])
19
30
  assert out.closed?
20
31
 
21
32
  assert out.length > 0, "output didn't have data"
@@ -23,27 +34,15 @@ class ResponseTest < Test::Unit::TestCase
23
34
 
24
35
  def test_response_string_status
25
36
  out = StringIO.new
26
- http_response_write(out,['200', {}, []])
37
+ http_response_write(out,'200', {}, [])
27
38
  assert out.closed?
28
39
  assert out.length > 0, "output didn't have data"
29
40
  assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/).size
30
41
  end
31
42
 
32
- def test_response_OFS_set
33
- old_ofs = $,
34
- $, = "\f\v"
35
- out = StringIO.new
36
- http_response_write(out,[200, {"X-k" => "cd","X-y" => "z"}, ["cool"]])
37
- assert out.closed?
38
- resp = out.string
39
- assert ! resp.include?("\f\v"), "output didn't use $, ($OFS)"
40
- ensure
41
- $, = old_ofs
42
- end
43
-
44
43
  def test_response_200
45
44
  io = StringIO.new
46
- http_response_write(io, [200, {}, []])
45
+ http_response_write(io, 200, {}, [])
47
46
  assert io.closed?
48
47
  assert io.length > 0, "output didn't have data"
49
48
  end
@@ -51,7 +50,7 @@ class ResponseTest < Test::Unit::TestCase
51
50
  def test_response_with_default_reason
52
51
  code = 400
53
52
  io = StringIO.new
54
- http_response_write(io, [code, {}, []])
53
+ http_response_write(io, code, {}, [])
55
54
  assert io.closed?
56
55
  lines = io.string.split(/\r\n/)
57
56
  assert_match(/.* Bad Request$/, lines.first,
@@ -60,7 +59,7 @@ class ResponseTest < Test::Unit::TestCase
60
59
 
61
60
  def test_rack_multivalue_headers
62
61
  out = StringIO.new
63
- http_response_write(out,[200, {"X-Whatever" => "stuff\nbleh"}, []])
62
+ http_response_write(out,200, {"X-Whatever" => "stuff\nbleh"}, [])
64
63
  assert out.closed?
65
64
  assert_match(/^X-Whatever: stuff\r\nX-Whatever: bleh\r\n/, out.string)
66
65
  end
@@ -69,7 +68,7 @@ class ResponseTest < Test::Unit::TestCase
69
68
  # some broken clients still rely on it
70
69
  def test_status_header_added
71
70
  out = StringIO.new
72
- http_response_write(out,[200, {"X-Whatever" => "stuff"}, []])
71
+ http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
73
72
  assert out.closed?
74
73
  assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
75
74
  end
@@ -80,7 +79,7 @@ class ResponseTest < Test::Unit::TestCase
80
79
  def test_status_header_ignores_app_hash
81
80
  out = StringIO.new
82
81
  header_hash = {"X-Whatever" => "stuff", 'StaTus' => "666" }
83
- http_response_write(out,[200, header_hash, []])
82
+ http_response_write(out,200, header_hash, [])
84
83
  assert out.closed?
85
84
  assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
86
85
  assert_equal 1, out.string.split(/\r\n/).grep(/^Status:/i).size
@@ -91,7 +90,7 @@ class ResponseTest < Test::Unit::TestCase
91
90
  body = StringIO.new(expect_body)
92
91
  body.rewind
93
92
  out = StringIO.new
94
- http_response_write(out,[200, {}, body])
93
+ http_response_write(out,200, {}, body)
95
94
  assert out.closed?
96
95
  assert body.closed?
97
96
  assert_match(expect_body, out.string.split(/\r\n/).last)
@@ -99,7 +98,7 @@ class ResponseTest < Test::Unit::TestCase
99
98
 
100
99
  def test_unknown_status_pass_through
101
100
  out = StringIO.new
102
- http_response_write(out,["666 I AM THE BEAST", {}, [] ])
101
+ http_response_write(out,"666 I AM THE BEAST", {}, [] )
103
102
  assert out.closed?
104
103
  headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
105
104
  assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
data/unicorn.gemspec CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |s|
38
38
  s.add_dependency(%q<kgio>, '~> 2.1')
39
39
 
40
40
  s.add_development_dependency('isolate', '~> 3.0.0')
41
- s.add_development_dependency('wrongdoc', '~> 1.0.1')
41
+ s.add_development_dependency('wrongdoc', '~> 1.3')
42
42
 
43
43
  # s.licenses = %w(GPLv2 Ruby) # licenses= method is not in older RubyGems
44
44
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unicorn
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 3
8
- - 2
9
- - 1
10
- version: 3.2.1
8
+ - 3
9
+ - 0
10
+ version: 3.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Unicorn hackers
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-26 00:00:00 +00:00
18
+ date: 2011-01-05 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -71,12 +71,11 @@ dependencies:
71
71
  requirements:
72
72
  - - ~>
73
73
  - !ruby/object:Gem::Version
74
- hash: 21
74
+ hash: 9
75
75
  segments:
76
76
  - 1
77
- - 0
78
- - 1
79
- version: 1.0.1
77
+ - 3
78
+ version: "1.3"
80
79
  type: :development
81
80
  version_requirements: *id004
82
81
  description: |-
@@ -177,6 +176,7 @@ files:
177
176
  - ext/unicorn_http/ext_help.h
178
177
  - ext/unicorn_http/extconf.rb
179
178
  - ext/unicorn_http/global_variables.h
179
+ - ext/unicorn_http/httpdate.c
180
180
  - ext/unicorn_http/unicorn_http.c
181
181
  - ext/unicorn_http/unicorn_http.rl
182
182
  - ext/unicorn_http/unicorn_http_common.rl