unicorn Ruby/Rack server user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
Search results ordered by [date|relevance]  view[summary|nested|Atom feed]
thread overview below | download mbox.gz: |
* [PATCH 0/4] a small pile of patches
@ 2024-03-23 19:45  2% Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2024-03-23 19:45 UTC (permalink / raw)
  To: unicorn-public

[-- Attachment #1: Type: text/plain, Size: 4446 bytes --]

Some stuff to future-proof against future Ruby incompatibilities.
More coming....

I've also pushed out preliminary work (started in 2021) to the
`pico' branch to switch the HTTP parser from Ragel to
picohttpparser.  It will simplify the build + maintenance,
especially when distros carry different Ragel versions (or don't
package it all, as some hackers can't afford bandwidth and disk
for a C++ toolchain).

Other notes: New releases will probably be hosted on yhbt.net if
the Rubygems.org MFA threshold is reached.  Caring about the
identity of hackers is totally misguided when we already show
our code (and even document it!).  If you can't audit the code
yourself, get an actual professional to do it and don't bother
amateurs like me.

Eric Wong (4):
  t/integration: disable proxies when running curl(1)
  tests: port back-out-of-upgrade to Perl 5
  doc: various updates and disclaimers
  treewide: future-proof frozen_string_literal changes

 HACKING                             |  13 +++-
 README                              |   9 +++
 Rakefile                            |   1 +
 TODO                                |   4 +-
 bin/unicorn                         |   1 +
 bin/unicorn_rails                   |   1 +
 examples/big_app_gc.rb              |   1 +
 examples/echo.ru                    |   1 +
 examples/logger_mp_safe.rb          |   1 +
 examples/unicorn.conf.minimal.rb    |   1 +
 examples/unicorn.conf.rb            |   1 +
 ext/unicorn_http/extconf.rb         |   1 +
 lib/unicorn.rb                      |   1 +
 lib/unicorn/app/old_rails.rb        |   1 +
 lib/unicorn/app/old_rails/static.rb |   1 +
 lib/unicorn/cgi_wrapper.rb          |   1 +
 lib/unicorn/configurator.rb         |   1 +
 lib/unicorn/const.rb                |   1 +
 lib/unicorn/http_request.rb         |   1 +
 lib/unicorn/http_response.rb        |   1 +
 lib/unicorn/http_server.rb          |   1 +
 lib/unicorn/launcher.rb             |   1 +
 lib/unicorn/oob_gc.rb               |   1 +
 lib/unicorn/preread_input.rb        |   1 +
 lib/unicorn/select_waiter.rb        |   1 +
 lib/unicorn/socket_helper.rb        |   1 +
 lib/unicorn/stream_input.rb         |   1 +
 lib/unicorn/tee_input.rb            |   1 +
 lib/unicorn/tmpio.rb                |   1 +
 lib/unicorn/util.rb                 |   1 +
 lib/unicorn/worker.rb               |   1 +
 setup.rb                            |   1 +
 t/back-out-of-upgrade.t             |  44 +++++++++++
 t/broken-app.ru                     |   1 +
 t/client_body_buffer_size.ru        |   1 +
 t/detach.ru                         |   1 +
 t/env.ru                            |   1 +
 t/fails-rack-lint.ru                |   1 +
 t/heartbeat-timeout.ru              |   1 +
 t/integration.ru                    |   1 +
 t/integration.t                     |   1 +
 t/lib.perl                          |  67 ++++++++++++++---
 t/listener_names.ru                 |   1 +
 t/oob_gc.ru                         |   1 +
 t/oob_gc_path.ru                    |   1 +
 t/pid.ru                            |   1 +
 t/preread_input.ru                  |   1 +
 t/reopen-logs.ru                    |   1 +
 t/t0008-back_out_of_upgrade.sh      | 110 ----------------------------
 t/t0013.ru                          |   1 +
 t/t0014.ru                          |   1 +
 t/t0301.ru                          |   1 +
 test/aggregate.rb                   |   1 +
 test/benchmark/dd.ru                |   1 +
 test/benchmark/ddstream.ru          |   1 +
 test/benchmark/readinput.ru         |   1 +
 test/benchmark/stack.ru             |   1 +
 test/exec/test_exec.rb              |   1 +
 test/test_helper.rb                 |   1 +
 test/unit/test_ccc.rb               |   1 +
 test/unit/test_configurator.rb      |   1 +
 test/unit/test_droplet.rb           |   1 +
 test/unit/test_http_parser.rb       |   1 +
 test/unit/test_http_parser_ng.rb    |   1 +
 test/unit/test_request.rb           |   1 +
 test/unit/test_server.rb            |   1 +
 test/unit/test_signals.rb           |   1 +
 test/unit/test_socket_helper.rb     |   1 +
 test/unit/test_stream_input.rb      |   1 +
 test/unit/test_tee_input.rb         |   1 +
 test/unit/test_util.rb              |   1 +
 test/unit/test_waiter.rb            |   1 +
 unicorn.gemspec                     |   1 +
 73 files changed, 188 insertions(+), 126 deletions(-)
 create mode 100644 t/back-out-of-upgrade.t
 delete mode 100755 t/t0008-back_out_of_upgrade.sh

[-- Attachment #2: 0001-t-integration-disable-proxies-when-running-curl-1.patch --]
[-- Type: text/x-diff, Size: 737 bytes --]

From f3acce5dce62ac4b0288d3c0ddf0a6db2cbd9e7f Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Tue, 9 Jan 2024 21:35:08 +0000
Subject: [PATCH 1/4] t/integration: disable proxies when running curl(1)

This was also done in t/test-lib.sh, but using '*' is more
encompassing.
---
 t/integration.t | 1 +
 1 file changed, 1 insertion(+)

diff --git a/t/integration.t b/t/integration.t
index 7310ff2..d17ace0 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -27,6 +27,7 @@ listen "$u1"
 EOM
 my $ar = unicorn(qw(-E none t/integration.ru -c), $u_conf, { 3 => $srv });
 my $curl = which('curl');
+local $ENV{NO_PROXY} = '*'; # for curl
 my $fifo = "$tmpdir/fifo";
 POSIX::mkfifo($fifo, 0600) or die "mkfifo: $!";
 my %PUT = (

[-- Attachment #3: 0002-tests-port-back-out-of-upgrade-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 8889 bytes --]

From 724fb631c76f09964ec289ee8e144886ba15d380 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 6 Nov 2023 05:45:29 +0000
Subject: [PATCH 2/4] tests: port back-out-of-upgrade to Perl 5

Another place where we can be faster without adding more
dependencies on Ruby maintaining stable behavior.
---
 t/back-out-of-upgrade.t        |  44 +++++++++++++
 t/lib.perl                     |  67 +++++++++++++++++---
 t/t0008-back_out_of_upgrade.sh | 110 ---------------------------------
 3 files changed, 102 insertions(+), 119 deletions(-)
 create mode 100644 t/back-out-of-upgrade.t
 delete mode 100755 t/t0008-back_out_of_upgrade.sh

diff --git a/t/back-out-of-upgrade.t b/t/back-out-of-upgrade.t
new file mode 100644
index 0000000..cf3b09f
--- /dev/null
+++ b/t/back-out-of-upgrade.t
@@ -0,0 +1,44 @@
+#!perl -w
+# Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+# test backing out of USR2 upgrade
+use v5.14; BEGIN { require './t/lib.perl' };
+use autodie;
+my $srv = tcp_server();
+mkfifo_die $fifo;
+write_file '>', $u_conf, <<EOM;
+preload_app true
+stderr_path "$err_log"
+pid "$pid_file"
+after_fork { |s,w| File.open('$fifo', 'w') { |fp| fp.write "pid=#\$\$" } }
+EOM
+my $ar = unicorn(qw(-E none t/pid.ru -c), $u_conf, { 3 => $srv });
+
+like(my $wpid_orig_1 = slurp($fifo), qr/\Apid=\d+\z/a, 'got worker pid');
+
+ok $ar->do_kill('USR2'), 'USR2 to start upgrade';
+ok $ar->do_kill('WINCH'), 'drop old worker';
+
+like(my $wpid_new = slurp($fifo), qr/\Apid=\d+\z/a, 'got pid from new master');
+chomp(my $new_pid = slurp($pid_file));
+isnt $new_pid, $ar->{pid}, 'PID file changed';
+chomp(my $pid_oldbin = slurp("$pid_file.oldbin"));
+is $pid_oldbin, $ar->{pid}, '.oldbin PID valid';
+
+ok $ar->do_kill('HUP'), 'HUP old master';
+like(my $wpid_orig_2 = slurp($fifo), qr/\Apid=\d+\z/a, 'got worker new pid');
+ok kill('QUIT', $new_pid), 'abort old master';
+kill_until_dead $new_pid;
+
+my ($st, $hdr, $req_pid) = do_req $srv, 'GET /';
+chomp $req_pid;
+is $wpid_orig_2, "pid=$req_pid", 'new worker on old worker serves';
+
+ok !-f "$pid_file.oldbin", '.oldbin PID file gone';
+chomp(my $old_pid = slurp($pid_file));
+is $old_pid, $ar->{pid}, 'PID file restored';
+
+my @log = grep !/ERROR -- : reaped .*? exec\(\)-ed/, slurp($err_log);
+check_stderr @log;
+undef $tmpdir;
+done_testing;
diff --git a/t/lib.perl b/t/lib.perl
index 9254b23..b20a2c6 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -6,30 +6,58 @@ use v5.14;
 use parent qw(Exporter);
 use autodie;
 use Test::More;
+use Socket qw(SOMAXCONN);
 use Time::HiRes qw(sleep time);
 use IO::Socket::INET;
+use IO::Socket::UNIX;
+use Carp qw(croak);
 use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
 use File::Temp 0.19 (); # 0.19 for ->newdir
 our ($tmpdir, $errfh, $err_log, $u_sock, $u_conf, $daemon_pid,
-	$pid_file);
+	$pid_file, $wtest_sock, $fifo);
 our @EXPORT = qw(unicorn slurp tcp_server tcp_start unicorn
 	$tmpdir $errfh $err_log $u_sock $u_conf $daemon_pid $pid_file
+	$wtest_sock $fifo
 	SEEK_SET tcp_host_port which spawn check_stderr unix_start slurp_hdr
-	do_req stop_daemon sleep time);
+	do_req stop_daemon sleep time mkfifo_die kill_until_dead write_file);
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
+
+$wtest_sock = "$tmpdir/wtest.sock";
 $err_log = "$tmpdir/err.log";
 $pid_file = "$tmpdir/pid";
+$fifo = "$tmpdir/fifo";
 $u_sock = "$tmpdir/u.sock";
 $u_conf = "$tmpdir/u.conf.rb";
 open($errfh, '>>', $err_log);
 
+if (my $t = $ENV{TAIL}) {
+	my @tail = $t =~ /tail/ ? split(/\s+/, $t) : (qw(tail -F));
+	push @tail, $err_log;
+	my $pid = fork;
+	if ($pid == 0) {
+		open STDOUT, '>&', \*STDERR;
+		exec @tail;
+		die "exec(@tail): $!";
+	}
+	say "# @tail";
+	sleep 0.2;
+	UnicornTest::AutoReap->new($pid);
+}
+
+sub kill_until_dead ($;%) {
+	my ($pid, %opt) = @_;
+	my $tries = $opt{tries} // 1000;
+	my $sig = $opt{sig} // 0;
+	while (CORE::kill($sig, $pid) && --$tries) { sleep(0.01) }
+	$tries or croak "PID: $pid died after signal ($sig)";
+}
+
 sub stop_daemon (;$) {
 	my ($is_END) = @_;
 	kill('TERM', $daemon_pid);
-	my $tries = 1000;
-	while (CORE::kill(0, $daemon_pid) && --$tries) { sleep(0.01) }
+	kill_until_dead $daemon_pid;
 	if ($is_END && CORE::kill(0, $daemon_pid)) { # after done_testing
 		CORE::kill('KILL', $daemon_pid);
 		die "daemon_pid=$daemon_pid did not die";
@@ -44,8 +72,9 @@ END {
 	stop_daemon(1) if defined $daemon_pid;
 };
 
-sub check_stderr () {
-	my @log = slurp($err_log);
+sub check_stderr (@) {
+	my @log = @_;
+	slurp($err_log) if !@log;
 	diag("@log") if $ENV{V};
 	my @err = grep(!/NameError.*Unicorn::Waiter/, grep(/error/i, @log));
 	@err = grep(!/failed to set accept_filter=/, @err);
@@ -63,6 +92,16 @@ sub slurp_hdr {
 	($status, \@hdr);
 }
 
+sub unix_server (;$@) {
+	my $l = shift // $u_sock;
+	IO::Socket::UNIX->new(Listen => SOMAXCONN, Local => $l, Blocking => 0,
+				Type => SOCK_STREAM, @_);
+}
+
+sub unix_connect ($) {
+	IO::Socket::UNIX->new(Peer => $_[0], Type => SOCK_STREAM);
+}
+
 sub tcp_server {
 	my %opt = (
 		ReuseAddr => 1,
@@ -95,8 +134,7 @@ sub tcp_host_port {
 
 sub unix_start ($@) {
 	my ($dst, @req) = @_;
-	my $s = IO::Socket::UNIX->new(Peer => $dst, Type => SOCK_STREAM) or
-		BAIL_OUT "unix connect $dst: $!";
+	my $s = unix_connect($dst) or BAIL_OUT "unix connect $dst: $!";
 	$s->autoflush(1);
 	print $s @req, "\r\n\r\n" if @req;
 	$s;
@@ -201,7 +239,7 @@ sub unicorn {
 	state $ver = $ENV{TEST_RUBY_VERSION} // `$ruby -e 'print RUBY_VERSION'`;
 	state $eng = $ENV{TEST_RUBY_ENGINE} // `$ruby -e 'print RUBY_ENGINE'`;
 	state $ext = File::Spec->rel2abs("test/$eng-$ver/ext/unicorn_http");
-	state $exe = File::Spec->rel2abs('bin/unicorn');
+	state $exe = File::Spec->rel2abs("test/$eng-$ver/bin/unicorn");
 	my $pid = spawn(\%env, $ruby, '-I', $lib, '-I', $ext, $exe, @args);
 	UnicornTest::AutoReap->new($pid);
 }
@@ -219,6 +257,17 @@ sub do_req ($@) {
 	($status, $hdr, $bdy);
 }
 
+sub mkfifo_die ($;$) {
+	POSIX::mkfifo($_[0], $_[1] // 0600) or croak "mkfifo: $!";
+}
+
+sub write_file ($$@) { # mode, filename, LIST (for print)
+	open(my $fh, shift, shift);
+	print $fh @_;
+	# return $fh for futher writes if user wants it:
+	defined(wantarray) && !wantarray ? $fh : close $fh;
+}
+
 # automatically kill + reap children when this goes out-of-scope
 package UnicornTest::AutoReap;
 use v5.14;
diff --git a/t/t0008-back_out_of_upgrade.sh b/t/t0008-back_out_of_upgrade.sh
deleted file mode 100755
index 96d4057..0000000
--- a/t/t0008-back_out_of_upgrade.sh
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 13 "backout of USR2 upgrade"
-
-worker_wait_start () {
-	test xSTART = x"$(cat $fifo)"
-	unicorn_pid=$(cat $pid)
-}
-
-t_begin "setup and start" && {
-	unicorn_setup
-	rm -f $pid.oldbin
-
-cat >> $unicorn_config <<EOF
-after_fork do |server, worker|
-  # test script will block while reading from $fifo,
-  # so notify the script on the first worker we spawn
-  # by opening the FIFO
-  if worker.nr == 0
-    File.open("$fifo", "wb") { |fp| fp.syswrite "START" }
-  end
-end
-EOF
-	unicorn -D -c $unicorn_config pid.ru
-	worker_wait_start
-	orig_master_pid=$unicorn_pid
-}
-
-t_begin "read original worker pid" && {
-	orig_worker_pid=$(curl -sSf http://$listen/)
-	test -n "$orig_worker_pid" && kill -0 $orig_worker_pid
-}
-
-t_begin "upgrade to new master" && {
-	kill -USR2 $orig_master_pid
-}
-
-t_begin "kill old worker" && {
-	kill -WINCH $orig_master_pid
-}
-
-t_begin "wait for new worker to start" && {
-	worker_wait_start
-	test $unicorn_pid -ne $orig_master_pid
-	new_master_pid=$unicorn_pid
-}
-
-t_begin "old master pid is stashed in $pid.oldbin" && {
-	test -s "$pid.oldbin"
-	test $orig_master_pid -eq $(cat $pid.oldbin)
-}
-
-t_begin "ensure old worker is no longer running" && {
-	i=0
-	while kill -0 $orig_worker_pid 2>/dev/null
-	do
-		i=$(( $i + 1 ))
-		test $i -lt 600 || die "timed out"
-		sleep 1
-	done
-}
-
-t_begin "capture pid of new worker" && {
-	new_worker_pid=$(curl -sSf http://$listen/)
-}
-
-t_begin "reload old master process" && {
-	kill -HUP $orig_master_pid
-	worker_wait_start
-}
-
-t_begin "gracefully kill new master and ensure it dies" && {
-	kill -QUIT $new_master_pid
-	i=0
-	while kill -0 $new_worker_pid 2>/dev/null
-	do
-		i=$(( $i + 1 ))
-		test $i -lt 600 || die "timed out"
-		sleep 1
-	done
-}
-
-t_begin "ensure $pid.oldbin does not exist" && {
-	i=0
-	while test -s $pid.oldbin
-	do
-		i=$(( $i + 1 ))
-		test $i -lt 600 || die "timed out"
-		sleep 1
-	done
-	while ! test -s $pid
-	do
-		i=$(( $i + 1 ))
-		test $i -lt 600 || die "timed out"
-		sleep 1
-	done
-}
-
-t_begin "ensure $pid is correct" && {
-	cur_master_pid=$(cat $pid)
-	test $orig_master_pid -eq $cur_master_pid
-}
-
-t_begin "killing succeeds" && {
-	kill $orig_master_pid
-}
-
-dbgcat r_err
-
-t_done

[-- Attachment #4: 0003-doc-various-updates-and-disclaimers.patch --]
[-- Type: text/x-diff, Size: 3343 bytes --]

From 69d15a7a51a096b6acf00ccf23e1b988076d3b5f Mon Sep 17 00:00:00 2001
From: Eric Wong <bofh@yhbt.net>
Date: Mon, 1 Jan 2024 10:43:13 +0000
Subject: [PATCH 3/4] doc: various updates and disclaimers

Covering my ass from draconian legislation.
---
 HACKING | 13 +++++++++----
 README  |  9 +++++++++
 TODO    |  4 +---
 3 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/HACKING b/HACKING
index 5aca83e..777e75e 100644
--- a/HACKING
+++ b/HACKING
@@ -6,6 +6,8 @@ Like Mongrel, we use Ruby where it makes sense, and Ragel with C where
 it helps performance.  All of the code that actually runs your Rack
 application is written Ruby, Ragel or C.
 
+Ragel may be dropped in favor of a picohttpparser-based one in the future.
+
 As far as tests and documentation goes, we're not afraid to embrace Unix
 and use traditional Unix tools where they make sense and get the job
 done.
@@ -16,6 +18,9 @@ Tests are good, but slow tests make development slow, so we make tests
 faster (in parallel) with GNU make (instead of Rake) and avoiding
 RubyGems.
 
+New tests are written in Perl 5 and use TAP <https://testanything.org/>
+to ensure stability and immunity from Ruby incompatibilities.
+
 Users of GNU-based systems (such as GNU/Linux) usually have GNU make
 installed as "make" instead of "gmake".
 
@@ -69,10 +74,10 @@ supported by the versions of Ruby we target.
 
 === Ragel Compatibility
 
-We target the latest released version of Ragel and will update our code
-to keep up with new releases.  Packaged tarballs and gems include the
-generated source code so they will remain usable if compatibility is
-broken.
+We target the latest released version of Ragel in Debian and will update
+our code to keep up with new releases.  Packaged tarballs and gems
+include the generated source code so they will remain usable if
+compatibility is broken.
 
 == Contributing
 
diff --git a/README b/README
index 84c0fdf..b60ed00 100644
--- a/README
+++ b/README
@@ -122,6 +122,7 @@ supported.  Run `unicorn -h` to see command-line options.
 
 There is NO WARRANTY whatsoever if anything goes wrong, but
 {let us know}[link:ISSUES.html] and maybe someone can fix it.
+No commercial support will ever be provided by the amateur maintainer.
 
 unicorn is designed to only serve fast clients either on the local host
 or a fast LAN.  See the PHILOSOPHY and DESIGN documents for more details
@@ -132,6 +133,14 @@ damage done to the entire Ruby ecosystem.  Its unintentional popularity
 set Ruby back decades in parallelism, concurrency and robustness since
 it prolongs and proliferates the existence of poorly-written code.
 
+unicorn hackers are NOT responsible for your supply chain security:
+read and understand it yourself or get someone you trust to audit it.
+Malicious commits and releases will be made if under duress.  The only
+defense you'll ever have is from reviewing the source code.
+
+No user or contributor will ever be expected to sacrifice their own
+security by running JavaScript or revealing any personal information.
+
 == Contact
 
 All feedback (bug reports, user/development dicussion, patches, pull
diff --git a/TODO b/TODO
index ebbccdc..a3b18fd 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1 @@
-* Documentation improvements
-
-* improve test suite
+* improve test suite (port to Perl 5 for stability and maintainability)

[-- Attachment #5: 0004-treewide-future-proof-frozen_string_literal-changes.patch --]
[-- Type: text/x-diff, Size: 24100 bytes --]

From ccf2443901c18ffb26b2785f52d921005e862167 Mon Sep 17 00:00:00 2001
From: Eric Wong <bofh@yhbt.net>
Date: Thu, 8 Feb 2024 12:16:31 +0000
Subject: [PATCH 4/4] treewide: future-proof frozen_string_literal changes

Once again Ruby seems ready to introduce more incompatibilities
and force busywork upon maintainers[1].  In order to avoid
incompatibilities in the future, I used a Perl script[2] to
prepend `frozen_string_literal: false' to every Ruby file.

Somebody interested will have to go through every Ruby source
file and enable frozen_string_literal once they've thoroughly
verified it's safe to do so.

[1] https://bugs.ruby-lang.org/issues/20205
[2] https://yhbt.net/add-fsl.git/74d7689/s/?b=add-fsl.perl
---
 Rakefile                            | 1 +
 bin/unicorn                         | 1 +
 bin/unicorn_rails                   | 1 +
 examples/big_app_gc.rb              | 1 +
 examples/echo.ru                    | 1 +
 examples/logger_mp_safe.rb          | 1 +
 examples/unicorn.conf.minimal.rb    | 1 +
 examples/unicorn.conf.rb            | 1 +
 ext/unicorn_http/extconf.rb         | 1 +
 lib/unicorn.rb                      | 1 +
 lib/unicorn/app/old_rails.rb        | 1 +
 lib/unicorn/app/old_rails/static.rb | 1 +
 lib/unicorn/cgi_wrapper.rb          | 1 +
 lib/unicorn/configurator.rb         | 1 +
 lib/unicorn/const.rb                | 1 +
 lib/unicorn/http_request.rb         | 1 +
 lib/unicorn/http_response.rb        | 1 +
 lib/unicorn/http_server.rb          | 1 +
 lib/unicorn/launcher.rb             | 1 +
 lib/unicorn/oob_gc.rb               | 1 +
 lib/unicorn/preread_input.rb        | 1 +
 lib/unicorn/select_waiter.rb        | 1 +
 lib/unicorn/socket_helper.rb        | 1 +
 lib/unicorn/stream_input.rb         | 1 +
 lib/unicorn/tee_input.rb            | 1 +
 lib/unicorn/tmpio.rb                | 1 +
 lib/unicorn/util.rb                 | 1 +
 lib/unicorn/worker.rb               | 1 +
 setup.rb                            | 1 +
 t/broken-app.ru                     | 1 +
 t/client_body_buffer_size.ru        | 1 +
 t/detach.ru                         | 1 +
 t/env.ru                            | 1 +
 t/fails-rack-lint.ru                | 1 +
 t/heartbeat-timeout.ru              | 1 +
 t/integration.ru                    | 1 +
 t/listener_names.ru                 | 1 +
 t/oob_gc.ru                         | 1 +
 t/oob_gc_path.ru                    | 1 +
 t/pid.ru                            | 1 +
 t/preread_input.ru                  | 1 +
 t/reopen-logs.ru                    | 1 +
 t/t0013.ru                          | 1 +
 t/t0014.ru                          | 1 +
 t/t0301.ru                          | 1 +
 test/aggregate.rb                   | 1 +
 test/benchmark/dd.ru                | 1 +
 test/benchmark/ddstream.ru          | 1 +
 test/benchmark/readinput.ru         | 1 +
 test/benchmark/stack.ru             | 1 +
 test/exec/test_exec.rb              | 1 +
 test/test_helper.rb                 | 1 +
 test/unit/test_ccc.rb               | 1 +
 test/unit/test_configurator.rb      | 1 +
 test/unit/test_droplet.rb           | 1 +
 test/unit/test_http_parser.rb       | 1 +
 test/unit/test_http_parser_ng.rb    | 1 +
 test/unit/test_request.rb           | 1 +
 test/unit/test_server.rb            | 1 +
 test/unit/test_signals.rb           | 1 +
 test/unit/test_socket_helper.rb     | 1 +
 test/unit/test_stream_input.rb      | 1 +
 test/unit/test_tee_input.rb         | 1 +
 test/unit/test_util.rb              | 1 +
 test/unit/test_waiter.rb            | 1 +
 unicorn.gemspec                     | 1 +
 66 files changed, 66 insertions(+)

diff --git a/Rakefile b/Rakefile
index 37569ce..fe1588b 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # optional rake-compiler support in case somebody needs to cross compile
 begin
   mk = "ext/unicorn_http/Makefile"
diff --git a/bin/unicorn b/bin/unicorn
index 00c8464..af8353c 100755
--- a/bin/unicorn
+++ b/bin/unicorn
@@ -1,5 +1,6 @@
 #!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'unicorn/launcher'
 require 'optparse'
 
diff --git a/bin/unicorn_rails b/bin/unicorn_rails
index 354c1df..374fd8e 100755
--- a/bin/unicorn_rails
+++ b/bin/unicorn_rails
@@ -1,5 +1,6 @@
 #!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'unicorn/launcher'
 require 'optparse'
 require 'fileutils'
diff --git a/examples/big_app_gc.rb b/examples/big_app_gc.rb
index c1bae10..0baea26 100644
--- a/examples/big_app_gc.rb
+++ b/examples/big_app_gc.rb
@@ -1,2 +1,3 @@
+# frozen_string_literal: false
 # see {Unicorn::OobGC}[https://yhbt.net/unicorn/Unicorn/OobGC.html]
 # Unicorn::OobGC was broken in Unicorn v3.3.1 - v3.6.1 and fixed in v3.6.2
diff --git a/examples/echo.ru b/examples/echo.ru
index e982180..453a5e6 100644
--- a/examples/echo.ru
+++ b/examples/echo.ru
@@ -1,4 +1,5 @@
 #\-E none
+# frozen_string_literal: false
 #
 # Example application that echoes read data back to the HTTP client.
 # This emulates the old echo protocol people used to run.
diff --git a/examples/logger_mp_safe.rb b/examples/logger_mp_safe.rb
index 05ad3fa..f2c0500 100644
--- a/examples/logger_mp_safe.rb
+++ b/examples/logger_mp_safe.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # Multi-Processing-safe monkey patch for Logger
 #
 # This monkey patch fixes the case where "preload_app true" is used and
diff --git a/examples/unicorn.conf.minimal.rb b/examples/unicorn.conf.minimal.rb
index 46fd634..4f96ede 100644
--- a/examples/unicorn.conf.minimal.rb
+++ b/examples/unicorn.conf.minimal.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # Minimal sample configuration file for Unicorn (not Rack) when used
 # with daemonization (unicorn -D) started in your working directory.
 #
diff --git a/examples/unicorn.conf.rb b/examples/unicorn.conf.rb
index d90bdc4..5bae830 100644
--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # Sample verbose configuration file for Unicorn (not Rack)
 #
 # This configuration file documents many features of Unicorn
diff --git a/ext/unicorn_http/extconf.rb b/ext/unicorn_http/extconf.rb
index 11099cd..de896fe 100644
--- a/ext/unicorn_http/extconf.rb
+++ b/ext/unicorn_http/extconf.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'mkmf'
 
 have_func("rb_hash_clear", "ruby.h") or abort 'Ruby 2.0+ required'
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index 564cb30..fb91679 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'etc'
 require 'stringio'
 require 'raindrops'
diff --git a/lib/unicorn/app/old_rails.rb b/lib/unicorn/app/old_rails.rb
index 1e8c41a..54b3e69 100644
--- a/lib/unicorn/app/old_rails.rb
+++ b/lib/unicorn/app/old_rails.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # :enddoc:
 # This code is based on the original Rails handler in Mongrel
diff --git a/lib/unicorn/app/old_rails/static.rb b/lib/unicorn/app/old_rails/static.rb
index 2257270..cf34e02 100644
--- a/lib/unicorn/app/old_rails/static.rb
+++ b/lib/unicorn/app/old_rails/static.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :enddoc:
 # This code is based on the original Rails handler in Mongrel
 # Copyright (c) 2005 Zed A. Shaw
diff --git a/lib/unicorn/cgi_wrapper.rb b/lib/unicorn/cgi_wrapper.rb
index d9b7fe5..fb43605 100644
--- a/lib/unicorn/cgi_wrapper.rb
+++ b/lib/unicorn/cgi_wrapper.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # :enddoc:
 # This code is based on the original CGIWrapper from Mongrel
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index b21a01d..3c81596 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'logger'
 
 # Implements a simple DSL for configuring a unicorn server.
diff --git a/lib/unicorn/const.rb b/lib/unicorn/const.rb
index 33ab4ac..8032863 100644
--- a/lib/unicorn/const.rb
+++ b/lib/unicorn/const.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 module Unicorn::Const # :nodoc:
   # default TCP listen host address (0.0.0.0, all interfaces)
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index ab3bd6e..a48dab7 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :enddoc:
 # no stable API here
 require 'unicorn_http'
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb
index 0ed0ae3..3634165 100644
--- a/lib/unicorn/http_response.rb
+++ b/lib/unicorn/http_response.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :enddoc:
 # Writes a Rack response to your client using the HTTP/1.1 specification.
 # You use it by simply doing:
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index ed5bbf1..08fbe40 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # This is the process manager of Unicorn. This manages worker
 # processes which in turn handle the I/O and application process.
diff --git a/lib/unicorn/launcher.rb b/lib/unicorn/launcher.rb
index 78e8f39..bd3324e 100644
--- a/lib/unicorn/launcher.rb
+++ b/lib/unicorn/launcher.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # :enddoc:
 $stdout.sync = $stderr.sync = true
diff --git a/lib/unicorn/oob_gc.rb b/lib/unicorn/oob_gc.rb
index db9f2cb..efd9177 100644
--- a/lib/unicorn/oob_gc.rb
+++ b/lib/unicorn/oob_gc.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Strongly consider https://github.com/tmm1/gctools if using Ruby 2.1+
 # It is built on new APIs in Ruby 2.1, so it is more intelligent than
diff --git a/lib/unicorn/preread_input.rb b/lib/unicorn/preread_input.rb
index 12eb3e8..c62cc09 100644
--- a/lib/unicorn/preread_input.rb
+++ b/lib/unicorn/preread_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 module Unicorn
 # This middleware is used to ensure input is buffered to memory
diff --git a/lib/unicorn/select_waiter.rb b/lib/unicorn/select_waiter.rb
index cb84aab..d11ea57 100644
--- a/lib/unicorn/select_waiter.rb
+++ b/lib/unicorn/select_waiter.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # fallback for non-Linux and Linux <4.5 systems w/o EPOLLEXCLUSIVE
 class Unicorn::SelectWaiter # :nodoc:
   def get_readers(ready, readers, timeout) # :nodoc:
diff --git a/lib/unicorn/socket_helper.rb b/lib/unicorn/socket_helper.rb
index 06ec2b2..986932f 100644
--- a/lib/unicorn/socket_helper.rb
+++ b/lib/unicorn/socket_helper.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :enddoc:
 require 'socket'
 
diff --git a/lib/unicorn/stream_input.rb b/lib/unicorn/stream_input.rb
index 9246f73..23a9976 100644
--- a/lib/unicorn/stream_input.rb
+++ b/lib/unicorn/stream_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # When processing uploads, unicorn may expose a StreamInput object under
 # "rack.input" of the Rack environment when
diff --git a/lib/unicorn/tee_input.rb b/lib/unicorn/tee_input.rb
index 2ccc2d9..b3c6535 100644
--- a/lib/unicorn/tee_input.rb
+++ b/lib/unicorn/tee_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Acts like tee(1) on an input input to provide a input-like stream
 # while providing rewindable semantics through a File/StringIO backing
diff --git a/lib/unicorn/tmpio.rb b/lib/unicorn/tmpio.rb
index 0bbf6ec..deecd80 100644
--- a/lib/unicorn/tmpio.rb
+++ b/lib/unicorn/tmpio.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :stopdoc:
 require 'tmpdir'
 
diff --git a/lib/unicorn/util.rb b/lib/unicorn/util.rb
index b826de4..f28d929 100644
--- a/lib/unicorn/util.rb
+++ b/lib/unicorn/util.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require 'fcntl'
 module Unicorn::Util # :nodoc:
diff --git a/lib/unicorn/worker.rb b/lib/unicorn/worker.rb
index 4af31be..d2445d5 100644
--- a/lib/unicorn/worker.rb
+++ b/lib/unicorn/worker.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require "raindrops"
 
 # This class and its members can be considered a stable interface
diff --git a/setup.rb b/setup.rb
index cf1abd9..96cf75a 100644
--- a/setup.rb
+++ b/setup.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 #
 # setup.rb
 #
diff --git a/t/broken-app.ru b/t/broken-app.ru
index d05d7ab..5966bff 100644
--- a/t/broken-app.ru
+++ b/t/broken-app.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # we do not want Rack::Lint or anything to protect us
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
diff --git a/t/client_body_buffer_size.ru b/t/client_body_buffer_size.ru
index 44161a5..1a0fb16 100644
--- a/t/client_body_buffer_size.ru
+++ b/t/client_body_buffer_size.ru
@@ -1,4 +1,5 @@
 #\ -E none
+# frozen_string_literal: false
 app = lambda do |env|
   input = env['rack.input']
   case env["PATH_INFO"]
diff --git a/t/detach.ru b/t/detach.ru
index bbd998e..8d35951 100644
--- a/t/detach.ru
+++ b/t/detach.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentType, "text/plain"
 fifo_path = ENV["TEST_FIFO"] or abort "TEST_FIFO not set"
 run lambda { |env|
diff --git a/t/env.ru b/t/env.ru
index 388412e..86c3cfa 100644
--- a/t/env.ru
+++ b/t/env.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
 run lambda { |env| [ 200, {}, [ env.inspect << "\n" ] ] }
diff --git a/t/fails-rack-lint.ru b/t/fails-rack-lint.ru
index 82bfb5f..8b8b5ec 100644
--- a/t/fails-rack-lint.ru
+++ b/t/fails-rack-lint.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # This rack app returns an invalid status code, which will cause
 # Rack::Lint to throw an exception if it is present.  This
 # is used to check whether Rack::Lint is in the stack or not.
diff --git a/t/heartbeat-timeout.ru b/t/heartbeat-timeout.ru
index 3eeb5d6..ccc6a8e 100644
--- a/t/heartbeat-timeout.ru
+++ b/t/heartbeat-timeout.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 headers = { 'content-type' => 'text/plain' }
 run lambda { |env|
diff --git a/t/integration.ru b/t/integration.ru
index 888833a..6df481c 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -1,4 +1,5 @@
 #!ruby
+# frozen_string_literal: false
 # Copyright (C) unicorn hackers <unicorn-public@80x24.org>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 
diff --git a/t/listener_names.ru b/t/listener_names.ru
index edb4e6a..f52c59b 100644
--- a/t/listener_names.ru
+++ b/t/listener_names.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
 names = Unicorn.listener_names.inspect # rely on preload_app=true
diff --git a/t/oob_gc.ru b/t/oob_gc.ru
index 224cb06..2ae58a8 100644
--- a/t/oob_gc.ru
+++ b/t/oob_gc.ru
@@ -1,4 +1,5 @@
 #\-E none
+# frozen_string_literal: false
 require 'unicorn/oob_gc'
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
diff --git a/t/oob_gc_path.ru b/t/oob_gc_path.ru
index 7f40601..5358222 100644
--- a/t/oob_gc_path.ru
+++ b/t/oob_gc_path.ru
@@ -1,4 +1,5 @@
 #\-E none
+# frozen_string_literal: false
 require 'unicorn/oob_gc'
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
diff --git a/t/pid.ru b/t/pid.ru
index f5fd31f..b49b137 100644
--- a/t/pid.ru
+++ b/t/pid.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
 run lambda { |env| [ 200, {}, [ "#$$\n" ] ] }
diff --git a/t/preread_input.ru b/t/preread_input.ru
index 18af221..5f68fe9 100644
--- a/t/preread_input.ru
+++ b/t/preread_input.ru
@@ -1,4 +1,5 @@
 #\-E none
+# frozen_string_literal: false
 require 'digest/md5'
 require 'unicorn/preread_input'
 use Unicorn::PrereadInput
diff --git a/t/reopen-logs.ru b/t/reopen-logs.ru
index c39e8f6..488da85 100644
--- a/t/reopen-logs.ru
+++ b/t/reopen-logs.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
 run lambda { |env|
diff --git a/t/t0013.ru b/t/t0013.ru
index 48a3a34..e425093 100644
--- a/t/t0013.ru
+++ b/t/t0013.ru
@@ -1,4 +1,5 @@
 #\ -E none
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, 'text/plain'
 app = lambda do |env|
diff --git a/t/t0014.ru b/t/t0014.ru
index b0bd2b7..686d214 100644
--- a/t/t0014.ru
+++ b/t/t0014.ru
@@ -1,4 +1,5 @@
 #\ -E none
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, 'text/plain'
 app = lambda do |env|
diff --git a/t/t0301.ru b/t/t0301.ru
index ce68213..54929b1 100644
--- a/t/t0301.ru
+++ b/t/t0301.ru
@@ -1,4 +1,5 @@
 #\-N --debug
+# frozen_string_literal: false
 run(lambda do |env|
   case env['PATH_INFO']
   when '/vars'
diff --git a/test/aggregate.rb b/test/aggregate.rb
index 5eebbe5..0f32b2f 100755
--- a/test/aggregate.rb
+++ b/test/aggregate.rb
@@ -1,5 +1,6 @@
 #!/usr/bin/ruby -n
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 BEGIN { $tests = $assertions = $failures = $errors = 0 }
 
diff --git a/test/benchmark/dd.ru b/test/benchmark/dd.ru
index 111fa2e..5bd2739 100644
--- a/test/benchmark/dd.ru
+++ b/test/benchmark/dd.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # This benchmark is the simplest test of the I/O facilities in
 # unicorn.  It is meant to return a fixed-sized blob to test
 # the performance of things in Unicorn, _NOT_ the app.
diff --git a/test/benchmark/ddstream.ru b/test/benchmark/ddstream.ru
index b14c973..fd40ced 100644
--- a/test/benchmark/ddstream.ru
+++ b/test/benchmark/ddstream.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # This app is intended to test large HTTP responses with or without
 # a fully-buffering reverse proxy such as nginx. Without a fully-buffering
 # reverse proxy, unicorn will be unresponsive when client count exceeds
diff --git a/test/benchmark/readinput.ru b/test/benchmark/readinput.ru
index c91bec3..95c0226 100644
--- a/test/benchmark/readinput.ru
+++ b/test/benchmark/readinput.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # This app is intended to test large HTTP requests with or without
 # a fully-buffering reverse proxy such as nginx. Without a fully-buffering
 # reverse proxy, unicorn will be unresponsive when client count exceeds
diff --git a/test/benchmark/stack.ru b/test/benchmark/stack.ru
index fc9193f..17a565b 100644
--- a/test/benchmark/stack.ru
+++ b/test/benchmark/stack.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 run(lambda { |env|
   body = "#{caller.size}\n"
   h = {
diff --git a/test/exec/test_exec.rb b/test/exec/test_exec.rb
index 8494452..807f724 100644
--- a/test/exec/test_exec.rb
+++ b/test/exec/test_exec.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # Don't add to this file, new tests are in Perl 5. See t/README
 FLOCK_PATH = File.expand_path(__FILE__)
 require './test/test_helper'
diff --git a/test/test_helper.rb b/test/test_helper.rb
index d86f83b..0bf3c90 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2005 Zed A. Shaw
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_ccc.rb b/test/unit/test_ccc.rb
index f518230..a0a2bff 100644
--- a/test/unit/test_ccc.rb
+++ b/test/unit/test_ccc.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 require 'socket'
 require 'unicorn'
 require 'io/wait'
diff --git a/test/unit/test_configurator.rb b/test/unit/test_configurator.rb
index 1298f0e..1a89aca 100644
--- a/test/unit/test_configurator.rb
+++ b/test/unit/test_configurator.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require 'test/unit'
 require 'tempfile'
diff --git a/test/unit/test_droplet.rb b/test/unit/test_droplet.rb
index 81ad82b..4b2d2d0 100644
--- a/test/unit/test_droplet.rb
+++ b/test/unit/test_droplet.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 require 'test/unit'
 require 'unicorn'
 
diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb
index 697af44..adcc84f 100644
--- a/test/unit/test_http_parser.rb
+++ b/test/unit/test_http_parser.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2005 Zed A. Shaw
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_http_parser_ng.rb b/test/unit/test_http_parser_ng.rb
index 425d5ad..fd47246 100644
--- a/test/unit/test_http_parser_ng.rb
+++ b/test/unit/test_http_parser_ng.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require './test/test_helper'
 require 'digest/md5'
diff --git a/test/unit/test_request.rb b/test/unit/test_request.rb
index 53ae944..9d1b350 100644
--- a/test/unit/test_request.rb
+++ b/test/unit/test_request.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2009 Eric Wong
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_server.rb b/test/unit/test_server.rb
index 7ffa48f..5a2252f 100644
--- a/test/unit/test_server.rb
+++ b/test/unit/test_server.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2005 Zed A. Shaw
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_signals.rb b/test/unit/test_signals.rb
index 6c48754..49ff3c7 100644
--- a/test/unit/test_signals.rb
+++ b/test/unit/test_signals.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2009 Eric Wong
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_socket_helper.rb b/test/unit/test_socket_helper.rb
index a446f06..4363474 100644
--- a/test/unit/test_socket_helper.rb
+++ b/test/unit/test_socket_helper.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require './test/test_helper'
 require 'tempfile'
diff --git a/test/unit/test_stream_input.rb b/test/unit/test_stream_input.rb
index 7986ca7..7ee98e4 100644
--- a/test/unit/test_stream_input.rb
+++ b/test/unit/test_stream_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require 'test/unit'
 require 'digest/sha1'
diff --git a/test/unit/test_tee_input.rb b/test/unit/test_tee_input.rb
index 607ce87..8f05c77 100644
--- a/test/unit/test_tee_input.rb
+++ b/test/unit/test_tee_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require 'test/unit'
 require 'digest/sha1'
diff --git a/test/unit/test_util.rb b/test/unit/test_util.rb
index bc7b233..ce53b86 100644
--- a/test/unit/test_util.rb
+++ b/test/unit/test_util.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require './test/test_helper'
 require 'tempfile'
diff --git a/test/unit/test_waiter.rb b/test/unit/test_waiter.rb
index 0995de2..a20994b 100644
--- a/test/unit/test_waiter.rb
+++ b/test/unit/test_waiter.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 require 'test/unit'
 require 'unicorn'
 require 'unicorn/select_waiter'
diff --git a/unicorn.gemspec b/unicorn.gemspec
index e7e3ef7..36700a8 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 manifest = File.exist?('.manifest') ?
   IO.readlines('.manifest').map!(&:chomp!) : `git ls-files`.split("\n")
 

^ permalink raw reply related	[relevance 2%]

* Re: Bus Error with Unicorn 6.0 on OpenBSD/adJ 6.8 with Ruby 3.0
  2021-04-04 11:06  0%     ` vtamara
@ 2021-04-04 16:43  0%       ` Jeremy Evans
  0 siblings, 0 replies; 25+ results
From: Jeremy Evans @ 2021-04-04 16:43 UTC (permalink / raw)
  To: vtamara; +Cc: Eric Wong, unicorn-public

On 04/04 07:06, vtamara wrote:
> Hi
> 
> Thank you very much for the reply and the interest in helping.
> 
> > vtamara: did previous versions of Ruby, unicorn, or OpenBSD work
> > for your app?
> 
> The application was working withouth issue on OpenBSD/adJ amd64 6.7, ruby
> 2.7 and unicorn 6.0.  So there is something in conflict betweeen Unicorn 6.0
> with OpenBSD/adJ 6.8 and ruby 3.0.
> 
> > Is preload_app in use?  If so, does the problem manifest without
> > preload_app?
> 
> No
> 
> > Are you using Ractor or any other new/experimental stuff
> > in Ruby?
> 
> No
> 
> > Can you reproduce the problem with a simple "hello world"-style app?
> 
> I don't have a hello world example at hand, but some projects that present
> this problem when I run them on OpenBSD/adJ amd64 6.8, nginx 1.18.0, node
> 12.16.1, PostgreSQL 13.2, Ruby 3.0, Rails 6.1 and Unicorn 6.0 are:
> 
> https://github.com/pasosdeJesus/cor1440
> https://github.com/pasosdeJesus/cor1440_pdJ
> https://github.com/pasosdeJesus/sivel2
> https://github.com/pasosdeJesus/si_anzorc
> https://github.com/pasosdeJesus/si_asom
> https://github.com/pasosdeJesus/si_codacop
> https://github.com/pasosdeJesus/sivel2_mujeresindigenas/

Unfortunately, given the required amount of setup, I don't think I'll
have time to look into this.  The best way to debug this is to start
removing code/dependencies piece by piece until the problem goes away.
Once the problem goes away, add that code back, and make sure it fails
again, then start removing other code/dependencies.  Repeat this process
until you have the most minimal example, where removing any additional
code/dependencies will stop the code from failing.

Sorry,
Jeremy

> 
> The simplest of them is https://github.com/pasosdeJesus/cor1440, althoug it
> requires:
> * PostgreSQL
> * nginx
> * nodejs
> * yarn. I install it with: doas pkg_add bash; ftp -o-
> https://yarnpkg.com/install.sh | bash
> * A directory /var/www/bundler/ruby/3.0 where I install the gems with the
> permissions of a user (not root) that runs the application, and better
> running:
>   bundle config path /var/www/bundler/.
> * unicorn installed globally with:
>   doas gem install unicorn
>   doas ln -s /usr/local/bin/unicorn_rails30 /usr/local/bin/unicorn
> 
> To create the PostgreSQL user (in the following example sipdes) and a
> database for the production environment  (in the following example
> cor1440gen_pro):
> 
>   doas su - _postgresql
>   createuser -h /var/www/var/run/postgresql/ -U postgres -s sipdes
>   createdb -h /var/www/var/run/postgresql/ -U postgres -O sipdes
> cor1440gen_pro
>   psql -h /var/www/var/run/postgresql/ -U postgres
>   postgres=# alter user sipdes with password 'xyz';
>   postgres=# \q
>   exit
> 
> With nginx suppossng I will run the application in localhost over SSL, what
> I do is to choose an unused port (let's say 2009) and add to the
> /etc/nginx/nginx.conf configuration in its http block:
> 
>  upstream unicorncor1440 {
> 	  server 127.0.0.1:2009 fail_timeout=0;
>   }
> 
> I also add a server block inside the http block with:
> 
>   server {
>       listen 443 ssl;
>       ssl_certificate /etc/ssl/server.crt;
>       ssl_certificate_key /etc/ssl/private/server.key;
>       root /var/www/htdocs/cor1440/;
>       server_name 127.0.0.1;
>       error_log logs/cor1440.rror.log;
> 
>       location /cor1440 {
>         try_files $uri @unicorncor1440;
>       }
> 
>       location @unicorncor1440 {
>         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
>         proxy_set_header X-Forwarded-Proto $scheme;
>         proxy_set_header Host $http_host;
>         proxy_redirect off;
>         proxy_pass http://unicorncor1440;
>         error_page 500 502 503 504 /500.html;
>         keepalive_timeout 10;
>       }
> 
>       location ^~ /cor1440/assets/ {
>         gzip_static on;
>         expires max;
>         add_header Cache-Control public;
>         root /var/www/htdocs/cor1440/public/;
>       }
> 
>       location ^~ /cor1440/images/ {
>         gzip_static on;
>         expires max;
>         add_header Cache-Control public;
>         root /var/www/htdocs/cor1440/public/;
>       }
> 
>       location ^~ /cor1440/packs/ {
>         gzip_static on;
>         add_header Cache-Control public;
>         root /var/www/htdocs/cor1440/public/;
>       }
>     }
> 
> And restarted nginx:
> 
>   doas rcctl restart nginx
> 
> Then to configure:
> 
>   cd /tmp
>   git clone git@github.com:pasosdeJesus/cor1440
>   doas mv cor1440 /var/www/htdocs/
>   doas chown -R $USER:$USER /var/www/htdocs/cor1440
>   cd /var/www/htdocs/cor1440
>   mkdir .bundle
>   echo '---' > .bundle/config
>   echo 'BUNDLE_PATH: "/var/www/bundler"' >> .bundle/config
>   echo 'BUNDLE_DISABLE_SHARED_GEMS: "true"' >> .bundle/config
>   bundle
> 
> Here some gems will require installing with special permissions for example:
> 
>   doas gem install --install-dir /var/www/bundler/ruby/3.0 unicorn
> 
> Other gems that will require this kind of installation will be:
>   racc nio4r websocket-driver bcrypt bindex msgpack ffi redcarpet kgio
> libxml-ruby pg
>   nokogiri bootsnap puma sassc
> 
> Once bundle runs completely:
> 
>   cp .env.plantilla .env
>   vi .env
> 
> There I set the variables:
> * BD_USUARIO with the database user,
> * BD_CLAVE with the password
> * BD_PRO with the name of the production database
> * CONFIG_HOSTS=127.0.0.1
> * DIRAP with the path to the application
> * RAILS_ENV=production
> * PUERTOUNICORN with the port I choosed and set in nginx.conf
> * USUARIO_AP with the operating system user who will run the application
> 
> Prepare credentials and database:
> 
>   bin/rails credentials:edit
>   RAILS_ENV=production bin/rails credentials:edit
>   bin/railsp db:setup  db:migrate sip:indices
> 
> Precompile assets:
> 
>   CXX=c++ yarn
>   doas ln -s /usr/local/bin/node /tmp  # To overcome bug of node with rails
> on OpenBSD
>   bin/railsp assets:precompile
> 
> Run the application with:
>   SECRET_KEY_BASE=cf46 DIRAP=/var/www/htdocs/cor1440/ bin/u.sh
> 
> Trying to see the application running with a browser in
> https://127.0.0.1/cor1440 will produce the errors described in
> log/unicorn.log.
> 
> Blessings. Thank you.
> 
> El 2021-03-30 00:00, Jeremy Evans escribi??:
> > On 03/30 01:58, Eric Wong wrote:
> > > vtamara <vtamara@pasosdeJesus.org> wrote:
> > > > Hi
> > > >
> > > > Launching a rails application with Ruby 3, Unicorn 6 on OpenBSD/adJ 6.8, I'm
> > > > experiencing a lot of "Bus Error" and "Segmentation Faults" during
> > > > aproximately half hour.
> > > 
> > > +Cc: Jeremy Evans, who is more familiar with OpenBSD and Ruby 3.0
> > > than I
> > > cf. https://yhbt.net/unicorn-public/6acb1c84c7392d7b4a64572b20498549@pasosdeJesus.org/
> > 
> > I'll be happy to look into this if a minimal self-contained reproducible
> > example is posted.  Personally, I run my Ruby apps on OpenBSD amd64 with
> > Unicorn, and haven't seen similar errors.  However, I don't use
> > preload_app and use fairly minimal dependencies.
> > 
> > Thanks,
> > Jeremy
> 
> -- 
> Dios, gracias por tu amor infinito.
> --
>   Vladimir T??mara Pati??o.  http://vtamara.pasosdeJesus.org/
>   http://www.pasosdejesus.org/dominio_publico_colombia.html

^ permalink raw reply	[relevance 0%]

* Re: Bus Error with Unicorn 6.0 on OpenBSD/adJ 6.8 with Ruby 3.0
  @ 2021-04-04 11:06  0%     ` vtamara
  2021-04-04 16:43  0%       ` Jeremy Evans
  0 siblings, 1 reply; 25+ results
From: vtamara @ 2021-04-04 11:06 UTC (permalink / raw)
  To: Jeremy Evans; +Cc: Eric Wong, unicorn-public

Hi

Thank you very much for the reply and the interest in helping.

> vtamara: did previous versions of Ruby, unicorn, or OpenBSD work
> for your app?

The application was working withouth issue on OpenBSD/adJ amd64 6.7, 
ruby 2.7 and unicorn 6.0.  So there is something in conflict betweeen 
Unicorn 6.0 with OpenBSD/adJ 6.8 and ruby 3.0.

> Is preload_app in use?  If so, does the problem manifest without
> preload_app?

No

> Are you using Ractor or any other new/experimental stuff
> in Ruby?

No

> Can you reproduce the problem with a simple "hello world"-style app?

I don't have a hello world example at hand, but some projects that 
present this problem when I run them on OpenBSD/adJ amd64 6.8, nginx 
1.18.0, node 12.16.1, PostgreSQL 13.2, Ruby 3.0, Rails 6.1 and Unicorn 
6.0 are:

https://github.com/pasosdeJesus/cor1440
https://github.com/pasosdeJesus/cor1440_pdJ
https://github.com/pasosdeJesus/sivel2
https://github.com/pasosdeJesus/si_anzorc
https://github.com/pasosdeJesus/si_asom
https://github.com/pasosdeJesus/si_codacop
https://github.com/pasosdeJesus/sivel2_mujeresindigenas/

The simplest of them is https://github.com/pasosdeJesus/cor1440, althoug 
it requires:
* PostgreSQL
* nginx
* nodejs
* yarn. I install it with: doas pkg_add bash; ftp -o- 
https://yarnpkg.com/install.sh | bash
* A directory /var/www/bundler/ruby/3.0 where I install the gems with 
the permissions of a user (not root) that runs the application, and 
better running:
   bundle config path /var/www/bundler/.
* unicorn installed globally with:
   doas gem install unicorn
   doas ln -s /usr/local/bin/unicorn_rails30 /usr/local/bin/unicorn

To create the PostgreSQL user (in the following example sipdes) and a 
database for the production environment  (in the following example 
cor1440gen_pro):

   doas su - _postgresql
   createuser -h /var/www/var/run/postgresql/ -U postgres -s sipdes
   createdb -h /var/www/var/run/postgresql/ -U postgres -O sipdes 
cor1440gen_pro
   psql -h /var/www/var/run/postgresql/ -U postgres
   postgres=# alter user sipdes with password 'xyz';
   postgres=# \q
   exit

With nginx suppossng I will run the application in localhost over SSL, 
what I do is to choose an unused port (let's say 2009) and add to the 
/etc/nginx/nginx.conf configuration in its http block:

  upstream unicorncor1440 {
	  server 127.0.0.1:2009 fail_timeout=0;
   }

I also add a server block inside the http block with:

   server {
       listen 443 ssl;
       ssl_certificate /etc/ssl/server.crt;
       ssl_certificate_key /etc/ssl/private/server.key;
       root /var/www/htdocs/cor1440/;
       server_name 127.0.0.1;
       error_log logs/cor1440.rror.log;

       location /cor1440 {
         try_files $uri @unicorncor1440;
       }

       location @unicorncor1440 {
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_set_header Host $http_host;
         proxy_redirect off;
         proxy_pass http://unicorncor1440;
         error_page 500 502 503 504 /500.html;
         keepalive_timeout 10;
       }

       location ^~ /cor1440/assets/ {
         gzip_static on;
         expires max;
         add_header Cache-Control public;
         root /var/www/htdocs/cor1440/public/;
       }

       location ^~ /cor1440/images/ {
         gzip_static on;
         expires max;
         add_header Cache-Control public;
         root /var/www/htdocs/cor1440/public/;
       }

       location ^~ /cor1440/packs/ {
         gzip_static on;
         add_header Cache-Control public;
         root /var/www/htdocs/cor1440/public/;
       }
     }

And restarted nginx:

   doas rcctl restart nginx

Then to configure:

   cd /tmp
   git clone git@github.com:pasosdeJesus/cor1440
   doas mv cor1440 /var/www/htdocs/
   doas chown -R $USER:$USER /var/www/htdocs/cor1440
   cd /var/www/htdocs/cor1440
   mkdir .bundle
   echo '---' > .bundle/config
   echo 'BUNDLE_PATH: "/var/www/bundler"' >> .bundle/config
   echo 'BUNDLE_DISABLE_SHARED_GEMS: "true"' >> .bundle/config
   bundle

Here some gems will require installing with special permissions for 
example:

   doas gem install --install-dir /var/www/bundler/ruby/3.0 unicorn

Other gems that will require this kind of installation will be:
   racc nio4r websocket-driver bcrypt bindex msgpack ffi redcarpet kgio 
libxml-ruby pg
   nokogiri bootsnap puma sassc

Once bundle runs completely:

   cp .env.plantilla .env
   vi .env

There I set the variables:
* BD_USUARIO with the database user,
* BD_CLAVE with the password
* BD_PRO with the name of the production database
* CONFIG_HOSTS=127.0.0.1
* DIRAP with the path to the application
* RAILS_ENV=production
* PUERTOUNICORN with the port I choosed and set in nginx.conf
* USUARIO_AP with the operating system user who will run the application

Prepare credentials and database:

   bin/rails credentials:edit
   RAILS_ENV=production bin/rails credentials:edit
   bin/railsp db:setup  db:migrate sip:indices

Precompile assets:

   CXX=c++ yarn
   doas ln -s /usr/local/bin/node /tmp  # To overcome bug of node with 
rails on OpenBSD
   bin/railsp assets:precompile

Run the application with:
   SECRET_KEY_BASE=cf46 DIRAP=/var/www/htdocs/cor1440/ bin/u.sh

Trying to see the application running with a browser in 
https://127.0.0.1/cor1440 will produce the errors described in 
log/unicorn.log.

Blessings. Thank you.

El 2021-03-30 00:00, Jeremy Evans escribió:
> On 03/30 01:58, Eric Wong wrote:
>> vtamara <vtamara@pasosdeJesus.org> wrote:
>> > Hi
>> >
>> > Launching a rails application with Ruby 3, Unicorn 6 on OpenBSD/adJ 6.8, I'm
>> > experiencing a lot of "Bus Error" and "Segmentation Faults" during
>> > aproximately half hour.
>> 
>> +Cc: Jeremy Evans, who is more familiar with OpenBSD and Ruby 3.0 than 
>> I
>> cf. 
>> https://yhbt.net/unicorn-public/6acb1c84c7392d7b4a64572b20498549@pasosdeJesus.org/
> 
> I'll be happy to look into this if a minimal self-contained 
> reproducible
> example is posted.  Personally, I run my Ruby apps on OpenBSD amd64 
> with
> Unicorn, and haven't seen similar errors.  However, I don't use
> preload_app and use fairly minimal dependencies.
> 
> Thanks,
> Jeremy

-- 
Dios, gracias por tu amor infinito.
--
   Vladimir Támara Patiño.  http://vtamara.pasosdeJesus.org/
   http://www.pasosdejesus.org/dominio_publico_colombia.html

^ permalink raw reply	[relevance 0%]

* Re: Bus Error with Unicorn 6.0 on OpenBSD/adJ 6.8 with Ruby 3.0
  @ 2021-03-30  1:58  6% ` Eric Wong
    0 siblings, 1 reply; 25+ results
From: Eric Wong @ 2021-03-30  1:58 UTC (permalink / raw)
  To: vtamara; +Cc: unicorn-public, Jeremy Evans

vtamara <vtamara@pasosdeJesus.org> wrote:
> Hi
> 
> Launching a rails application with Ruby 3, Unicorn 6 on OpenBSD/adJ 6.8, I'm
> experiencing a lot of "Bus Error" and "Segmentation Faults" during
> aproximately half hour.

+Cc: Jeremy Evans, who is more familiar with OpenBSD and Ruby 3.0 than I
cf. https://yhbt.net/unicorn-public/6acb1c84c7392d7b4a64572b20498549@pasosdeJesus.org/

vtamara: did previous versions of Ruby, unicorn, or OpenBSD work
for your app?

Is preload_app in use?  If so, does the problem manifest without
preload_app?

Are you using Ractor or any other new/experimental stuff
in Ruby?

Can you reproduce the problem with a simple "hello world"-style app?

> The line 80 of the file
> /var/www/bundler/ruby/3.0/gems/unicorn-6.0.0/lib/unicorn.rb is:
> 
> case ENV["RACK_ENV"]
> 
> Only 4 errors had a backtrace, pasting the first one:
> 
> ----------------------------------------
> I, [2021-03-29T09:34:31.278771 #37959]  INFO -- : Refreshing Gem list
> /var/www/bundler/ruby/3.0/gems/unicorn-6.0.0/lib/unicorn.rb:80: [BUG]
> Segmentation fault at 0x0000000000000019

That's a strange one, I wonder if OpenBSD getenv(3) has some
restrictions or other odd behaviors with it?

Do you have any code/dependencies which are calling "ENV#[]=" in
Ruby; setenv()/putenv() in C, or modifying "environ" directly
in C?

Especially in sub-threads/Ractors at startup...

<snip>

> * Loaded features:
> 
>     0 enumerator.so
>     1 thread.rb
>     2 rational.so
>     3 complex.so
>     4 ruby2_keywords.rb
>     5 /usr/local/lib/ruby/3.0/x86_64-openbsd/enc/encdb.so
>     6 /usr/local/lib/ruby/3.0/x86_64-openbsd/enc/trans/transdb.so

<snip>

> ...

Any other .so extensions?  C extensions can do any number of
things and maybe some interact badly...

^ permalink raw reply	[relevance 6%]

* [PATCH 2/2] build: revamp and avoid unnecessary rebuilds
  @ 2020-07-26  1:57  5% ` Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2020-07-26  1:57 UTC (permalink / raw)
  To: unicorn-public

We can limit the amount of Ruby-version-specific code to
just the stuff in ext/* and bin/*, reducing I/O traffic
and FS + page cache footprint.

Furthermore, rely on GNU make behavior to copy all the necessary
files so we don't trigger unnecessary extconf.rb invocations
just by touching a .rb file in lib.
---
 GNUmakefile   | 160 ++++++++++++++++++++++++++++++++------------------
 t/GNUmakefile |  75 +----------------------
 2 files changed, 105 insertions(+), 130 deletions(-)

diff --git a/GNUmakefile b/GNUmakefile
index eac3473f..d80e6080 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -10,6 +10,7 @@ RAGEL = ragel
 RSYNC = rsync
 OLDDOC = olddoc
 RDOC = rdoc
+INSTALL = install
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
 	@./GIT-VERSION-GEN
@@ -25,7 +26,38 @@ endif
 
 RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
 
-MYLIBS = $(RUBYLIB)
+# we should never package more than one ext to avoid DSO proliferation:
+# https://udrepper.livejournal.com/8790.html
+ext := $(firstword $(wildcard ext/*))
+
+ragel: $(ext)/unicorn_http.c
+
+rl_files := $(wildcard $(ext)/*.rl)
+ragel: $(ext)/unicorn_http.c
+$(ext)/unicorn_http.c: $(rl_files)
+	cd $(@D) && $(RAGEL) unicorn_http.rl -C $(RLFLAGS) -o $(@F)
+ext_pfx := test/$(RUBY_ENGINE)-$(RUBY_VERSION)
+tmp_bin := $(ext_pfx)/bin
+ext_h := $(wildcard $(ext)/*/*.h $(ext)/*.h)
+ext_src := $(sort $(wildcard $(ext)/*.c) $(ext_h) $(ext)/unicorn_http.c)
+ext_pfx_src := $(addprefix $(ext_pfx)/,$(ext_src))
+ext_dir := $(ext_pfx)/$(ext)
+$(ext)/extconf.rb: $(wildcard $(ext)/*.h)
+	@>>$@
+$(ext_dir) $(tmp_bin) man/man1 doc/man1 pkg t/trash:
+	@mkdir -p $@
+$(ext_pfx)/$(ext)/%: $(ext)/% | $(ext_dir)
+	$(INSTALL) -m 644 $< $@
+$(ext_pfx)/$(ext)/Makefile: $(ext)/extconf.rb $(ext_h) | $(ext_dir)
+	$(RM) -f $(@D)/*.o
+	cd $(@D) && $(RUBY) $(CURDIR)/$(ext)/extconf.rb $(EXTCONF_ARGS)
+ext_sfx := _ext.$(DLEXT)
+ext_dl := $(ext_pfx)/$(ext)/$(notdir $(ext)_ext.$(DLEXT))
+$(ext_dl): $(ext_src) $(ext_pfx_src) $(ext_pfx)/$(ext)/Makefile
+	$(MAKE) -C $(@D)
+lib := $(CURDIR)/lib:$(CURDIR)/$(ext_pfx)/$(ext)
+http build: $(ext_dl)
+$(ext_pfx)/$(ext)/unicorn_http.c: ext/unicorn_http/unicorn_http.c
 
 # dunno how to implement this as concisely in Ruby, and hell, I love awk
 awk_slow := awk '/def test_/{print FILENAME"--"$$2".n"}' 2>/dev/null
@@ -37,44 +69,21 @@ T := $(filter-out $(slow_tests), $(wildcard test/*/test*.rb))
 T_n := $(shell $(awk_slow) $(slow_tests))
 T_log := $(subst .rb,$(log_suffix),$(T))
 T_n_log := $(subst .n,$(log_suffix),$(T_n))
-test_prefix = $(CURDIR)/test/$(RUBY_ENGINE)-$(RUBY_VERSION)
 
-ext := ext/unicorn_http
-c_files := $(ext)/unicorn_http.c $(ext)/httpdate.c $(wildcard $(ext)/*.h)
-rl_files := $(wildcard $(ext)/*.rl)
 base_bins := unicorn unicorn_rails
 bins := $(addprefix bin/, $(base_bins))
 man1_rdoc := $(addsuffix _1, $(base_bins))
 man1_bins := $(addsuffix .1, $(base_bins))
 man1_paths := $(addprefix man/man1/, $(man1_bins))
-rb_files := $(bins) $(shell find lib ext -type f -name '*.rb')
-inst_deps := $(c_files) $(rb_files) GNUmakefile test/test_helper.rb
-
-ragel: $(ext)/unicorn_http.c
-$(ext)/unicorn_http.c: $(rl_files)
-	cd $(@D) && $(RAGEL) unicorn_http.rl -C $(RLFLAGS) -o $(@F)
-$(ext)/Makefile: $(ext)/extconf.rb $(c_files)
-	cd $(@D) && $(RUBY) extconf.rb
-$(ext)/unicorn_http.$(DLEXT): $(ext)/Makefile
-	$(MAKE) -C $(@D)
-http: $(ext)/unicorn_http.$(DLEXT)
+tmp_bins = $(addprefix $(tmp_bin)/, unicorn unicorn_rails)
+pid := $(shell echo $$PPID)
 
-# only used for tests
-http-install: $(ext)/unicorn_http.$(DLEXT)
-	install -m644 $< lib/
+$(tmp_bin)/%: bin/% | $(tmp_bin)
+	$(INSTALL) -m 755 $< $@.$(pid)
+	$(MRI) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@.$(pid)
+	mv $@.$(pid) $@
 
-test-install: $(test_prefix)/.stamp
-$(test_prefix)/.stamp: $(inst_deps)
-	mkdir -p $(test_prefix)/.ccache
-	tar cf - $(inst_deps) GIT-VERSION-GEN | \
-	  (cd $(test_prefix) && tar xf -)
-	$(MAKE) -C $(test_prefix) clean
-	$(MAKE) -C $(test_prefix) http-install shebang RUBY="$(RUBY)"
-	> $@
-
-# this is only intended to be run within $(test_prefix)
-shebang: $(bins)
-	$(MRI) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $^
+bins: $(tmp_bins)
 
 t_log := $(T_log) $(T_n_log)
 test: $(T) $(T_n)
@@ -83,15 +92,54 @@ test: $(T) $(T_n)
 
 test-exec: $(wildcard test/exec/test_*.rb)
 test-unit: $(wildcard test/unit/test_*.rb)
-$(slow_tests): $(test_prefix)/.stamp
+$(slow_tests): $(ext_dl)
 	@$(MAKE) $(shell $(awk_slow) $@)
 
 # ensure we can require just the HTTP parser without the rest of unicorn
-test-require: $(ext)/unicorn_http.$(DLEXT)
-	$(RUBY) --disable-gems -I$(ext) -runicorn_http -e Unicorn
+test-require: $(ext_dl)
+	$(RUBY) --disable-gems -I$(ext_pfx)/$(ext) -runicorn_http -e Unicorn
+
+test_prereq := $(tmp_bins) $(ext_dl)
+
+SH_TEST_OPTS =
+ifdef V
+  ifeq ($(V),2)
+    SH_TEST_OPTS += --trace
+  else
+    SH_TEST_OPTS += --verbose
+  endif
+endif
 
-test-integration: $(test_prefix)/.stamp
-	$(MAKE) -C t
+# do we trust Ruby behavior to be stable? some tests are
+# (mostly) POSIX sh (not bash or ksh93, so no "set -o pipefail"
+# TRACER = strace -f -o $(t_pfx).strace -s 100000
+# TRACER = /usr/bin/time -o $(t_pfx).time
+t_pfx = trash/$@-$(RUBY_ENGINE)-$(RUBY_VERSION)
+T_sh = $(wildcard t/t[0-9][0-9][0-9][0-9]-*.sh)
+$(T_sh): export RUBY := $(RUBY)
+$(T_sh): export PATH := $(CURDIR)/$(tmp_bin):$(PATH)
+$(T_sh): export RUBYLIB := $(lib):$(RUBYLIB)
+$(T_sh): dep $(test_prereq) t/random_blob t/trash/.gitignore
+	cd t && $(TRACER) $(SHELL) $(SH_TEST_OPTS) $(@F) $(TEST_OPTS)
+
+t/trash/.gitignore : | t/trash
+	echo '*' >$@
+
+dependencies := socat curl
+deps := $(addprefix t/.dep+,$(dependencies))
+$(deps): dep_bin = $(lastword $(subst +, ,$@))
+$(deps):
+	@which $(dep_bin) > $@.$(pid) 2>/dev/null || :
+	@test -s $@.$(pid) || \
+	  { echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
+	@mv $@.$(pid) $@
+dep: $(deps)
+
+t/random_blob:
+	dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
+	mv $@.$(pid) $@
+
+test-integration: $(T_sh)
 
 check: test-require test test-integration
 test-all: check
@@ -122,16 +170,16 @@ run_test = $(quiet_pre) \
 
 %.n: arg = $(subst .n,,$(subst --, -n ,$@))
 %.n: t = $(subst .n,$(log_suffix),$@)
-%.n: export PATH := $(test_prefix)/bin:$(PATH)
-%.n: export RUBYLIB := $(test_prefix)/lib:$(MYLIBS)
-%.n: $(test_prefix)/.stamp
+%.n: export PATH := $(CURDIR)/$(tmp_bin):$(PATH)
+%.n: export RUBYLIB := $(lib):$(RUBYLIB)
+%.n: $(test_prereq)
 	$(run_test)
 
 $(T): arg = $@
 $(T): t = $(subst .rb,$(log_suffix),$@)
-$(T): export PATH := $(test_prefix)/bin:$(PATH)
-$(T): export RUBYLIB := $(test_prefix)/lib:$(MYLIBS)
-$(T): $(test_prefix)/.stamp
+$(T): export PATH := $(CURDIR)/$(tmp_bin):$(PATH)
+$(T): export RUBYLIB := $(lib):$(RUBYLIB)
+$(T): $(test_prereq)
 	$(run_test)
 
 install: $(bins) $(ext)/unicorn_http.c
@@ -152,18 +200,16 @@ clean:
 	-$(MAKE) -C $(ext) clean
 	$(RM) $(ext)/Makefile
 	$(RM) $(setup_rb_files) $(t_log)
-	$(RM) -r $(test_prefix) man
-	$(RM) $(man1) $(html1)
+	$(RM) -r $(ext_pfx) man t/trash
+	$(RM) $(html1)
 
 man1 := $(addprefix Documentation/, unicorn.1 unicorn_rails.1)
 html1 := $(addsuffix .html, $(man1))
-man :
-	mkdir -p man/man1
-	install -m 644 $(man1) man/man1
+man : $(man1) | man/man1
+	$(INSTALL) -m 644 $(man1) man/man1
 
-html : $(html1)
-	mkdir -p doc/man1
-	install -m 644 $(html1) doc/man1
+html : $(html1) | doc/man1
+	$(INSTALL) -m 644 $(html1) doc/man1
 
 %.1.html: %.1
 	$(OLDDOC) man2html -o $@ ./$<
@@ -187,10 +233,10 @@ doc: .document $(ext)/unicorn_http.c man html .olddoc.yml $(PLACEHOLDERS)
 	$(OLDDOC) prepare
 	$(RDOC) -f dark216
 	$(OLDDOC) merge
-	install -m644 COPYING doc/COPYING
-	install -m644 NEWS.atom.xml doc/NEWS.atom.xml
-	install -m644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/
-	install -m644 $(man1_paths) doc/
+	$(INSTALL) -m 644 COPYING doc/COPYING
+	$(INSTALL) -m 644 NEWS.atom.xml doc/NEWS.atom.xml
+	$(INSTALL) -m 644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/
+	$(INSTALL) -m 644 $(man1_paths) doc/
 	tar cf - $$(git ls-files examples/) | (cd doc && tar xf -)
 
 # publishes docs to https://yhbt.net/unicorn/
@@ -231,9 +277,8 @@ gem: $(pkggem)
 install-gem: $(pkggem)
 	gem install --local $(CURDIR)/$<
 
-$(pkggem): .manifest fix-perms
+$(pkggem): .manifest fix-perms | pkg
 	gem build $(rfpackage).gemspec
-	mkdir -p pkg
 	mv $(@F) $@
 
 $(pkgtgz): distdir = $(basename $@)
@@ -264,5 +309,4 @@ check-warnings:
 	  do $(RUBY) --disable-gems -d -W2 -c \
 	  $$i; done) | grep -v '^Syntax OK$$' || :
 
-.PHONY: .FORCE-GIT-VERSION-FILE doc $(T) $(slow_tests) man
-.PHONY: test-install
+.PHONY: .FORCE-GIT-VERSION-FILE doc $(T) $(slow_tests) man $(T_sh) clean
diff --git a/t/GNUmakefile b/t/GNUmakefile
index 5f5d9bc3..0ac9b9a3 100644
--- a/t/GNUmakefile
+++ b/t/GNUmakefile
@@ -1,74 +1,5 @@
-# we can run tests in parallel with GNU make
+# there used to be more, here, but we stopped relying on recursive make
 all::
+	$(MAKE) -C .. test-integration
 
-pid := $(shell echo $$PPID)
-
-RUBY = ruby
-RAKE = rake
--include ../local.mk
-ifeq ($(RUBY_VERSION),)
-  RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
-endif
-
-ifeq ($(RUBY_VERSION),)
-  $(error unable to detect RUBY_VERSION)
-endif
-
-RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
-export RUBY_ENGINE
-
-MYLIBS := $(RUBYLIB)
-
-T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
-
-all:: $(T)
-
-# can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
-t_pfx = trash/$@-$(RUBY_ENGINE)-$(RUBY_VERSION)
-TEST_OPTS =
-# TRACER = strace -f -o $(t_pfx).strace -s 100000
-# TRACER = /usr/bin/time -o $(t_pfx).time
-
-ifdef V
-  ifeq ($(V),2)
-    TEST_OPTS += --trace
-  else
-    TEST_OPTS += --verbose
-  endif
-endif
-
-random_blob:
-	dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
-	mv $@.$(pid) $@
-
-$(T): random_blob
-
-dependencies := socat curl
-deps := $(addprefix .dep+,$(dependencies))
-$(deps): dep_bin = $(lastword $(subst +, ,$@))
-$(deps):
-	@which $(dep_bin) > $@.$(pid) 2>/dev/null || :
-	@test -s $@.$(pid) || \
-	  { echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
-	@mv $@.$(pid) $@
-dep: $(deps)
-
-test_prefix := $(CURDIR)/../test/$(RUBY_ENGINE)-$(RUBY_VERSION)
-$(test_prefix)/.stamp:
-	$(MAKE) -C .. test-install
-
-$(T): export RUBY := $(RUBY)
-$(T): export RAKE := $(RAKE)
-$(T): export PATH := $(test_prefix)/bin:$(PATH)
-$(T): export RUBYLIB := $(test_prefix)/lib:$(MYLIBS)
-$(T): dep $(test_prefix)/.stamp trash/.gitignore
-	$(TRACER) $(SHELL) $(SH_TEST_OPTS) $@ $(TEST_OPTS)
-
-trash/.gitignore:
-	mkdir -p $(@D)
-	echo '*' > $@
-
-clean:
-	$(RM) -r trash/*
-
-.PHONY: $(T) clean
+.PHONY: all

^ permalink raw reply related	[relevance 5%]

* [PATCH] doc: s/bogomips.org/yhbt.net/g
  @ 2020-01-14  7:46  1% ` Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2020-01-14  7:46 UTC (permalink / raw)
  To: unicorn-public

bogomips.org is due to expire, soon, and I'm not willing to pay
extortionist fees to Ethos Capital/PIR/ICANN to keep a .org.  So
it's at yhbt.net, for now, but it will change again to
whatever's affordable...  Identity is overrated.

Tor users can use .onions and kick ICANN to the curb:

	torsocks w3m http://unicorn.ou63pmih66umazou.onion/
	torsocks git clone http://ou63pmih66umazou.onion/unicorn.git/
	torsocks w3m http://ou63pmih66umazou.onion/unicorn-public/

While we're at it, `s/news.gmane.org/news.gmane.io/g', too.
(but I suspect that'll need to be resynched since our mail
"List-Id:" header is changing).
---
 .olddoc.yml                      | 19 ++++++++++++-------
 Documentation/unicorn.1          |  8 ++++----
 Documentation/unicorn_rails.1    |  8 ++++----
 FAQ                              |  2 +-
 GNUmakefile                      |  4 ++--
 HACKING                          |  2 +-
 ISSUES                           | 24 ++++++++++++------------
 KNOWN_ISSUES                     |  4 ++--
 Links                            | 10 +++++-----
 README                           | 12 ++++++------
 SIGNALS                          |  2 +-
 Sandbox                          |  4 ++--
 archive/slrnpull.conf            |  2 +-
 examples/big_app_gc.rb           |  2 +-
 examples/logrotate.conf          |  4 ++--
 examples/nginx.conf              |  2 +-
 examples/unicorn.conf.minimal.rb |  4 ++--
 examples/unicorn.conf.rb         |  4 ++--
 ext/unicorn_http/unicorn_http.rl |  2 +-
 lib/unicorn.rb                   |  2 +-
 lib/unicorn/configurator.rb      |  6 +++---
 lib/unicorn/http_server.rb       |  2 +-
 lib/unicorn/oob_gc.rb            |  4 ++--
 unicorn.gemspec                  |  4 ++--
 24 files changed, 71 insertions(+), 66 deletions(-)

diff --git a/.olddoc.yml b/.olddoc.yml
index d2d340f..0609bdb 100644
--- a/.olddoc.yml
+++ b/.olddoc.yml
@@ -1,8 +1,9 @@
 ---
-cgit_url: https://bogomips.org/unicorn.git
-git_url: https://bogomips.org/unicorn.git
-rdoc_url: https://bogomips.org/unicorn/
-ml_url: https://bogomips.org/unicorn-public/
+cgit_url: https://yhbt.net/unicorn.git
+rdoc_url: https://yhbt.net/unicorn/
+ml_url:
+- https://yhbt.net/unicorn-public/
+- http://ou63pmih66umazou.onion/unicorn-public/
 merge_html:
   unicorn_1: Documentation/unicorn.1.html
   unicorn_rails_1: Documentation/unicorn_rails.1.html
@@ -11,7 +12,11 @@ noindex:
 - LATEST
 - TODO
 - unicorn_rails_1
-public_email: unicorn-public@bogomips.org
+public_email: unicorn-public@yhbt.net
 nntp_url:
-  - nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
-  - nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+- nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
+- nntp://ou63pmih66umazou.onion/inbox.comp.lang.ruby.unicorn
+- nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
+source_code:
+- git clone https://yhbt.net/unicorn.git
+- torsocks git clone http://ou63pmih66umazou.onion/unicorn.git
diff --git a/Documentation/unicorn.1 b/Documentation/unicorn.1
index 3f8cb96..d76d40f 100644
--- a/Documentation/unicorn.1
+++ b/Documentation/unicorn.1
@@ -154,7 +154,7 @@ TTIN \- increment the number of worker processes by one
 .IP \[bu] 2
 TTOU \- decrement the number of worker processes by one
 .PP
-See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
+See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
 full description of all signals used by Unicorn.
 .SH RACK ENVIRONMENT
 .PP
@@ -204,11 +204,11 @@ the unicorn config file.
 \f[I]Rack::Builder\f[] ri/RDoc
 .IP \[bu] 2
 \f[I]Unicorn::Configurator\f[] ri/RDoc
-.UR https://bogomips.org/unicorn/Unicorn/Configurator.html
+.UR https://yhbt.net/unicorn/Unicorn/Configurator.html
 .UE
 .IP \[bu] 2
 unicorn RDoc
-.UR https://bogomips.org/unicorn/
+.UR https://yhbt.net/unicorn/
 .UE
 .IP \[bu] 2
 Rack RDoc
@@ -219,4 +219,4 @@ Rackup HowTo
 .UR https://github.com/rack/rack/wiki/(tutorial)-rackup-howto
 .UE
 .SH AUTHORS
-The Unicorn Community <unicorn-public@bogomips.org>.
+The Unicorn Community <unicorn-public@yhbt.net>.
diff --git a/Documentation/unicorn_rails.1 b/Documentation/unicorn_rails.1
index 71c80be..fec0a2a 100644
--- a/Documentation/unicorn_rails.1
+++ b/Documentation/unicorn_rails.1
@@ -180,7 +180,7 @@ TTIN \- increment the number of worker processes by one
 .IP \[bu] 2
 TTOU \- decrement the number of worker processes by one
 .PP
-See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
+See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
 full description of all signals used by Unicorn.
 .SH SEE ALSO
 .IP \[bu] 2
@@ -189,11 +189,11 @@ unicorn(1)
 \f[I]Rack::Builder\f[] ri/RDoc
 .IP \[bu] 2
 \f[I]Unicorn::Configurator\f[] ri/RDoc
-.UR https://bogomips.org/unicorn/Unicorn/Configurator.html
+.UR https://yhbt.net/unicorn/Unicorn/Configurator.html
 .UE
 .IP \[bu] 2
 unicorn RDoc
-.UR https://bogomips.org/unicorn/
+.UR https://yhbt.net/unicorn/
 .UE
 .IP \[bu] 2
 Rack RDoc
@@ -204,4 +204,4 @@ Rackup HowTo
 .UR https://github.com/rack/rack/wiki/(tutorial)-rackup-howto
 .UE
 .SH AUTHORS
-The Unicorn Community <unicorn-public@bogomips.org>.
+The Unicorn Community <unicorn-public@yhbt.net>.
diff --git a/FAQ b/FAQ
index 4ae2034..018ca92 100644
--- a/FAQ
+++ b/FAQ
@@ -7,7 +7,7 @@ drained entirely by the application.  This may happen when request
 bodies are gzipped, as unicorn reads request body data lazily to avoid
 overhead from bad requests.
 
-Ref: https://bogomips.org/unicorn-public/FC91211E-FD32-432C-92FC-0318714C2170@zendesk.com/
+Ref: https://yhbt.net/unicorn-public/FC91211E-FD32-432C-92FC-0318714C2170@zendesk.com/
 
 === Why aren't my Rails log files rotated when I use SIGUSR1?
 
diff --git a/GNUmakefile b/GNUmakefile
index 94c46ee..eac3473 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -193,13 +193,13 @@ doc: .document $(ext)/unicorn_http.c man html .olddoc.yml $(PLACEHOLDERS)
 	install -m644 $(man1_paths) doc/
 	tar cf - $$(git ls-files examples/) | (cd doc && tar xf -)
 
-# publishes docs to https://bogomips.org/unicorn/
+# publishes docs to https://yhbt.net/unicorn/
 publish_doc:
 	-git set-file-times
 	$(MAKE) doc
 	$(MAKE) doc_gz
 	chmod 644 $$(find doc -type f)
-	$(RSYNC) -av doc/ bogomips.org:/srv/bogomips/unicorn/
+	$(RSYNC) -av doc/ yhbt.net:/srv/yhbt/unicorn/
 	git ls-files | xargs touch
 
 # Create gzip variants of the same timestamp as the original so nginx
diff --git a/HACKING b/HACKING
index be1bb85..976bf92 100644
--- a/HACKING
+++ b/HACKING
@@ -57,7 +57,7 @@ Please wrap documentation at 72 characters-per-line or less (long URLs
 are exempt) so it is comfortably readable from terminals.
 
 When referencing mailing list posts, use
-<tt>https://bogomips.org/unicorn-public/$MESSAGE_ID/</tt> if possible
+<tt>https://yhbt.net/unicorn-public/$MESSAGE_ID/</tt> if possible
 since the Message-ID remains searchable even if a particular site
 becomes unavailable.
 
diff --git a/ISSUES b/ISSUES
index 473da2f..d11dc56 100644
--- a/ISSUES
+++ b/ISSUES
@@ -1,9 +1,9 @@
 = Issues
 
-mailto:unicorn-public@bogomips.org is the best place to report bugs,
+mailto:unicorn-public@yhbt.net is the best place to report bugs,
 submit patches and/or obtain support after you have searched the
-{email archives}[https://bogomips.org/unicorn-public/] and
-{documentation}[https://bogomips.org/unicorn/].
+{email archives}[https://yhbt.net/unicorn-public/] and
+{documentation}[https://yhbt.net/unicorn/].
 
 * No subscription will ever be required to email us
 * Cc: all participants in a thread or commit, as subscription is optional
@@ -12,12 +12,12 @@ submit patches and/or obtain support after you have searched the
 * Do not send HTML mail or images,
   they hurt reader privacy and will be flagged as spam
 * Anonymous and pseudonymous messages will ALWAYS be welcome
-* The email submission port (587) is enabled on the bogomips.org MX:
-  https://bogomips.org/unicorn-public/20141004232241.GA23908@dcvr.yhbt.net/t/
+* The email submission port (587) is enabled on the yhbt.net MX:
+  https://yhbt.net/unicorn-public/20141004232241.GA23908@dcvr.yhbt.net/t/
 
 We will never have a centralized or formal bug tracker.  Instead we
 can interoperate with any bug tracker which can Cc: us plain-text to
-mailto:unicorn-public@bogomips.org   This includes the Debian BTS
+mailto:unicorn-public@yhbt.net   This includes the Debian BTS
 at https://bugs.debian.org/unicorn and possibly others.
 
 If your issue is of a sensitive nature or you're just shy in public,
@@ -73,10 +73,10 @@ document distributed with git) on guidelines for patch submission.
 
 == Contact Info
 
-* public: mailto:unicorn-public@bogomips.org
-* nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+* public: mailto:unicorn-public@yhbt.net
+* nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
 * nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
-* https://bogomips.org/unicorn-public/
+* https://yhbt.net/unicorn-public/
 * http://ou63pmih66umazou.onion/unicorn-public/
 
 Mailing list subscription is optional, so Cc: all participants.
@@ -84,13 +84,13 @@ Mailing list subscription is optional, so Cc: all participants.
 You can follow along via NNTP (read-only):
 
 	nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
-	nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+	nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
 
 Or Atom feeds:
 
-	https://bogomips.org/unicorn-public/new.atom
+	https://yhbt.net/unicorn-public/new.atom
 	http://ou63pmih66umazou.onion/unicorn-public/new.atom
 
-	The HTML archives at https://bogomips.org/unicorn-public/
+	The HTML archives at https://yhbt.net/unicorn-public/
 	also has links to per-thread Atom feeds and downloadable
 	mboxes.
diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES
index ebd4822..0017f20 100644
--- a/KNOWN_ISSUES
+++ b/KNOWN_ISSUES
@@ -9,7 +9,7 @@ acceptable solution.  Those issues are documented here.
   handlers.
 
 * Issues with FreeBSD jails can be worked around as documented by Tatsuya Ono:
-  https://bogomips.org/unicorn-public/CAHBuKRj09FdxAgzsefJWotexw-7JYZGJMtgUp_dhjPz9VbKD6Q@mail.gmail.com/
+  https://yhbt.net/unicorn-public/CAHBuKRj09FdxAgzsefJWotexw-7JYZGJMtgUp_dhjPz9VbKD6Q@mail.gmail.com/
 
 * PRNGs (pseudo-random number generators) loaded before forking
   (e.g. "preload_app true") may need to have their internal state
@@ -60,7 +60,7 @@ acceptable solution.  Those issues are documented here.
   application to use Rails 2.3.2 and you have no other choice, then
   you may edit your unicorn gemspec and remove the Rack dependency.
 
-  ref: https://bogomips.org/unicorn-public/20091014221552.GA30624@dcvr.yhbt.net/
+  ref: https://yhbt.net/unicorn-public/20091014221552.GA30624@dcvr.yhbt.net/
   Note: the workaround described in the article above only made
   the issue more subtle and we didn't notice them immediately.
 
diff --git a/Links b/Links
index 10551a6..f81142d 100644
--- a/Links
+++ b/Links
@@ -2,7 +2,7 @@
 
 If you're interested in unicorn, you may be interested in some of the projects
 listed below.  If you have any links to add/change/remove, please tell us at
-mailto:unicorn-public@bogomips.org!
+mailto:unicorn-public@yhbt.net!
 
 == Disclaimer
 
@@ -23,10 +23,10 @@ or services behind them.
 * {golden_brindle}[https://github.com/simonoff/golden_brindle] - tool to
   manage multiple unicorn instances/applications on a single server
 
-* {raindrops}[https://bogomips.org/raindrops/] - real-time stats for
+* {raindrops}[https://yhbt.net/raindrops/] - real-time stats for
   preforking Rack servers
 
-* {UnXF}[https://bogomips.org/unxf/]  Un-X-Forward* the Rack environment,
+* {UnXF}[https://yhbt.net/unxf/]  Un-X-Forward* the Rack environment,
   useful since unicorn is designed to be deployed behind a reverse proxy.
 
 === unicorn is written to work with
@@ -52,7 +52,7 @@ or services behind them.
 * {Mongrel}[https://rubygems.org/gems/mongrel] - the awesome webserver
   unicorn is based on.  A historical archive of the mongrel dev list
   featuring early discussions of unicorn is available at:
-  https://bogomips.org/mongrel-devel/
+  https://yhbt.net/mongrel-devel/
 
-* {david}[https://bogomips.org/david.git] - a tool to explain why you need
+* {david}[https://yhbt.net/david.git] - a tool to explain why you need
   nginx in front of unicorn
diff --git a/README b/README
index 89467fc..0e95f48 100644
--- a/README
+++ b/README
@@ -80,12 +80,12 @@ You may install it via RubyGems on RubyGems.org:
 You can get the latest source via git from the following locations
 (these versions may not be stable):
 
-  https://bogomips.org/unicorn.git
+  https://yhbt.net/unicorn.git
   https://repo.or.cz/unicorn.git (mirror)
 
 You may browse the code from the web:
 
-* https://bogomips.org/unicorn.git
+* https://yhbt.net/unicorn.git
 * https://repo.or.cz/w/unicorn.git (gitweb)
 
 See the HACKING guide on how to contribute and build prerelease gems
@@ -133,13 +133,13 @@ and libraries which run on top of it.
 
 All feedback (bug reports, user/development dicussion, patches, pull
 requests) go to the mailing list/newsgroup.  See the ISSUES document for
-information on the {mailing list}[mailto:unicorn-public@bogomips.org].
+information on the {mailing list}[mailto:unicorn-public@yhbt.net].
 
-The mailing list is archived at https://bogomips.org/unicorn-public/
+The mailing list is archived at https://yhbt.net/unicorn-public/
 Read-only NNTP access is available at:
 nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn and
-nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
 
 For the latest on unicorn releases, you may also finger us at
-unicorn@bogomips.org or check our NEWS page (and subscribe to our Atom
+unicorn@yhbt.net or check our NEWS page (and subscribe to our Atom
 feed).
diff --git a/SIGNALS b/SIGNALS
index 1af851d..7321f2b 100644
--- a/SIGNALS
+++ b/SIGNALS
@@ -8,7 +8,7 @@ should be possible to easily share process management scripts between
 Unicorn and nginx.
 
 One example init script is distributed with unicorn:
-https://bogomips.org/unicorn/examples/init.sh
+https://yhbt.net/unicorn/examples/init.sh
 
 === Master Process
 
diff --git a/Sandbox b/Sandbox
index d0f915e..651e5cd 100644
--- a/Sandbox
+++ b/Sandbox
@@ -34,7 +34,7 @@ is the primary issue with sandboxing tools such as Bundler and Isolate.
 If you're bundling unicorn, use "bundle exec unicorn" (or "bundle exec
 unicorn_rails") to start unicorn with the correct environment variables
 
-ref: https://bogomips.org/unicorn-public/9ECF07C4-5216-47BE-961D-AFC0F0C82060@internetfamo.us/
+ref: https://yhbt.net/unicorn-public/9ECF07C4-5216-47BE-961D-AFC0F0C82060@internetfamo.us/
 
 Otherwise (if you choose to not sandbox your unicorn installation), we
 expect the tips for Isolate (below) apply, too.
@@ -44,7 +44,7 @@ expect the tips for Isolate (below) apply, too.
 This is no longer be an issue as of bundler 0.9.17
 
 ref:
-https://bogomips.org/unicorn-public/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com/
+https://yhbt.net/unicorn-public/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com/
 
 === BUNDLE_GEMFILE for Capistrano users
 
diff --git a/archive/slrnpull.conf b/archive/slrnpull.conf
index fcfcafe..fd04f97 100644
--- a/archive/slrnpull.conf
+++ b/archive/slrnpull.conf
@@ -1,4 +1,4 @@
 # group_name                         max        expire     headers_only
 gmane.comp.lang.ruby.unicorn.general 1000000000 1000000000 0
 
-# usage: slrnpull -d $PWD -h news.gmane.org --no-post
+# usage: slrnpull -d $PWD -h news.gmane.io --no-post
diff --git a/examples/big_app_gc.rb b/examples/big_app_gc.rb
index 9d05719..c1bae10 100644
--- a/examples/big_app_gc.rb
+++ b/examples/big_app_gc.rb
@@ -1,2 +1,2 @@
-# see {Unicorn::OobGC}[https://bogomips.org/unicorn/Unicorn/OobGC.html]
+# see {Unicorn::OobGC}[https://yhbt.net/unicorn/Unicorn/OobGC.html]
 # Unicorn::OobGC was broken in Unicorn v3.3.1 - v3.6.1 and fixed in v3.6.2
diff --git a/examples/logrotate.conf b/examples/logrotate.conf
index 77a01b5..c3aa40d 100644
--- a/examples/logrotate.conf
+++ b/examples/logrotate.conf
@@ -5,7 +5,7 @@
 #    https://linux.die.net/man/8/logrotate
 #
 # public logrotate-related discussion in our archives:
-#    https://bogomips.org/unicorn-public/?q=logrotate
+#    https://yhbt.net/unicorn-public/?q=logrotate
 
 # Modify the following glob to match the logfiles your app writes to:
 /var/log/unicorn_app/*.log {
@@ -33,7 +33,7 @@
 		systemctl kill -s SIGUSR1 unicorn@2.service
 
 		# Examples for other process management systems appreciated
-		# Mail us at unicorn-public@bogomips.org
+		# Mail us at unicorn-public@yhbt.net
 		# (see above for archives)
 
 		# If you use a pid file and assuming your pid file
diff --git a/examples/nginx.conf b/examples/nginx.conf
index b6b69c1..c5026f9 100644
--- a/examples/nginx.conf
+++ b/examples/nginx.conf
@@ -113,7 +113,7 @@ http {
     # try_files directive appeared in in nginx 0.7.27 and has stabilized
     # over time.  Older versions of nginx (e.g. 0.6.x) requires
     # "if (!-f $request_filename)" which was less efficient:
-    # https://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
+    # https://yhbt.net/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
     try_files $uri/index.html $uri.html $uri @app;
 
     location @app {
diff --git a/examples/unicorn.conf.minimal.rb b/examples/unicorn.conf.minimal.rb
index 2d1bf0a..46fd634 100644
--- a/examples/unicorn.conf.minimal.rb
+++ b/examples/unicorn.conf.minimal.rb
@@ -1,9 +1,9 @@
 # Minimal sample configuration file for Unicorn (not Rack) when used
 # with daemonization (unicorn -D) started in your working directory.
 #
-# See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
+# See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
 # documentation.
-# See also https://bogomips.org/unicorn/examples/unicorn.conf.rb for
+# See also https://yhbt.net/unicorn/examples/unicorn.conf.rb for
 # a more verbose configuration using more features.
 
 listen 2007 # by default Unicorn listens on port 8080
diff --git a/examples/unicorn.conf.rb b/examples/unicorn.conf.rb
index d2897ef..d90bdc4 100644
--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -2,10 +2,10 @@
 #
 # This configuration file documents many features of Unicorn
 # that may not be needed for some applications. See
-# https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
+# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
 # for a much simpler configuration file.
 #
-# See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
+# See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
 # documentation.
 
 # Use at least one worker per core if you're on a dedicated server,
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 8ef23bc..dfe3a63 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -487,7 +487,7 @@ static void set_url_scheme(VALUE env, VALUE *server_port)
      * and X-Forwarded-Proto handling from this parser?  We've had it
      * forever and nobody has said anything against it, either.
      * Anyways, please send comments to our public mailing list:
-     * unicorn-public@bogomips.org (no HTML mail, no subscription necessary)
+     * unicorn-public@yhbt.net (no HTML mail, no subscription necessary)
      */
     scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
     if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index dd5dff4..d5991fe 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -96,7 +96,7 @@ def self.builder(ru, op)
 
   # returns an array of strings representing TCP listen socket addresses
   # and Unix domain socket paths.  This is useful for use with
-  # Raindrops::Middleware under Linux: https://bogomips.org/raindrops/
+  # Raindrops::Middleware under Linux: https://yhbt.net/raindrops/
   def self.listener_names
     Unicorn::HttpServer::LISTENERS.map do |io|
       Unicorn::SocketHelper.sock_name(io)
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index e8b76f5..c3a4f2d 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -3,11 +3,11 @@
 
 # Implements a simple DSL for configuring a unicorn server.
 #
-# See https://bogomips.org/unicorn/examples/unicorn.conf.rb and
-# https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
+# See https://yhbt.net/unicorn/examples/unicorn.conf.rb and
+# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
 # example configuration files.  An example config file for use with
 # nginx is also available at
-# https://bogomips.org/unicorn/examples/nginx.conf
+# https://yhbt.net/unicorn/examples/nginx.conf
 #
 # See the link:/TUNING.html document for more information on tuning unicorn.
 class Unicorn::Configurator
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 5334fa0..a52931a 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -6,7 +6,7 @@
 # forked worker children.
 #
 # Users do not need to know the internals of this class, but reading the
-# {source}[https://bogomips.org/unicorn.git/tree/lib/unicorn/http_server.rb]
+# {source}[https://yhbt.net/unicorn.git/tree/lib/unicorn/http_server.rb]
 # is education for programmers wishing to learn how unicorn works.
 # See Unicorn::Configurator for information on how to configure unicorn.
 class Unicorn::HttpServer
diff --git a/lib/unicorn/oob_gc.rb b/lib/unicorn/oob_gc.rb
index c4741a0..3b2f488 100644
--- a/lib/unicorn/oob_gc.rb
+++ b/lib/unicorn/oob_gc.rb
@@ -43,8 +43,8 @@
 #     use Unicorn::OobGC, 2, %r{\A/(?:expensive/foo|more_expensive/foo)}
 #
 # Feedback from users of early implementations of this module:
-# * https://bogomips.org/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
-# * https://bogomips.org/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
+# * https://yhbt.net/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
+# * https://yhbt.net/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
 
 module Unicorn::OobGC
 
diff --git a/unicorn.gemspec b/unicorn.gemspec
index ceea831..a189f8d 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -15,14 +15,14 @@
   s.authors = ['unicorn hackers']
   s.summary = 'Rack HTTP server for fast clients and Unix'
   s.description = File.read('README').split("\n\n")[1]
-  s.email = %q{unicorn-public@bogomips.org}
+  s.email = %q{unicorn-public@yhbt.net}
   s.executables = %w(unicorn unicorn_rails)
   s.extensions = %w(ext/unicorn_http/extconf.rb)
   s.extra_rdoc_files = IO.readlines('.document').map!(&:chomp!).keep_if do |f|
     File.exist?(f)
   end
   s.files = manifest
-  s.homepage = 'https://bogomips.org/unicorn/'
+  s.homepage = 'https://yhbt.net/unicorn/'
   s.test_files = test_files
 
   # technically we need ">= 1.9.3", too, but avoid the array here since

^ permalink raw reply related	[relevance 1%]

* [PATCH 2/3] http: use gperf for common fields optimization
  @ 2019-07-04 22:01  6% ` Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2019-07-04 22:01 UTC (permalink / raw)
  To: unicorn-public

GNU gperf is a commonly-used tool for generating perfect hashes
and available on every platform unicorn runs on.  C Ruby, gcc,
glibc all already use it.

Using a hash lookup instead of a linear scan already shows
measurable improvements when memoized header keys are all
used:

* test/benchmark/http_parser.rb (no options):

   100000 iterations
         user     system      total        real
  -  0.411857   0.000200   0.412057 (  0.412070)
  +  0.397960   0.000181   0.398141 (  0.398149)

Results which require generating a new string from an unmemoized
header is less significant, but still consistent measurable:

* test/benchmark/http_parser.rb -H 'DNT: 1'

   100000 iterations
         user     system      total        real
  -  0.461416   0.000000   0.461416 (  0.461417)
  +  0.461329   0.000000   0.461329 (  0.461363)

Most importantly, this change allows us to memoize more keys
without worrying too much about the overhead of a O(n) scan.
---
 .gitignore                                   |  1 +
 GNUmakefile                                  | 18 ++++-
 ext/unicorn_http/common_field_optimization.h | 79 +++++---------------
 ext/unicorn_http/common_fields.gperf         | 56 ++++++++++++++
 ext/unicorn_http/gperf.rb                    | 27 +++++++
 5 files changed, 117 insertions(+), 64 deletions(-)
 create mode 100644 ext/unicorn_http/common_fields.gperf
 create mode 100644 ext/unicorn_http/gperf.rb

diff --git a/.gitignore b/.gitignore
index ad92808..05e5e45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
 /test/ruby-*
 ext/unicorn_http/Makefile
 ext/unicorn_http/unicorn_http.c
+ext/unicorn_http/common_fields.h
 log/
 pkg/
 /vendor
diff --git a/GNUmakefile b/GNUmakefile
index a7e4102..3dbad2c 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -10,6 +10,7 @@ RAGEL = ragel
 RSYNC = rsync
 OLDDOC = olddoc
 RDOC = rdoc
+GPERF = gperf
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
 	@./GIT-VERSION-GEN
@@ -40,7 +41,9 @@ T_n_log := $(subst .n,$(log_suffix),$(T_n))
 test_prefix = $(CURDIR)/test/$(RUBY_ENGINE)-$(RUBY_VERSION)
 
 ext := ext/unicorn_http
-c_files := $(ext)/unicorn_http.c $(ext)/httpdate.c $(wildcard $(ext)/*.h)
+c_files := $(addprefix $(ext)/, unicorn_http.c common_fields.h \
+	httpdate.c common_field_optimization.h ext_help.h \
+	global_variables.h)
 rl_files := $(wildcard $(ext)/*.rl)
 base_bins := unicorn unicorn_rails
 bins := $(addprefix bin/, $(base_bins))
@@ -48,7 +51,14 @@ man1_rdoc := $(addsuffix _1, $(base_bins))
 man1_bins := $(addsuffix .1, $(base_bins))
 man1_paths := $(addprefix man/man1/, $(man1_bins))
 rb_files := $(bins) $(shell find lib ext -type f -name '*.rb')
-inst_deps := $(c_files) $(rb_files) GNUmakefile test/test_helper.rb
+inst_deps := $(c_files) $(rb_files) GNUmakefile test/test_helper.rb \
+		$(ext)/common_fields.gperf
+
+gperf :: $(ext)/common_fields.h
+$(ext)/common_fields.h : $(ext)/common_fields.gperf $(ext)/gperf.rb
+	$(GPERF) $< >$@-
+	$(MRI) --disable-gems $(ext)/gperf.rb <$@- >$@+
+	test -s $@+ && mv $@+ $@ && $(RM) $@-
 
 ragel: $(ext)/unicorn_http.c
 $(ext)/unicorn_http.c: $(rl_files)
@@ -159,12 +169,12 @@ man html:
 	$(MAKE) -C Documentation install-$@
 
 pkg_extra := GIT-VERSION-FILE lib/unicorn/version.rb LATEST NEWS \
-             $(ext)/unicorn_http.c $(man1_paths)
+             $(ext)/unicorn_http.c $(ext)/common_fields.h $(man1_paths)
 
 NEWS:
 	$(OLDDOC) prepare
 
-.manifest: $(ext)/unicorn_http.c man NEWS
+.manifest: $(ext)/unicorn_http.c $(ext)/common_fields.h man NEWS
 	(git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
 	  LC_ALL=C sort > $@+
 	cmp $@+ $@ || mv $@+ $@
diff --git a/ext/unicorn_http/common_field_optimization.h b/ext/unicorn_http/common_field_optimization.h
index 0659fc7..f69b618 100644
--- a/ext/unicorn_http/common_field_optimization.h
+++ b/ext/unicorn_http/common_field_optimization.h
@@ -3,58 +3,12 @@
 #include "ruby.h"
 #include "c_util.h"
 
-struct common_field {
-  const signed long len;
-  const char *name;
-  VALUE value;
-};
-
 /*
  * A list of common HTTP headers we expect to receive.
  * This allows us to avoid repeatedly creating identical string
  * objects to be used with rb_hash_aset().
  */
-static struct common_field common_http_fields[] = {
-# define f(N) { (sizeof(N) - 1), N, Qnil }
-  f("ACCEPT"),
-  f("ACCEPT_CHARSET"),
-  f("ACCEPT_ENCODING"),
-  f("ACCEPT_LANGUAGE"),
-  f("ALLOW"),
-  f("AUTHORIZATION"),
-  f("CACHE_CONTROL"),
-  f("CONNECTION"),
-  f("CONTENT_ENCODING"),
-  f("CONTENT_LENGTH"),
-  f("CONTENT_TYPE"),
-  f("COOKIE"),
-  f("DATE"),
-  f("EXPECT"),
-  f("FROM"),
-  f("HOST"),
-  f("IF_MATCH"),
-  f("IF_MODIFIED_SINCE"),
-  f("IF_NONE_MATCH"),
-  f("IF_RANGE"),
-  f("IF_UNMODIFIED_SINCE"),
-  f("KEEP_ALIVE"), /* Firefox sends this */
-  f("MAX_FORWARDS"),
-  f("PRAGMA"),
-  f("PROXY_AUTHORIZATION"),
-  f("RANGE"),
-  f("REFERER"),
-  f("TE"),
-  f("TRAILER"),
-  f("TRANSFER_ENCODING"),
-  f("UPGRADE"),
-  f("USER_AGENT"),
-  f("VIA"),
-  f("X_FORWARDED_FOR"), /* common for proxies */
-  f("X_FORWARDED_PROTO"), /* common for proxies */
-  f("X_REAL_IP"), /* common for proxies */
-  f("WARNING")
-# undef f
-};
+#include "common_fields.h"
 
 #define HTTP_PREFIX "HTTP_"
 #define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
@@ -79,21 +33,27 @@ static VALUE str_new_dd_freeze(const char *ptr, long len)
 /* this function is not performance-critical, called only at load time */
 static void init_common_fields(void)
 {
-  int i;
-  struct common_field *cf = common_http_fields;
+  size_t i;
   char tmp[64];
 
   id_uminus = rb_intern("-@");
   memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
 
-  for(i = ARRAY_SIZE(common_http_fields); --i >= 0; cf++) {
+  for (i = 0; i < ARRAY_SIZE(cf_wordlist); i++) {
+    long len = (long)cf_lengthtable[i];
+    struct common_field *cf = &cf_wordlist[i];
+    const char *s;
+
+    if (!len)
+      continue;
+
+    s = cf->name + cf_stringpool;
     /* Rack doesn't like certain headers prefixed with "HTTP_" */
-    if (!strcmp("CONTENT_LENGTH", cf->name) ||
-        !strcmp("CONTENT_TYPE", cf->name)) {
-      cf->value = str_new_dd_freeze(cf->name, cf->len);
+    if (!strcmp("CONTENT_LENGTH", s) || !strcmp("CONTENT_TYPE", s)) {
+      cf->value = str_new_dd_freeze(s, len);
     } else {
-      memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
-      cf->value = str_new_dd_freeze(tmp, HTTP_PREFIX_LEN + cf->len);
+      memcpy(tmp + HTTP_PREFIX_LEN, s, len + 1);
+      cf->value = str_new_dd_freeze(tmp, HTTP_PREFIX_LEN + len);
     }
     rb_gc_register_mark_object(cf->value);
   }
@@ -102,12 +62,11 @@ static void init_common_fields(void)
 /* this function is called for every header set */
 static VALUE find_common_field(const char *field, size_t flen)
 {
-  int i;
-  struct common_field *cf = common_http_fields;
+  struct common_field *cf = cf_lookup(field, flen);
 
-  for(i = ARRAY_SIZE(common_http_fields); --i >= 0; cf++) {
-    if (cf->len == (long)flen && !memcmp(cf->name, field, flen))
-      return cf->value;
+  if (cf) {
+    assert(cf->value);
+    return cf->value;
   }
   return Qnil;
 }
diff --git a/ext/unicorn_http/common_fields.gperf b/ext/unicorn_http/common_fields.gperf
new file mode 100644
index 0000000..179afe5
--- /dev/null
+++ b/ext/unicorn_http/common_fields.gperf
@@ -0,0 +1,56 @@
+%{
+#include <ruby.h>
+%}
+%compare-lengths
+%enum
+%global-table
+%language=ANSI-C
+%pic
+%struct-type
+%define hash-function-name cf_hash
+%define length-table-name cf_lengthtable
+%define lookup-function-name cf_lookup
+%define string-pool-name cf_stringpool
+%define word-array-name cf_wordlist
+
+struct common_field { size_t name; VALUE value; };
+%%
+ACCEPT
+ACCEPT_CHARSET
+ACCEPT_ENCODING
+ACCEPT_LANGUAGE
+ALLOW
+AUTHORIZATION
+CACHE_CONTROL
+CONNECTION
+CONTENT_ENCODING
+CONTENT_LENGTH
+CONTENT_TYPE
+COOKIE
+DATE
+EXPECT
+FROM
+HOST
+IF_MATCH
+IF_MODIFIED_SINCE
+IF_NONE_MATCH
+IF_RANGE
+IF_UNMODIFIED_SINCE
+# Firefox sends Keep-Alive (or maybe only old versions?)
+KEEP_ALIVE
+MAX_FORWARDS
+PRAGMA
+PROXY_AUTHORIZATION
+RANGE
+REFERER
+TE
+TRAILER
+TRANSFER_ENCODING
+UPGRADE
+USER_AGENT
+VIA
+# common proxies set some of these X- headers
+X_FORWARDED_FOR
+X_FORWARDED_PROTO
+X_REAL_IP
+WARNING
diff --git a/ext/unicorn_http/gperf.rb b/ext/unicorn_http/gperf.rb
new file mode 100644
index 0000000..9765f86
--- /dev/null
+++ b/ext/unicorn_http/gperf.rb
@@ -0,0 +1,27 @@
+#!/usr/bin/ruby -w
+buf = STDIN.read # output of: gperf ext/unicorn_http/common_fields.gperf
+
+# this is supposed to fail if it doesn't subsitute anything:
+print buf.sub!(
+
+# make sure all functions are static
+/\nstruct \w+ \*\n(\w+_)?lookup/) {
+  "\nstatic#$&"
+}.
+
+gsub!(
+# gperf 3.0.x used "(int)(long)", 3.1 uses "(int)(size_t)",
+#  input: {(int)(size_t)&((struct cf_pool_t *)0)->cf_pool_str3},
+# output: {offsetof(struct cf_pool_t, cf_pool_str3)},
+/{\(int\)\(\w+\)\&\(\((struct \w+) *\*\)0\)->(\w+)}/) {
+  "{offsetof(#$1, #$2)}"
+}.
+
+# make sure everything is 64-bit safe and compilers don't truncate
+gsub!(/\b(?:unsigned )?int\b/, 'size_t').
+
+# This isn't need for %switch%, but we'll experiment with to see
+# if it's necessary, or not.
+# don't give compilers a reason to complain, (struct foo *)->name
+# is size_t, so unused slots should be size_t:
+gsub(/\{-1\}/, '{(size_t)-1}')
-- 
EW


^ permalink raw reply related	[relevance 6%]

* [PATCH] gemspec: remove olddoc from build dependency
  @ 2017-03-23 23:34 21%       ` Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2017-03-23 23:34 UTC (permalink / raw)
  To: Pirate Praveen; +Cc: Hleb Valoshka, debian-ruby, unicorn-public

Pirate Praveen <praveen@onenetbeyond.org> wrote:
> If you can make it optional that would be great. For now I've just
> patched out the gemspec to not use olddoc. The patch and fix in rules is
> ugly.

Pushed the following to "master" of git://bogomips.org/unicorn

-----8<----
Subject: [PATCH] gemspec: remove olddoc from build dependency

It's a little less DRY, and there'll be no NEWS file generated,
but it's one less thing to install, so perhaps that's worth it.
The website at https://bogomips.org/unicorn/ will continue
to use olddoc, of course,
---
 HACKING         |  1 -
 unicorn.gemspec | 22 ++++++++++------------
 2 files changed, 10 insertions(+), 13 deletions(-)

diff --git a/HACKING b/HACKING
index d55f1c7..be1bb85 100644
--- a/HACKING
+++ b/HACKING
@@ -104,7 +104,6 @@ don't email the git mailing list or maintainer with Unicorn patches :)
 
 In order to build the gem, you must install the following components:
 
- * olddoc (RubyGem)
  * pandoc
 
 You can build the Unicorn gem with the following command:
diff --git a/unicorn.gemspec b/unicorn.gemspec
index cf65aef..6dc0086 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -1,9 +1,6 @@
 # -*- encoding: binary -*-
-ENV["VERSION"] or abort "VERSION= must be specified"
-manifest = File.readlines('.manifest').map! { |x| x.chomp! }
-require 'olddoc'
-extend Olddoc::Gemspec
-name, summary, title = readme_metadata
+manifest = File.exist?('.manifest') ?
+  IO.readlines('.manifest').map!(&:chomp!) : `git ls-files`.split("\n")
 
 # don't bother with tests that fork, not worth our time to get working
 # with `gem check -t` ... (of course we care for them when testing with
@@ -14,16 +11,18 @@
 
 Gem::Specification.new do |s|
   s.name = %q{unicorn}
-  s.version = ENV["VERSION"].dup
-  s.authors = ["#{name} hackers"]
-  s.summary = summary
-  s.description = readme_description
+  s.version = (ENV['VERSION'] || '5.2.0').dup
+  s.authors = ['unicorn hackers']
+  s.summary = 'Rack HTTP server for fast clients and Unix'
+  s.description = File.read('README').split("\n\n")[1]
   s.email = %q{unicorn-public@bogomips.org}
   s.executables = %w(unicorn unicorn_rails)
   s.extensions = %w(ext/unicorn_http/extconf.rb)
-  s.extra_rdoc_files = extra_rdoc_files(manifest)
+  s.extra_rdoc_files = IO.readlines('.document').map!(&:chomp!).keep_if do |f|
+    File.exist?(f)
+  end
   s.files = manifest
-  s.homepage = Olddoc.config['rdoc_url']
+  s.homepage = 'https://bogomips.org/unicorn/'
   s.test_files = test_files
 
   # technically we need ">= 1.9.3", too, but avoid the array here since
@@ -40,7 +39,6 @@
   s.add_dependency(%q<raindrops>, '~> 0.7')
 
   s.add_development_dependency('test-unit', '~> 3.0')
-  s.add_development_dependency('olddoc', '~> 1.2')
 
   # Note: To avoid ambiguity, we intentionally avoid the SPDX-compatible
   # 'Ruby' here since Ruby 1.9.3 switched to BSD-2-Clause, but we
-- 
EW

^ permalink raw reply related	[relevance 21%]

* [PATCH 2/3] gemspec: relax Ruby version requirement for old RubyGems
  @ 2015-11-01  8:37  5% ` Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2015-11-01  8:37 UTC (permalink / raw)
  To: unicorn-public; +Cc: Eric Wong

Older RubyGems (1.8.23.2 at least) does not seem to support
multiple version requirements for the Ruby version; so drop
the lower 1.9.3 requirement for now.
---
 unicorn.gemspec | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/unicorn.gemspec b/unicorn.gemspec
index 23450f5..1099361 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -25,7 +25,11 @@ Gem::Specification.new do |s|
   s.files = manifest
   s.homepage = Olddoc.config['rdoc_url']
   s.test_files = test_files
-  s.required_ruby_version = [ '>= 1.9.3', '< 3.0' ]
+
+  # technically we need ">= 1.9.3", too, but avoid the array here since
+  # old rubygems versions (1.8.23.2 at least) do not support multiple
+  # version requirements here.
+  s.required_ruby_version = '< 3.0'
 
   # for people that are absolutely stuck on Rails 2.3.2 and can't
   # up/downgrade to any other version, the Rack dependency may be
@@ -38,5 +42,9 @@ Gem::Specification.new do |s|
   s.add_development_dependency('test-unit', '~> 3.0')
   s.add_development_dependency('olddoc', '~> 1.0')
 
-  s.licenses = ["GPLv2+", "Ruby 1.8"]
+  # Note: To avoid ambiguity, we intentionally avoid the SPDX-compatible
+  # 'Ruby' here since Ruby 1.9.3 switched to BSD-2-Clause, but we
+  # inherited our license from Mongrel when Ruby was at 1.8.
+  # We cannot automatically switch licenses when Ruby changes.
+  s.licenses = ['GPL-2.0+', 'Ruby-1.8']
 end
-- 
EW


^ permalink raw reply related	[relevance 5%]

* [PATCH] gemspec: limit to 1.9.3 and 2.x
@ 2015-08-22  4:42  6% Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2015-08-22  4:42 UTC (permalink / raw)
  To: unicorn-public

It does not look like we'll be compatible with Ruby 3.0 with
the plan for immutable string literals.

However, keep in mind 3.0 is still many years away and
decisions can change, so it would be premature to stop
assuming frozen string literals this year.

ref: https://bugs.ruby-lang.org/issues/11473
---
 unicorn.gemspec | 1 +
 1 file changed, 1 insertion(+)

diff --git a/unicorn.gemspec b/unicorn.gemspec
index 47d5670..23450f5 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
   s.files = manifest
   s.homepage = Olddoc.config['rdoc_url']
   s.test_files = test_files
+  s.required_ruby_version = [ '>= 1.9.3', '< 3.0' ]
 
   # for people that are absolutely stuck on Rails 2.3.2 and can't
   # up/downgrade to any other version, the Rack dependency may be
-- 
EW


^ permalink raw reply related	[relevance 6%]

* [PATCH] GNUmakefile: fix clean gem build + reduce build cruft
@ 2015-02-04 20:16 11% Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2015-02-04 20:16 UTC (permalink / raw)
  To: unicorn-public

Ensure we have a NEWS file for building the gem beforehand.
We don't need to polute lib/ with object files, either.
---
 GNUmakefile | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/GNUmakefile b/GNUmakefile
index 4c40dc9..8bd63ee 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -57,10 +57,7 @@ $(ext)/Makefile: $(ext)/extconf.rb $(c_files)
 	cd $(@D) && $(RUBY) extconf.rb
 $(ext)/unicorn_http.$(DLEXT): $(ext)/Makefile
 	$(MAKE) -C $(@D)
-lib/unicorn_http.$(DLEXT): $(ext)/unicorn_http.$(DLEXT)
-	@mkdir -p lib
-	install -m644 $< $@
-http: lib/unicorn_http.$(DLEXT)
+http: $(ext)/unicorn_http.$(DLEXT)
 
 test-install: $(test_prefix)/.stamp
 $(test_prefix)/.stamp: $(inst_deps)
@@ -85,6 +82,7 @@ test-unit: $(wildcard test/unit/test_*.rb)
 $(slow_tests): $(test_prefix)/.stamp
 	@$(MAKE) $(shell $(awk_slow) $@)
 
+# ensure we can require just the HTTP parser without the rest of unicorn
 test-require: $(ext)/unicorn_http.$(DLEXT)
 	$(RUBY) --disable-gems -I$(ext) -runicorn_http -e Unicorn
 
@@ -134,7 +132,6 @@ $(T): $(test_prefix)/.stamp
 
 install: $(bins) $(ext)/unicorn_http.c
 	$(prep_setup_rb)
-	$(RM) lib/unicorn_http.$(DLEXT)
 	$(RM) -r .install-tmp
 	mkdir .install-tmp
 	cp -p bin/* .install-tmp
@@ -150,7 +147,7 @@ prep_setup_rb := @-$(RM) $(setup_rb_files);$(MAKE) -C $(ext) clean
 clean:
 	-$(MAKE) -C $(ext) clean
 	-$(MAKE) -C Documentation clean
-	$(RM) $(ext)/Makefile lib/unicorn_http.$(DLEXT)
+	$(RM) $(ext)/Makefile
 	$(RM) $(setup_rb_files) $(t_log)
 	$(RM) -r $(test_prefix) man
 
@@ -160,7 +157,10 @@ man html:
 pkg_extra := GIT-VERSION-FILE lib/unicorn/version.rb LATEST NEWS \
              $(ext)/unicorn_http.c $(man1_paths)
 
-.manifest: $(ext)/unicorn_http.c man
+NEWS:
+	$(OLDDOC) prepare
+
+.manifest: $(ext)/unicorn_http.c man NEWS
 	(git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
 	  LC_ALL=C sort > $@+
 	cmp $@+ $@ || mv $@+ $@
-- 
EW


^ permalink raw reply related	[relevance 11%]

* [PATCH 1/4] remove RubyForge and Freecode references
  @ 2014-09-22  1:05 11% ` Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2014-09-22  1:05 UTC (permalink / raw)
  To: unicorn-public; +Cc: Eric Wong

Both sites are gone.
---
 GNUmakefile     |  7 -------
 Rakefile        | 44 --------------------------------------------
 unicorn.gemspec |  1 -
 3 files changed, 52 deletions(-)

diff --git a/GNUmakefile b/GNUmakefile
index 50819fc..6916b6e 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -245,15 +245,8 @@ $(pkgtgz): .manifest fix-perms
 package: $(pkgtgz) $(pkggem)
 
 release: verify package $(release_notes) $(release_changes)
-	# make tgz release on RubyForge
-	rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
-	  $(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
 	# push gem to Gemcutter
 	gem push $(pkggem)
-	# in case of gem downloads from RubyForge releases page
-	-rubyforge add_file \
-	  $(rfproject) $(rfpackage) $(VERSION) $(pkggem)
-	$(RAKE) fm_update VERSION=$(VERSION)
 else
 gem install-gem: GIT-VERSION-FILE
 	$(MAKE) $@ VERSION=$(GIT_VERSION)
diff --git a/Rakefile b/Rakefile
index 01ff5d0..37569ce 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,47 +1,3 @@
-# -*- encoding: binary -*-
-autoload :Gem, 'rubygems'
-require 'wrongdoc'
-
-cgit_url = Wrongdoc.config[:cgit_url]
-git_url = Wrongdoc.config[:git_url]
-
-desc "post to FM"
-task :fm_update do
-  require 'tempfile'
-  require 'net/http'
-  require 'net/netrc'
-  require 'json'
-  version = ENV['VERSION'] or abort "VERSION= needed"
-  uri = URI.parse('https://freecode.com/projects/unicorn/releases.json')
-  rc = Net::Netrc.locate('unicorn-fm') or abort "~/.netrc not found"
-  api_token = rc.password
-  _, subject, body = `git cat-file tag v#{version}`.split(/\n\n/, 3)
-  tmp = Tempfile.new('fm-changelog')
-  tmp.puts subject
-  tmp.puts
-  tmp.puts body
-  tmp.flush
-  system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?"
-  changelog = File.read(tmp.path).strip
-
-  req = {
-    "auth_code" => api_token,
-    "release" => {
-      "tag_list" => "Experimental",
-      "version" => version,
-      "changelog" => changelog,
-    },
-  }.to_json
-
-  if ! changelog.strip.empty? && version =~ %r{\A[\d\.]+\d+\z}
-    Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
-      p http.post(uri.path, req, {'Content-Type'=>'application/json'})
-    end
-  else
-    warn "not updating freshmeat for v#{version}"
-  end
-end
-
 # optional rake-compiler support in case somebody needs to cross compile
 begin
   mk = "ext/unicorn_http/Makefile"
diff --git a/unicorn.gemspec b/unicorn.gemspec
index 9456db2..8623d44 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -26,7 +26,6 @@ Gem::Specification.new do |s|
   s.files = manifest
   s.homepage = Wrongdoc.config[:rdoc_url]
   s.rdoc_options = rdoc_options
-  s.rubyforge_project = %q{mongrel}
   s.test_files = test_files
 
   # for people that are absolutely stuck on Rails 2.3.2 and can't
-- 
EW


^ permalink raw reply related	[relevance 11%]

* Re: High number of TCP retransmits on Solaris
  2014-07-22 20:56  4% ` Eric Wong
@ 2014-07-22 21:56  0%   ` Paul Henry
  0 siblings, 0 replies; 25+ results
From: Paul Henry @ 2014-07-22 21:56 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Thanks so much! It turned out to be a separate issue entirely, one that
appears only in Solaris. When you have a high rate of connections, sockets
sit around in the TIME_WAIT state for a default of 60s. Basically, there's
a bug in Solaris that causes a new connection to be routed (based on what
chooses the ephemeral port) to an existing TIME_WAIT connection. When the
new client sends a SYN, the server responds with an ACK that has an
incorrect sequence number (intended for the old client). This causes the
client to retransmit the SYN and then the server responds correctly.

You can read more about this issue in detail here:
http://lists.illumos.org/pipermail/developer/2011-April/001958.html

Cheers,
Paul


On Tue, Jul 22, 2014 at 1:56 PM, Eric Wong <e@80x24.org> wrote:

> Paul Henry <paul@wanelo.com> wrote:
> > Hello!
> >
> > When using unicorn unicorn v4.8.2, we're seeing a high number of TCP
> > retransmits at a high connection rate.
> >
> > Smartos version: 20131218T184706Z
> >
> > Rackup file used:
> >
> > > cat server.ru
> > run -> (env) { [200, {"Content-Type" => "text/plain"},
> > [Time.now.iso8601(6)]] }
> >
> > To start unicorn:
> > > bundle exec unicorn -l 9292 server.ru
> >
> > To benchmark unicorn:
> > >> Benchmark.measure { 1000.times { time=Benchmark.measure {
> open("http://<ip>:9292/api/v1/settings/time",
> > "Host" => "api.wanelo.com")}.real; p (time*1000).round(2) if time >
> 0.05 } }
>
> Thanks for including a small example to reproduce the problem on your
> end.  Is that open call is from "open-uri" in the stdlib?
> (does it attempt persistent connections?)
>
> > After the total number of connections on the system goes above 8,000
> > (16,000 is the average number of connections), we start seeing delays of
> > around 1.1 seconds.
>
> I was not able to reproduce the issue with two Linux machines over
> my (pretty bad) 100Mbps LAN.
>
> I used the script at the bottom to create a lot of client connections
> from my client machine to the machine running unicorn (but this is not
> connecting to unicorn, but a scalable server (e.g. nginx) with infinite
> persistence).
>
> > We don't see the issue over the local loopback interface, only over the
> > net. When using Webrick, we also don't see this issue.
>
> What's strange is the issue does not manifest under Webrick for you.
> Which version of Ruby is that Webrick from?
>
> strace-ing "rackup -s webrick server.ru" reveals several differences:
>
> 1) webrick uses a listen backlog of only 128 (unicorn uses 1024)
> 2) webrick does not disable Nagle's algorithm.
> 3) webrick does not set SO_KEEPALIVE (not really needed for unicorn)
>
> So perhaps you can try config like the following to more closely match
> what webrick does:
>
>         listen 9292, backlog: 128, tcp_nodelay: false
>
> On the other hand, maybe webrick is too slow.
>
> > Our tcp initial retransmit interval is 1 second. When the interval is
> > reduced, the occasional latency goes down. We also see the retransmits in
> > netstat, about 1 - 2 every second.
> >
> > Anything that we should look at next?
>
> Try the above config to minimize the differences between webrick
> and unicorn.
>
> If that fails, perhaps disabling SO_KEEPALIVE will work, but I'm
> a bit lost as I'm not familiar with SmartOS quirks.
> (you'll need to comment it out in lib/unicorn/socket_helper.rb)
>
> Maybe try a little mock server like this, too (should be fastest :)
> ---------------------------- hello_world.rb ----------------
> require 'socket'
> s = TCPServer.new(host, port)
> # start changing knobs here:
> # s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
> # s.setsockopt(:IPPROTOL_TCP, :TCP_NODELAY, 1)
> res = "HTTP/1.0 200 OK\r\nContent-Length: 12\r\n\r\nhello world\n"
> junk = ""
> loop do
>   c = s.accept
>   c.readpartial(1024, junk)
>   c.write(res)
>   c.close
> end
> ----------------------------- many.rb --------------------------
> # opens a lot of idle connections, be careful :)
> require 'socket'
> pids = []
> host = '10.45.14.175'
> port = 7500 # not unicorn
> at_exit { pids.each { |pid| Process.kill(:TERM, pid) } }
>
> 24.times do
>   pid = fork do
>     keep = []
>     begin
>       s = TCPSocket.new(host, port)
>       # put something in the socket buffers
>       s.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
>       keep << s
>     rescue => e
>       $stdout.syswrite("#$$ done (#{keep.size}): #{e.message}\n")
>       sleep
>     end while true
>   end
>   pids << pid
> end
> p Process.waitall
> --
> EW
>


^ permalink raw reply	[relevance 0%]

* Re: High number of TCP retransmits on Solaris
  @ 2014-07-22 20:56  4% ` Eric Wong
  2014-07-22 21:56  0%   ` Paul Henry
  0 siblings, 1 reply; 25+ results
From: Eric Wong @ 2014-07-22 20:56 UTC (permalink / raw)
  To: Paul Henry; +Cc: unicorn-public

Paul Henry <paul@wanelo.com> wrote:
> Hello!
> 
> When using unicorn unicorn v4.8.2, we're seeing a high number of TCP
> retransmits at a high connection rate.
> 
> Smartos version: 20131218T184706Z
> 
> Rackup file used:
> 
> > cat server.ru
> run -> (env) { [200, {"Content-Type" => "text/plain"},
> [Time.now.iso8601(6)]] }
> 
> To start unicorn:
> > bundle exec unicorn -l 9292 server.ru
> 
> To benchmark unicorn:
> >> Benchmark.measure { 1000.times { time=Benchmark.measure { open("http://<ip>:9292/api/v1/settings/time",
> "Host" => "api.wanelo.com")}.real; p (time*1000).round(2) if time > 0.05 } }

Thanks for including a small example to reproduce the problem on your
end.  Is that open call is from "open-uri" in the stdlib?
(does it attempt persistent connections?)

> After the total number of connections on the system goes above 8,000
> (16,000 is the average number of connections), we start seeing delays of
> around 1.1 seconds.

I was not able to reproduce the issue with two Linux machines over
my (pretty bad) 100Mbps LAN.

I used the script at the bottom to create a lot of client connections
from my client machine to the machine running unicorn (but this is not
connecting to unicorn, but a scalable server (e.g. nginx) with infinite
persistence).

> We don't see the issue over the local loopback interface, only over the
> net. When using Webrick, we also don't see this issue.

What's strange is the issue does not manifest under Webrick for you.
Which version of Ruby is that Webrick from?

strace-ing "rackup -s webrick server.ru" reveals several differences:

1) webrick uses a listen backlog of only 128 (unicorn uses 1024)
2) webrick does not disable Nagle's algorithm.
3) webrick does not set SO_KEEPALIVE (not really needed for unicorn)

So perhaps you can try config like the following to more closely match
what webrick does:

	listen 9292, backlog: 128, tcp_nodelay: false

On the other hand, maybe webrick is too slow.

> Our tcp initial retransmit interval is 1 second. When the interval is
> reduced, the occasional latency goes down. We also see the retransmits in
> netstat, about 1 - 2 every second.
> 
> Anything that we should look at next?

Try the above config to minimize the differences between webrick
and unicorn.

If that fails, perhaps disabling SO_KEEPALIVE will work, but I'm
a bit lost as I'm not familiar with SmartOS quirks.
(you'll need to comment it out in lib/unicorn/socket_helper.rb)

Maybe try a little mock server like this, too (should be fastest :)
---------------------------- hello_world.rb ----------------
require 'socket'
s = TCPServer.new(host, port)
# start changing knobs here:
# s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
# s.setsockopt(:IPPROTOL_TCP, :TCP_NODELAY, 1)
res = "HTTP/1.0 200 OK\r\nContent-Length: 12\r\n\r\nhello world\n"
junk = ""
loop do
  c = s.accept
  c.readpartial(1024, junk)
  c.write(res)
  c.close
end
----------------------------- many.rb --------------------------
# opens a lot of idle connections, be careful :)
require 'socket'
pids = []
host = '10.45.14.175'
port = 7500 # not unicorn
at_exit { pids.each { |pid| Process.kill(:TERM, pid) } }

24.times do
  pid = fork do
    keep = []
    begin
      s = TCPSocket.new(host, port)
      # put something in the socket buffers
      s.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
      keep << s
    rescue => e
      $stdout.syswrite("#$$ done (#{keep.size}): #{e.message}\n")
      sleep
    end while true
  end
  pids << pid
end
p Process.waitall
-- 
EW

^ permalink raw reply	[relevance 4%]

* Re: Combating nginx 499 HTTP responses during flash traffic scenario
  2012-11-06 21:23  0%                   ` Eric Wong
@ 2012-11-29 15:52  0%                     ` Tom Burns
  0 siblings, 0 replies; 25+ results
From: Tom Burns @ 2012-11-29 15:52 UTC (permalink / raw)
  To: unicorn list

On Tue, Nov 6, 2012 at 4:23 PM, Eric Wong <normalperson@yhbt.net> wrote:
>
> Tom Burns <tom.burns@jadedpixel.com> wrote:
> > On Mon, Nov 5, 2012 at 6:48 AM, Eric Wong <normalperson@yhbt.net> wrote:
> > > We can wait until you can report on how this change improves your
> > > situation before fleshing this out.  Real-world results are always
> > > good to have :>
> >
> > Agreed.
> >
> > We're going to be testing this this week so I should have some results
> > to share by the weekend.  We only experience the problem during major
> > sales so it may or may not manifest without our traffic generation
> > tool.
>
> Holidays are coming up, should be lots of traffic :)

So we just finished the US Black Friday / Cyber Monday weekend running
unicorn forked with the last version of the patch I had sent you.  It
worked splendidly and helped us handle huge flash sales without
increased response time over the weekend.

Whereas in previous flash traffic scenarios we would see the number of
HTTP 499 responses grow past the number of real HTTP 200 responses,
over the weekend we saw no growth in 499s during flash sales.

Unexpectedly the patch also helped us ward off a DoS attack where the
attackers were disconnecting immediately after making a request.

I've attached the patch again, with your last comments addressed.  Let
me know if there's anything else.

Thanks again for your help in this matter.

Cheers,
Tom

>From 89c8c51355c75b0196469812580443fba390632e Mon Sep 17 00:00:00 2001
From: Tom Burns <tom.burns@jadedpixel.com>
Date: Tue, 30 Oct 2012 16:22:21 -0400
Subject: [PATCH] Begin writing HTTP request headers early to detect
 disconnected clients

This patch checks incoming connections and avoids calling the application
if the connection has been closed.

It works by sending the beginning of the HTTP response before calling
the application to see if the socket can successfully be written to.

By enabling this feature users can avoid wasting application rendering
time only to find the connection is closed when attempting to write, and
throwing out the result.

When a client disconnects while being queued or processed, Nginx will log
HTTP response 499 but the application will log a 200.

Enabling this feature will minimize the time window during which the problem
can arise.

The feature is disabled by default and can be enabled by adding
'check_client_connection true' to the unicorn config.
---
 examples/unicorn.conf.rb         |    6 ++++++
 ext/unicorn_http/unicorn_http.rl |    4 +++-
 lib/unicorn/configurator.rb      |   25 +++++++++++++++++++++++++
 lib/unicorn/const.rb             |   12 ++++++++----
 lib/unicorn/http_request.rb      |   19 +++++++++++++++++++
 lib/unicorn/http_response.rb     |    5 +++--
 lib/unicorn/http_server.rb       |   22 ++++++++++++++++++++--
 test/exec/test_exec.rb           |    7 ++++++-
 test/unit/test_configurator.rb   |   24 ++++++++++++++++++++++++
 9 files changed, 114 insertions(+), 10 deletions(-)

diff --git a/examples/unicorn.conf.rb b/examples/unicorn.conf.rb
index 0238043..1f4c9c0 100644
--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -46,6 +46,12 @@ preload_app true
 GC.respond_to?(:copy_on_write_friendly=) and
   GC.copy_on_write_friendly = true

+# Enable this flag to have unicorn test client connections by writing the
+# beginning of the HTTP headers before calling the application.  This
+# prevents calling the application for connections that have disconnected
+# while queued.
+check_client_connection false
+
 before_fork do |server, worker|
   # the following is highly recomended for Rails + "preload_app true"
   # as there's no need for the master process to hold a connection
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 96dcf83..1a8003f 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -115,7 +115,7 @@ struct http_parser {
   } len;
 };

-static ID id_clear, id_set_backtrace;
+static ID id_clear, id_set_backtrace, id_response_start_sent;

 static void finalize_header(struct http_parser *hp);

@@ -626,6 +626,7 @@ static VALUE HttpParser_clear(VALUE self)

   http_parser_init(hp);
   rb_funcall(hp->env, id_clear, 0);
+  rb_ivar_set(self, id_response_start_sent, Qfalse);

   return self;
 }
@@ -1031,6 +1032,7 @@ void Init_unicorn_http(void)
   SET_GLOBAL(g_http_connection, "CONNECTION");
   id_clear = rb_intern("clear");
   id_set_backtrace = rb_intern("set_backtrace");
+  id_response_start_sent = rb_intern("@response_start_sent");
   init_unicorn_httpdate();
 }
 #undef SET_GLOBAL
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index 89cbf5c..9752cdd 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -45,6 +45,7 @@ class Unicorn::Configurator
       },
     :pid => nil,
     :preload_app => false,
+    :check_client_connection => false,
     :rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
     :client_body_buffer_size => Unicorn::Const::MAX_BODY,
     :trust_x_forwarded => true,
@@ -96,6 +97,18 @@ class Unicorn::Configurator
     if ready_pipe = RACKUP.delete(:ready_pipe)
       server.ready_pipe = ready_pipe
     end
+    if set[:check_client_connection]
+      set[:listeners].each do |address|
+        if set[:listener_opts][address][:tcp_nopush] == true
+          raise ArgumentError,
+            "check_client_connection is incompatible with tcp_nopush:true"
+        end
+        if set[:listener_opts][address][:tcp_nodelay] == true
+          raise ArgumentError,
+            "check_client_connection is incompatible with tcp_nodelay:true"
+        end
+      end
+    end
     set.each do |key, value|
       value == :unset and next
       skip.include?(key) and next
@@ -454,6 +467,18 @@ class Unicorn::Configurator
     set_int(:client_body_buffer_size, bytes, 0)
   end

+  # When enabled, unicorn will check the client connection by writing
+  # the beginning of the HTTP headers before calling the application.
+  #
+  # This will prevent calling the application for clients who have
+  # disconnected while their connection was queued.
+  #
+  # This option cannot be used in conjunction with tcp_nodelay or
+  # tcp_nopush.
+  def check_client_connection(bool)
+    set_bool(:check_client_connection, bool)
+  end
+
   # Allow redirecting $stderr to a given path.  Unlike doing this from
   # the shell, this allows the unicorn process to know the path its
   # writing to and rotate the file if it is used for logging.  The
diff --git a/lib/unicorn/const.rb b/lib/unicorn/const.rb
index b3d8d71..f0c4c12 100644
--- a/lib/unicorn/const.rb
+++ b/lib/unicorn/const.rb
@@ -29,12 +29,16 @@ module Unicorn::Const

   # :stopdoc:
   # common errors we'll send back
-  ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
-  ERROR_414_RESPONSE = "HTTP/1.1 414 Request-URI Too Long\r\n\r\n"
-  ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
-  ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
+  ERROR_400_RESPONSE = "400 Bad Request\r\n\r\n"
+  ERROR_414_RESPONSE = "414 Request-URI Too Long\r\n\r\n"
+  ERROR_413_RESPONSE = "413 Request Entity Too Large\r\n\r\n"
+  ERROR_500_RESPONSE = "500 Internal Server Error\r\n\r\n"
+
   EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
+  EXPECT_100_RESPONSE_SUFFIXED = "100 Continue\r\n\r\nHTTP/1.1 "

+  HTTP_RESPONSE_START = ['HTTP', '/1.1 ']
   HTTP_EXPECT = "HTTP_EXPECT"
+
   # :startdoc:
 end
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index a0435d6..79ead2e 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -22,11 +22,14 @@ class Unicorn::HttpParser

   NULL_IO = StringIO.new("")

+  attr_accessor :response_start_sent
+
   # :stopdoc:
   # A frozen format for this is about 15% faster
   REMOTE_ADDR = 'REMOTE_ADDR'.freeze
   RACK_INPUT = 'rack.input'.freeze
   @@input_class = Unicorn::TeeInput
+  @@check_client_connection = false

   def self.input_class
     @@input_class
@@ -35,6 +38,15 @@ class Unicorn::HttpParser
   def self.input_class=(klass)
     @@input_class = klass
   end
+
+  def self.check_client_connection
+    @@check_client_connection
+  end
+
+  def self.check_client_connection=(bool)
+    @@check_client_connection = bool
+  end
+
   # :startdoc:

   # Does the majority of the IO processing.  It has been written in
@@ -70,6 +82,13 @@ class Unicorn::HttpParser
       # an Exception thrown from the parser will throw us out of the loop
       false until add_parse(socket.kgio_read!(16384))
     end
+
+    # detect if the socket is valid by writing a partial response:
+    if @@check_client_connection && headers?
+      @response_start_sent = true
+      Unicorn::Const::HTTP_RESPONSE_START.each { |c| socket.write(c) }
+    end
+
     e[RACK_INPUT] = 0 == content_length ?
                     NULL_IO : @@input_class.new(socket, self)
     e.merge!(DEFAULTS)
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb
index b781e20..9c2bacc 100644
--- a/lib/unicorn/http_response.rb
+++ b/lib/unicorn/http_response.rb
@@ -18,11 +18,12 @@ module Unicorn::HttpResponse
   CRLF = "\r\n"

   # writes the rack_response to socket as an HTTP response
-  def http_response_write(socket, status, headers, body)
+  def http_response_write(socket, status, headers, body,
response_start_sent=false)
     status = CODES[status.to_i] || status

+    http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
     if headers
-      buf = "HTTP/1.1 #{status}\r\n" \
+      buf = "#{http_response_start}#{status}\r\n" \
             "Date: #{httpdate}\r\n" \
             "Status: #{status}\r\n" \
             "Connection: close\r\n"
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 13df55a..8729830 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -17,6 +17,7 @@ class Unicorn::HttpServer
                 :listener_opts, :preload_app,
                 :reexec_pid, :orig_app, :init_listeners,
                 :master_pid, :config, :ready_pipe, :user
+
   attr_reader :pid, :logger
   include Unicorn::SocketHelper
   include Unicorn::HttpResponse
@@ -355,6 +356,14 @@ class Unicorn::HttpServer
     Unicorn::HttpParser.trust_x_forwarded = bool
   end

+  def check_client_connection
+    Unicorn::HttpRequest.check_client_connection
+  end
+
+  def check_client_connection=(bool)
+    Unicorn::HttpRequest.check_client_connection = bool
+  end
+
   private

   # wait for a signal hander to wake us up and then consume the pipe
@@ -524,23 +533,32 @@ class Unicorn::HttpServer
       Unicorn.log_error(@logger, "app error", e)
       Unicorn::Const::ERROR_500_RESPONSE
     end
+    msg = "HTTP/1.1 #{msg}" unless @request.response_start_sent
     client.kgio_trywrite(msg)
     client.close
     rescue
   end

+  def expect_100_response
+    if @request.response_start_sent
+      Unicorn::Const::EXPECT_100_RESPONSE_SUFFIXED
+    else
+      Unicorn::Const::EXPECT_100_RESPONSE
+    end
+  end
+
   # once a client is accepted, it is processed in its entirety here
   # in 3 easy steps: read request, call app, write app response
   def process_client(client)
     status, headers, body = @app.call(env = @request.read(client))

     if 100 == status.to_i
-      client.write(Unicorn::Const::EXPECT_100_RESPONSE)
+      client.write(expect_100_response)
       env.delete(Unicorn::Const::HTTP_EXPECT)
       status, headers, body = @app.call(env)
     end
     @request.headers? or headers = nil
-    http_response_write(client, status, headers, body)
+    http_response_write(client, status, headers, body,
@request.response_start_sent)
     client.shutdown # in case of fork() in Rack app
     client.close # flush and uncork socket immediately, no keepalive
   rescue => e
diff --git a/test/exec/test_exec.rb b/test/exec/test_exec.rb
index b30a3d6..1cee2b7 100644
--- a/test/exec/test_exec.rb
+++ b/test/exec/test_exec.rb
@@ -871,13 +871,14 @@ EOF
     wait_for_death(pid)
   end

-  def hup_test_common(preload)
+  def hup_test_common(preload, check_client=false)
     File.open("config.ru", "wb") { |fp| fp.syswrite(HI.gsub("HI", '#$$')) }
     pid_file = Tempfile.new('pid')
     ucfg = Tempfile.new('unicorn_test_config')
     ucfg.syswrite("listen '#@addr:#@port'\n")
     ucfg.syswrite("pid '#{pid_file.path}'\n")
     ucfg.syswrite("preload_app true\n") if preload
+    ucfg.syswrite("check_client_connection true\n") if check_client
     ucfg.syswrite("stderr_path 'test_stderr.#$$.log'\n")
     ucfg.syswrite("stdout_path 'test_stdout.#$$.log'\n")
     pid = xfork {
@@ -942,6 +943,10 @@ EOF
     hup_test_common(false)
   end

+  def test_check_client_hup
+    hup_test_common(false, true)
+  end
+
   def test_default_listen_hup_holds_listener
     default_listen_lock do
       res, pid_path = default_listen_setup
diff --git a/test/unit/test_configurator.rb b/test/unit/test_configurator.rb
index c19c427..b9c22d7 100644
--- a/test/unit/test_configurator.rb
+++ b/test/unit/test_configurator.rb
@@ -139,6 +139,30 @@ class TestConfigurator < Test::Unit::TestCase
     end
   end

+  def test_check_client_connection
+    tmp = Tempfile.new('unicorn_config')
+    test_struct = TestStruct.new
+    tmp.syswrite("check_client_connection true\n")
+
+    assert_nothing_raised do
+      Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
+    end
+
+    assert test_struct.check_client_connection
+  end
+
+  def test_check_client_connection_with_tcp_bad
+    tmp = Tempfile.new('unicorn_config')
+    test_struct = TestStruct.new
+    listener = "127.0.0.1:12345"
+    tmp.syswrite("check_client_connection true\n")
+    tmp.syswrite("listen '#{listener}', :tcp_nopush => true\n")
+
+    assert_raises(ArgumentError) do
+      Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
+    end
+  end
+
   def test_after_fork_proc
     test_struct = TestStruct.new
     [ proc { |a,b| }, Proc.new { |a,b| }, lambda { |a,b| } ].each do |my_proc|
-- 
1.7.10.2 (Apple Git-33)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply related	[relevance 0%]

* Re: .manifest file?
  2012-11-14  2:42 14% .manifest file? mike
@ 2012-11-14  3:00  7% ` Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2012-11-14  3:00 UTC (permalink / raw)
  To: unicorn list

mike <michael.p.thompson@gmail.com> wrote:
> Just switched form a zip download to pulling the master branch of git.
> The .gemspec file is trying to parse a .manifest. Was this supposed to
> be included or generated? I am just poking around about it now.

It should be autogenerated with "make gem"

(wrongdoc (a dev-dependency) needs to be installed to generate the
ChangeLog/NEWS files, but that should run well under rbx).
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 7%]

* .manifest file?
@ 2012-11-14  2:42 14% mike
  2012-11-14  3:00  7% ` Eric Wong
  0 siblings, 1 reply; 25+ results
From: mike @ 2012-11-14  2:42 UTC (permalink / raw)
  To: mongrel-unicorn

Just switched form a zip download to pulling the master branch of git.
The .gemspec file is trying to parse a .manifest. Was this supposed to
be included or generated? I am just poking around about it now.

Thanks for your time,

-Mike Thompson
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 14%]

* Re: Combating nginx 499 HTTP responses during flash traffic scenario
  2012-11-06  3:16  2%                 ` Tom Burns
@ 2012-11-06 21:23  0%                   ` Eric Wong
  2012-11-29 15:52  0%                     ` Tom Burns
  0 siblings, 1 reply; 25+ results
From: Eric Wong @ 2012-11-06 21:23 UTC (permalink / raw)
  To: unicorn list

Tom Burns <tom.burns@jadedpixel.com> wrote:
> On Mon, Nov 5, 2012 at 6:48 AM, Eric Wong <normalperson@yhbt.net> wrote:
> > We can wait until you can report on how this change improves your
> > situation before fleshing this out.  Real-world results are always
> > good to have :>
> 
> Agreed.
> 
> We're going to be testing this this week so I should have some results
> to share by the weekend.  We only experience the problem during major
> sales so it may or may not manifest without our traffic generation
> tool.

Holidays are coming up, should be lots of traffic :)

> I'd like to be testing a patch close to the finished product so I've
> addressed all of your comments from the previous email (including the
> modification to the C extension) in the patch below.

Thanks for the updated patch.  A few comments below, apologies for the
nitpicks :)

> > Random thought:  HttpResponse generation gains even more coupling
> > with the current Http{Request,Parser} state.  Organizing code is hard :x
> Agreed.  Without a class wrapping the raw socket to hold state I
> didn't see a better way to accomplish this, but I've only been looking
> at this code for the past week or so.

I'm leaning towards just having one HttpState class which encompasses
the entire client state (request/response).  I did that earlier this
year for a single-purpose (non-Ruby) HTTP daemon and that design makes
the most sense to me.

Maybe we'll modify unicorn around that sooner or later...

> diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
> index 96dcf83..52f7f3c 100644
> --- a/ext/unicorn_http/unicorn_http.rl
> +++ b/ext/unicorn_http/unicorn_http.rl
> @@ -115,7 +115,7 @@ struct http_parser {
>    } len;
>  };
> 
> -static ID id_clear, id_set_backtrace;
> +static ID id_clear, id_set_backtrace, id_response_start_sent;
> 
>  static void finalize_header(struct http_parser *hp);
> 
> @@ -626,6 +626,7 @@ static VALUE HttpParser_clear(VALUE self)
> 
>    http_parser_init(hp);
>    rb_funcall(hp->env, id_clear, 0);
> +  rb_funcall(self, id_response_start_sent, 1, Qfalse);

Nitpick:  Since HttpParser is already an alias of the HttpRequest class,
rb_ivar_set() should be a hair faster as it won't have to go through
normal method lookup.

>    return self;
>  }
> @@ -1031,6 +1032,7 @@ void Init_unicorn_http(void)
>    SET_GLOBAL(g_http_connection, "CONNECTION");
>    id_clear = rb_intern("clear");
>    id_set_backtrace = rb_intern("set_backtrace");
> +  id_response_start_sent = rb_intern("response_start_sent=");
>    init_unicorn_httpdate();
>  }
>  #undef SET_GLOBAL
> diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
> index 89cbf5c..5f329af 100644
> --- a/lib/unicorn/configurator.rb
> +++ b/lib/unicorn/configurator.rb
> @@ -45,6 +45,7 @@ class Unicorn::Configurator
>        },
>      :pid => nil,
>      :preload_app => false,
> +    :check_client_connection => false,
>      :rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
>      :client_body_buffer_size => Unicorn::Const::MAX_BODY,
>      :trust_x_forwarded => true,
> @@ -96,6 +97,13 @@ class Unicorn::Configurator
>      if ready_pipe = RACKUP.delete(:ready_pipe)
>        server.ready_pipe = ready_pipe
>      end
> +    if set[:check_client_connection]
> +      set[:listeners].each do |address|
> +        if set[:listener_opts][address][:tcp_nopush] == true ||
> set[:listener_opts][address][:tcp_nodelay] == true

Oops, I missed long lines the first time.  It looks like your MUA did
mangle the long line.  Wrap everything at <80 columns should help avoid
the issue (regardless of MUA, my brain cannot process >80 columns well)

> +          raise ArgumentError, "With check_client_connection set to
> true both :tcp_nopush and :tcp_nodelay listener options must be set to
> false."

Likewise, error messages should be more concise and easier to read
in smaller terminals.

Probably:

check_client_connection is incompatible with (tcp_nopush|tcp_nodelay):true

> @@ -454,6 +462,12 @@ class Unicorn::Configurator
>      set_int(:client_body_buffer_size, bytes, 0)
>    end
> 
> +  # When enabled, unicorn will check the client connection by writing
> +  # the beginning of the HTTP headers before calling the application.

Good to document the :tcp_* listener option incompatibilities here, too.

> +  def check_client_connection(bool)
> +    set_bool(:check_client_connection, bool)
> +  end
> +
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: Combating nginx 499 HTTP responses during flash traffic scenario
  @ 2012-11-06  3:16  2%                 ` Tom Burns
  2012-11-06 21:23  0%                   ` Eric Wong
  0 siblings, 1 reply; 25+ results
From: Tom Burns @ 2012-11-06  3:16 UTC (permalink / raw)
  To: unicorn list

On Mon, Nov 5, 2012 at 6:48 AM, Eric Wong <normalperson@yhbt.net> wrote:
> We can wait until you can report on how this change improves your
> situation before fleshing this out.  Real-world results are always
> good to have :>
Agreed.

We're going to be testing this this week so I should have some results
to share by the weekend.  We only experience the problem during major
sales so it may or may not manifest without our traffic generation
tool.

I'd like to be testing a patch close to the finished product so I've
addressed all of your comments from the previous email (including the
modification to the C extension) in the patch below.

>
> Random thought:  HttpResponse generation gains even more coupling
> with the current Http{Request,Parser} state.  Organizing code is hard :x
Agreed.  Without a class wrapping the raw socket to hold state I
didn't see a better way to accomplish this, but I've only been looking
at this code for the past week or so.

Cheers,
Tom

---
>From c696bc0cb4df223a096142b6f314c8c2d83255be Mon Sep 17 00:00:00 2001
From: Tom Burns <tom.burns@jadedpixel.com>
Date: Tue, 30 Oct 2012 16:22:21 -0400
Subject: [PATCH] Begin writing HTTP request headers early to detect
 disconnected clients

This patch checks incoming connections and avoids calling the application
if the connection has been closed.

It works by sending the beginning of the HTTP response before calling
the application to see if the socket can successfully be written to.

By enabling this feature users can avoid wasting application rendering
time only to find the connection is closed when attempting to write, and
throwing out the result.

When a client disconnects while being queued or processed, Nginx will log
HTTP response 499 but the application will log a 200.

Enabling this feature will minimize the time window during which the problem
can arise.

The feature is disabled by default and can be enabled by adding
'check_client_connection true' to the unicorn config.
---
 examples/unicorn.conf.rb         |    6 ++++++
 ext/unicorn_http/unicorn_http.rl |    4 +++-
 lib/unicorn/configurator.rb      |   14 ++++++++++++++
 lib/unicorn/const.rb             |   12 ++++++++----
 lib/unicorn/http_request.rb      |   19 +++++++++++++++++++
 lib/unicorn/http_response.rb     |    5 +++--
 lib/unicorn/http_server.rb       |   22 ++++++++++++++++++++--
 test/exec/test_exec.rb           |    7 ++++++-
 test/unit/test_configurator.rb   |   24 ++++++++++++++++++++++++
 9 files changed, 103 insertions(+), 10 deletions(-)

diff --git a/examples/unicorn.conf.rb b/examples/unicorn.conf.rb
index 0238043..1f4c9c0 100644
--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -46,6 +46,12 @@ preload_app true
 GC.respond_to?(:copy_on_write_friendly=) and
   GC.copy_on_write_friendly = true

+# Enable this flag to have unicorn test client connections by writing the
+# beginning of the HTTP headers before calling the application.  This
+# prevents calling the application for connections that have disconnected
+# while queued.
+check_client_connection false
+
 before_fork do |server, worker|
   # the following is highly recomended for Rails + "preload_app true"
   # as there's no need for the master process to hold a connection
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 96dcf83..52f7f3c 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -115,7 +115,7 @@ struct http_parser {
   } len;
 };

-static ID id_clear, id_set_backtrace;
+static ID id_clear, id_set_backtrace, id_response_start_sent;

 static void finalize_header(struct http_parser *hp);

@@ -626,6 +626,7 @@ static VALUE HttpParser_clear(VALUE self)

   http_parser_init(hp);
   rb_funcall(hp->env, id_clear, 0);
+  rb_funcall(self, id_response_start_sent, 1, Qfalse);

   return self;
 }
@@ -1031,6 +1032,7 @@ void Init_unicorn_http(void)
   SET_GLOBAL(g_http_connection, "CONNECTION");
   id_clear = rb_intern("clear");
   id_set_backtrace = rb_intern("set_backtrace");
+  id_response_start_sent = rb_intern("response_start_sent=");
   init_unicorn_httpdate();
 }
 #undef SET_GLOBAL
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index 89cbf5c..5f329af 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -45,6 +45,7 @@ class Unicorn::Configurator
       },
     :pid => nil,
     :preload_app => false,
+    :check_client_connection => false,
     :rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
     :client_body_buffer_size => Unicorn::Const::MAX_BODY,
     :trust_x_forwarded => true,
@@ -96,6 +97,13 @@ class Unicorn::Configurator
     if ready_pipe = RACKUP.delete(:ready_pipe)
       server.ready_pipe = ready_pipe
     end
+    if set[:check_client_connection]
+      set[:listeners].each do |address|
+        if set[:listener_opts][address][:tcp_nopush] == true ||
set[:listener_opts][address][:tcp_nodelay] == true
+          raise ArgumentError, "With check_client_connection set to
true both :tcp_nopush and :tcp_nodelay listener options must be set to
false."
+        end
+      end
+    end
     set.each do |key, value|
       value == :unset and next
       skip.include?(key) and next
@@ -454,6 +462,12 @@ class Unicorn::Configurator
     set_int(:client_body_buffer_size, bytes, 0)
   end

+  # When enabled, unicorn will check the client connection by writing
+  # the beginning of the HTTP headers before calling the application.
+  def check_client_connection(bool)
+    set_bool(:check_client_connection, bool)
+  end
+
   # Allow redirecting $stderr to a given path.  Unlike doing this from
   # the shell, this allows the unicorn process to know the path its
   # writing to and rotate the file if it is used for logging.  The
diff --git a/lib/unicorn/const.rb b/lib/unicorn/const.rb
index b3d8d71..f0c4c12 100644
--- a/lib/unicorn/const.rb
+++ b/lib/unicorn/const.rb
@@ -29,12 +29,16 @@ module Unicorn::Const

   # :stopdoc:
   # common errors we'll send back
-  ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
-  ERROR_414_RESPONSE = "HTTP/1.1 414 Request-URI Too Long\r\n\r\n"
-  ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
-  ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
+  ERROR_400_RESPONSE = "400 Bad Request\r\n\r\n"
+  ERROR_414_RESPONSE = "414 Request-URI Too Long\r\n\r\n"
+  ERROR_413_RESPONSE = "413 Request Entity Too Large\r\n\r\n"
+  ERROR_500_RESPONSE = "500 Internal Server Error\r\n\r\n"
+
   EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
+  EXPECT_100_RESPONSE_SUFFIXED = "100 Continue\r\n\r\nHTTP/1.1 "

+  HTTP_RESPONSE_START = ['HTTP', '/1.1 ']
   HTTP_EXPECT = "HTTP_EXPECT"
+
   # :startdoc:
 end
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index a0435d6..79ead2e 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -22,11 +22,14 @@ class Unicorn::HttpParser

   NULL_IO = StringIO.new("")

+  attr_accessor :response_start_sent
+
   # :stopdoc:
   # A frozen format for this is about 15% faster
   REMOTE_ADDR = 'REMOTE_ADDR'.freeze
   RACK_INPUT = 'rack.input'.freeze
   @@input_class = Unicorn::TeeInput
+  @@check_client_connection = false

   def self.input_class
     @@input_class
@@ -35,6 +38,15 @@ class Unicorn::HttpParser
   def self.input_class=(klass)
     @@input_class = klass
   end
+
+  def self.check_client_connection
+    @@check_client_connection
+  end
+
+  def self.check_client_connection=(bool)
+    @@check_client_connection = bool
+  end
+
   # :startdoc:

   # Does the majority of the IO processing.  It has been written in
@@ -70,6 +82,13 @@ class Unicorn::HttpParser
       # an Exception thrown from the parser will throw us out of the loop
       false until add_parse(socket.kgio_read!(16384))
     end
+
+    # detect if the socket is valid by writing a partial response:
+    if @@check_client_connection && headers?
+      @response_start_sent = true
+      Unicorn::Const::HTTP_RESPONSE_START.each { |c| socket.write(c) }
+    end
+
     e[RACK_INPUT] = 0 == content_length ?
                     NULL_IO : @@input_class.new(socket, self)
     e.merge!(DEFAULTS)
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb
index b781e20..9c2bacc 100644
--- a/lib/unicorn/http_response.rb
+++ b/lib/unicorn/http_response.rb
@@ -18,11 +18,12 @@ module Unicorn::HttpResponse
   CRLF = "\r\n"

   # writes the rack_response to socket as an HTTP response
-  def http_response_write(socket, status, headers, body)
+  def http_response_write(socket, status, headers, body,
response_start_sent=false)
     status = CODES[status.to_i] || status

+    http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
     if headers
-      buf = "HTTP/1.1 #{status}\r\n" \
+      buf = "#{http_response_start}#{status}\r\n" \
             "Date: #{httpdate}\r\n" \
             "Status: #{status}\r\n" \
             "Connection: close\r\n"
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 13df55a..8729830 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -17,6 +17,7 @@ class Unicorn::HttpServer
                 :listener_opts, :preload_app,
                 :reexec_pid, :orig_app, :init_listeners,
                 :master_pid, :config, :ready_pipe, :user
+
   attr_reader :pid, :logger
   include Unicorn::SocketHelper
   include Unicorn::HttpResponse
@@ -355,6 +356,14 @@ class Unicorn::HttpServer
     Unicorn::HttpParser.trust_x_forwarded = bool
   end

+  def check_client_connection
+    Unicorn::HttpRequest.check_client_connection
+  end
+
+  def check_client_connection=(bool)
+    Unicorn::HttpRequest.check_client_connection = bool
+  end
+
   private

   # wait for a signal hander to wake us up and then consume the pipe
@@ -524,23 +533,32 @@ class Unicorn::HttpServer
       Unicorn.log_error(@logger, "app error", e)
       Unicorn::Const::ERROR_500_RESPONSE
     end
+    msg = "HTTP/1.1 #{msg}" unless @request.response_start_sent
     client.kgio_trywrite(msg)
     client.close
     rescue
   end

+  def expect_100_response
+    if @request.response_start_sent
+      Unicorn::Const::EXPECT_100_RESPONSE_SUFFIXED
+    else
+      Unicorn::Const::EXPECT_100_RESPONSE
+    end
+  end
+
   # once a client is accepted, it is processed in its entirety here
   # in 3 easy steps: read request, call app, write app response
   def process_client(client)
     status, headers, body = @app.call(env = @request.read(client))

     if 100 == status.to_i
-      client.write(Unicorn::Const::EXPECT_100_RESPONSE)
+      client.write(expect_100_response)
       env.delete(Unicorn::Const::HTTP_EXPECT)
       status, headers, body = @app.call(env)
     end
     @request.headers? or headers = nil
-    http_response_write(client, status, headers, body)
+    http_response_write(client, status, headers, body,
@request.response_start_sent)
     client.shutdown # in case of fork() in Rack app
     client.close # flush and uncork socket immediately, no keepalive
   rescue => e
diff --git a/test/exec/test_exec.rb b/test/exec/test_exec.rb
index b30a3d6..1cee2b7 100644
--- a/test/exec/test_exec.rb
+++ b/test/exec/test_exec.rb
@@ -871,13 +871,14 @@ EOF
     wait_for_death(pid)
   end

-  def hup_test_common(preload)
+  def hup_test_common(preload, check_client=false)
     File.open("config.ru", "wb") { |fp| fp.syswrite(HI.gsub("HI", '#$$')) }
     pid_file = Tempfile.new('pid')
     ucfg = Tempfile.new('unicorn_test_config')
     ucfg.syswrite("listen '#@addr:#@port'\n")
     ucfg.syswrite("pid '#{pid_file.path}'\n")
     ucfg.syswrite("preload_app true\n") if preload
+    ucfg.syswrite("check_client_connection true\n") if check_client
     ucfg.syswrite("stderr_path 'test_stderr.#$$.log'\n")
     ucfg.syswrite("stdout_path 'test_stdout.#$$.log'\n")
     pid = xfork {
@@ -942,6 +943,10 @@ EOF
     hup_test_common(false)
   end

+  def test_check_client_hup
+    hup_test_common(false, true)
+  end
+
   def test_default_listen_hup_holds_listener
     default_listen_lock do
       res, pid_path = default_listen_setup
diff --git a/test/unit/test_configurator.rb b/test/unit/test_configurator.rb
index c19c427..b9c22d7 100644
--- a/test/unit/test_configurator.rb
+++ b/test/unit/test_configurator.rb
@@ -139,6 +139,30 @@ class TestConfigurator < Test::Unit::TestCase
     end
   end

+  def test_check_client_connection
+    tmp = Tempfile.new('unicorn_config')
+    test_struct = TestStruct.new
+    tmp.syswrite("check_client_connection true\n")
+
+    assert_nothing_raised do
+      Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
+    end
+
+    assert test_struct.check_client_connection
+  end
+
+  def test_check_client_connection_with_tcp_bad
+    tmp = Tempfile.new('unicorn_config')
+    test_struct = TestStruct.new
+    listener = "127.0.0.1:12345"
+    tmp.syswrite("check_client_connection true\n")
+    tmp.syswrite("listen '#{listener}', :tcp_nopush => true\n")
+
+    assert_raises(ArgumentError) do
+      Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
+    end
+  end
+
   def test_after_fork_proc
     test_struct = TestStruct.new
     [ proc { |a,b| }, Proc.new { |a,b| }, lambda { |a,b| } ].each do |my_proc|
-- 
1.7.7.5 (Apple Git-26)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply related	[relevance 2%]

* Re: after_fork and redis
  @ 2012-10-01 20:28  5%   ` Ben Somers
  0 siblings, 0 replies; 25+ results
From: Ben Somers @ 2012-10-01 20:28 UTC (permalink / raw)
  To: unicorn list

>> When I don't do this, each worker/pid seems to have their own redis
>> instance.  So, why is this needed?  Here's the logs of me printing out
>> $redis.client.inspect when both $redis = Redis.new in the after_fork
>> and just $redis = Redis.new in the environment.rb.

Checked through the redis.rb source a little bit, and I believe it's
got code specifically to avoid opening the connection until it's
needed, to prevent some shared connection problems
(https://github.com/redis/redis-rb/commit/d1bc88f85bc9bfbb7aabdfc2700bf4b30c0b0115).
You probably aren't making actual use of the connection until after
app startup, so that's why things are working so far for you. It's still
a good idea to initialize Redis in the after_fork, though, because if
something in your app's startup or initializer code does wind up using
that redis connection, you'd still get the shared connection problem.

> Sharing stream sockets across processes/threads is nearly
> always a bad idea: data streams become interleaved and impossible to
> separate in either direction.

"Nearly always a bad idea" is a bit of an understatement; the
interleaved data streams tend to create catastrophic errors, things
like users unable to log in or (god forbid) logging into other
people's sessions. And they usually manifest as race conditions, so
they're hard to debug if you don't know what to look for.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 5%]

* Re: [PATCH] Ensure that 'make gem' builds the documentation too.
  2011-06-06 17:31 12% [PATCH] Ensure that 'make gem' builds the documentation too Hongli Lai
@ 2011-06-06 17:51 13% ` Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2011-06-06 17:51 UTC (permalink / raw)
  To: unicorn list

Hongli Lai <hongli@phusion.nl> wrote:
> >From bfefc2cf0efb0913a42862886363b3140dcdbb2a Mon Sep 17 00:00:00 2001
> From: Hongli Lai (Phusion) <hongli@phusion.nl>
> Date: Mon, 6 Jun 2011 13:39:00 +0200
> Subject: [PATCH] Ensure that 'make gem' builds the documentation too.
> 
> If autogenerated documentation files, like man pages, don't exist then
> 'make gem' will fail, complaining that some files are not found. By
> depending the 'gem' target on the 'doc' target we ensure that 'make gem'
> always works.
> 
> Signed-off-by: Hongli Lai (Phusion) <hongli@phusion.nl>

Oops, this was a regression introduced when I switched to wrongdoc
in f62ef19a4aa3d3e4ce1aa37a499907ff776a8964

Perhaps this is better?  It'll also affect the tgz target and not
just the gem target.

diff --git a/GNUmakefile b/GNUmakefile
index da55052..61fb739 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -164,7 +164,7 @@ pkg_extra := GIT-VERSION-FILE ChangeLog LATEST NEWS \
 ChangeLog: GIT-VERSION-FILE .wrongdoc.yml
 	wrongdoc prepare
 
-.manifest: ChangeLog $(ext)/unicorn_http.c
+.manifest: ChangeLog $(ext)/unicorn_http.c man
 	(git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
 	  LC_ALL=C sort > $@+
 	cmp $@+ $@ || mv $@+ $@
-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply related	[relevance 13%]

* [PATCH] Ensure that 'make gem' builds the documentation too.
@ 2011-06-06 17:31 12% Hongli Lai
  2011-06-06 17:51 13% ` Eric Wong
  0 siblings, 1 reply; 25+ results
From: Hongli Lai @ 2011-06-06 17:31 UTC (permalink / raw)
  To: unicorn list

>From bfefc2cf0efb0913a42862886363b3140dcdbb2a Mon Sep 17 00:00:00 2001
From: Hongli Lai (Phusion) <hongli@phusion.nl>
Date: Mon, 6 Jun 2011 13:39:00 +0200
Subject: [PATCH] Ensure that 'make gem' builds the documentation too.

If autogenerated documentation files, like man pages, don't exist then
'make gem' will fail, complaining that some files are not found. By
depending the 'gem' target on the 'doc' target we ensure that 'make gem'
always works.

Signed-off-by: Hongli Lai (Phusion) <hongli@phusion.nl>
---
 GNUmakefile |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/GNUmakefile b/GNUmakefile
index 4072826..90cc451 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -257,7 +257,7 @@ gem: $(pkggem)
 install-gem: $(pkggem)
 	gem install $(CURDIR)/$<

-$(pkggem): .manifest fix-perms
+$(pkggem): .manifest fix-perms doc
 	gem build $(rfpackage).gemspec
 	mkdir -p pkg
 	mv $(@F) $@
-- 
1.7.5

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply related	[relevance 12%]

* draft release notes (so far) for upcoming 0.93.0
@ 2009-10-01  8:13  7% Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2009-10-01  8:13 UTC (permalink / raw)
  To: mongrel-unicorn

The one minor bugfix is only for users who set RAILS_RELATIVE_URL_ROOT
in a config file.  Users of the "--path" switch or those who set the
environment variable in the shell were unaffected by this bug.  Note
that we still don't have relative URL root support for Rails < 2.3, and
are unlikely to bother with it unless there is visible demand for it.  I
didn't even know people used/cared for relative URL roots before today
(it was a Rails 2.3.4 user).

New features (so far) includes support for :tries and :delay when
specifying a "listen" in an after_fork hook.  This was inspired by Chris
Wanstrath's example of binding per-worker listen sockets in a loop while
migrating (or upgrading) Unicorn.  Setting a negative value for :tries
means we'll retry the listen indefinitely until the socket becomes
available.

So you can do something like this in an after_fork hook:

    after_fork do |server,worker|
      addr = "127.0.0.1:#{9293 + worker.nr}"
      server.listen(addr, :tries => -1, :delay => 5)
    end

There's also the usual round of added documentation, packaging fixes,
code cleanups and minor performance improvements that are viewable
in the git log....

Shortlog since v0.92.0 (so far) below:

Eric Wong (45):
      build: hardcode the canonical git URL
      build: manifest dropped manpages
      build: smaller ChangeLog
      doc/LATEST: remove trailing newline
      http: don't force -fPIC if it can't be used
      .gitignore on *.rbc files Rubinius generates
      README/gemspec: a better description, hopefully
      GNUmakefile: add missing .manifest dep on test installs
      Add HACKING document
      configurator: fix user switch example in RDoc
      local.mk.sample: time and perms enforcement
      unicorn_rails: show "RAILS_ENV" in help message
      gemspec: compatibility with older Rubygems
      Split out KNOWN_ISSUES document
      KNOWN_ISSUES: add notes about the "isolate" gem
      gemspec: fix test_files regexp match
      gemspec: remove tests that fork from test_files
      test_signals: ensure we can parse pids in response
      GNUmakefile: cleanup test/manifest generation
      util: remove APPEND_FLAGS constant
      http_request: simplify and remove handle_body method
      http_response: simplify and remove const dependencies
      local.mk.sample: fix .js times
      TUNING: notes about benchmarking a high :backlog
      HttpServer#listen accepts :tries and :delay parameters
      "make install" avoids installing multiple .so objects
      Use Configurator#expand_addr in HttpServer#listen
      configurator: move initialization stuff to #initialize
      Remove "Z" constant for binary strings
      cgi_wrapper: don't warn about stdoutput usage
      cgi_wrapper: simplify status handling in response
      cgi_wrapper: use Array#concat instead of +=
      server: correctly unset reexec_pid on child death
      configurator: update and modernize examples
      configurator: add colons in front of listen() options
      configurator: remove DEFAULT_LOGGER constant
      gemspec: clarify commented-out licenses section
      Add makefile targets for non-release installs
      cleanup: use question mark op for 1-byte comparisons
      RDoc for Unicorn::HttpServer::Worker
      small cleanup to pid file handling + documentation
      rails: RAILS_RELATIVE_URL_ROOT may be set in Unicorn config
      unicorn_rails: undeprecate --path switch
      manpages: document environment variables
      README: remove reference to different versions

-- 
Eric Wong

^ permalink raw reply	[relevance 7%]

* rolling your own Unicorn gem prerelease
@ 2009-09-30 23:44  7% Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2009-09-30 23:44 UTC (permalink / raw)
  To: mongrel-unicorn

Instead of relying entirely on the ridiculous test suite, it's now more
easily possible to roll your own properly-versioned prerelease gems.

You'll need RubyGems >= 1.3.5 to handle pre-release version numbers.

Of course setup.rb users (like myself) have always had this capability,
I just lack real applications to test against...

>From 9cc4f87353b84f5229d4a8bae78260c24cd02154 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Wed, 30 Sep 2009 13:41:26 -0700
Subject: [PATCH] Add makefile targets for non-release installs

This should make it easier to test and run unreleased
versions.
---
 GNUmakefile |    8 ++++++++
 HACKING     |   15 +++++++++++++++
 2 files changed, 23 insertions(+), 0 deletions(-)

diff --git a/GNUmakefile b/GNUmakefile
index 8becc89..3087082 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -227,6 +227,11 @@ verify:
 	test `git rev-parse --verify HEAD^0` = \
 	     `git rev-parse --verify refs/tags/v$(VERSION)^{}`
 
+gem: $(pkggem)
+
+install-gem: $(pkggem)
+	gem install $(CURDIR)/$<
+
 $(pkggem): manifest
 	gem build $(rfpackage).gemspec
 	mkdir -p pkg
@@ -249,6 +254,9 @@ release: verify package $(release_notes) $(release_changes)
 	  $(rfproject) $(rfpackage) $(VERSION) $(pkggem)
 	rubyforge add_file \
 	  $(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
+else
+gem install-gem: GIT-VERSION-FILE
+	$(MAKE) $@ VERSION=$(GIT_VERSION)
 endif
 
 .PHONY: .FORCE-GIT-VERSION-FILE doc $(T) $(slow_tests) manifest man
diff --git a/HACKING b/HACKING
index 5085545..08aa76d 100644
--- a/HACKING
+++ b/HACKING
@@ -96,3 +96,18 @@ We will adhere to mostly the same conventions for patch submissions as
 git itself.  See the Documentation/SubmittingPatches document
 distributed with git on on patch submission guidelines to follow.  Just
 don't email the git mailing list or maintainer with Unicorn patches :)
+
+== Running Development Versions
+
+It is easy to install the contents of your git working directory:
+
+Via RubyGems (RubyGems 1.3.5+ recommended for prerelease versions):
+
+  make install-gem
+
+Without RubyGems (via setup.rb):
+
+  make install
+
+It is not at all recommended to mix a RubyGems installation with an
+installation done without RubyGems, however.
-- 
Eric Wong

^ permalink raw reply related	[relevance 7%]

* [ANN] unicorn 0.92.0
@ 2009-09-18 22:16  3% Eric Wong
  0 siblings, 0 replies; 25+ results
From: Eric Wong @ 2009-09-18 22:16 UTC (permalink / raw)
  To: mongrel-unicorn

Unicorn is a Rack HTTP server for Unix and fast clients

Small fixes and documentation are the focus of this release.

James Golick reported and helped me track down a bug that caused
SIGHUP to drop the default listener (0.0.0.0:8080) if and only
if listeners were completely unspecified in both the
command-line and Unicorn config file.  The Unicorn config file
remains the recommended option for specifying listeners as it
allows fine-tuning of the :backlog, :rcvbuf, :sndbuf,
:tcp_nopush, and :tcp_nodelay options.

There are some documentation (and resulting website)
improvements.  setup.rb users will notice the new section 1
manpages for `unicorn` and `unicorn_rails`, Rubygems users
will have to install manpages manually or use the website.

  Edit: That's not entirely true, I screwed up the package but
  you can get them from http://unicorn.bogomips.org/unicorn.1
  and http://unicorn.bogomips.org/unicorn_rails.1

The HTTP parser got a 3rd-party code review which resulted in
some cleanups and one insignificant bugfix as a result.

Additionally, the HTTP parser compiles, runs and passes unit
tests under Rubinius.  The pure-Ruby parts still do not work yet
and we currently lack the resources/interest to pursue this
further but help will be gladly accepted.

The website now has an Atom feed for new release announcements.
Those unfamiliar with Atom or HTTP may finger unicorn@bogomips.org
for the latest announcements.

Eric Wong (53):
      README: update with current version
      http: cleanup and avoid potential signedness warning
      http: clarify the setting of the actual header in the hash
      http: switch to macros for bitflag handling
      http: refactor keepalive tracking to functions
      http: use explicit elses for readability
      http: remove needless goto
      http: extra assertion when advancing p manually
      http: verbose assertions
      http: NIL_P(var) instead of var == Qnil
      http: rb_gc_mark already ignores immediates
      http: ignore Host: continuation lines with absolute URIs
      doc/SIGNALS: fix the no-longer-true bit about socket options
      "encoding: binary" comments for all sources (1.9)
      http_response: don't "rescue nil" for body.close
      CONTRIBUTORS: fix capitalization for why
      http: support Rubies without the OBJ_FROZEN macro
      http: define OFFT2NUM macro on Rubies without it
      http: no-op rb_str_modify() for Rubies without it
      http: compile with -fPIC
      http: use rb_str_{update,flush} if available
      http: create a new string buffer on empty values
      Update documentation for Rubinius support status
      http: cleanup assertion for memoized header strings
      http: add #endif comment labels where appropriate
      Add .mailmap file for "git shortlog" and other tools
      Update Manifest with mailmap
      Fix comment about speculative accept()
      SIGNALS: use "Unicorn" when referring to the web server
      Add new Documentation section for manpages
      test_exec: add extra tests for HUP and preload_app
      socket_helper: (FreeBSD) don't freeze the accept filter constant
      Avoid freezing objects that don't benefit from it
      SIGHUP no longer drops lone, default listener
      doc: generate ChangeLog and NEWS file for RDoc
      Remove Echoe and roll our own packaging/release...
      unicorn_rails: close parentheses in help message
      launchers: deprecate ambiguous -P/--p* switches
      man1/unicorn: avoid unnecessary emphasis
      Add unicorn_rails(1) manpage
      Documentation: don't force --rsyncable flag with gzip(1)
      Simplify and standardize manpages build/install
      GNUmakefile: package .tgz includes all generated files
      doc: begin integration of HTML manpages into RDoc
      Update TODO
      html: add Atom feeds
      doc: latest news is available through finger
      NEWS.atom: file timestamp matches latest entry
      pandoc needs the standalone switch for manpages
      man1/unicorn: split out RACK ENVIRONMENT section
      man1/unicorn_rails: fix unescaped underscore
      NEWS.atom.xml only lists the first 10 entries
      unicorn 0.92.0

* site: http://unicorn.bogomips.org/
* git: git://git.bogomips.org/unicorn.git
* cgit: http://git.bogomips.org/cgit/unicorn.git/
* list: mongrel-unicorn@rubyforge.org
* nntp: nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
* finger: unicorn@bogomips.org

-- 
Eric Wong

^ permalink raw reply	[relevance 3%]

Results 1-25 of 25 | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
2009-09-18 22:16  3% [ANN] unicorn 0.92.0 Eric Wong
2009-09-30 23:44  7% rolling your own Unicorn gem prerelease Eric Wong
2009-10-01  8:13  7% draft release notes (so far) for upcoming 0.93.0 Eric Wong
2011-06-06 17:31 12% [PATCH] Ensure that 'make gem' builds the documentation too Hongli Lai
2011-06-06 17:51 13% ` Eric Wong
2012-10-01 15:31     after_fork and redis bradford
2012-10-01 19:31     ` Eric Wong
2012-10-01 20:28  5%   ` Ben Somers
2012-10-29 17:44     Combating nginx 499 HTTP responses during flash traffic scenario Tom Burns
2012-10-29 18:45     ` Eric Wong
2012-10-29 21:53       ` Eric Wong
2012-10-30 20:40         ` Tom Burns
2012-10-30 21:37           ` Eric Wong
2012-11-02 17:59             ` Tom Burns
2012-11-02 19:38               ` Eric Wong
2012-11-03 22:45                 ` Tom Burns
2012-11-05 11:48                   ` Eric Wong
2012-11-06  3:16  2%                 ` Tom Burns
2012-11-06 21:23  0%                   ` Eric Wong
2012-11-29 15:52  0%                     ` Tom Burns
2012-11-14  2:42 14% .manifest file? mike
2012-11-14  3:00  7% ` Eric Wong
2014-07-22 18:52     High number of TCP retransmits on Solaris Paul Henry
2014-07-22 20:56  4% ` Eric Wong
2014-07-22 21:56  0%   ` Paul Henry
2014-09-22  1:05     [PATCH 0/4] a few more minor cleanups pushed out Eric Wong
2014-09-22  1:05 11% ` [PATCH 1/4] remove RubyForge and Freecode references Eric Wong
2015-02-04 20:16 11% [PATCH] GNUmakefile: fix clean gem build + reduce build cruft Eric Wong
2015-08-22  4:42  6% [PATCH] gemspec: limit to 1.9.3 and 2.x Eric Wong
2015-11-01  8:37     [PATCH 0/3] last updates before 5.0 release Eric Wong
2015-11-01  8:37  5% ` [PATCH 2/3] gemspec: relax Ruby version requirement for old RubyGems Eric Wong
     [not found]     <f1d5eacb-b6a2-be96-34aa-fe061a194a62@onenetbeyond.org>
     [not found]     ` <CAAB-Kcnwzc8Tcszv3FCPkyJRKRCsHRH6k_qBhKfBpSODxqKy5g@mail.gmail.com>
2016-10-28  0:23       ` trying to update unicorn to 5.1, build failure: VERSION= must be specified Eric Wong
2016-11-03 15:46         ` Pirate Praveen
2017-03-23 23:34 21%       ` [PATCH] gemspec: remove olddoc from build dependency Eric Wong
2019-07-04 22:01     [PATCH 0/3] http: use gperf for common field memoization Eric Wong
2019-07-04 22:01  6% ` [PATCH 2/3] http: use gperf for common fields optimization Eric Wong
2020-01-14  7:46     [PATCH] doc: s/bogomips.org/yhbt.net/g Eric Wong
2020-01-14  7:46  1% ` Eric Wong
2020-07-26  1:57     [PATCH 0/2] minor test improvements Eric Wong
2020-07-26  1:57  5% ` [PATCH 2/2] build: revamp and avoid unnecessary rebuilds Eric Wong
2021-03-29 19:06     Bus Error with Unicorn 6.0 on OpenBSD/adJ 6.8 with Ruby 3.0 vtamara
2021-03-30  1:58  6% ` Eric Wong
2021-03-30  4:00       ` Jeremy Evans
2021-04-04 11:06  0%     ` vtamara
2021-04-04 16:43  0%       ` Jeremy Evans
2024-03-23 19:45  2% [PATCH 0/4] a small pile of patches Eric Wong

Code repositories for project(s) associated with this public inbox

	https://yhbt.net/unicorn.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).