unicorn 3.2.1 → 3.3.0

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