thin 1.8.0 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of thin might be problematic. Click here for more details.

Files changed (108) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG +29 -107
  3. data/Gemfile +8 -0
  4. data/README.md +44 -78
  5. data/Rakefile +28 -18
  6. data/bin/thin +4 -4
  7. data/examples/async.ru +21 -0
  8. data/examples/thin.conf.rb +39 -0
  9. data/lib/thin.rb +2 -44
  10. data/lib/thin/async.rb +108 -0
  11. data/lib/thin/backends/prefork.rb +44 -0
  12. data/lib/thin/backends/single_process.rb +28 -0
  13. data/lib/thin/chunked_body.rb +28 -0
  14. data/lib/thin/configurator.rb +118 -0
  15. data/lib/thin/connection.rb +246 -172
  16. data/lib/thin/listener.rb +114 -0
  17. data/lib/thin/request.rb +94 -76
  18. data/lib/thin/response.rb +112 -45
  19. data/lib/thin/runner.rb +134 -197
  20. data/lib/thin/server.rb +203 -252
  21. data/lib/thin/system.rb +49 -0
  22. data/lib/thin/version.rb +11 -26
  23. data/man/index.txt +3 -0
  24. data/man/thin-conf.5.ronn +121 -0
  25. data/man/thin.1.ronn +105 -0
  26. data/site/.gitignore +2 -0
  27. data/site/README.md +21 -0
  28. data/site/Rakefile +20 -0
  29. data/site/config.ru +4 -0
  30. data/site/public/images/grid.png +0 -0
  31. data/site/public/javascripts/dd_belatedpng.js +13 -0
  32. data/site/public/javascripts/modernizr-1.6.min.js +30 -0
  33. data/site/public/man/thin-conf.5.html +220 -0
  34. data/site/public/man/thin.1.html +177 -0
  35. data/site/site/assets/javascripts/main.coffee +2 -0
  36. data/site/site/assets/stylesheets/_config.scss +55 -0
  37. data/site/site/assets/stylesheets/main.scss +24 -0
  38. data/site/site/helpers.rb +17 -0
  39. data/site/site/layouts/base.erb +55 -0
  40. data/site/site/layouts/default.erb +17 -0
  41. data/site/site/pages/about.md +5 -0
  42. data/site/site/pages/index.erb +10 -0
  43. data/site/site/partials/.gitkeep +0 -0
  44. data/test/fixtures/big.txt +1 -0
  45. data/test/fixtures/small.txt +1 -0
  46. data/test/fixtures/thin.conf.rb +15 -0
  47. data/test/integration/async_test.rb +35 -0
  48. data/test/integration/big_request_test.rb +30 -0
  49. data/test/integration/config.ru +57 -0
  50. data/test/integration/daemonize_test.rb +26 -0
  51. data/test/integration/env_test.rb +44 -0
  52. data/test/integration/error_test.rb +37 -0
  53. data/test/integration/file_sending_test.rb +24 -0
  54. data/test/integration/keep_alive_test.rb +35 -0
  55. data/test/integration/robustness_test.rb +37 -0
  56. data/test/integration/single_process_test.rb +15 -0
  57. data/test/integration/socket_family_test.rb +38 -0
  58. data/test/integration/worker_test.rb +22 -0
  59. data/test/test_helper.rb +195 -0
  60. data/test/unit/configurator_test.rb +43 -0
  61. data/test/unit/connection_test.rb +94 -0
  62. data/test/unit/listener_test.rb +74 -0
  63. data/test/unit/request_test.rb +74 -0
  64. data/test/unit/response_test.rb +90 -0
  65. data/test/unit/server_test.rb +29 -0
  66. data/test/unit/system_test.rb +17 -0
  67. data/thin.gemspec +26 -0
  68. data/v2.todo +21 -0
  69. metadata +138 -93
  70. checksums.yaml +0 -7
  71. data/example/adapter.rb +0 -32
  72. data/example/async_app.ru +0 -126
  73. data/example/async_chat.ru +0 -247
  74. data/example/async_tailer.ru +0 -100
  75. data/example/config.ru +0 -22
  76. data/example/monit_sockets +0 -20
  77. data/example/monit_unixsock +0 -20
  78. data/example/myapp.rb +0 -1
  79. data/example/ramaze.ru +0 -12
  80. data/example/thin.god +0 -80
  81. data/example/thin_solaris_smf.erb +0 -36
  82. data/example/thin_solaris_smf.readme.txt +0 -150
  83. data/example/vlad.rake +0 -72
  84. data/ext/thin_parser/common.rl +0 -59
  85. data/ext/thin_parser/ext_help.h +0 -14
  86. data/ext/thin_parser/extconf.rb +0 -6
  87. data/ext/thin_parser/parser.c +0 -1447
  88. data/ext/thin_parser/parser.h +0 -49
  89. data/ext/thin_parser/parser.rl +0 -152
  90. data/ext/thin_parser/thin.c +0 -435
  91. data/lib/rack/adapter/loader.rb +0 -75
  92. data/lib/rack/adapter/rails.rb +0 -178
  93. data/lib/rack/handler/thin.rb +0 -38
  94. data/lib/thin/backends/base.rb +0 -169
  95. data/lib/thin/backends/swiftiply_client.rb +0 -56
  96. data/lib/thin/backends/tcp_server.rb +0 -34
  97. data/lib/thin/backends/unix_server.rb +0 -56
  98. data/lib/thin/command.rb +0 -53
  99. data/lib/thin/controllers/cluster.rb +0 -178
  100. data/lib/thin/controllers/controller.rb +0 -189
  101. data/lib/thin/controllers/service.rb +0 -76
  102. data/lib/thin/controllers/service.sh.erb +0 -39
  103. data/lib/thin/daemonizing.rb +0 -199
  104. data/lib/thin/headers.rb +0 -40
  105. data/lib/thin/logging.rb +0 -174
  106. data/lib/thin/stats.html.erb +0 -216
  107. data/lib/thin/stats.rb +0 -52
  108. data/lib/thin/statuses.rb +0 -48
