thin 0.5.3-x86-mswin32-60

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 (117) hide show
  1. data/CHANGELOG +40 -0
  2. data/COPYING +18 -0
  3. data/README +60 -0
  4. data/Rakefile +11 -0
  5. data/benchmark/simple.rb +48 -0
  6. data/bin/thin +123 -0
  7. data/doc/benchmarks.txt +86 -0
  8. data/doc/rdoc/classes/Process.html +181 -0
  9. data/doc/rdoc/classes/Rack.html +156 -0
  10. data/doc/rdoc/classes/Rack/Adapter.html +155 -0
  11. data/doc/rdoc/classes/Rack/Adapter/Rails.html +289 -0
  12. data/doc/rdoc/classes/Rack/Adapter/Rails/CGIWrapper.html +359 -0
  13. data/doc/rdoc/classes/Rack/Handler.html +155 -0
  14. data/doc/rdoc/classes/Rack/Handler/Thin.html +175 -0
  15. data/doc/rdoc/classes/Thin.html +164 -0
  16. data/doc/rdoc/classes/Thin/Cluster.html +399 -0
  17. data/doc/rdoc/classes/Thin/Connection.html +223 -0
  18. data/doc/rdoc/classes/Thin/Daemonizable.html +260 -0
  19. data/doc/rdoc/classes/Thin/Daemonizable/ClassMethods.html +197 -0
  20. data/doc/rdoc/classes/Thin/Headers.html +238 -0
  21. data/doc/rdoc/classes/Thin/InvalidRequest.html +144 -0
  22. data/doc/rdoc/classes/Thin/Logging.html +201 -0
  23. data/doc/rdoc/classes/Thin/Request.html +231 -0
  24. data/doc/rdoc/classes/Thin/Response.html +271 -0
  25. data/doc/rdoc/classes/Thin/Server.html +295 -0
  26. data/doc/rdoc/classes/Thin/StopServer.html +143 -0
  27. data/doc/rdoc/created.rid +1 -0
  28. data/doc/rdoc/files/README.html +226 -0
  29. data/doc/rdoc/files/bin/thin.html +245 -0
  30. data/doc/rdoc/files/lib/rack/adapter/rails_rb.html +146 -0
  31. data/doc/rdoc/files/lib/rack/handler/thin_rb.html +146 -0
  32. data/doc/rdoc/files/lib/thin/cluster_rb.html +146 -0
  33. data/doc/rdoc/files/lib/thin/connection_rb.html +145 -0
  34. data/doc/rdoc/files/lib/thin/daemonizing_rb.html +146 -0
  35. data/doc/rdoc/files/lib/thin/headers_rb.html +146 -0
  36. data/doc/rdoc/files/lib/thin/logging_rb.html +146 -0
  37. data/doc/rdoc/files/lib/thin/request_rb.html +145 -0
  38. data/doc/rdoc/files/lib/thin/response_rb.html +145 -0
  39. data/doc/rdoc/files/lib/thin/server_rb.html +145 -0
  40. data/doc/rdoc/files/lib/thin/statuses_rb.html +145 -0
  41. data/doc/rdoc/files/lib/thin/version_rb.html +145 -0
  42. data/doc/rdoc/files/lib/thin_rb.html +152 -0
  43. data/doc/rdoc/index.html +10 -0
  44. data/doc/rdoc/logo.gif +0 -0
  45. data/doc/rdoc/rdoc-style.css +55 -0
  46. data/example/config.ru +9 -0
  47. data/example/thin.god +72 -0
  48. data/ext/thin_parser/common.rl +54 -0
  49. data/ext/thin_parser/ext_help.h +14 -0
  50. data/ext/thin_parser/extconf.rb +6 -0
  51. data/ext/thin_parser/parser.c +1199 -0
  52. data/ext/thin_parser/parser.h +49 -0
  53. data/ext/thin_parser/parser.rl +143 -0
  54. data/ext/thin_parser/thin.c +424 -0
  55. data/lib/rack/adapter/rails.rb +155 -0
  56. data/lib/rack/handler/thin.rb +13 -0
  57. data/lib/thin.rb +36 -0
  58. data/lib/thin/cluster.rb +106 -0
  59. data/lib/thin/connection.rb +46 -0
  60. data/lib/thin/daemonizing.rb +112 -0
  61. data/lib/thin/headers.rb +37 -0
  62. data/lib/thin/logging.rb +23 -0
  63. data/lib/thin/request.rb +72 -0
  64. data/lib/thin/response.rb +48 -0
  65. data/lib/thin/server.rb +80 -0
  66. data/lib/thin/statuses.rb +43 -0
  67. data/lib/thin/version.rb +11 -0
  68. data/lib/thin_parser.so +0 -0
  69. data/spec/cluster_spec.rb +58 -0
  70. data/spec/daemonizing_spec.rb +93 -0
  71. data/spec/headers_spec.rb +35 -0
  72. data/spec/rack_rails_spec.rb +92 -0
  73. data/spec/rails_app/app/controllers/application.rb +10 -0
  74. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  75. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  76. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  77. data/spec/rails_app/config/boot.rb +109 -0
  78. data/spec/rails_app/config/environment.rb +64 -0
  79. data/spec/rails_app/config/environments/development.rb +18 -0
  80. data/spec/rails_app/config/environments/production.rb +19 -0
  81. data/spec/rails_app/config/environments/test.rb +22 -0
  82. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  83. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  84. data/spec/rails_app/config/routes.rb +35 -0
  85. data/spec/rails_app/public/404.html +30 -0
  86. data/spec/rails_app/public/422.html +30 -0
  87. data/spec/rails_app/public/500.html +30 -0
  88. data/spec/rails_app/public/dispatch.cgi +10 -0
  89. data/spec/rails_app/public/dispatch.fcgi +24 -0
  90. data/spec/rails_app/public/dispatch.rb +10 -0
  91. data/spec/rails_app/public/favicon.ico +0 -0
  92. data/spec/rails_app/public/images/rails.png +0 -0
  93. data/spec/rails_app/public/index.html +277 -0
  94. data/spec/rails_app/public/javascripts/application.js +2 -0
  95. data/spec/rails_app/public/javascripts/controls.js +963 -0
  96. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  97. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  98. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  99. data/spec/rails_app/public/robots.txt +5 -0
  100. data/spec/rails_app/script/about +3 -0
  101. data/spec/rails_app/script/console +3 -0
  102. data/spec/rails_app/script/destroy +3 -0
  103. data/spec/rails_app/script/generate +3 -0
  104. data/spec/rails_app/script/performance/benchmarker +3 -0
  105. data/spec/rails_app/script/performance/profiler +3 -0
  106. data/spec/rails_app/script/performance/request +3 -0
  107. data/spec/rails_app/script/plugin +3 -0
  108. data/spec/rails_app/script/process/inspector +3 -0
  109. data/spec/rails_app/script/process/reaper +3 -0
  110. data/spec/rails_app/script/process/spawner +3 -0
  111. data/spec/rails_app/script/runner +3 -0
  112. data/spec/rails_app/script/server +3 -0
  113. data/spec/request_spec.rb +258 -0
  114. data/spec/response_spec.rb +56 -0
  115. data/spec/server_spec.rb +75 -0
  116. data/spec/spec_helper.rb +127 -0
  117. metadata +219 -0
