eac-rack 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +399 -0
  4. data/bin/rackup +4 -0
  5. data/contrib/rack_logo.svg +111 -0
  6. data/example/lobster.ru +4 -0
  7. data/example/protectedlobster.rb +14 -0
  8. data/example/protectedlobster.ru +8 -0
  9. data/lib/rack.rb +92 -0
  10. data/lib/rack/adapter/camping.rb +22 -0
  11. data/lib/rack/auth/abstract/handler.rb +37 -0
  12. data/lib/rack/auth/abstract/request.rb +37 -0
  13. data/lib/rack/auth/basic.rb +58 -0
  14. data/lib/rack/auth/digest/md5.rb +124 -0
  15. data/lib/rack/auth/digest/nonce.rb +51 -0
  16. data/lib/rack/auth/digest/params.rb +55 -0
  17. data/lib/rack/auth/digest/request.rb +40 -0
  18. data/lib/rack/builder.rb +80 -0
  19. data/lib/rack/cascade.rb +41 -0
  20. data/lib/rack/chunked.rb +49 -0
  21. data/lib/rack/commonlogger.rb +49 -0
  22. data/lib/rack/conditionalget.rb +47 -0
  23. data/lib/rack/config.rb +15 -0
  24. data/lib/rack/content_length.rb +29 -0
  25. data/lib/rack/content_type.rb +23 -0
  26. data/lib/rack/deflater.rb +96 -0
  27. data/lib/rack/directory.rb +157 -0
  28. data/lib/rack/etag.rb +23 -0
  29. data/lib/rack/file.rb +90 -0
  30. data/lib/rack/handler.rb +88 -0
  31. data/lib/rack/handler/cgi.rb +61 -0
  32. data/lib/rack/handler/evented_mongrel.rb +8 -0
  33. data/lib/rack/handler/fastcgi.rb +89 -0
  34. data/lib/rack/handler/lsws.rb +63 -0
  35. data/lib/rack/handler/mongrel.rb +90 -0
  36. data/lib/rack/handler/scgi.rb +62 -0
  37. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  38. data/lib/rack/handler/thin.rb +18 -0
  39. data/lib/rack/handler/webrick.rb +69 -0
  40. data/lib/rack/head.rb +19 -0
  41. data/lib/rack/lint.rb +575 -0
  42. data/lib/rack/lobster.rb +65 -0
  43. data/lib/rack/lock.rb +16 -0
  44. data/lib/rack/logger.rb +20 -0
  45. data/lib/rack/methodoverride.rb +27 -0
  46. data/lib/rack/mime.rb +206 -0
  47. data/lib/rack/mock.rb +189 -0
  48. data/lib/rack/nulllogger.rb +18 -0
  49. data/lib/rack/recursive.rb +57 -0
  50. data/lib/rack/reloader.rb +109 -0
  51. data/lib/rack/request.rb +271 -0
  52. data/lib/rack/response.rb +149 -0
  53. data/lib/rack/rewindable_input.rb +100 -0
  54. data/lib/rack/runtime.rb +27 -0
  55. data/lib/rack/sendfile.rb +142 -0
  56. data/lib/rack/server.rb +212 -0
  57. data/lib/rack/session/abstract/id.rb +140 -0
  58. data/lib/rack/session/cookie.rb +90 -0
  59. data/lib/rack/session/memcache.rb +119 -0
  60. data/lib/rack/session/pool.rb +100 -0
  61. data/lib/rack/showexceptions.rb +349 -0
  62. data/lib/rack/showstatus.rb +106 -0
  63. data/lib/rack/static.rb +38 -0
  64. data/lib/rack/urlmap.rb +56 -0
  65. data/lib/rack/utils.rb +614 -0
  66. data/rack.gemspec +38 -0
  67. data/test/spec_rack_auth_basic.rb +73 -0
  68. data/test/spec_rack_auth_digest.rb +226 -0
  69. data/test/spec_rack_builder.rb +84 -0
  70. data/test/spec_rack_camping.rb +51 -0
  71. data/test/spec_rack_cascade.rb +48 -0
  72. data/test/spec_rack_cgi.rb +89 -0
  73. data/test/spec_rack_chunked.rb +62 -0
  74. data/test/spec_rack_commonlogger.rb +61 -0
  75. data/test/spec_rack_conditionalget.rb +41 -0
  76. data/test/spec_rack_config.rb +24 -0
  77. data/test/spec_rack_content_length.rb +43 -0
  78. data/test/spec_rack_content_type.rb +30 -0
  79. data/test/spec_rack_deflater.rb +127 -0
  80. data/test/spec_rack_directory.rb +61 -0
  81. data/test/spec_rack_etag.rb +17 -0
  82. data/test/spec_rack_fastcgi.rb +89 -0
  83. data/test/spec_rack_file.rb +75 -0
  84. data/test/spec_rack_handler.rb +43 -0
  85. data/test/spec_rack_head.rb +30 -0
  86. data/test/spec_rack_lint.rb +528 -0
  87. data/test/spec_rack_lobster.rb +45 -0
  88. data/test/spec_rack_lock.rb +38 -0
  89. data/test/spec_rack_logger.rb +21 -0
  90. data/test/spec_rack_methodoverride.rb +60 -0
  91. data/test/spec_rack_mock.rb +243 -0
  92. data/test/spec_rack_mongrel.rb +189 -0
  93. data/test/spec_rack_nulllogger.rb +13 -0
  94. data/test/spec_rack_recursive.rb +77 -0
  95. data/test/spec_rack_request.rb +545 -0
  96. data/test/spec_rack_response.rb +221 -0
  97. data/test/spec_rack_rewindable_input.rb +118 -0
  98. data/test/spec_rack_runtime.rb +35 -0
  99. data/test/spec_rack_sendfile.rb +86 -0
  100. data/test/spec_rack_session_cookie.rb +73 -0
  101. data/test/spec_rack_session_memcache.rb +273 -0
  102. data/test/spec_rack_session_pool.rb +172 -0
  103. data/test/spec_rack_showexceptions.rb +21 -0
  104. data/test/spec_rack_showstatus.rb +72 -0
  105. data/test/spec_rack_static.rb +37 -0
  106. data/test/spec_rack_thin.rb +91 -0
  107. data/test/spec_rack_urlmap.rb +215 -0
  108. data/test/spec_rack_utils.rb +554 -0
  109. data/test/spec_rack_webrick.rb +130 -0
  110. data/test/spec_rackup.rb +154 -0
  111. metadata +311 -0
