yahns 0.0.0TP1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/COPYING +674 -0
  4. data/GIT-VERSION-GEN +41 -0
  5. data/GNUmakefile +90 -0
  6. data/README +127 -0
  7. data/Rakefile +60 -0
  8. data/bin/yahns +32 -0
  9. data/examples/README +3 -0
  10. data/examples/init.sh +76 -0
  11. data/examples/logger_mp_safe.rb +28 -0
  12. data/examples/logrotate.conf +32 -0
  13. data/examples/yahns_multi.conf.rb +89 -0
  14. data/examples/yahns_rack_basic.conf.rb +27 -0
  15. data/lib/yahns.rb +73 -0
  16. data/lib/yahns/acceptor.rb +28 -0
  17. data/lib/yahns/client_expire.rb +40 -0
  18. data/lib/yahns/client_expire_portable.rb +39 -0
  19. data/lib/yahns/config.rb +344 -0
  20. data/lib/yahns/daemon.rb +51 -0
  21. data/lib/yahns/fdmap.rb +90 -0
  22. data/lib/yahns/http_client.rb +198 -0
  23. data/lib/yahns/http_context.rb +65 -0
  24. data/lib/yahns/http_response.rb +184 -0
  25. data/lib/yahns/log.rb +73 -0
  26. data/lib/yahns/queue.rb +7 -0
  27. data/lib/yahns/queue_egg.rb +23 -0
  28. data/lib/yahns/queue_epoll.rb +57 -0
  29. data/lib/yahns/rack.rb +80 -0
  30. data/lib/yahns/server.rb +336 -0
  31. data/lib/yahns/server_mp.rb +181 -0
  32. data/lib/yahns/sigevent.rb +7 -0
  33. data/lib/yahns/sigevent_efd.rb +18 -0
  34. data/lib/yahns/sigevent_pipe.rb +29 -0
  35. data/lib/yahns/socket_helper.rb +117 -0
  36. data/lib/yahns/stream_file.rb +34 -0
  37. data/lib/yahns/stream_input.rb +150 -0
  38. data/lib/yahns/tee_input.rb +114 -0
  39. data/lib/yahns/tmpio.rb +27 -0
  40. data/lib/yahns/wbuf.rb +36 -0
  41. data/lib/yahns/wbuf_common.rb +32 -0
  42. data/lib/yahns/worker.rb +58 -0
  43. data/test/covshow.rb +29 -0
  44. data/test/helper.rb +115 -0
  45. data/test/server_helper.rb +65 -0
  46. data/test/test_bin.rb +97 -0
  47. data/test/test_client_expire.rb +132 -0
  48. data/test/test_config.rb +56 -0
  49. data/test/test_fdmap.rb +19 -0
  50. data/test/test_output_buffering.rb +291 -0
  51. data/test/test_queue.rb +59 -0
  52. data/test/test_rack.rb +28 -0
  53. data/test/test_serve_static.rb +42 -0
  54. data/test/test_server.rb +415 -0
  55. data/test/test_stream_file.rb +30 -0
  56. data/test/test_wbuf.rb +136 -0
  57. data/yahns.gemspec +19 -0
  58. metadata +165 -0
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ CONSTANT = "Yahns::VERSION"
5
+ RVF = "lib/yahns/version.rb"
6
+ GVF = "GIT-VERSION-FILE"
7
+ DEF_VER = "v0.0.0TP1"
8
+ vn = DEF_VER
9
+
10
+ # First see if there is a version file (included in release tarballs),
11
+ # then try git-describe, then default.
12
+ if File.exist?(".git")
13
+ describe = `git describe --abbrev=4 HEAD 2>/dev/null`.strip
14
+ case describe
15
+ when /\Av[0-9]*/
16
+ vn = describe
17
+ system(*%w(git update-index -q --refresh))
18
+ unless `git diff-index --name-only HEAD --`.chomp.empty?
19
+ vn << "-dirty"
20
+ end
21
+ vn.tr!('-', '.')
22
+ end
23
+ end
24
+
25
+ vn = vn.sub!(/\Av/, "")
26
+ new_ruby_version = "#{CONSTANT} = '#{vn}' # :nodoc:\n"
27
+ cur_ruby_version = File.read(RVF) rescue nil
28
+ if new_ruby_version != cur_ruby_version
29
+ File.open(RVF, "w") { |fp| fp.write(new_ruby_version) }
30
+ end
31
+ File.chmod(0644, RVF)
32
+
33
+ # generate the makefile snippet
34
+ new_make_version = "VERSION = #{vn}\n"
35
+ cur_make_version = File.read(GVF) rescue nil
36
+ if new_make_version != cur_make_version
37
+ File.open(GVF, "w") { |fp| fp.write(new_make_version) }
38
+ end
39
+ File.chmod(0644, GVF)
40
+
41
+ puts vn if $0 == __FILE__
@@ -0,0 +1,90 @@
1
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
2
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
3
+ all::
4
+ pkg = yahns
5
+ RUBY = ruby
6
+ GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
7
+ @./GIT-VERSION-GEN
8
+ -include GIT-VERSION-FILE
9
+ lib := lib
10
+
11
+ all:: test
12
+ test_units := $(wildcard test/test_*.rb)
13
+ test: $(test_units)
14
+ $(test_units):
15
+ $(RUBY) -w -I $(lib) $@ -v
16
+
17
+ test-mt: export N = $(shell nproc 2>/dev/null || echo 4)
18
+ test-mt:
19
+ $(RUBY) -w -I $(lib) $(addprefix -r./,$(test_units)) -eexit --
20
+
21
+ check-warnings:
22
+ @(for i in $$(git ls-files '*.rb'| grep -v '^setup\.rb$$'); \
23
+ do $(RUBY) -d -W2 -c $$i; done) | grep -v '^Syntax OK$$' || :
24
+
25
+ check: test
26
+ coverage: export COVERAGE=1
27
+ coverage:
28
+ > coverage.dump
29
+ $(MAKE) check
30
+ $(RUBY) ./test/covshow.rb
31
+
32
+ coverage-mt: export COVERAGE=1
33
+ coverage-mt:
34
+ > coverage.dump
35
+ $(MAKE) test-mt
36
+ $(RUBY) ./test/covshow.rb
37
+
38
+ pkggem := pkg/$(pkg)-$(VERSION).gem
39
+ pkgtgz := pkg/$(pkg)-$(VERSION).tar.gz
40
+
41
+ fix-perms:
42
+ git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644
43
+ git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755
44
+
45
+ gem: $(pkggem)
46
+
47
+ install-gem: $(pkggem)
48
+ gem install $(CURDIR)/$<
49
+
50
+ $(pkggem): .gem-manifest
51
+ VERSION=$(VERSION) gem build $(pkg).gemspec
52
+ mkdir -p pkg
53
+ mv $(@F) $@
54
+
55
+ pkg_extra := GIT-VERSION-FILE lib/yahns/version.rb NEWS
56
+ NEWS:
57
+ rake -s $@
58
+
59
+ gem-man:
60
+ $(MAKE) -C Documentation/ gem-man
61
+ tgz-man:
62
+ $(MAKE) -C Documentation/ install-man mandir=$(CURDIR)/man
63
+ .PHONY: tgz-man gem-man
64
+
65
+ .gem-manifest: .manifest
66
+ # (ls man/*.?; cat .manifest) | LC_ALL=C sort > $@+
67
+ LC_ALL=C sort < .manifest > $@+
68
+ cmp $@+ $@ || mv $@+ $@; rm -f $@+
69
+ .tgz-manifest: .manifest
70
+ LC_ALL=C sort < .manifest > $@+
71
+ cmp $@+ $@ || mv $@+ $@; rm -f $@+
72
+ .manifest: NEWS fix-perms
73
+ rm -rf man
74
+ (git ls-files; \
75
+ for i in $(pkg_extra); do echo $$i; done) | \
76
+ LC_ALL=C sort > $@+
77
+ cmp $@+ $@ || mv $@+ $@; rm -f $@+
78
+ $(pkgtgz): distdir = pkg/$(pkg)-$(VERSION)
79
+ $(pkgtgz): .tgz-manifest
80
+ @test -n "$(distdir)"
81
+ $(RM) -r $(distdir)
82
+ mkdir -p $(distdir)
83
+ tar cf - $$(cat .tgz-manifest) | (cd $(distdir) && tar xf -)
84
+ cd pkg && tar cf - $(pkg)-$(VERSION) | gzip -9 > $(@F)+
85
+ mv $@+ $@
86
+
87
+ package: $(pkgtgz) $(pkggem)
88
+
89
+ .PHONY: all .FORCE-GIT-VERSION-FILE test $(test_units) NEWS
90
+ .PHONY: check-warnings fix-perms
data/README ADDED
@@ -0,0 +1,127 @@
1
+ yahns - sleepy, multi-threaded, non-blocking application server for Ruby
2
+ ------------------------------------------------------------------------
3
+
4
+ A Free Software, multi-threaded, non-blocking network application server
5
+ designed for low _idle_ power consumption. It is primarily optimized
6
+ for applications with occasional users which see little or no traffic.
7
+ yahns currently hosts Rack/HTTP applications, but may eventually support
8
+ other application types. Unlike some existing servers, yahns is
9
+ extremely sensitive to fatal bugs in the applications it hosts.
10
+
11
+ Features
12
+ --------
13
+
14
+ * _zero_ wakeups when all clients are idle
15
+ * idle client connections may live forever if there is no FD pressure
16
+ * suitable for slow clients, fast clients, or a mixture of both
17
+ * HTTP/0.9 support
18
+ * HTTP/1.1 persistent connections and pipelining
19
+ * decodes HTTP chunked encoding for requests
20
+ * parses HTTP/1.1 trailers in requests
21
+ * supports streaming responses with lazy buffering for slow clients
22
+ * optional streaming input for fast clients
23
+ * able to host multiple applications with different settings
24
+ * uses epoll to scale to many idle connections
25
+ * abuses epoll as a load balancer between threads inside a process
26
+ * optional multi-process support (in addition to threads)
27
+ * fairly balances new clients between multiple processes (on Linux)
28
+
29
+ Supported Platforms
30
+ -------------------
31
+
32
+ yahns is developed primarily for modern GNU/Linux systems.
33
+
34
+ We may support kqueue for FreeBSD/OpenBSD/NetBSD if there is significant
35
+ interest. Non-Free systems/dependencies will never be supported
36
+
37
+ Supported Ruby implementations:
38
+ * (Matz) Ruby 2.0 or later
39
+ * Rubinius 2.0 or later (planned)
40
+
41
+ Contact
42
+ -------
43
+
44
+ We are happy to see feedback of all types via plain-text email.
45
+ Please send comments, user/dev discussion, patches, bug reports,
46
+ and pull requests to Eric Wong at: normalperson@yhbt.net
47
+
48
+ Public mailing list coming soon.
49
+
50
+ This README is our homepage, we would rather be working on HTTP servers
51
+ all day than worrying about the next browser vulnerability because
52
+ HTML/CSS/JS is too complicated for us.
53
+
54
+ * http://yahns.yhbt.net/README
55
+
56
+ Hacking
57
+ -------
58
+
59
+ We use git and follow the same development model as git itself
60
+ (mailing list-oriented, benevolent dictator).
61
+
62
+ git clone git://yahns.yhbt.net/yahns
63
+
64
+ Please use git-format-patch(1) and git-send-email(1) distributed with
65
+ the git(7) suite for generating and sending patches. Please format
66
+ pull requests with the git-request-pull(1) script (also distributed
67
+ with git(7)) and send them via email.
68
+
69
+ See http://www.git-scm.com/ for more information on git.
70
+
71
+ Design
72
+ ------
73
+
74
+ yahns is designed to optimimally use multiple threads with non-blocking I/O.
75
+ The event loop is not a traditional single-threaded design with a mutex
76
+ slapped on as an afterthought, but designed from the beginning to utilize
77
+ multiple threads.
78
+
79
+ * two classes of long-lived, persistent threads
80
+ 1. blocking acceptors
81
+ 2. non-blocking event loop workers
82
+ * epoll acts as a queue (by using one-shot notifications)
83
+ * acceptors accept new clients and put them in the epoll "queue"
84
+ * workers pull clients off the queue, rearming them to epoll on EAGAIN
85
+
86
+ The end result is clients transition freely and fairly between threads
87
+ and will always be able to find the next idle thread to run on.
88
+
89
+ This design works with kqueue, too, and we will support kqueue if there
90
+ is interest. In fact, got our design inspiration from the name "kqueue"
91
+ when working on another project. We may also support libkqueue:
92
+
93
+ http://sourceforge.net/projects/libkqueue/
94
+
95
+ In addition to multiple threads, yahns optionally supports multiple
96
+ processes to work around low FD limits as well as contention in the:
97
+
98
+ * kernel (socket (de)allocation from accept/close)
99
+ * standard C library (malloc/free)
100
+ * Ruby VM (GVL, GC)
101
+ * application it hosts
102
+
103
+ Copyright
104
+ ---------
105
+
106
+ Copyright 2013, Eric Wong <normalperson@yhbt.net> and all contributors.
107
+ License: GPLv3 or later <https://www.gnu.org/licenses/gpl-3.0.txt>
108
+
109
+ yahns is copyrighted Free Software by all contributors, see logs in
110
+ revision control for names and email addresses of all of them. yahns
111
+ contains code from Mongrel, unicorn, and Rainbows! which may also be
112
+ licensed under the GPLv2 or later.
113
+
114
+ yahns is free software; you can redistribute it and/or modify it
115
+ under the terms of the GNU General Public License as published by the
116
+ Free Software Foundation; either version 3 of the License, or (at your
117
+ option) any later version.
118
+
119
+ yahns is distributed in the hope that it will be useful, but WITHOUT ANY
120
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
121
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
122
+ for more details.
123
+
124
+ You should have received a copy of the GNU General Public License along
125
+ with this program; if not, see https://www.gnu.org/licenses/gpl-3.0.txt
126
+
127
+ lrg nabgure ubeevoyl-anzrq freire :>
@@ -0,0 +1,60 @@
1
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
2
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
3
+ require 'tempfile'
4
+ include Rake::DSL
5
+ task "NEWS" do
6
+ latest = nil
7
+ fp = Tempfile.new("NEWS", ".")
8
+ fp.sync = true
9
+ `git tag -l`.split(/\n/).reverse.each do |tag|
10
+ %r{\Av(.+)} =~ tag or next
11
+ version = $1
12
+ header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
13
+ header = header.split(/\n/)
14
+ tagger = header.grep(/\Atagger /)[0]
15
+ time = Time.at(tagger.split(/ /)[-2].to_i).utc
16
+ latest ||= time
17
+ date = time.strftime("%Y-%m-%d")
18
+ fp.puts "# #{version} / #{date}\n\n#{subject}"
19
+ if body && body.strip.size > 0
20
+ fp.puts "\n\n#{body}"
21
+ end
22
+ fp.puts
23
+ end
24
+ fp.write("Unreleased\n\n") unless fp.size > 0
25
+ fp.puts "# COPYRIGHT"
26
+ bdfl = 'Eric Wong <normalperson@yhbt.net>'
27
+ fp.puts "Copyright (C) 2013, #{bdfl} and all contributors"
28
+ fp.puts "License: GPLv3 or later (http://www.gnu.org/licenses/gpl-3.0.txt)"
29
+ fp.rewind
30
+ assert_equal fp.read, File.read("NEWS") rescue nil
31
+ fp.chmod 0644
32
+ File.rename(fp.path, "NEWS")
33
+ fp.close!
34
+ end
35
+
36
+ task rsync_docs: "NEWS" do
37
+ dest = ENV["RSYNC_DEST"] || "yahns.yhbt.net:/srv/yahns/"
38
+ top = %w(NEWS README COPYING)
39
+ files = []
40
+
41
+ # git-set-file-times is distributed with rsync,
42
+ # Also available at: http://yhbt.net/git-set-file-times
43
+ # on Debian systems: /usr/share/doc/rsync/scripts/git-set-file-times.gz
44
+ sh("git", "set-file-times", "examples", *top)
45
+
46
+ `git ls-files Documentation/*.txt`.split(/\n/).concat(top).each do |txt|
47
+ gz = "#{txt}.gz"
48
+ tmp = "#{gz}.#$$"
49
+ sh("gzip -9 < #{txt} > #{tmp}")
50
+ st = File.stat(txt)
51
+ File.utime(st.atime, st.mtime, tmp) # make nginx gzip_static happy
52
+ File.rename(tmp, gz)
53
+ files << txt
54
+ files << gz
55
+ end
56
+ sh("rsync --chmod=Fugo=r -av #{files.join(' ')} #{dest}")
57
+
58
+ examples = `git ls-files examples`.split("\n")
59
+ sh("rsync --chmod=Fugo=r -av #{examples.join(' ')} #{dest}/examples/")
60
+ end
@@ -0,0 +1,32 @@
1
+ #!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby
2
+ # -*- encoding: binary -*-
3
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
4
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
5
+ $stdout.sync = $stderr.sync = true
6
+ require 'yahns'
7
+ require 'optparse'
8
+ config_file = daemonize = nil
9
+ OptionParser.new("", 24, " ") do |opts|
10
+ cmd = File.basename($0)
11
+ opts.banner = "Usage: #{cmd} [options]"
12
+ opts.separator "#{cmd} options:"
13
+ opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
14
+ daemonize = !!d
15
+ end
16
+ opts.on("-c", "--config-file FILE", "yahns config file") do |f|
17
+ config_file = f
18
+ end
19
+ opts.separator "Common options:"
20
+ opts.on_tail("-h", "--help", "Show this message") do
21
+ puts opts.to_s
22
+ exit
23
+ end
24
+ opts.on_tail("-v", "--version", "Show version") do
25
+ puts "#{cmd} v#{Yahns::VERSION}"
26
+ exit
27
+ end
28
+ opts.parse!(ARGV)
29
+ end
30
+ server = Yahns::Server.new(Yahns::Config.new(config_file))
31
+ Yahns::Daemon.daemon(server) if daemonize
32
+ server.start.join
@@ -0,0 +1,3 @@
1
+ All files in this example directory (including this one) are CC0:
2
+ To the extent possible under law, Eric Wong has waived all copyright and
3
+ related or neighboring rights to these examples.
@@ -0,0 +1,76 @@
1
+ #!/bin/sh
2
+ # To the extent possible under law, Eric Wong has waived all copyright and
3
+ # related or neighboring rights to this examples
4
+ set -e
5
+ # Example init script, this can be used with nginx, too,
6
+ # since nginx and yahns accept the same signals
7
+
8
+ # Feel free to change any of the following variables for your app:
9
+ TIMEOUT=${TIMEOUT-60}
10
+ APP_ROOT=/home/x/my_app/current
11
+ PID=$APP_ROOT/tmp/pids/yahns.pid
12
+ CMD="/usr/bin/yahns -D -c $APP_ROOT/config/yahns.rb"
13
+ INIT_CONF=$APP_ROOT/config/init.conf
14
+ action="$1"
15
+ set -u
16
+
17
+ test -f "$INIT_CONF" && . $INIT_CONF
18
+
19
+ old_pid="$PID.oldbin"
20
+
21
+ cd $APP_ROOT || exit 1
22
+
23
+ sig () {
24
+ test -s "$PID" && kill -$1 `cat $PID`
25
+ }
26
+
27
+ oldsig () {
28
+ test -s $old_pid && kill -$1 `cat $old_pid`
29
+ }
30
+
31
+ case $action in
32
+ start)
33
+ sig 0 && echo >&2 "Already running" && exit 0
34
+ $CMD
35
+ ;;
36
+ stop)
37
+ sig QUIT && exit 0
38
+ echo >&2 "Not running"
39
+ ;;
40
+ force-stop)
41
+ sig TERM && exit 0
42
+ echo >&2 "Not running"
43
+ ;;
44
+ restart|reload)
45
+ sig HUP && echo reloaded OK && exit 0
46
+ echo >&2 "Couldn't reload, starting '$CMD' instead"
47
+ $CMD
48
+ ;;
49
+ upgrade)
50
+ if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
51
+ then
52
+ n=$TIMEOUT
53
+ while test -s $old_pid && test $n -ge 0
54
+ do
55
+ printf '.' && sleep 1 && n=$(( $n - 1 ))
56
+ done
57
+ echo
58
+
59
+ if test $n -lt 0 && test -s $old_pid
60
+ then
61
+ echo >&2 "$old_pid still exists after $TIMEOUT seconds"
62
+ exit 1
63
+ fi
64
+ exit 0
65
+ fi
66
+ echo >&2 "Couldn't upgrade, starting '$CMD' instead"
67
+ $CMD
68
+ ;;
69
+ reopen-logs)
70
+ sig USR1
71
+ ;;
72
+ *)
73
+ echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
74
+ exit 1
75
+ ;;
76
+ esac
@@ -0,0 +1,28 @@
1
+ # To the extent possible under law, Eric Wong has waived all copyright and
2
+ # related or neighboring rights to this examples
3
+ #
4
+ # Multi-Processing-safe monkey patch for Logger
5
+ #
6
+ # This monkey patch fixes the case where "preload: true" is used and
7
+ # the application spawns a background thread upon being loaded.
8
+ #
9
+ # This removes all lock from the Logger code and solely relies on the
10
+ # underlying filesystem to handle write(2) system calls atomically when
11
+ # O_APPEND is used. This is safe in the presence of both multiple
12
+ # threads (native or green) and multiple processes when writing to
13
+ # a filesystem with POSIX O_APPEND semantics.
14
+ #
15
+ # It should be noted that the original locking on Logger could _never_ be
16
+ # considered reliable on non-POSIX filesystems with multiple processes,
17
+ # either, so nothing is lost in that case.
18
+
19
+ require 'logger'
20
+ class Logger::LogDevice
21
+ def write(message)
22
+ @dev.syswrite(message)
23
+ end
24
+
25
+ def close
26
+ @dev.close
27
+ end
28
+ end