kastner-rack 0.3.171

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 (94) hide show
  1. data/AUTHORS +8 -0
  2. data/COPYING +18 -0
  3. data/KNOWN-ISSUES +18 -0
  4. data/README +273 -0
  5. data/Rakefile +185 -0
  6. data/bin/rackup +172 -0
  7. data/contrib/rack_logo.svg +111 -0
  8. data/example/lobster.ru +4 -0
  9. data/example/protectedlobster.rb +14 -0
  10. data/example/protectedlobster.ru +8 -0
  11. data/lib/rack.rb +85 -0
  12. data/lib/rack/adapter/camping.rb +22 -0
  13. data/lib/rack/auth/abstract/handler.rb +28 -0
  14. data/lib/rack/auth/abstract/request.rb +37 -0
  15. data/lib/rack/auth/basic.rb +58 -0
  16. data/lib/rack/auth/digest/md5.rb +124 -0
  17. data/lib/rack/auth/digest/nonce.rb +51 -0
  18. data/lib/rack/auth/digest/params.rb +55 -0
  19. data/lib/rack/auth/digest/request.rb +40 -0
  20. data/lib/rack/auth/openid.rb +437 -0
  21. data/lib/rack/builder.rb +67 -0
  22. data/lib/rack/cascade.rb +36 -0
  23. data/lib/rack/commonlogger.rb +61 -0
  24. data/lib/rack/conditionalget.rb +42 -0
  25. data/lib/rack/deflater.rb +63 -0
  26. data/lib/rack/directory.rb +149 -0
  27. data/lib/rack/file.rb +84 -0
  28. data/lib/rack/handler.rb +46 -0
  29. data/lib/rack/handler/cgi.rb +57 -0
  30. data/lib/rack/handler/evented_mongrel.rb +8 -0
  31. data/lib/rack/handler/fastcgi.rb +86 -0
  32. data/lib/rack/handler/lsws.rb +52 -0
  33. data/lib/rack/handler/mongrel.rb +78 -0
  34. data/lib/rack/handler/scgi.rb +57 -0
  35. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  36. data/lib/rack/handler/webrick.rb +61 -0
  37. data/lib/rack/head.rb +19 -0
  38. data/lib/rack/lint.rb +463 -0
  39. data/lib/rack/lobster.rb +65 -0
  40. data/lib/rack/methodoverride.rb +21 -0
  41. data/lib/rack/mime.rb +204 -0
  42. data/lib/rack/mock.rb +160 -0
  43. data/lib/rack/recursive.rb +57 -0
  44. data/lib/rack/reloader.rb +64 -0
  45. data/lib/rack/request.rb +217 -0
  46. data/lib/rack/response.rb +171 -0
  47. data/lib/rack/session/abstract/id.rb +140 -0
  48. data/lib/rack/session/cookie.rb +89 -0
  49. data/lib/rack/session/memcache.rb +97 -0
  50. data/lib/rack/session/pool.rb +73 -0
  51. data/lib/rack/showexceptions.rb +348 -0
  52. data/lib/rack/showstatus.rb +105 -0
  53. data/lib/rack/static.rb +38 -0
  54. data/lib/rack/urlmap.rb +48 -0
  55. data/lib/rack/utils.rb +318 -0
  56. data/rack.gemspec +31 -0
  57. data/test/cgi/lighttpd.conf +20 -0
  58. data/test/cgi/test +9 -0
  59. data/test/cgi/test.fcgi +8 -0
  60. data/test/cgi/test.ru +7 -0
  61. data/test/spec_rack_auth_basic.rb +69 -0
  62. data/test/spec_rack_auth_digest.rb +169 -0
  63. data/test/spec_rack_auth_openid.rb +137 -0
  64. data/test/spec_rack_builder.rb +84 -0
  65. data/test/spec_rack_camping.rb +51 -0
  66. data/test/spec_rack_cascade.rb +50 -0
  67. data/test/spec_rack_cgi.rb +89 -0
  68. data/test/spec_rack_commonlogger.rb +32 -0
  69. data/test/spec_rack_conditionalget.rb +41 -0
  70. data/test/spec_rack_deflater.rb +70 -0
  71. data/test/spec_rack_directory.rb +56 -0
  72. data/test/spec_rack_fastcgi.rb +89 -0
  73. data/test/spec_rack_file.rb +57 -0
  74. data/test/spec_rack_handler.rb +24 -0
  75. data/test/spec_rack_head.rb +30 -0
  76. data/test/spec_rack_lint.rb +371 -0
  77. data/test/spec_rack_lobster.rb +45 -0
  78. data/test/spec_rack_methodoverride.rb +31 -0
  79. data/test/spec_rack_mock.rb +152 -0
  80. data/test/spec_rack_mongrel.rb +170 -0
  81. data/test/spec_rack_recursive.rb +77 -0
  82. data/test/spec_rack_request.rb +426 -0
  83. data/test/spec_rack_response.rb +173 -0
  84. data/test/spec_rack_session_cookie.rb +78 -0
  85. data/test/spec_rack_session_memcache.rb +132 -0
  86. data/test/spec_rack_session_pool.rb +84 -0
  87. data/test/spec_rack_showexceptions.rb +21 -0
  88. data/test/spec_rack_showstatus.rb +72 -0
  89. data/test/spec_rack_static.rb +37 -0
  90. data/test/spec_rack_urlmap.rb +175 -0
  91. data/test/spec_rack_utils.rb +174 -0
  92. data/test/spec_rack_webrick.rb +123 -0
  93. data/test/testrequest.rb +45 -0
  94. metadata +177 -0
