From normalperson at yhbt.net Sun Aug 9 20:10:22 2009 From: normalperson at yhbt.net (Eric Wong) Date: Sun, 9 Aug 2009 17:10:22 -0700 Subject: [Mongrel-development] [PATCH] join repeated headers with a comma Message-ID: <20090810001022.GA17572@dcvr.yhbt.net> These are joined in in accordance with rfc2616, section 4.2[1]. This is also ticket #50 in Trac: http://mongrel.rubyforge.org/ticket/50 [1] - http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 --- This applies against c365ba16d12a14bdf1cc50a26f67dd3b45f5a4d8 in Evan's fauna repository, I'm completely certain where I should be applying this but it should be trivial to port this patch to anything else remotely resembling it... ext/http11/http11.c | 9 ++++++++- test/unit/test_http_parser.rb | 10 +++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ext/http11/http11.c b/ext/http11/http11.c index a228019..a770c60 100644 --- a/ext/http11/http11.c +++ b/ext/http11/http11.c @@ -182,6 +182,7 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s VALUE req = (VALUE)data; VALUE v = Qnil; VALUE f = Qnil; + VALUE e = Qnil; VALIDATE_MAX_LENGTH(flen, FIELD_NAME); VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE); @@ -208,7 +209,13 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s /* fprintf(stderr, "UNKNOWN HEADER <%s>\n", RSTRING_PTR(f)); */ } - rb_hash_aset(req, f, v); + e = rb_hash_aref(req, f); + if (e == Qnil) { + rb_hash_aset(req, f, v); + } else { + rb_str_buf_cat(e, ",", 1); + rb_str_buf_append(e, v); + } } void request_method(void *data, const char *at, size_t length) diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb index c89cd0d..70d9c91 100644 --- a/test/unit/test_http_parser.rb +++ b/test/unit/test_http_parser.rb @@ -173,6 +173,14 @@ class HttpParserTest < Test::Unit::TestCase assert_equal 4,res["zed"].length, "wrong number for zed" assert_equal "11",res["frank"], "wrong number for frank" end - + + def test_combine_repeat_headers + parser = HttpParser.new + http = "GET / HTTP/1.1\r\nA: 1\r\nA: 2\r\n\r\n" + req = {} + assert_nothing_raised { parser.execute(req, http, 0) } + assert_equal '1,2', req['HTTP_A'] + end + end -- Eric Wong From normalperson at yhbt.net Sun Aug 9 20:19:54 2009 From: normalperson at yhbt.net (Eric Wong) Date: Sun, 9 Aug 2009 17:19:54 -0700 Subject: [Mongrel-development] [PATCH] join repeated headers with a comma In-Reply-To: <20090810001022.GA17572@dcvr.yhbt.net> References: <20090810001022.GA17572@dcvr.yhbt.net> Message-ID: <20090810001954.GA18021@dcvr.yhbt.net> > --- a/ext/http11/http11.c > +++ b/ext/http11/http11.c > @@ -182,6 +182,7 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s > VALUE req = (VALUE)data; > VALUE v = Qnil; > VALUE f = Qnil; > + VALUE e = Qnil; > > VALIDATE_MAX_LENGTH(flen, FIELD_NAME); > VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE); On second thought, we're doing the concatenation _after_ the length validation. I don't think this affects things much in practice since we already have an overall header size limit and anything capable of running Ruby/Mongrel shouldn't have to worry given about a few tens of kilobytes... > @@ -208,7 +209,13 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s > /* fprintf(stderr, "UNKNOWN HEADER <%s>\n", RSTRING_PTR(f)); */ > } > > - rb_hash_aset(req, f, v); > + e = rb_hash_aref(req, f); > + if (e == Qnil) { > + rb_hash_aset(req, f, v); > + } else { > + rb_str_buf_cat(e, ",", 1); > + rb_str_buf_append(e, v); > + } > } From normalperson at yhbt.net Thu Aug 13 21:18:06 2009 From: normalperson at yhbt.net (Eric Wong) Date: Thu, 13 Aug 2009 18:18:06 -0700 Subject: [Mongrel-development] teaching Mongrel to inherit sockets (and replace mongrel_cluster) Message-ID: <20090814011806.GA13323@dcvr.yhbt.net> This change to Mongrel should be completely harmless for non-UNIX systems, too (it won't break (or do) anything). This minimal change let Mongrels inherit listen sockets from a parent process, so that parent could be a cluster manager. This means no more port juggling with mongrel_cluster! The entire Mongrel pack will all share one port. Only the manager script (see below) itself relies on UNIX-isms (but I think mongrel_cluster did, too). diff --git a/lib/mongrel.rb b/lib/mongrel.rb index 0619abe..90e99e7 100644 --- a/lib/mongrel.rb +++ b/lib/mongrel.rb @@ -91,7 +91,11 @@ module Mongrel def initialize(host, port, num_processors=950, throttle=0, timeout=60) tries = 0 - @socket = TCPServer.new(host, port) + @socket = if ENV['LISTEN_FD'] + TCPServer.for_fd(ENV['LISTEN_FD'].to_i) + else + TCPServer.new(host, port) + end if defined?(Fcntl::FD_CLOEXEC) @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) end --- Here's my initial cut of a mongrel_cluster replacement, it can definitely be cleaned up and improved upon with real option parsing and such... ---------------------------------- 8< ---------------------------------- #!/usr/bin/env ruby require 'socket' nr_workers = 4 pids = {} cmd = %w(mongrel_rails start) server = TCPServer.new('0.0.0.0', 3000) server.listen 1024 ENV['LISTEN_FD'] = server.fileno.to_s # pass any signals we receive onto each child process: %w(TERM INT USR1 USR2).each do |sig| trap(sig) do pids.keys.each { |pid| Process.kill(sig, pid) rescue nil } exit 0 if sig == 'TERM' || sig == 'INT' # TERM and INT mean death end end nr_workers.times { |i| pids[fork { exec *cmd }] = i } loop do pid, status = Process.waitpid2(-1) i = pids.delete(pid) or next STDERR.puts "reaped worker #{i}: #{status}, respawning..." pids[fork { exec *cmd }] = i end ---------------------------------- 8< ---------------------------------- -- Eric Wong