@@ -0,0 +1,37 @@
1
+ module Thin
2
+ # Acts like a Hash, but allows duplicated keys
3
+ class Headers
4
+ HEADER_FORMAT = "%s: %s\r\n".freeze
5
+ ALLOWED_DUPLICATES = %w(Set-Cookie Set-Cookie2 Warning WWW-Authenticate).freeze
6
+
7
+ def initialize
8
+ @sent = {}
9
+ @items = []
10
+ end
11
+
12
+ def []=(key, value)
13
+ if @sent.has_key?(key) && !ALLOWED_DUPLICATES.include?(key)
14
+ # If we don't allow duplicate for that field
15
+ # we overwrite the one that is already there
16
+ @items.assoc(key)[1] = value
17
+ else
18
+ @sent[key] = true
19
+ @items << [key, value]
20
+ end
21
+ end
22
+
23
+ def [](key)
24
+ if item = @items.assoc(key)
25
+ item[1]
26
+ end
27
+ end
28
+
29
+ def size
30
+ @items.size
31
+ end
32
+
33
+ def to_s
34
+ @items.inject('') { |out, (name, value)| out << HEADER_FORMAT % [name, value] }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module Thin
2
+ # To be included into classes to allow some basic logging
3
+ # that can be silented (+silent+) or made more verbose ($DEBUG=true).
4
+ module Logging
5
+ # Don't output any message if +true+.
6
+ attr_accessor :silent
7
+
8
+ protected
9
+ # Log a message to the console
10
+ def log(msg)
11
+ puts msg unless @silent
12
+ end
13
+
14
+ # Log a message to the console if tracing is activated
15
+ def trace(msg=nil)
16
+ puts msg || yield if $DEBUG && !@silent
17
+ end
18
+
19
+ def log_error(e)
20
+ trace { "#{e}\n\t" + e.backtrace.join("\n\t") }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,72 @@
1
+ require 'thin_parser'
2
+
3
+ module Thin
4
+ # Raised when an incoming request is not valid
5
+ # and the server can not process it.
6
+ class InvalidRequest < IOError; end
7
+
8
+ # A request sent by the client to the server.
9
+ class Request
10
+ MAX_HEADER = 1024 * (80 + 32)
11
+ MAX_HEADER_MSG = 'Header longer than allowed'.freeze
12
+
13
+ SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze
14
+ REMOTE_ADDR = 'REMOTE_ADDR'.freeze
15
+ FORWARDED_FOR = 'HTTP_X_FORWARDED_FOR'.freeze
16
+
17
+ CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
18
+
19
+ RACK_INPUT = 'rack.input'.freeze
20
+ RACK_VERSION = 'rack.version'.freeze
21
+ RACK_ERRORS = 'rack.errors'.freeze
22
+ RACK_MULTITHREAD = 'rack.multithread'.freeze
23
+ RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
24
+ RACK_RUN_ONCE = 'rack.run_once'.freeze
25
+
26
+ attr_reader :env, :data, :body
27
+
28
+ def initialize
29
+ @parser = HttpParser.new
30
+ @data = ''
31
+ @nparsed = 0
32
+ @body = StringIO.new
33
+ @env = {
34
+ SERVER_SOFTWARE => SERVER,
35
+
36
+ # Rack stuff
37
+ RACK_INPUT => @body,
38
+
39
+ RACK_VERSION => [0, 2],
40
+ RACK_ERRORS => STDERR,
41
+
42
+ RACK_MULTITHREAD => false,
43
+ RACK_MULTIPROCESS => false,
44
+ RACK_RUN_ONCE => false
45
+ }
46
+ end
47
+
48
+ def parse(data)
49
+ @data << data
50
+
51
+ if @parser.finished? # Header finished, can only be some more body
52
+ body << data
53
+ elsif @data.size > MAX_HEADER # Oho! very big header, must be a mean person
54
+ raise InvalidRequest, MAX_HEADER_MSG
55
+ else # Parse more header using the super parser
56
+ @nparsed = @parser.execute(@env, @data, @nparsed)
57
+ end
58
+
59
+ # Check if header and body are complete
60
+ if @parser.finished? && body.size >= content_length
61
+ body.rewind
62
+ return true # Request is fully parsed
63
+ end
64
+
65
+ false # Not finished, need more data
66
+ end
67
+
68
+ def content_length
69
+ @env[CONTENT_LENGTH].to_i
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,48 @@
1
+ module Thin
2
+ # A response sent to the client.
3
+ class Response
4
+ CONTENT_LENGTH = 'Content-Length'.freeze
5
+ CONNECTION = 'Connection'.freeze
6
+ CLOSE = 'close'.freeze
7
+
8
+ attr_accessor :status
9
+ attr_reader :headers, :body
10
+
11
+ def initialize
12
+ @headers = Headers.new
13
+ @body = StringIO.new
14
+ @status = 200
15
+ end
16
+
17
+ def headers_output
18
+ @headers[CONTENT_LENGTH] = @body.size
19
+ @headers[CONNECTION] = CLOSE
20
+ @headers.to_s
21
+ end
22
+
23
+ def head
24
+ "HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status.to_i]}\r\n#{headers_output}\r\n"
25
+ end
26
+
27
+ def headers=(key_value_pairs)
28
+ key_value_pairs.each do |k, vs|
29
+ vs.each { |v| @headers[k] = v.chomp }
30
+ end
31
+ end
32
+
33
+ def body=(stream)
34
+ stream.each do |part|
35
+ @body << part
36
+ end
37
+ end
38
+
39
+ def close
40
+ @body.close
41
+ end
42
+
43
+ def to_s
44
+ @body.rewind
45
+ head + @body.read
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,80 @@
1
+ module Thin
2
+ # Raise when we require the server to stop
3
+ class StopServer < Exception; end
4
+
5
+ # The Thin HTTP server used to served request.
6
+ # It listen for incoming request on a given port
7
+ # and forward all request to +app+.
8
+ #
9
+ # Based on HTTP 1.1 protocol specs:
10
+ # http://www.w3.org/Protocols/rfc2616/rfc2616.html
11
+ class Server
12
+ include Logging
13
+ include Daemonizable
14
+
15
+ # Addresse and port on which the server is listening for connections.
16
+ attr_accessor :port, :host
17
+
18
+ # App called with the request that produce the response.
19
+ attr_accessor :app
20
+
21
+ # Maximum time for incoming data to arrive
22
+ attr_accessor :timeout
23
+
24
+ # Creates a new server binded to <tt>host:port</tt>
25
+ # that will pass request to +app+.
26
+ def initialize(host, port, app=nil)
27
+ @host = host
28
+ @port = port.to_i
29
+ @app = app
30
+ @timeout = 60 # sec
31
+ end
32
+
33
+ # Starts the handlers.
34
+ def start
35
+ raise ArgumentError, "app required" unless @app
36
+
37
+ log ">> Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
38
+ trace ">> Tracing ON"
39
+ end
40
+
41
+ # Start the server and listen for connections
42
+ def start!
43
+ start
44
+ listen!
45
+ end
46
+
47
+ # Start listening for connections
48
+ def listen!
49
+ trap('INT') { stop }
50
+ trap('TERM') { stop! }
51
+
52
+ # See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
53
+ EventMachine.epoll
54
+
55
+ EventMachine.run do
56
+ begin
57
+ log ">> Listening on #{@host}:#{@port}, CTRL+C to stop"
58
+ EventMachine.start_server(@host, @port, Connection) do |connection|
59
+ connection.comm_inactivity_timeout = @timeout
60
+ connection.app = @app
61
+ connection.silent = @silent
62
+ end
63
+ rescue StopServer
64
+ EventMachine.stop_event_loop
65
+ end
66
+ end
67
+ end
68
+
69
+
70
+ def stop
71
+ EventMachine.stop_event_loop
72
+ rescue
73
+ warn "Error stopping : #{$!}"
74
+ end
75
+
76
+ def stop!
77
+ raise StopServer
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,43 @@
1
+ module Thin
2
+ # Every standard HTTP code mapped to the appropriate message.
3
+ # Stolent from Mongrel.
4
+ HTTP_STATUS_CODES = {
5
+ 100 => 'Continue',
6
+ 101 => 'Switching Protocols',
7
+ 200 => 'OK',
8
+ 201 => 'Created',
9
+ 202 => 'Accepted',
10
+ 203 => 'Non-Authoritative Information',
11
+ 204 => 'No Content',
12
+ 205 => 'Reset Content',
13
+ 206 => 'Partial Content',
14
+ 300 => 'Multiple Choices',
15
+ 301 => 'Moved Permanently',
16
+ 302 => 'Moved Temporarily',
17
+ 303 => 'See Other',
18
+ 304 => 'Not Modified',
19
+ 305 => 'Use Proxy',
20
+ 400 => 'Bad Request',
21
+ 401 => 'Unauthorized',
22
+ 402 => 'Payment Required',
23
+ 403 => 'Forbidden',
24
+ 404 => 'Not Found',
25
+ 405 => 'Method Not Allowed',
26
+ 406 => 'Not Acceptable',
27
+ 407 => 'Proxy Authentication Required',
28
+ 408 => 'Request Time-out',
29
+ 409 => 'Conflict',
30
+ 410 => 'Gone',
31
+ 411 => 'Length Required',
32
+ 412 => 'Precondition Failed',
33
+ 413 => 'Request Entity Too Large',
34
+ 414 => 'Request-URI Too Large',
35
+ 415 => 'Unsupported Media Type',
36
+ 500 => 'Internal Server Error',
37
+ 501 => 'Not Implemented',
38
+ 502 => 'Bad Gateway',
39
+ 503 => 'Service Unavailable',
40
+ 504 => 'Gateway Time-out',
41
+ 505 => 'HTTP Version not supported'
42
+ }
43
+ end
@@ -0,0 +1,11 @@
1
+ module Thin
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 5
5
+ TINY = 3
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+
9
+ CODENAME = 'Purple Yogurt'
10
+ end
11
+ end
Binary file
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Cluster do
4
+ before do
5
+ @cluster = Thin::Cluster.new(:chdir => File.dirname(__FILE__) + '/rails_app',
6
+ :address => '0.0.0.0',
7
+ :port => 3000,
8
+ :servers => 3,
9
+ :timeout => 10,
10
+ :log => 'thin.log',
11
+ :pid => 'thin.pid'
12
+ )
13
+ @cluster.script = File.dirname(__FILE__) + '/../bin/thin'
14
+ @cluster.silent = true
15
+ end
16
+
17
+ it 'should include port number in file names' do
18
+ @cluster.send(:include_port_number, 'thin.log', 3000).should == 'thin.3000.log'
19
+ @cluster.send(:include_port_number, 'thin.pid', 3000).should == 'thin.3000.pid'
20
+ proc { @cluster.send(:include_port_number, 'thin', 3000) }.should raise_error(ArgumentError)
21
+ end
22
+
23
+ it 'should call each server' do
24
+ calls = []
25
+ @cluster.send(:with_each_server) do |port|
26
+ calls << port
27
+ end
28
+ calls.should == [3000, 3001, 3002]
29
+ end
30
+
31
+ it 'should shellify command' do
32
+ out = @cluster.send(:shellify, :start, :port => 3000, :daemonize => true, :log => 'hi.log', :pid => nil)
33
+ out.should include('--port=3000', '--daemonize', '--log="hi.log"', 'thin start --')
34
+ out.should_not include('--pid=')
35
+ end
36
+
37
+ it 'should absolutize file path' do
38
+ @cluster.pid_file_for(3000).should == File.expand_path(File.dirname(__FILE__) + "/rails_app/thin.3000.pid")
39
+ end
40
+
41
+ it 'should start on specified port' do
42
+ @cluster.should_receive(:`) do |with|
43
+ with.should include('thin start', '--daemonize', 'thin.3001.log', 'thin.3001.pid', '--port=3001')
44
+ ''
45
+ end
46
+
47
+ @cluster.start_on_port 3001
48
+ end
49
+
50
+ it 'should stop on specified port' do
51
+ @cluster.should_receive(:`) do |with|
52
+ with.should include('thin stop', '--daemonize', 'thin.3001.log', 'thin.3001.pid', '--port=3001')
53
+ ''
54
+ end
55
+
56
+ @cluster.stop_on_port 3001
57
+ end
58
+ end
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe 'Daemonizing' do
4
+ before do
5
+ @server = Server.new('0.0.0.0', 3000, nil)
6
+ @server.log_file = File.dirname(__FILE__) + '/../log/daemonizing_test.log'
7
+ @server.pid_file = 'test.pid'
8
+ @pid = nil
9
+ end
10
+
11
+ it 'should have a pid file' do
12
+ @server.should respond_to(:pid_file)
13
+ @server.should respond_to(:pid_file=)
14
+ end
15
+
16
+ it 'should create a pid file' do
17
+ @pid = fork do
18
+ @server.daemonize
19
+ sleep 1
20
+ end
21
+
22
+ Process.wait(@pid)
23
+ File.exist?(@server.pid_file).should be_true
24
+ @pid = @server.pid
25
+
26
+ proc { sleep 0.1 while File.exist?(@server.pid_file) }.should take_less_then(2)
27
+ end
28
+
29
+ it 'should redirect stdio to a log file' do
30
+ @pid = fork do
31
+ @server.log_file = 'daemon_test.log'
32
+ @server.daemonize
33
+
34
+ puts "simple puts"
35
+ STDERR.puts "STDERR.puts"
36
+ STDOUT.puts "STDOUT.puts"
37
+ end
38
+ Process.wait(@pid)
39
+ # Wait for the file to close and magical stuff to happen
40
+ proc { sleep 0.1 until File.exist?('daemon_test.log') }.should take_less_then(3)
41
+ sleep 0.5
42
+
43
+ log = File.read('daemon_test.log')
44
+ log.should include('simple puts', 'STDERR.puts', 'STDOUT.puts')
45
+
46
+ File.delete 'daemon_test.log'
47
+ end
48
+
49
+ it 'should change privilege' do
50
+ @pid = fork do
51
+ @server.daemonize
52
+ @server.change_privilege('root', 'admin')
53
+ end
54
+ Process.wait(@pid)
55
+ $?.should be_a_success
56
+ end
57
+
58
+ it 'should kill process in pid file' do
59
+ @pid = fork do
60
+ @server.daemonize
61
+ loop { sleep 1 }
62
+ end
63
+
64
+ proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(3)
65
+
66
+ silence_stream STDOUT do
67
+ Server.kill(@server.pid_file, 1)
68
+ end
69
+
70
+ File.exist?(@server.pid_file).should_not be_true
71
+ end
72
+
73
+ it 'should send kill signal if timeout' do
74
+ @pid = fork do
75
+ @server.should_receive(:stop) # pretend we cannot handle the INT signal
76
+ @server.daemonize
77
+ sleep 5
78
+ end
79
+
80
+ proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(10)
81
+
82
+ silence_stream STDOUT do
83
+ Server.kill(@server.pid_file, 1)
84
+ end
85
+
86
+ File.exist?(@server.pid_file).should be_false
87
+ Process.running?(@pid).should be_false
88
+ end
89
+
90
+ after do
91
+ Process.kill(9, @pid.to_i) if @pid && Process.running?(@pid.to_i)
92
+ end
93
+ end