data/bin/thin CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- # Thin command line interface script.
3
- # Run <tt>thin -h</tt> to get more usage.
4
2
 
5
- require 'thin'
6
- Thin::Runner.new(ARGV).run!
3
+ require "thin"
4
+ require "thin/runner"
5
+
6
+ Thin::Runner.run(ARGV)
@@ -0,0 +1,21 @@
1
+ require "thin/async"
2
+
3
+ class Async
4
+ def call(env)
5
+ response = Thin::AsyncResponse.new(env)
6
+
7
+ # Webkit requires some padding before displaying something.
8
+ response << " " * 1024
9
+
10
+ response << "this is ... "
11
+ # Will be sent to the browse 1 sec after.
12
+ EM.add_timer(1) do
13
+ response << "async stuff!"
14
+ response.done # close the connection
15
+ end
16
+
17
+ response.finish
18
+ end
19
+ end
20
+
21
+ run Async.new
@@ -0,0 +1,39 @@
1
+ # Event backend options
2
+ worker_connections 1024
3
+ # Disabling epoll or kqueue will fallback to select.
4
+ # use_epoll false
5
+ # use_kqueue false
6
+
7
+ # Threading
8
+ # For slow apps, you can enable the use of threads.
9
+ # If you're using this in Rails, make sure to call config.threadsafe!
10
+ threaded true # Call the app in a thread.
11
+ thread_pool_size 20
12
+
13
+ # Worker options
14
+ # worker_processes 0 # runs in a single process w/ limited features.
15
+ worker_processes 4
16
+ timeout 30 # seconds
17
+
18
+ # Preload the app (the .ru file) before forking to workers.
19
+ # Enable with copy-on-write garbage collection for better memory usage.
20
+ preload_app true
21
+
22
+ # Logging
23
+ log_path "./thin.log"
24
+ pid_path "./thin.pid"
25
+
26
+ # Listeners
27
+ listen 3000, :backlog => 1024, :tcp_no_delay => true
28
+ listen "0.0.0.0:8080"
29
+ listen "[::]:8081" # IPv6
30
+ listen "/tmp/thin.sock" # UNIX domain socket
31
+
32
+ # Callbacks
33
+ before_fork do |server|
34
+ puts "Preparing to fork a new worker ..."
35
+ end
36
+
37
+ after_fork do |server|
38
+ puts "Worker forked!"
39
+ end
@@ -1,45 +1,3 @@
1
- require 'fileutils'
2
- require 'timeout'
3
- require 'stringio'
4
- require 'time'
5
- require 'forwardable'
6
- require 'openssl'
7
- require 'eventmachine'
8
- require 'rack'
9
-
10
- module Thin
11
- autoload :Command, "thin/command"
12
- autoload :Connection, "thin/connection"
13
- autoload :Daemonizable, "thin/daemonizing"
14
- autoload :Logging, "thin/logging"
15
- autoload :Headers, "thin/headers"
16
- autoload :Request, "thin/request"
17
- autoload :Response, "thin/response"
18
- autoload :Runner, "thin/runner"
19
- autoload :Server, "thin/server"
20
- autoload :Stats, "thin/stats"
21
-
22
- module Backends
23
- autoload :Base, "thin/backends/base"
24
- autoload :SwiftiplyClient, "thin/backends/swiftiply_client"
25
- autoload :TcpServer, "thin/backends/tcp_server"
26
- autoload :UnixServer, "thin/backends/unix_server"
27
- end
28
-
29
- module Controllers
30
- autoload :Cluster, "thin/controllers/cluster"
31
- autoload :Controller, "thin/controllers/controller"
32
- autoload :Service, "thin/controllers/service"
33
- end
34
- end
35
-
36
1
  require "thin/version"