@@ -0,0 +1,149 @@
1
+ require 'rack/request'
2
+ require 'rack/utils'
3
+
4
+ module Rack
5
+ # Rack::Response provides a convenient interface to create a Rack
6
+ # response.
7
+ #
8
+ # It allows setting of headers and cookies, and provides useful
9
+ # defaults (a OK response containing HTML).
10
+ #
11
+ # You can use Response#write to iteratively generate your response,
12
+ # but note that this is buffered by Rack::Response until you call
13
+ # +finish+. +finish+ however can take a block inside which calls to
14
+ # +write+ are syncronous with the Rack response.
15
+ #
16
+ # Your application's +call+ should end returning Response#finish.
17
+
18
+ class Response
19
+ attr_accessor :length
20
+
21
+ def initialize(body=[], status=200, header={}, &block)
22
+ @status = status.to_i
23
+ @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.
24
+ merge(header))
25
+
26
+ @writer = lambda { |x| @body << x }
27
+ @block = nil
28
+ @length = 0
29
+
30
+ @body = []
31
+
32
+ if body.respond_to? :to_str
33
+ write body.to_str
34
+ elsif body.respond_to?(:each)
35
+ body.each { |part|
36
+ write part.to_s
37
+ }
38
+ else
39
+ raise TypeError, "stringable or iterable required"
40
+ end
41
+
42
+ yield self if block_given?
43
+ end
44
+
45
+ attr_reader :header
46
+ attr_accessor :status, :body
47
+
48
+ def [](key)
49
+ header[key]
50
+ end
51
+
52
+ def []=(key, value)
53
+ header[key] = value
54
+ end
55
+
56
+ def set_cookie(key, value)
57
+ Utils.set_cookie_header!(header, key, value)
58
+ end
59
+
60
+ def delete_cookie(key, value={})
61
+ Utils.delete_cookie_header!(header, key, value)
62
+ end
63
+
64
+ def redirect(target, status=302)
65
+ self.status = status
66
+ self["Location"] = target
67
+ end
68
+
69
+ def finish(&block)
70
+ @block = block
71
+
72
+ if [204, 304].include?(status.to_i)
73
+ header.delete "Content-Type"
74
+ [status.to_i, header, []]
75
+ else
76
+ [status.to_i, header, self]
77
+ end
78
+ end
79
+ alias to_a finish # For *response
80
+
81
+ def each(&callback)
82
+ @body.each(&callback)
83
+ @writer = callback
84
+ @block.call(self) if @block
85
+ end
86
+
87
+ # Append to body and update Content-Length.
88
+ #
89
+ # NOTE: Do not mix #write and direct #body access!
90
+ #
91
+ def write(str)
92
+ s = str.to_s
93
+ @length += Rack::Utils.bytesize(s)
94
+ @writer.call s
95
+
96
+ header["Content-Length"] = @length.to_s
97
+ str
98
+ end
99
+
100
+ def close
101
+ body.close if body.respond_to?(:close)
102
+ end
103
+
104
+ def empty?
105
+ @block == nil && @body.empty?
106
+ end
107
+
108
+ alias headers header
109
+
110
+ module Helpers
111
+ def invalid?; @status < 100 || @status >= 600; end
112
+
113
+ def informational?; @status >= 100 && @status < 200; end
114
+ def successful?; @status >= 200 && @status < 300; end
115
+ def redirection?; @status >= 300 && @status < 400; end
116
+ def client_error?; @status >= 400 && @status < 500; end
117
+ def server_error?; @status >= 500 && @status < 600; end
118
+
119
+ def ok?; @status == 200; end
120
+ def forbidden?; @status == 403; end
121
+ def not_found?; @status == 404; end
122
+
123
+ def redirect?; [301, 302, 303, 307].include? @status; end
124
+ def empty?; [201, 204, 304].include? @status; end
125
+
126
+ # Headers
127
+ attr_reader :headers, :original_headers
128
+
129
+ def include?(header)
130
+ !!headers[header]
131
+ end
132
+
133
+ def content_type
134
+ headers["Content-Type"]
135
+ end
136
+
137
+ def content_length
138
+ cl = headers["Content-Length"]
139
+ cl ? cl.to_i : cl
140
+ end
141
+
142
+ def location
143
+ headers["Location"]
144
+ end
145
+ end
146
+
147
+ include Helpers
148
+ end
149
+ end
@@ -0,0 +1,100 @@
1
+ require 'tempfile'
2
+
3
+ module Rack
4
+ # Class which can make any IO object rewindable, including non-rewindable ones. It does
5
+ # this by buffering the data into a tempfile, which is rewindable.
6
+ #
7
+ # rack.input is required to be rewindable, so if your input stream IO is non-rewindable
8
+ # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class
9
+ # to easily make it rewindable.
10
+ #
11
+ # Don't forget to call #close when you're done. This frees up temporary resources that
12
+ # RewindableInput uses, though it does *not* close the original IO object.
13
+ class RewindableInput
14
+ def initialize(io)
15
+ @io = io
16
+ @rewindable_io = nil
17
+ @unlinked = false
18
+ end
19
+
20
+ def gets
21
+ make_rewindable unless @rewindable_io
22
+ @rewindable_io.gets
23
+ end
24
+
25
+ def read(*args)
26
+ make_rewindable unless @rewindable_io
27
+ @rewindable_io.read(*args)
28
+ end
29
+
30
+ def each(&block)
31
+ make_rewindable unless @rewindable_io
32
+ @rewindable_io.each(&block)
33
+ end
34
+
35
+ def rewind
36
+ make_rewindable unless @rewindable_io
37
+ @rewindable_io.rewind
38
+ end
39
+
40
+ # Closes this RewindableInput object without closing the originally
41
+ # wrapped IO oject. Cleans up any temporary resources that this RewindableInput
42
+ # has created.
43
+ #
44
+ # This method may be called multiple times. It does nothing on subsequent calls.
45
+ def close
46
+ if @rewindable_io
47
+ if @unlinked
48
+ @rewindable_io.close
49
+ else
50
+ @rewindable_io.close!
51
+ end
52
+ @rewindable_io = nil
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ # Ruby's Tempfile class has a bug. Subclass it and fix it.
59
+ class Tempfile < ::Tempfile
60
+ def _close
61
+ @tmpfile.close if @tmpfile
62
+ @data[1] = nil if @data
63
+ @tmpfile = nil
64
+ end
65
+ end
66
+
67
+ def make_rewindable
68
+ # Buffer all data into a tempfile. Since this tempfile is private to this
69
+ # RewindableInput object, we chmod it so that nobody else can read or write
70
+ # it. On POSIX filesystems we also unlink the file so that it doesn't
71
+ # even have a file entry on the filesystem anymore, though we can still
72
+ # access it because we have the file handle open.
73
+ @rewindable_io = Tempfile.new('RackRewindableInput')
74
+ @rewindable_io.chmod(0000)
75
+ @rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
76
+ @rewindable_io.binmode
77
+ if filesystem_has_posix_semantics?
78
+ @rewindable_io.unlink
79
+ @unlinked = true
80
+ end
81
+
82
+ buffer = ""
83
+ while @io.read(1024 * 4, buffer)
84
+ entire_buffer_written_out = false
85
+ while !entire_buffer_written_out
86
+ written = @rewindable_io.write(buffer)
87
+ entire_buffer_written_out = written == buffer.size
88
+ if !entire_buffer_written_out
89
+ buffer.slice!(0 .. written - 1)
90
+ end
91
+ end
92
+ end
93
+ @rewindable_io.rewind
94
+ end
95
+
96
+ def filesystem_has_posix_semantics?
97
+ RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,27 @@
1
+ module Rack
2
+ # Sets an "X-Runtime" response header, indicating the response
3
+ # time of the request, in seconds
4
+ #
5
+ # You can put it right before the application to see the processing
6
+ # time, or before all the other middlewares to include time for them,
7
+ # too.
8
+ class Runtime
9
+ def initialize(app, name = nil)
10
+ @app = app
11
+ @header_name = "X-Runtime"
12
+ @header_name << "-#{name}" if name
13
+ end
14
+
15
+ def call(env)
16
+ start_time = Time.now
17
+ status, headers, body = @app.call(env)
18
+ request_time = Time.now - start_time
19
+
20
+ if !headers.has_key?(@header_name)
21
+ headers[@header_name] = "%0.6f" % request_time
22
+ end
23
+
24
+ [status, headers, body]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,142 @@
1
+ require 'rack/file'
2
+
3
+ module Rack
4
+ class File #:nodoc:
5
+ alias :to_path :path
6
+ end
7
+
8
+ # = Sendfile
9
+ #
10
+ # The Sendfile middleware intercepts responses whose body is being
11
+ # served from a file and replaces it with a server specific X-Sendfile
12
+ # header. The web server is then responsible for writing the file contents
13
+ # to the client. This can dramatically reduce the amount of work required
14
+ # by the Ruby backend and takes advantage of the web servers optimized file
15
+ # delivery code.
16
+ #
17
+ # In order to take advantage of this middleware, the response body must
18
+ # respond to +to_path+ and the request must include an X-Sendfile-Type
19
+ # header. Rack::File and other components implement +to_path+ so there's
20
+ # rarely anything you need to do in your application. The X-Sendfile-Type
21
+ # header is typically set in your web servers configuration. The following
22
+ # sections attempt to document
23
+ #
24
+ # === Nginx
25
+ #
26
+ # Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile
27
+ # but requires parts of the filesystem to be mapped into a private URL
28
+ # hierarachy.
29
+ #
30
+ # The following example shows the Nginx configuration required to create
31
+ # a private "/files/" area, enable X-Accel-Redirect, and pass the special
32
+ # X-Sendfile-Type and X-Accel-Mapping headers to the backend:
33
+ #
34
+ # location /files/ {
35
+ # internal;
36
+ # alias /var/www/;
37
+ # }
38
+ #
39
+ # location / {
40
+ # proxy_redirect false;
41
+ #
42
+ # proxy_set_header Host $host;
43
+ # proxy_set_header X-Real-IP $remote_addr;
44
+ # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
45
+ #
46
+ # proxy_set_header X-Sendfile-Type X-Accel-Redirect
47
+ # proxy_set_header X-Accel-Mapping /files/=/var/www/;
48
+ #
49
+ # proxy_pass http://127.0.0.1:8080/;
50
+ # }
51
+ #
52
+ # Note that the X-Sendfile-Type header must be set exactly as shown above. The
53
+ # X-Accel-Mapping header should specify the name of the private URL pattern,
54
+ # followed by an equals sign (=), followed by the location on the file system
55
+ # that it maps to. The middleware performs a simple substitution on the
56
+ # resulting path.
57
+ #
58
+ # See Also: http://wiki.codemongers.com/NginxXSendfile
59
+ #
60
+ # === lighttpd
61
+ #
62
+ # Lighttpd has supported some variation of the X-Sendfile header for some
63
+ # time, although only recent version support X-Sendfile in a reverse proxy
64
+ # configuration.
65
+ #
66
+ # $HTTP["host"] == "example.com" {
67
+ # proxy-core.protocol = "http"
68
+ # proxy-core.balancer = "round-robin"
69
+ # proxy-core.backends = (
70
+ # "127.0.0.1:8000",
71
+ # "127.0.0.1:8001",
72
+ # ...
73
+ # )
74
+ #
75
+ # proxy-core.allow-x-sendfile = "enable"
76
+ # proxy-core.rewrite-request = (
77
+ # "X-Sendfile-Type" => (".*" => "X-Sendfile")
78
+ # )
79
+ # }
80
+ #
81
+ # See Also: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModProxyCore
82
+ #
83
+ # === Apache
84
+ #
85
+ # X-Sendfile is supported under Apache 2.x using a separate module:
86
+ #
87
+ # http://tn123.ath.cx/mod_xsendfile/
88
+ #
89
+ # Once the module is compiled and installed, you can enable it using
90
+ # XSendFile config directive:
91
+ #
92
+ # RequestHeader Set X-Sendfile-Type X-Sendfile
93
+ # ProxyPassReverse / http://localhost:8001/
94
+ # XSendFile on
95
+
96
+ class Sendfile
97
+ F = ::File
98
+
99
+ def initialize(app, variation=nil)
100
+ @app = app
101
+ @variation = variation
102
+ end
103
+
104
+ def call(env)
105
+ status, headers, body = @app.call(env)
106
+ if body.respond_to?(:to_path)
107
+ case type = variation(env)
108
+ when 'X-Accel-Redirect'
109
+ path = F.expand_path(body.to_path)
110
+ if url = map_accel_path(env, path)
111
+ headers[type] = url
112
+ body = []
113
+ else
114
+ env['rack.errors'] << "X-Accel-Mapping header missing"
115
+ end
116
+ when 'X-Sendfile', 'X-Lighttpd-Send-File'
117
+ path = F.expand_path(body.to_path)
118
+ headers[type] = path
119
+ body = []
120
+ when '', nil
121
+ else
122
+ env['rack.errors'] << "Unknown x-sendfile variation: '#{variation}'.\n"
123
+ end
124
+ end
125
+ [status, headers, body]
126
+ end
127
+
128
+ private
129
+ def variation(env)
130
+ @variation ||
131
+ env['sendfile.type'] ||
132
+ env['HTTP_X_SENDFILE_TYPE']
133
+ end
134
+
135
+ def map_accel_path(env, file)
136
+ if mapping = env['HTTP_X_ACCEL_MAPPING']
137
+ internal, external = mapping.split('=', 2).map{ |p| p.strip }
138
+ file.sub(/^#{internal}/i, external)
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,212 @@
1
+ require 'optparse'
2
+
3
+ module Rack
4
+ class Server
5
+ class Options
6
+ def parse!(args)
7
+ options = {}
8
+ opt_parser = OptionParser.new("", 24, ' ') do |opts|
9
+ opts.banner = "Usage: rackup [ruby options] [rack options] [rackup config]"
10
+
11
+ opts.separator ""
12
+ opts.separator "Ruby options:"
13
+
14
+ lineno = 1
15
+ opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
16
+ eval line, TOPLEVEL_BINDING, "-e", lineno
17
+ lineno += 1
18
+ }
19
+
20
+ opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
21
+ options[:debug] = true
22
+ }
23
+ opts.on("-w", "--warn", "turn warnings on for your script") {
24
+ options[:warn] = true
25
+ }
26
+
27
+ opts.on("-I", "--include PATH",
28
+ "specify $LOAD_PATH (may be used more than once)") { |path|
29
+ options[:include] = path.split(":")
30
+ }
31
+
32
+ opts.on("-r", "--require LIBRARY",
33
+ "require the library, before executing your script") { |library|
34
+ options[:require] = library
35
+ }
36
+
37
+ opts.separator ""
38
+ opts.separator "Rack options:"
39
+ opts.on("-s", "--server SERVER", "serve using SERVER (webrick/mongrel)") { |s|
40
+ options[:server] = s
41
+ }
42
+
43
+ opts.on("-o", "--host HOST", "listen on HOST (default: 0.0.0.0)") { |host|
44
+ options[:Host] = host
45
+ }
46
+
47
+ opts.on("-p", "--port PORT", "use PORT (default: 9292)") { |port|
48
+ options[:Port] = port
49
+ }
50
+
51
+ opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e|
52
+ options[:environment] = e
53
+ }
54
+
55
+ opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
56
+ options[:daemonize] = d ? true : false
57
+ }
58
+
59
+ opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f|
60
+ options[:pid] = f
61
+ }
62
+
63
+ opts.separator ""
64
+ opts.separator "Common options:"
65
+
66
+ opts.on_tail("-h", "--help", "Show this message") do
67
+ puts opts
68
+ exit
69
+ end
70
+
71
+ opts.on_tail("--version", "Show version") do
72
+ puts "Rack #{Rack.version}"
73
+ exit
74
+ end
75
+ end
76
+ opt_parser.parse! args
77
+ options[:config] = args.last if args.last
78
+ options
79
+ end
80
+ end
81
+
82
+ def self.start
83
+ new.start
84
+ end
85
+
86
+ attr_accessor :options
87
+
88
+ def initialize(options = nil)
89
+ @options = options
90
+ end
91
+
92
+ def options
93
+ @options ||= parse_options(ARGV)
94
+ end
95
+
96
+ def default_options
97
+ {
98
+ :environment => "development",
99
+ :pid => nil,
100
+ :Port => 9292,
101
+ :Host => "0.0.0.0",
102
+ :AccessLog => [],
103
+ :config => "config.ru"
104
+ }
105
+ end
106
+
107
+ def app
108
+ @app ||= begin
109
+ if !::File.exist? options[:config]
110
+ abort "configuration #{options[:config]} not found"
111
+ end
112
+
113
+ app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
114
+ self.options.merge! options
115
+ app
116
+ end
117
+ end
118
+
119
+ def self.middleware
120
+ @middleware ||= begin
121
+ m = Hash.new {|h,k| h[k] = []}
122
+ m["deployment"].concat [lambda {|server| server.server =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr] }]
123
+ m["development"].concat m["deployment"] + [[Rack::ShowExceptions], [Rack::Lint]]
124
+ m
125
+ end
126
+ end
127
+
128
+ def middleware
129
+ self.class.middleware
130
+ end
131
+
132
+ def start
133
+ if options[:debug]
134
+ $DEBUG = true
135
+ require 'pp'
136
+ p options[:server]
137
+ pp wrapped_app
138
+ pp app
139
+ end
140
+
141
+ if options[:warn]
142
+ $-w = true
143
+ end
144
+
145
+ if includes = options[:include]
146
+ $LOAD_PATH.unshift *includes
147
+ end
148
+
149
+ if library = options[:require]
150
+ require library
151
+ end
152
+
153
+ daemonize_app if options[:daemonize]
154
+ write_pid if options[:pid]
155
+ server.run wrapped_app, options
156
+ end
157
+
158
+ def server
159
+ @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default
160
+ end
161
+
162
+ private
163
+ def parse_options(args)
164
+ options = default_options
165
+
166
+ # Don't evaluate CGI ISINDEX parameters.
167
+ # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
168
+ args.clear if ENV.include?("REQUEST_METHOD")
169
+
170
+ options.merge! opt_parser.parse! args
171
+ options
172
+ end
173
+
174
+ def opt_parser
175
+ Options.new
176
+ end
177
+
178
+ def build_app(app)
179
+ middleware[options[:environment]].reverse_each do |middleware|
180
+ middleware = middleware.call(self) if middleware.respond_to?(:call)
181
+ next unless middleware
182
+ klass = middleware.shift
183
+ app = klass.new(app, *middleware)
184
+ end
185
+ app
186
+ end
187
+
188
+ def wrapped_app
189
+ @wrapped_app ||= build_app app
190
+ end
191
+
192
+ def daemonize_app
193
+ if RUBY_VERSION < "1.9"
194
+ exit if fork
195
+ Process.setsid
196
+ exit if fork
197
+ Dir.chdir "/"
198
+ ::File.umask 0000
199
+ STDIN.reopen "/dev/null"
200
+ STDOUT.reopen "/dev/null", "a"
201
+ STDERR.reopen "/dev/null", "a"
202
+ else
203
+ Process.daemon
204
+ end
205
+ end
206
+
207
+ def write_pid
208
+ ::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") }
209
+ at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) }
210
+ end
211
+ end
212
+ end