sinatra 0.2.1 → 0.2.2

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

Potentially problematic release.


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

Files changed (83) hide show
  1. data/CHANGELOG +1 -0
  2. data/LICENSE +22 -0
  3. data/Manifest +78 -1
  4. data/lib/sinatra.rb +12 -1
  5. data/sinatra.gemspec +7 -14
  6. data/vendor/rack/AUTHORS +7 -0
  7. data/vendor/rack/COPYING +18 -0
  8. data/vendor/rack/KNOWN-ISSUES +18 -0
  9. data/vendor/rack/README +242 -0
  10. data/vendor/rack/Rakefile +174 -0
  11. data/vendor/rack/bin/rackup +153 -0
  12. data/vendor/rack/contrib/rack_logo.svg +111 -0
  13. data/vendor/rack/example/lobster.ru +4 -0
  14. data/vendor/rack/example/protectedlobster.rb +14 -0
  15. data/vendor/rack/example/protectedlobster.ru +8 -0
  16. data/vendor/rack/lib/rack.rb +92 -0
  17. data/vendor/rack/lib/rack/adapter/camping.rb +22 -0
  18. data/vendor/rack/lib/rack/auth/abstract/handler.rb +28 -0
  19. data/vendor/rack/lib/rack/auth/abstract/request.rb +37 -0
  20. data/vendor/rack/lib/rack/auth/basic.rb +58 -0
  21. data/vendor/rack/lib/rack/auth/digest/md5.rb +124 -0
  22. data/vendor/rack/lib/rack/auth/digest/nonce.rb +51 -0
  23. data/vendor/rack/lib/rack/auth/digest/params.rb +55 -0
  24. data/vendor/rack/lib/rack/auth/digest/request.rb +40 -0
  25. data/vendor/rack/lib/rack/auth/openid.rb +116 -0
  26. data/vendor/rack/lib/rack/builder.rb +56 -0
  27. data/vendor/rack/lib/rack/cascade.rb +36 -0
  28. data/vendor/rack/lib/rack/commonlogger.rb +56 -0
  29. data/vendor/rack/lib/rack/file.rb +112 -0
  30. data/vendor/rack/lib/rack/handler/cgi.rb +57 -0
  31. data/vendor/rack/lib/rack/handler/fastcgi.rb +83 -0
  32. data/vendor/rack/lib/rack/handler/lsws.rb +52 -0
  33. data/vendor/rack/lib/rack/handler/mongrel.rb +78 -0
  34. data/vendor/rack/lib/rack/handler/scgi.rb +57 -0
  35. data/vendor/rack/lib/rack/handler/webrick.rb +57 -0
  36. data/vendor/rack/lib/rack/lint.rb +394 -0
  37. data/vendor/rack/lib/rack/lobster.rb +65 -0
  38. data/vendor/rack/lib/rack/mock.rb +160 -0
  39. data/vendor/rack/lib/rack/recursive.rb +57 -0
  40. data/vendor/rack/lib/rack/reloader.rb +64 -0
  41. data/vendor/rack/lib/rack/request.rb +197 -0
  42. data/vendor/rack/lib/rack/response.rb +166 -0
  43. data/vendor/rack/lib/rack/session/abstract/id.rb +126 -0
  44. data/vendor/rack/lib/rack/session/cookie.rb +71 -0
  45. data/vendor/rack/lib/rack/session/memcache.rb +83 -0
  46. data/vendor/rack/lib/rack/session/pool.rb +67 -0
  47. data/vendor/rack/lib/rack/showexceptions.rb +344 -0
  48. data/vendor/rack/lib/rack/showstatus.rb +103 -0
  49. data/vendor/rack/lib/rack/static.rb +38 -0
  50. data/vendor/rack/lib/rack/urlmap.rb +48 -0
  51. data/vendor/rack/lib/rack/utils.rb +240 -0
  52. data/vendor/rack/test/cgi/lighttpd.conf +20 -0
  53. data/vendor/rack/test/cgi/test +9 -0
  54. data/vendor/rack/test/cgi/test.fcgi +7 -0
  55. data/vendor/rack/test/cgi/test.ru +7 -0
  56. data/vendor/rack/test/spec_rack_auth_basic.rb +69 -0
  57. data/vendor/rack/test/spec_rack_auth_digest.rb +169 -0
  58. data/vendor/rack/test/spec_rack_builder.rb +50 -0
  59. data/vendor/rack/test/spec_rack_camping.rb +47 -0
  60. data/vendor/rack/test/spec_rack_cascade.rb +50 -0
  61. data/vendor/rack/test/spec_rack_cgi.rb +91 -0
  62. data/vendor/rack/test/spec_rack_commonlogger.rb +32 -0
  63. data/vendor/rack/test/spec_rack_fastcgi.rb +91 -0
  64. data/vendor/rack/test/spec_rack_file.rb +40 -0
  65. data/vendor/rack/test/spec_rack_lint.rb +317 -0
  66. data/vendor/rack/test/spec_rack_lobster.rb +45 -0
  67. data/vendor/rack/test/spec_rack_mock.rb +152 -0
  68. data/vendor/rack/test/spec_rack_mongrel.rb +165 -0
  69. data/vendor/rack/test/spec_rack_recursive.rb +77 -0
  70. data/vendor/rack/test/spec_rack_request.rb +384 -0
  71. data/vendor/rack/test/spec_rack_response.rb +167 -0
  72. data/vendor/rack/test/spec_rack_session_cookie.rb +49 -0
  73. data/vendor/rack/test/spec_rack_session_memcache.rb +100 -0
  74. data/vendor/rack/test/spec_rack_session_pool.rb +84 -0
  75. data/vendor/rack/test/spec_rack_showexceptions.rb +21 -0
  76. data/vendor/rack/test/spec_rack_showstatus.rb +71 -0
  77. data/vendor/rack/test/spec_rack_static.rb +37 -0
  78. data/vendor/rack/test/spec_rack_urlmap.rb +175 -0
  79. data/vendor/rack/test/spec_rack_utils.rb +57 -0
  80. data/vendor/rack/test/spec_rack_webrick.rb +106 -0
  81. data/vendor/rack/test/testrequest.rb +43 -0
  82. metadata +81 -4
  83. data/Rakefile +0 -24