37
- require "thin/statuses"
38
- require "rack/adapter/loader"
39
- require "thin_parser"
40
-
41
- module Rack
42
- module Adapter
43
- autoload :Rails, "rack/adapter/rails"
44
- end
45
- end
2
+ require "thin/system"
3
+ require "thin/server"
@@ -0,0 +1,108 @@
1
+ module Thin
2
+ unless defined?(DeferrableBody)
3
+ # Based on version from James Tucker <raggi@rubyforge.org>
4
+ class DeferrableBody
5
+ include EM::Deferrable
6
+
7
+ def initialize
8
+ @queue = []
9
+ end
10
+
11
+ def call(body)
12
+ @queue << body
13
+ schedule_dequeue
14
+ end
15
+
16
+ def each(&blk)
17
+ @body_callback = blk
18
+ schedule_dequeue
19
+ end
20
+
21
+ private
22
+ def schedule_dequeue
23
+ return unless @body_callback
24
+ EM.next_tick do
25
+ next unless body = @queue.shift
26
+ body.each do |chunk|
27
+ @body_callback.call(chunk)
28
+ end
29
+ schedule_dequeue unless @queue.empty?
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ # Response whos body is sent asynchronously.
36
+ #
37
+ # A nice wrapper around Thin's obscure async callback used to send response body asynchronously.
38
+ # Which means you can send the response in chunks while allowing Thin to process other requests.
39
+ #
40
+ # Crazy delicious with em-http-request for file upload, image processing, proxying, etc.
41
+ #
42
+ # == _WARNING_
43
+ # You should not use long blocking operations (Net::HTTP or slow shell calls) with this as it
44
+ # will prevent the EventMachine event loop from running and block all other requests.
45
+ #
46
+ # Also disable the Rack::Lint middleware to use Thin's async feature since it requires sending
47
+ # back an invalid status code to the server.
48
+ #
49
+ # == Usage
50
+ # Inside your Rack app #call(env):
51
+ #
52
+ # response = Thin::AsyncResponse.new(env)
53
+ # response.status = 201
54
+ # response.headers["X-Muffin-Mode"] = "ACTIVATED!"
55
+ #
56
+ # response << "this is ... "
57
+ #
58
+ # EM.add_timer(1) do
59
+ # # This will be sent to the client 1 sec later without blocking other requests.
60
+ # response << "async!"
61
+ # response.done
62
+ # end
63
+ #
64
+ # response.finish
65
+ #
66
+ class AsyncResponse
67
+ include Rack::Response::Helpers
68
+
69
+ Marker = [-1, {}, []].freeze
70
+
71
+ attr_reader :headers, :callback
72
+ attr_accessor :status
73
+
74
+ def initialize(env, status=200, headers={})
75
+ @callback = env['async.callback']
76
+ @body = DeferrableBody.new
77
+ @status = status
78
+ @headers = headers
79
+ @headers_sent = false
80
+
81
+ yield self if block_given?
82
+ end
83
+
84
+ def send_headers(response=nil)
85
+ return if @headers_sent
86
+ @callback.call response || [@status, @headers, @body]
87
+ @headers_sent = true
88
+ end
89
+
90
+ def write(body)
91
+ send_headers
92
+ @body.call(body.respond_to?(:each) ? body : [body])
93
+ end
94
+ alias :<< :write
95
+
96
+ # Tell Thin the response is complete and the connection can be closed.
97
+ def done(response=nil)
98
+ send_headers(response)
99
+ EM.next_tick { @body.succeed }
100
+ end
101
+
102
+ # Tell Thin the response is gonna be sent asynchronously.
103
+ # The status code of -1 is the magic trick here.
104
+ def finish
105
+ Marker
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,44 @@
1
+ require "preforker"
2
+
3
+ module Thin
4
+ module Backends
5
+ class Prefork
6
+ def initialize(server)
7
+ @server = server
8
+ end
9
+
10
+ def start(daemonize)
11
+ @server.before_fork.call(@server) if @server.before_fork
12
+
13
+ @prefork = Preforker.new(
14
+ :app_name => @server.to_s,
15
+ :workers => @server.worker_processes,
16
+ :timeout => @server.timeout,
17
+ :pid_path => @server.pid_path,
18
+ :stderr_path => @server.log_path,
19
+ :stdout_path => @server.log_path,
20
+ :logger => Logger.new(@server.log_path || $stdout)
21
+ ) do |master|
22
+
23
+ EM.run do
24
+ EM.add_periodic_timer(4) do
25
+ EM.stop_event_loop unless master.wants_me_alive?
26
+ end
27
+
28
+ @server.after_fork.call(@server, master) if @server.after_fork
29
+
30
+ yield
31
+ end
32
+ end
33
+
34
+ if daemonize
35
+ @prefork.start
36
+ else
37
+ @prefork.run
38
+ end
39
+
40
+ at_exit { @server.stop }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ module Thin
2
+ module Backends
3
+ class SingleProcess
4
+ def initialize(server)
5
+ @server = server
6
+ end
7
+
8
+ def start(daemonize)
9
+ raise NotImplementedError, "Daemonization not supported in single process mode" if daemonize
10
+
11
+ @pid_manager = Preforker::PidManager.new(@server.pid_path)
12
+
13
+ $0 = @server.to_s
14
+
15
+ # Install signals
16
+ %w( INT TERM ).each { |signal| trap(signal, "EXIT") }
17
+ at_exit do
18
+ @server.stop
19
+ @pid_manager.unlink
20
+ end
21
+
22
+ EM.run do
23
+ yield
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module Thin
2
+ # Same as Rack::Chunked::Body, but doesn't send the tail automaticaly.
3
+ class ChunkedBody
4
+ TERM = "\r\n"
5
+ TAIL = "0#{TERM}#{TERM}"
6
+
7
+ include Rack::Utils
8
+
9
+ def initialize(body)
10
+ @body = body
11
+ end
12
+
13
+ def each
14
+ term = TERM
15
+ @body.each do |chunk|
16
+ size = bytesize(chunk)
17
+ next if size == 0
18
+
19
+ chunk = chunk.dup.force_encoding(Encoding::BINARY) if chunk.respond_to?(:force_encoding)
20
+ yield [size.to_s(16), term, chunk, term].join
21
+ end
22
+ end
23
+
24
+ def close
25
+ @body.close if @body.respond_to?(:close)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,118 @@
1
+ module Thin
2
+ class Configurator
3
+ attr_reader :options
4
+
5
+ def initialize(defaults={}, &block)
6
+ @options = defaults.dup
7
+ @options[:listeners] ||= []
8
+
9
+ instance_eval(&block) if block
10
+ end
11
+
12
+ # {include:Thin::Server#worker_processes}
13
+ # @see Thin::Server#worker_processes
14
+ def worker_processes(number)
15
+ set :worker_processes, number, Integer
16
+ end
17
+
18
+ # {include:Thin::Server#worker_connections}
19
+ # @see Thin::Server#worker_connections
20
+ def worker_connections(number)
21
+ set :worker_connections, number, Integer
22
+ end
23
+
24
+ # {include:Thin::Server#listen}
25
+ # @see Thin::Server#listen
26
+ def listen(address, options={})
27
+ @options[:listeners] << Listener.new(address, options)
28
+ end
29
+
30
+ # {include:Thin::Server#preload_app}
31
+ # @see Thin::Server#preload_app
32
+ def preload_app(value)
33
+ set :preload_app, value, TrueClass, FalseClass
34
+ end
35
+
36
+ # {include:Thin::Server#timeout}
37
+ # @see Thin::Server#timeout
38
+ def timeout(seconds)
39
+ set :timeout, seconds, Integer
40
+ end
41
+
42
+ # {include:Thin::Server#timeout}
43
+ # @see Thin::Server#timeout
44
+ def keep_alive_requests(number)
45
+ set :max_keep_alive_requests, number, Integer
46
+ end
47
+
48
+ # {include:Thin::Server#log_path}
49
+ # @see Thin::Server#log_path
50
+ def log_path(path)
51
+ set :log_path, path, String
52
+ end
53
+
54
+ # {include:Thin::Server#pid_path}
55
+ # @see Thin::Server#pid_path
56
+ def pid_path(path)
57
+ set :pid_path, path, String
58
+ end
59
+
60
+ # {include:Thin::Server#use_epoll}
61
+ # @see Thin::Server#use_epoll
62
+ def use_epoll(value)
63
+ set :use_epoll, value, TrueClass, FalseClass
64
+ end
65
+
66
+ # {include:Thin::Server#use_kqueue}
67
+ # @see Thin::Server#use_kqueue
68
+ def use_kqueue(value)
69
+ set :use_kqueue, value, TrueClass, FalseClass
70
+ end
71
+
72
+ # {include:Thin::Server#threaded}
73
+ # @see Thin::Server#threaded
74
+ def threaded(value)
75
+ set :threaded, value, TrueClass, FalseClass
76
+ end
77
+
78
+ # {include:Thin::Server#thread_pool_size}
79
+ # @see Thin::Server#thread_pool_size
80
+ def thread_pool_size(value)
81
+ set :thread_pool_size, value, Integer
82
+ end
83
+
84
+ # {include:Thin::Server#before_fork}
85
+ # @see Thin::Server#before_fork
86
+ def before_fork(&block)
87
+ @options[:before_fork] = block
88
+ end
89
+
90
+ # {include:Thin::Server#after_fork}
91
+ # @see Thin::Server#after_fork
92
+ def after_fork(&block)
93
+ @options[:after_fork] = block
94
+ end
95
+
96
+ # Apply this configuration to the +server+ instance.
97
+ # @param [Thin::Server] server
98
+ def apply(server)
99
+ @options.each_pair { |name, value| server.send "#{name}=", value }
100
+ server
101
+ end
102
+
103
+ # Read and eval a configuration file and returns the resulting Configurator instance.
104
+ def self.load(file)
105
+ config = new
106
+ config.instance_eval(File.read(file), file)
107
+ config
108
+ end
109
+
110
+ private
111
+ def set(name, value, *classes)
112
+ if classes.any? && classes.none? { |c| value.is_a?(c) }
113
+ raise ArgumentError, "#{name}: #{value.inspect} is not of type " + classes.join(' or ')
114
+ end
115
+ @options[name] = value
116
+ end
117
+ end
118
+ end