@@ -0,0 +1,57 @@
1
+ require 'uri'
2
+
3
+ module Rack
4
+ # Rack::ForwardRequest gets caught by Rack::Recursive and redirects
5
+ # the current request to the app at +url+.
6
+ #
7
+ # raise ForwardRequest.new("/not-found")
8
+ #
9
+
10
+ class ForwardRequest < Exception
11
+ attr_reader :url, :env
12
+
13
+ def initialize(url, env={})
14
+ @url = URI(url)
15
+ @env = env
16
+
17
+ @env["PATH_INFO"] = @url.path
18
+ @env["QUERY_STRING"] = @url.query if @url.query
19
+ @env["HTTP_HOST"] = @url.host if @url.host
20
+ @env["HTTP_PORT"] = @url.port if @url.port
21
+ @env["rack.url_scheme"] = @url.scheme if @url.scheme
22
+
23
+ super "forwarding to #{url}"
24
+ end
25
+ end
26
+
27
+ # Rack::Recursive allows applications called down the chain to
28
+ # include data from other applications (by using
29
+ # <tt>rack['rack.recursive.include'][...]</tt> or raise a
30
+ # ForwardRequest to redirect internally.
31
+
32
+ class Recursive
33
+ def initialize(app)
34
+ @app = app
35
+ end
36
+
37
+ def call(env)
38
+ @script_name = env["SCRIPT_NAME"]
39
+ @app.call(env.merge('rack.recursive.include' => method(:include)))
40
+ rescue ForwardRequest => req
41
+ call(env.merge(req.env))
42
+ end
43
+
44
+ def include(env, path)
45
+ unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ ||
46
+ path[@script_name.size].nil?)
47
+ raise ArgumentError, "can only include below #{@script_name}, not #{path}"
48
+ end
49
+
50
+ env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name,
51
+ "REQUEST_METHOD" => "GET",
52
+ "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "",
53
+ "rack.input" => StringIO.new(""))
54
+ @app.call(env)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,64 @@
1
+ require 'thread'
2
+
3
+ module Rack
4
+ # Rack::Reloader checks on every request, but at most every +secs+
5
+ # seconds, if a file loaded changed, and reloads it, logging to
6
+ # rack.errors.
7
+ #
8
+ # It is recommended you use ShowExceptions to catch SyntaxErrors etc.
9
+
10
+ class Reloader
11
+ def initialize(app, secs=10)
12
+ @app = app
13
+ @secs = secs # reload every @secs seconds max
14
+ @last = Time.now
15
+ end
16
+
17
+ def call(env)
18
+ if Time.now > @last + @secs
19
+ Thread.exclusive {
20
+ reload!(env['rack.errors'])
21
+ @last = Time.now
22
+ }
23
+ end
24
+
25
+ @app.call(env)
26
+ end
27
+
28
+ def reload!(stderr=STDERR)
29
+ need_reload = $LOADED_FEATURES.find_all { |loaded|
30
+ begin
31
+ if loaded =~ /\A[.\/]/ # absolute filename or 1.9
32
+ abs = loaded
33
+ else
34
+ abs = $LOAD_PATH.map { |path| ::File.join(path, loaded) }.
35
+ find { |file| ::File.exist? file }
36
+ end
37
+
38
+ if abs
39
+ ::File.mtime(abs) > @last - @secs rescue false
40
+ else
41
+ false
42
+ end
43
+ end
44
+ }
45
+
46
+ need_reload.each { |l|
47
+ $LOADED_FEATURES.delete l
48
+ }
49
+
50
+ need_reload.each { |to_load|
51
+ begin
52
+ if require to_load
53
+ stderr.puts "#{self.class}: reloaded `#{to_load}'"
54
+ end
55
+ rescue LoadError, SyntaxError => e
56
+ raise e # Possibly ShowExceptions
57
+ end
58
+ }
59
+
60
+ stderr.flush
61
+ need_reload
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,217 @@
1
+ require 'rack/utils'
2
+
3
+ module Rack
4
+ # Rack::Request provides a convenient interface to a Rack
5
+ # environment. It is stateless, the environment +env+ passed to the
6
+ # constructor will be directly modified.
7
+ #
8
+ # req = Rack::Request.new(env)
9
+ # req.post?
10
+ # req.params["data"]
11
+
12
+ class Request
13
+ # The environment of the request.
14
+ attr_reader :env
15
+
16
+ def initialize(env)
17
+ @env = env
18
+ end
19
+
20
+ def body; @env["rack.input"] end
21
+ def scheme; @env["rack.url_scheme"] end
22
+ def script_name; @env["SCRIPT_NAME"].to_s end
23
+ def path_info; @env["PATH_INFO"].to_s end
24
+ def port; @env["SERVER_PORT"].to_i end
25
+ def request_method; @env["REQUEST_METHOD"] end
26
+ def query_string; @env["QUERY_STRING"].to_s end
27
+ def content_length; @env['CONTENT_LENGTH'] end
28
+ def content_type; @env['CONTENT_TYPE'] end
29
+
30
+ # The media type (type/subtype) portion of the CONTENT_TYPE header
31
+ # without any media type parameters. e.g., when CONTENT_TYPE is
32
+ # "text/plain;charset=utf-8", the media-type is "text/plain".
33
+ #
34
+ # For more information on the use of media types in HTTP, see:
35
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
36
+ def media_type
37
+ content_type && content_type.split(/\s*[;,]\s*/, 2)[0].downcase
38
+ end
39
+
40
+ # The media type parameters provided in CONTENT_TYPE as a Hash, or
41
+ # an empty Hash if no CONTENT_TYPE or media-type parameters were
42
+ # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
43
+ # this method responds with the following Hash:
44
+ # { 'charset' => 'utf-8' }
45
+ def media_type_params
46
+ return {} if content_type.nil?
47
+ content_type.split(/\s*[;,]\s*/)[1..-1].
48
+ collect { |s| s.split('=', 2) }.
49
+ inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash }
50
+ end
51
+
52
+ # The character set of the request body if a "charset" media type
53
+ # parameter was given, or nil if no "charset" was specified. Note
54
+ # that, per RFC2616, text/* media types that specify no explicit
55
+ # charset are to be considered ISO-8859-1.
56
+ def content_charset
57
+ media_type_params['charset']
58
+ end
59
+
60
+ def host
61
+ # Remove port number.
62
+ (@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '')
63
+ end
64
+
65
+ def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
66
+ def path_info=(s); @env["PATH_INFO"] = s.to_s end
67
+
68
+ def get?; request_method == "GET" end
69
+ def post?; request_method == "POST" end
70
+ def put?; request_method == "PUT" end
71
+ def delete?; request_method == "DELETE" end
72
+ def head?; request_method == "HEAD" end
73
+
74
+ # The set of form-data media-types. Requests that do not indicate
75
+ # one of the media types presents in this list will not be eligible
76
+ # for form-data / param parsing.
77
+ FORM_DATA_MEDIA_TYPES = [
78
+ nil,
79
+ 'application/x-www-form-urlencoded',
80
+ 'multipart/form-data'
81
+ ]
82
+
83
+ # Determine whether the request body contains form-data by checking
84
+ # the request media_type against registered form-data media-types:
85
+ # "application/x-www-form-urlencoded" and "multipart/form-data". The
86
+ # list of form-data media types can be modified through the
87
+ # +FORM_DATA_MEDIA_TYPES+ array.
88
+ def form_data?
89
+ FORM_DATA_MEDIA_TYPES.include?(media_type)
90
+ end
91
+
92
+ # Returns the data recieved in the query string.
93
+ def GET
94
+ if @env["rack.request.query_string"] == query_string
95
+ @env["rack.request.query_hash"]
96
+ else
97
+ @env["rack.request.query_string"] = query_string
98
+ @env["rack.request.query_hash"] =
99
+ Utils.parse_query(query_string)
100
+ end
101
+ end
102
+
103
+ # Returns the data recieved in the request body.
104
+ #
105
+ # This method support both application/x-www-form-urlencoded and
106
+ # multipart/form-data.
107
+ def POST
108
+ if @env["rack.request.form_input"].eql? @env["rack.input"]
109
+ @env["rack.request.form_hash"]
110
+ elsif form_data?
111
+ @env["rack.request.form_input"] = @env["rack.input"]
112
+ unless @env["rack.request.form_hash"] =
113
+ Utils::Multipart.parse_multipart(env)
114
+ @env["rack.request.form_vars"] = @env["rack.input"].read
115
+ @env["rack.request.form_hash"] = Utils.parse_query(@env["rack.request.form_vars"])
116
+ end
117
+ @env["rack.request.form_hash"]
118
+ else
119
+ {}
120
+ end
121
+ end
122
+
123
+ # The union of GET and POST data.
124
+ def params
125
+ self.GET.update(self.POST)
126
+ rescue EOFError => e
127
+ self.GET
128
+ end
129
+
130
+ # shortcut for request.params[key]
131
+ def [](key)
132
+ params[key.to_s]
133
+ end
134
+
135
+ # shortcut for request.params[key] = value
136
+ def []=(key, value)
137
+ params[key.to_s] = value
138
+ end
139
+
140
+ # like Hash#values_at
141
+ def values_at(*keys)
142
+ keys.map{|key| params[key] }
143
+ end
144
+
145
+ # the referer of the client or '/'
146
+ def referer
147
+ @env['HTTP_REFERER'] || '/'
148
+ end
149
+ alias referrer referer
150
+
151
+
152
+ def cookies
153
+ return {} unless @env["HTTP_COOKIE"]
154
+
155
+ if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
156
+ @env["rack.request.cookie_hash"]
157
+ else
158
+ @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
159
+ # According to RFC 2109:
160
+ # If multiple cookies satisfy the criteria above, they are ordered in
161
+ # the Cookie header such that those with more specific Path attributes
162
+ # precede those with less specific. Ordering with respect to other
163
+ # attributes (e.g., Domain) is unspecified.
164
+ @env["rack.request.cookie_hash"] =
165
+ Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)|
166
+ h[k] = Array === v ? v.first : v
167
+ h
168
+ }
169
+ end
170
+ end
171
+
172
+ def xhr?
173
+ @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
174
+ end
175
+
176
+ # Tries to return a remake of the original request URL as a string.
177
+ def url
178
+ url = scheme + "://"
179
+ url << host
180
+
181
+ if scheme == "https" && port != 443 ||
182
+ scheme == "http" && port != 80
183
+ url << ":#{port}"
184
+ end
185
+
186
+ url << fullpath
187
+
188
+ url
189
+ end
190
+
191
+ def fullpath
192
+ path = script_name + path_info
193
+ path << "?" << query_string unless query_string.empty?
194
+ path
195
+ end
196
+
197
+ def accept_encoding
198
+ @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part|
199
+ m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick
200
+
201
+ if m
202
+ [m[1], (m[2] || 1.0).to_f]
203
+ else
204
+ raise "Invalid value for Accept-Encoding: #{part.inspect}"
205
+ end
206
+ end
207
+ end
208
+
209
+ def ip
210
+ if addr = @env['HTTP_X_FORWARDED_FOR']
211
+ addr.split(',').last.strip
212
+ else
213
+ @env['REMOTE_ADDR']
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,171 @@
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
+ def initialize(body=[], status=200, header={}, &block)
20
+ @status = status
21
+ @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.
22
+ merge(header))
23
+
24
+ @writer = lambda { |x| @body << x }
25
+ @block = nil
26
+ @length = 0
27
+
28
+ @body = []
29
+
30
+ if body.respond_to? :to_str
31
+ write body.to_str
32
+ elsif body.respond_to?(:each)
33
+ body.each { |part|
34
+ write part.to_s
35
+ }
36
+ else
37
+ raise TypeError, "stringable or iterable required"
38
+ end
39
+
40
+ yield self if block_given?
41
+ end
42
+
43
+ attr_reader :header
44
+ attr_accessor :status, :body
45
+
46
+ def [](key)
47
+ header[key]
48
+ end
49
+
50
+ def []=(key, value)
51
+ header[key] = value
52
+ end
53
+
54
+ def set_cookie(key, value)
55
+ case value
56
+ when Hash
57
+ domain = "; domain=" + value[:domain] if value[:domain]
58
+ path = "; path=" + value[:path] if value[:path]
59
+ # According to RFC 2109, we need dashes here.
60
+ # N.B.: cgi.rb uses spaces...
61
+ expires = "; expires=" + value[:expires].clone.gmtime.
62
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
63
+ secure = "; secure" if value[:secure]
64
+ value = value[:value]
65
+ end
66
+ value = [value] unless Array === value
67
+ cookie = Utils.escape(key) + "=" +
68
+ value.map { |v| Utils.escape v }.join("&") +
69
+ "#{domain}#{path}#{expires}#{secure}"
70
+
71
+ case self["Set-Cookie"]
72
+ when Array
73
+ self["Set-Cookie"] << cookie
74
+ when String
75
+ self["Set-Cookie"] = [self["Set-Cookie"], cookie]
76
+ when nil
77
+ self["Set-Cookie"] = cookie
78
+ end
79
+ end
80
+
81
+ def delete_cookie(key, value={})
82
+ unless Array === self["Set-Cookie"]
83
+ self["Set-Cookie"] = [self["Set-Cookie"]].compact
84
+ end
85
+
86
+ self["Set-Cookie"].reject! { |cookie|
87
+ cookie =~ /\A#{Utils.escape(key)}=/
88
+ }
89
+
90
+ set_cookie(key,
91
+ {:value => '', :path => nil, :domain => nil,
92
+ :expires => Time.at(0) }.merge(value))
93
+ end
94
+
95
+
96
+ def finish(&block)
97
+ @block = block
98
+
99
+ if [204, 304].include?(status.to_i)
100
+ header.delete "Content-Type"
101
+ [status.to_i, header.to_hash, []]
102
+ else
103
+ header["Content-Length"] ||= @length.to_s
104
+ [status.to_i, header.to_hash, self]
105
+ end
106
+ end
107
+ alias to_a finish # For *response
108
+
109
+ def each(&callback)
110
+ @body.each(&callback)
111
+ @writer = callback
112
+ @block.call(self) if @block
113
+ end
114
+
115
+ def write(str)
116
+ s = str.to_s
117
+ @length += s.size
118
+ @writer.call s
119
+ str
120
+ end
121
+
122
+ def close
123
+ body.close if body.respond_to?(:close)
124
+ end
125
+
126
+ def empty?
127
+ @block == nil && @body.empty?
128
+ end
129
+
130
+ alias headers header
131
+
132
+ module Helpers
133
+ def invalid?; @status < 100 || @status >= 600; end
134
+
135
+ def informational?; @status >= 100 && @status < 200; end
136
+ def successful?; @status >= 200 && @status < 300; end
137
+ def redirection?; @status >= 300 && @status < 400; end
138
+ def client_error?; @status >= 400 && @status < 500; end
139
+ def server_error?; @status >= 500 && @status < 600; end
140
+
141
+ def ok?; @status == 200; end
142
+ def forbidden?; @status == 403; end
143
+ def not_found?; @status == 404; end
144
+
145
+ def redirect?; [301, 302, 303, 307].include? @status; end
146
+ def empty?; [201, 204, 304].include? @status; end
147
+
148
+ # Headers
149
+ attr_reader :headers, :original_headers
150
+
151
+ def include?(header)
152
+ !!headers[header]
153
+ end
154
+
155
+ def content_type
156
+ headers["Content-Type"]
157
+ end
158
+
159
+ def content_length
160
+ cl = headers["Content-Length"]
161
+ cl ? cl.to_i : cl
162
+ end
163
+
164
+ def location
165
+ headers["Location"]
166
+ end
167
+ end
168
+
169
+ include Helpers
170
+ end
171
+ end