@@ -0,0 +1,65 @@
1
+ require 'zlib'
2
+
3
+ require 'rack/request'
4
+ require 'rack/response'
5
+
6
+ module Rack
7
+ # Paste has a Pony, Rack has a Lobster!
8
+ class Lobster
9
+ LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2
10
+ P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0
11
+ t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ
12
+ I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0])
13
+
14
+ LambdaLobster = lambda { |env|
15
+ if env["QUERY_STRING"].include?("flip")
16
+ lobster = LobsterString.split("\n").
17
+ map { |line| line.ljust(42).reverse }.
18
+ join("\n")
19
+ href = "?"
20
+ else
21
+ lobster = LobsterString
22
+ href = "?flip"
23
+ end
24
+
25
+ [200, {"Content-Type" => "text/html"},
26
+ ["<title>Lobstericious!</title>",
27
+ "<pre>", lobster, "</pre>",
28
+ "<a href='#{href}'>flip!</a>"]
29
+ ]
30
+ }
31
+
32
+ def call(env)
33
+ req = Request.new(env)
34
+ if req.GET["flip"] == "left"
35
+ lobster = LobsterString.split("\n").
36
+ map { |line| line.ljust(42).reverse }.
37
+ join("\n")
38
+ href = "?flip=right"
39
+ elsif req.GET["flip"] == "crash"
40
+ raise "Lobster crashed"
41
+ else
42
+ lobster = LobsterString
43
+ href = "?flip=left"
44
+ end
45
+
46
+ Response.new.finish do |res|
47
+ res.write "<title>Lobstericious!</title>"
48
+ res.write "<pre>"
49
+ res.write lobster
50
+ res.write "</pre>"
51
+ res.write "<p><a href='#{href}'>flip!</a></p>"
52
+ res.write "<p><a href='?flip=crash'>crash!</a></p>"
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ if $0 == __FILE__
60
+ require 'rack'
61
+ require 'rack/showexceptions'
62
+ Rack::Handler::WEBrick.run \
63
+ Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)),
64
+ :Port => 9292
65
+ end
@@ -0,0 +1,160 @@
1
+ require 'uri'
2
+ require 'stringio'
3
+ require 'rack/lint'
4
+ require 'rack/utils'
5
+ require 'rack/response'
6
+
7
+ module Rack
8
+ # Rack::MockRequest helps testing your Rack application without
9
+ # actually using HTTP.
10
+ #
11
+ # After performing a request on a URL with get/post/put/delete, it
12
+ # returns a MockResponse with useful helper methods for effective
13
+ # testing.
14
+ #
15
+ # You can pass a hash with additional configuration to the
16
+ # get/post/put/delete.
17
+ # <tt>:input</tt>:: A String or IO-like to be used as rack.input.
18
+ # <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
19
+ # <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
20
+
21
+ class MockRequest
22
+ class FatalWarning < RuntimeError
23
+ end
24
+
25
+ class FatalWarner
26
+ def puts(warning)
27
+ raise FatalWarning, warning
28
+ end
29
+
30
+ def write(warning)
31
+ raise FatalWarning, warning
32
+ end
33
+
34
+ def flush
35
+ end
36
+
37
+ def string
38
+ ""
39
+ end
40
+ end
41
+
42
+ DEFAULT_ENV = {
43
+ "rack.version" => [0,1],
44
+ "rack.input" => StringIO.new,
45
+ "rack.errors" => StringIO.new,
46
+ "rack.multithread" => true,
47
+ "rack.multiprocess" => true,
48
+ "rack.run_once" => false,
49
+ }
50
+
51
+ def initialize(app)
52
+ @app = app
53
+ end
54
+
55
+ def get(uri, opts={}) request("GET", uri, opts) end
56
+ def post(uri, opts={}) request("POST", uri, opts) end
57
+ def put(uri, opts={}) request("PUT", uri, opts) end
58
+ def delete(uri, opts={}) request("DELETE", uri, opts) end
59
+
60
+ def request(method="GET", uri="", opts={})
61
+ env = self.class.env_for(uri, opts.merge(:method => method))
62
+
63
+ if opts[:lint]
64
+ app = Rack::Lint.new(@app)
65
+ else
66
+ app = @app
67
+ end
68
+
69
+ errors = env["rack.errors"]
70
+ MockResponse.new(*(app.call(env) + [errors]))
71
+ end
72
+
73
+ # Return the Rack environment used for a request to +uri+.
74
+ def self.env_for(uri="", opts={})
75
+ uri = URI(uri)
76
+ env = DEFAULT_ENV.dup
77
+
78
+ env["REQUEST_METHOD"] = opts[:method] || "GET"
79
+ env["SERVER_NAME"] = uri.host || "example.org"
80
+ env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
81
+ env["QUERY_STRING"] = uri.query.to_s
82
+ env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
83
+ env["rack.url_scheme"] = uri.scheme || "http"
84
+
85
+ env["SCRIPT_NAME"] = opts[:script_name] || ""
86
+
87
+ if opts[:fatal]
88
+ env["rack.errors"] = FatalWarner.new
89
+ else
90
+ env["rack.errors"] = StringIO.new
91
+ end
92
+
93
+ opts[:input] ||= ""
94
+ if String === opts[:input]
95
+ env["rack.input"] = StringIO.new(opts[:input])
96
+ else
97
+ env["rack.input"] = opts[:input]
98
+ end
99
+
100
+ opts.each { |field, value|
101
+ env[field] = value if String === field
102
+ }
103
+
104
+ env
105
+ end
106
+ end
107
+
108
+ # Rack::MockResponse provides useful helpers for testing your apps.
109
+ # Usually, you don't create the MockResponse on your own, but use
110
+ # MockRequest.
111
+
112
+ class MockResponse
113
+ def initialize(status, headers, body, errors=StringIO.new(""))
114
+ @status = status.to_i
115
+
116
+ @original_headers = headers
117
+ @headers = Rack::Utils::HeaderHash.new
118
+ headers.each { |field, values|
119
+ values.each { |value|
120
+ @headers[field] = value
121
+ }
122
+ @headers[field] = "" if values.empty?
123
+ }
124
+
125
+ @body = ""
126
+ body.each { |part| @body << part }
127
+
128
+ @errors = errors.string
129
+ end
130
+
131
+ # Status
132
+ attr_reader :status
133
+
134
+ # Headers
135
+ attr_reader :headers, :original_headers
136
+
137
+ def [](field)
138
+ headers[field]
139
+ end
140
+
141
+
142
+ # Body
143
+ attr_reader :body
144
+
145
+ def =~(other)
146
+ @body =~ other
147
+ end
148
+
149
+ def match(other)
150
+ @body.match other
151
+ end
152
+
153
+
154
+ # Errors
155
+ attr_accessor :errors
156
+
157
+
158
+ include Response::Helpers
159
+ end
160
+ end
@@ -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,197 @@
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
+ end
197
+ end