passenger 2.2.1 → 2.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 passenger might be problematic. Click here for more details.

Files changed (216) hide show
  1. data/Rakefile +66 -79
  2. data/bin/passenger-install-nginx-module +1 -1
  3. data/bin/passenger-memory-stats +1 -1
  4. data/bin/passenger-spawn-server +8 -2
  5. data/doc/Users guide Apache.html +33 -49
  6. data/doc/Users guide Apache.txt +28 -27
  7. data/doc/Users guide Nginx.html +9 -19
  8. data/doc/Users guide Nginx.txt +8 -20
  9. data/doc/cxxapi/Bucket_8h-source.html +1 -1
  10. data/doc/cxxapi/Configuration_8h-source.html +297 -300
  11. data/doc/cxxapi/DirectoryMapper_8h-source.html +1 -1
  12. data/doc/cxxapi/Hooks_8h-source.html +1 -1
  13. data/doc/cxxapi/annotated.html +1 -1
  14. data/doc/cxxapi/classHooks-members.html +1 -1
  15. data/doc/cxxapi/classHooks.html +1 -1
  16. data/doc/cxxapi/classPassenger_1_1DirectoryMapper-members.html +1 -1
  17. data/doc/cxxapi/classPassenger_1_1DirectoryMapper.html +1 -1
  18. data/doc/cxxapi/classes.html +1 -1
  19. data/doc/cxxapi/definitions_8h-source.html +1 -1
  20. data/doc/cxxapi/files.html +1 -1
  21. data/doc/cxxapi/functions.html +1 -1
  22. data/doc/cxxapi/functions_func.html +1 -1
  23. data/doc/cxxapi/graph_legend.html +1 -1
  24. data/doc/cxxapi/group__Configuration.html +1 -23
  25. data/doc/cxxapi/group__Core.html +1 -1
  26. data/doc/cxxapi/group__Hooks.html +1 -1
  27. data/doc/cxxapi/group__Support.html +1 -1
  28. data/doc/cxxapi/main.html +1 -1
  29. data/doc/cxxapi/modules.html +1 -1
  30. data/doc/users_guide_snippets/rackup_specifications.txt +4 -2
  31. data/ext/apache2/Configuration.h +0 -3
  32. data/ext/apache2/Hooks.cpp +5 -3
  33. data/ext/common/ApplicationPoolServer.h +1 -0
  34. data/ext/common/ApplicationPoolServerExecutable.cpp +5 -2
  35. data/ext/common/SpawnManager.h +1 -0
  36. data/ext/common/Utils.cpp +22 -23
  37. data/ext/common/Utils.h +32 -21
  38. data/ext/common/Version.h +31 -0
  39. data/ext/nginx/ContentHandler.c +61 -8
  40. data/ext/nginx/HelperServer.cpp +3 -0
  41. data/ext/nginx/HttpStatusExtractor.h +185 -0
  42. data/ext/nginx/StaticContentHandler.c +18 -3
  43. data/ext/nginx/config +2 -1
  44. data/ext/nginx/ngx_http_passenger_module.c +21 -15
  45. data/ext/oxt/backtrace.cpp +4 -2
  46. data/ext/oxt/spin_lock.hpp +3 -3
  47. data/lib/phusion_passenger/abstract_request_handler.rb +5 -3
  48. data/lib/phusion_passenger/admin_tools/control_process.rb +6 -1
  49. data/lib/phusion_passenger/constants.rb +2 -2
  50. data/lib/phusion_passenger/rack/application_spawner.rb +2 -1
  51. data/lib/phusion_passenger/rack/request_handler.rb +45 -35
  52. data/lib/phusion_passenger/templates/nginx/config_snippets.txt.erb +1 -1
  53. data/lib/phusion_passenger/utils.rb +13 -3
  54. data/misc/rake/cplusplus.rb +7 -0
  55. data/test/ApplicationPoolServer_ApplicationPoolTest.cpp +2 -0
  56. data/test/ApplicationPoolTest.cpp +39 -62
  57. data/test/CxxTestMain.cpp +6 -6
  58. data/test/HttpStatusExtractorTest.cpp +17 -0
  59. data/test/StandardApplicationPoolTest.cpp +2 -0
  60. data/test/UtilsTest.cpp +17 -28
  61. data/test/ruby/abstract_request_handler_spec.rb +3 -7
  62. data/test/ruby/utils_spec.rb +18 -13
  63. data/test/ruby/wsgi/application_spawner_spec.rb +5 -9
  64. data/test/stub/railsapp/app/controllers/{bar_controller_1.rb → bar_controller.rb} +0 -0
  65. data/test/stub/railsapp/app/controllers/bar_controller_1.txt +5 -0
  66. data/test/stub/railsapp/app/controllers/{bar_controller_2.rb → bar_controller_2.txt} +0 -0
  67. data/test/support/Support.h +20 -0
  68. data/test/support/config.rb +13 -0
  69. data/vendor/README +4 -3
  70. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/COPYING +1 -1
  71. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/KNOWN-ISSUES +0 -0
  72. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/README +54 -7
  73. data/vendor/rack-1.0.0-git/Rakefile +164 -0
  74. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack.rb +7 -3
  75. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/adapter/camping.rb +0 -0
  76. data/vendor/rack-1.0.0-git/lib/rack/auth/abstract/handler.rb +37 -0
  77. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/auth/abstract/request.rb +0 -0
  78. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/auth/basic.rb +0 -0
  79. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/auth/digest/md5.rb +1 -1
  80. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/auth/digest/nonce.rb +0 -0
  81. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/auth/digest/params.rb +0 -0
  82. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/auth/digest/request.rb +2 -2
  83. data/vendor/rack-1.0.0-git/lib/rack/auth/openid.rb +480 -0
  84. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/builder.rb +1 -5
  85. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/cascade.rb +0 -0
  86. data/vendor/rack-1.0.0-git/lib/rack/chunked.rb +49 -0
  87. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/commonlogger.rb +0 -0
  88. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/conditionalget.rb +4 -0
  89. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/content_length.rb +7 -3
  90. data/vendor/rack-1.0.0-git/lib/rack/content_type.rb +23 -0
  91. data/vendor/rack-1.0.0-git/lib/rack/deflater.rb +96 -0
  92. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/directory.rb +5 -2
  93. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/file.rb +4 -1
  94. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler.rb +22 -1
  95. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/cgi.rb +7 -3
  96. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/evented_mongrel.rb +0 -0
  97. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/fastcgi.rb +26 -24
  98. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/lsws.rb +7 -4
  99. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/mongrel.rb +5 -3
  100. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/scgi.rb +5 -3
  101. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/swiftiplied_mongrel.rb +0 -0
  102. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/thin.rb +3 -0
  103. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/handler/webrick.rb +11 -5
  104. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/head.rb +0 -0
  105. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/lint.rb +138 -66
  106. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/lobster.rb +0 -0
  107. data/vendor/rack-1.0.0-git/lib/rack/lock.rb +16 -0
  108. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/methodoverride.rb +0 -0
  109. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/mime.rb +4 -4
  110. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/mock.rb +42 -5
  111. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/recursive.rb +0 -0
  112. data/vendor/rack-1.0.0-git/lib/rack/reloader.rb +106 -0
  113. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/request.rb +46 -10
  114. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/response.rb +15 -3
  115. data/vendor/rack-1.0.0-git/lib/rack/rewindable_input.rb +98 -0
  116. data/vendor/rack-1.0.0-git/lib/rack/session/abstract/id.rb +142 -0
  117. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/session/cookie.rb +2 -0
  118. data/vendor/rack-1.0.0-git/lib/rack/session/memcache.rb +109 -0
  119. data/vendor/rack-1.0.0-git/lib/rack/session/pool.rb +100 -0
  120. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/showexceptions.rb +2 -1
  121. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/showstatus.rb +1 -1
  122. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/static.rb +0 -0
  123. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/urlmap.rb +12 -5
  124. data/vendor/{rack-0.9.1 → rack-1.0.0-git}/lib/rack/utils.rb +212 -65
  125. metadata +71 -170
  126. data/doc/rdoc/classes/ConditionVariable.html +0 -194
  127. data/doc/rdoc/classes/Exception.html +0 -120
  128. data/doc/rdoc/classes/GC.html +0 -113
  129. data/doc/rdoc/classes/IO.html +0 -169
  130. data/doc/rdoc/classes/PhusionPassenger.html +0 -238
  131. data/doc/rdoc/classes/PhusionPassenger/AbstractInstaller.html +0 -153
  132. data/doc/rdoc/classes/PhusionPassenger/AbstractRequestHandler.html +0 -506
  133. data/doc/rdoc/classes/PhusionPassenger/AbstractServer.html +0 -692
  134. data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerAlreadyStarted.html +0 -97
  135. data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerError.html +0 -96
  136. data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerNotStarted.html +0 -97
  137. data/doc/rdoc/classes/PhusionPassenger/AbstractServer/UnknownMessage.html +0 -96
  138. data/doc/rdoc/classes/PhusionPassenger/AbstractServerCollection.html +0 -598
  139. data/doc/rdoc/classes/PhusionPassenger/AdminTools.html +0 -140
  140. data/doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess.html +0 -264
  141. data/doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess/Instance.html +0 -138
  142. data/doc/rdoc/classes/PhusionPassenger/AppInitError.html +0 -154
  143. data/doc/rdoc/classes/PhusionPassenger/Application.html +0 -283
  144. data/doc/rdoc/classes/PhusionPassenger/ConsoleTextTemplate.html +0 -172
  145. data/doc/rdoc/classes/PhusionPassenger/FrameworkInitError.html +0 -145
  146. data/doc/rdoc/classes/PhusionPassenger/HTMLTemplate.html +0 -175
  147. data/doc/rdoc/classes/PhusionPassenger/InitializationError.html +0 -141
  148. data/doc/rdoc/classes/PhusionPassenger/InvalidPath.html +0 -92
  149. data/doc/rdoc/classes/PhusionPassenger/MessageChannel.html +0 -489
  150. data/doc/rdoc/classes/PhusionPassenger/NativeSupport.html +0 -350
  151. data/doc/rdoc/classes/PhusionPassenger/Rack.html +0 -91
  152. data/doc/rdoc/classes/PhusionPassenger/Rack/ApplicationSpawner.html +0 -185
  153. data/doc/rdoc/classes/PhusionPassenger/Rack/RequestHandler.html +0 -184
  154. data/doc/rdoc/classes/PhusionPassenger/Railz.html +0 -95
  155. data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner.html +0 -419
  156. data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner/Error.html +0 -98
  157. data/doc/rdoc/classes/PhusionPassenger/Railz/CGIFixed.html +0 -200
  158. data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner.html +0 -443
  159. data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner/Error.html +0 -98
  160. data/doc/rdoc/classes/PhusionPassenger/Railz/RequestHandler.html +0 -154
  161. data/doc/rdoc/classes/PhusionPassenger/SpawnManager.html +0 -402
  162. data/doc/rdoc/classes/PhusionPassenger/UnknownError.html +0 -125
  163. data/doc/rdoc/classes/PhusionPassenger/Utils.html +0 -694
  164. data/doc/rdoc/classes/PhusionPassenger/VersionNotFound.html +0 -140
  165. data/doc/rdoc/classes/PhusionPassenger/WSGI.html +0 -89
  166. data/doc/rdoc/classes/PhusionPassenger/WSGI/ApplicationSpawner.html +0 -188
  167. data/doc/rdoc/classes/PlatformInfo.html +0 -831
  168. data/doc/rdoc/classes/RakeExtensions.html +0 -197
  169. data/doc/rdoc/classes/Signal.html +0 -134
  170. data/doc/rdoc/created.rid +0 -1
  171. data/doc/rdoc/files/DEVELOPERS_TXT.html +0 -240
  172. data/doc/rdoc/files/README.html +0 -157
  173. data/doc/rdoc/files/ext/phusion_passenger/native_support_c.html +0 -92
  174. data/doc/rdoc/files/lib/phusion_passenger/abstract_installer_rb.html +0 -129
  175. data/doc/rdoc/files/lib/phusion_passenger/abstract_request_handler_rb.html +0 -131
  176. data/doc/rdoc/files/lib/phusion_passenger/abstract_server_collection_rb.html +0 -126
  177. data/doc/rdoc/files/lib/phusion_passenger/abstract_server_rb.html +0 -130
  178. data/doc/rdoc/files/lib/phusion_passenger/admin_tools/control_process_rb.html +0 -129
  179. data/doc/rdoc/files/lib/phusion_passenger/admin_tools_rb.html +0 -122
  180. data/doc/rdoc/files/lib/phusion_passenger/application_rb.html +0 -127
  181. data/doc/rdoc/files/lib/phusion_passenger/console_text_template_rb.html +0 -126
  182. data/doc/rdoc/files/lib/phusion_passenger/constants_rb.html +0 -122
  183. data/doc/rdoc/files/lib/phusion_passenger/dependencies_rb.html +0 -134
  184. data/doc/rdoc/files/lib/phusion_passenger/events_rb.html +0 -122
  185. data/doc/rdoc/files/lib/phusion_passenger/exceptions_rb.html +0 -122
  186. data/doc/rdoc/files/lib/phusion_passenger/html_template_rb.html +0 -126
  187. data/doc/rdoc/files/lib/phusion_passenger/message_channel_rb.html +0 -122
  188. data/doc/rdoc/files/lib/phusion_passenger/packaging_rb.html +0 -122
  189. data/doc/rdoc/files/lib/phusion_passenger/platform_info_rb.html +0 -127
  190. data/doc/rdoc/files/lib/phusion_passenger/rack/application_spawner_rb.html +0 -133
  191. data/doc/rdoc/files/lib/phusion_passenger/rack/request_handler_rb.html +0 -126
  192. data/doc/rdoc/files/lib/phusion_passenger/railz/application_spawner_rb.html +0 -143
  193. data/doc/rdoc/files/lib/phusion_passenger/railz/cgi_fixed_rb.html +0 -126
  194. data/doc/rdoc/files/lib/phusion_passenger/railz/framework_spawner_rb.html +0 -145
  195. data/doc/rdoc/files/lib/phusion_passenger/railz/request_handler_rb.html +0 -127
  196. data/doc/rdoc/files/lib/phusion_passenger/simple_benchmarking_rb.html +0 -122
  197. data/doc/rdoc/files/lib/phusion_passenger/spawn_manager_rb.html +0 -161
  198. data/doc/rdoc/files/lib/phusion_passenger/utils_rb.html +0 -175
  199. data/doc/rdoc/files/lib/phusion_passenger/wsgi/application_spawner_rb.html +0 -129
  200. data/doc/rdoc/files/misc/rake/extensions_rb.html +0 -130
  201. data/doc/rdoc/fr_class_index.html +0 -90
  202. data/doc/rdoc/fr_file_index.html +0 -76
  203. data/doc/rdoc/fr_method_index.html +0 -195
  204. data/doc/rdoc/index.html +0 -26
  205. data/doc/rdoc/rdoc-style.css +0 -187
  206. data/vendor/rack-0.9.1/AUTHORS +0 -8
  207. data/vendor/rack-0.9.1/ChangeLog +0 -1423
  208. data/vendor/rack-0.9.1/Rakefile +0 -188
  209. data/vendor/rack-0.9.1/SPEC +0 -129
  210. data/vendor/rack-0.9.1/lib/rack/auth/abstract/handler.rb +0 -28
  211. data/vendor/rack-0.9.1/lib/rack/auth/openid.rb +0 -438
  212. data/vendor/rack-0.9.1/lib/rack/deflater.rb +0 -87
  213. data/vendor/rack-0.9.1/lib/rack/reloader.rb +0 -64
  214. data/vendor/rack-0.9.1/lib/rack/session/abstract/id.rb +0 -153
  215. data/vendor/rack-0.9.1/lib/rack/session/memcache.rb +0 -97
  216. data/vendor/rack-0.9.1/lib/rack/session/pool.rb +0 -73
@@ -0,0 +1,16 @@
1
+ module Rack
2
+ class Lock
3
+ FLAG = 'rack.multithread'.freeze
4
+
5
+ def initialize(app, lock = Mutex.new)
6
+ @app, @lock = app, lock
7
+ end
8
+
9
+ def call(env)
10
+ old, env[FLAG] = env[FLAG], false
11
+ @lock.synchronize { @app.call(env) }
12
+ ensure
13
+ env[FLAG] = old
14
+ end
15
+ end
16
+ end
@@ -8,10 +8,10 @@ module Rack
8
8
  # Also see the documentation for MIME_TYPES
9
9
  #
10
10
  # Usage:
11
- # Rack::Utils.mime_type('.foo')
11
+ # Rack::Mime.mime_type('.foo')
12
12
  #
13
13
  # This is a shortcut for:
14
- # Rack::Utils::MIME_TYPES.fetch('.foo', 'application/octet-stream')
14
+ # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
15
15
 
16
16
  def mime_type(ext, fallback='application/octet-stream')
17
17
  MIME_TYPES.fetch(ext, fallback)
@@ -26,12 +26,12 @@ module Rack
26
26
  #
27
27
  # require 'webrick/httputils'
28
28
  # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types')
29
- # Rack::Utils::MIME_TYPES.merge!(list)
29
+ # Rack::Mime::MIME_TYPES.merge!(list)
30
30
  #
31
31
  # To add the list mongrel provides, use:
32
32
  #
33
33
  # require 'mongrel/handlers'
34
- # Rack::Utils::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
34
+ # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
35
35
 
36
36
  MIME_TYPES = {
37
37
  ".3gp" => "video/3gpp",
@@ -40,7 +40,7 @@ module Rack
40
40
  end
41
41
 
42
42
  DEFAULT_ENV = {
43
- "rack.version" => [0,1],
43
+ "rack.version" => [1,0],
44
44
  "rack.input" => StringIO.new,
45
45
  "rack.errors" => StringIO.new,
46
46
  "rack.multithread" => true,
@@ -73,14 +73,17 @@ module Rack
73
73
  # Return the Rack environment used for a request to +uri+.
74
74
  def self.env_for(uri="", opts={})
75
75
  uri = URI(uri)
76
+ uri.path = "/#{uri.path}" unless uri.path[0] == ?/
77
+
76
78
  env = DEFAULT_ENV.dup
77
79
 
78
- env["REQUEST_METHOD"] = opts[:method] || "GET"
80
+ env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
79
81
  env["SERVER_NAME"] = uri.host || "example.org"
80
82
  env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
81
83
  env["QUERY_STRING"] = uri.query.to_s
82
84
  env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
83
85
  env["rack.url_scheme"] = uri.scheme || "http"
86
+ env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
84
87
 
85
88
  env["SCRIPT_NAME"] = opts[:script_name] || ""
86
89
 
@@ -90,6 +93,40 @@ module Rack
90
93
  env["rack.errors"] = StringIO.new
91
94
  end
92
95
 
96
+ if params = opts[:params]
97
+ if env["REQUEST_METHOD"] == "GET"
98
+ params = Utils.parse_nested_query(params) if params.is_a?(String)
99
+ params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
100
+ env["QUERY_STRING"] = Utils.build_nested_query(params)
101
+ elsif !opts.has_key?(:input)
102
+ opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
103
+ if params.is_a?(Hash)
104
+ multipart = false
105
+ query = lambda { |value|
106
+ case value
107
+ when Array
108
+ value.each(&query)
109
+ when Hash
110
+ value.values.each(&query)
111
+ when Utils::Multipart::UploadedFile
112
+ multipart = true
113
+ end
114
+ }
115
+ opts[:params].values.each(&query)
116
+
117
+ if multipart
118
+ opts[:input] = Utils::Multipart.build_multipart(params)
119
+ opts["CONTENT_LENGTH"] ||= opts[:input].length.to_s
120
+ opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
121
+ else
122
+ opts[:input] = Utils.build_nested_query(params)
123
+ end
124
+ else
125
+ opts[:input] = params
126
+ end
127
+ end
128
+ end
129
+
93
130
  opts[:input] ||= ""
94
131
  if String === opts[:input]
95
132
  env["rack.input"] = StringIO.new(opts[:input])
@@ -97,6 +134,8 @@ module Rack
97
134
  env["rack.input"] = opts[:input]
98
135
  end
99
136
 
137
+ env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
138
+
100
139
  opts.each { |field, value|
101
140
  env[field] = value if String === field
102
141
  }
@@ -116,9 +155,7 @@ module Rack
116
155
  @original_headers = headers
117
156
  @headers = Rack::Utils::HeaderHash.new
118
157
  headers.each { |field, values|
119
- values.each { |value|
120
- @headers[field] = value
121
- }
158
+ @headers[field] = values
122
159
  @headers[field] = "" if values.empty?
123
160
  }
124
161
 
@@ -0,0 +1,106 @@
1
+ # Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ require 'pathname'
5
+
6
+ module Rack
7
+
8
+ # High performant source reloader
9
+ #
10
+ # This class acts as Rack middleware.
11
+ #
12
+ # What makes it especially suited for use in a production environment is that
13
+ # any file will only be checked once and there will only be made one system
14
+ # call stat(2).
15
+ #
16
+ # Please note that this will not reload files in the background, it does so
17
+ # only when actively called.
18
+ #
19
+ # It is performing a check/reload cycle at the start of every request, but
20
+ # also respects a cool down time, during which nothing will be done.
21
+ class Reloader
22
+ def initialize(app, cooldown = 10, backend = Stat)
23
+ @app = app
24
+ @cooldown = cooldown
25
+ @last = (Time.now - cooldown)
26
+ @cache = {}
27
+ @mtimes = {}
28
+
29
+ extend backend
30
+ end
31
+
32
+ def call(env)
33
+ if @cooldown and Time.now > @last + @cooldown
34
+ if Thread.list.size > 1
35
+ Thread.exclusive{ reload! }
36
+ else
37
+ reload!
38
+ end
39
+
40
+ @last = Time.now
41
+ end
42
+
43
+ @app.call(env)
44
+ end
45
+
46
+ def reload!(stderr = $stderr)
47
+ rotation do |file, mtime|
48
+ previous_mtime = @mtimes[file] ||= mtime
49
+ safe_load(file, mtime, stderr) if mtime > previous_mtime
50
+ end
51
+ end
52
+
53
+ # A safe Kernel::load, issuing the hooks depending on the results
54
+ def safe_load(file, mtime, stderr = $stderr)
55
+ load(file)
56
+ stderr.puts "#{self.class}: reloaded `#{file}'"
57
+ file
58
+ rescue LoadError, SyntaxError => ex
59
+ stderr.puts ex
60
+ ensure
61
+ @mtimes[file] = mtime
62
+ end
63
+
64
+ module Stat
65
+ def rotation
66
+ files = [$0, *$LOADED_FEATURES].uniq
67
+ paths = ['./', *$LOAD_PATH].uniq
68
+
69
+ files.map{|file|
70
+ next if file =~ /\.(so|bundle)$/ # cannot reload compiled files
71
+
72
+ found, stat = figure_path(file, paths)
73
+ next unless found and stat and mtime = stat.mtime
74
+
75
+ @cache[file] = found
76
+
77
+ yield(found, mtime)
78
+ }.compact
79
+ end
80
+
81
+ # Takes a relative or absolute +file+ name, a couple possible +paths+ that
82
+ # the +file+ might reside in. Returns the full path and File::Stat for the
83
+ # path.
84
+ def figure_path(file, paths)
85
+ found = @cache[file]
86
+ found = file if !found and Pathname.new(file).absolute?
87
+ found, stat = safe_stat(found)
88
+ return found, stat if found
89
+
90
+ paths.each do |possible_path|
91
+ path = ::File.join(possible_path, file)
92
+ found, stat = safe_stat(path)
93
+ return ::File.expand_path(found), stat if found
94
+ end
95
+ end
96
+
97
+ def safe_stat(file)
98
+ return unless file
99
+ stat = ::File.stat(file)
100
+ return file, stat if stat.file?
101
+ rescue Errno::ENOENT, Errno::ENOTDIR
102
+ @cache.delete(file) and false
103
+ end
104
+ end
105
+ end
106
+ end
@@ -8,11 +8,23 @@ module Rack
8
8
  # req = Rack::Request.new(env)
9
9
  # req.post?
10
10
  # req.params["data"]
11
+ #
12
+ # The environment hash passed will store a reference to the Request object
13
+ # instantiated so that it will only instantiate if an instance of the Request
14
+ # object doesn't already exist.
11
15
 
12
16
  class Request
13
17
  # The environment of the request.
14
18
  attr_reader :env
15
19
 
20
+ def self.new(env, *args)
21
+ if self == Rack::Request
22
+ env["rack.request"] ||= super
23
+ else
24
+ super
25
+ end
26
+ end
27
+
16
28
  def initialize(env)
17
29
  @env = env
18
30
  end
@@ -26,6 +38,8 @@ module Rack
26
38
  def query_string; @env["QUERY_STRING"].to_s end
27
39
  def content_length; @env['CONTENT_LENGTH'] end
28
40
  def content_type; @env['CONTENT_TYPE'] end
41
+ def session; @env['rack.session'] ||= {} end
42
+ def session_options; @env['rack.session.options'] ||= {} end
29
43
 
30
44
  # The media type (type/subtype) portion of the CONTENT_TYPE header
31
45
  # without any media type parameters. e.g., when CONTENT_TYPE is
@@ -34,7 +48,7 @@ module Rack
34
48
  # For more information on the use of media types in HTTP, see:
35
49
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
36
50
  def media_type
37
- content_type && content_type.split(/\s*[;,]\s*/, 2)[0].downcase
51
+ content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
38
52
  end
39
53
 
40
54
  # The media type parameters provided in CONTENT_TYPE as a Hash, or
@@ -80,6 +94,14 @@ module Rack
80
94
  'multipart/form-data'
81
95
  ]
82
96
 
97
+ # The set of media-types. Requests that do not indicate
98
+ # one of the media types presents in this list will not be eligible
99
+ # for param parsing like soap attachments or generic multiparts
100
+ PARSEABLE_DATA_MEDIA_TYPES = [
101
+ 'multipart/related',
102
+ 'multipart/mixed'
103
+ ]
104
+
83
105
  # Determine whether the request body contains form-data by checking
84
106
  # the request media_type against registered form-data media-types:
85
107
  # "application/x-www-form-urlencoded" and "multipart/form-data". The
@@ -89,6 +111,12 @@ module Rack
89
111
  FORM_DATA_MEDIA_TYPES.include?(media_type)
90
112
  end
91
113
 
114
+ # Determine whether the request body contains data by checking
115
+ # the request media_type against registered parse-data media-types
116
+ def parseable_data?
117
+ PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
118
+ end
119
+
92
120
  # Returns the data recieved in the query string.
93
121
  def GET
94
122
  if @env["rack.request.query_string"] == query_string
@@ -96,7 +124,7 @@ module Rack
96
124
  else
97
125
  @env["rack.request.query_string"] = query_string
98
126
  @env["rack.request.query_hash"] =
99
- Utils.parse_query(query_string)
127
+ Utils.parse_nested_query(query_string)
100
128
  end
101
129
  end
102
130
 
@@ -107,13 +135,19 @@ module Rack
107
135
  def POST
108
136
  if @env["rack.request.form_input"].eql? @env["rack.input"]
109
137
  @env["rack.request.form_hash"]
110
- elsif form_data?
138
+ elsif form_data? || parseable_data?
111
139
  @env["rack.request.form_input"] = @env["rack.input"]
112
140
  unless @env["rack.request.form_hash"] =
113
141
  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
- @env["rack.input"].rewind if @env["rack.input"].respond_to?(:rewind)
142
+ form_vars = @env["rack.input"].read
143
+
144
+ # Fix for Safari Ajax postings that always append \0
145
+ form_vars.sub!(/\0\z/, '')
146
+
147
+ @env["rack.request.form_vars"] = form_vars
148
+ @env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars)
149
+
150
+ @env["rack.input"].rewind
117
151
  end
118
152
  @env["rack.request.form_hash"]
119
153
  else
@@ -188,11 +222,13 @@ module Rack
188
222
 
189
223
  url
190
224
  end
191
-
225
+
226
+ def path
227
+ script_name + path_info
228
+ end
229
+
192
230
  def fullpath
193
- path = script_name + path_info
194
- path << "?" << query_string unless query_string.empty?
195
- path
231
+ query_string.empty? ? path : "#{path}?#{query_string}"
196
232
  end
197
233
 
198
234
  def accept_encoding
@@ -16,6 +16,8 @@ module Rack
16
16
  # Your application's +call+ should end returning Response#finish.
17
17
 
18
18
  class Response
19
+ attr_accessor :length
20
+
19
21
  def initialize(body=[], status=200, header={}, &block)
20
22
  @status = status
21
23
  @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.
@@ -61,12 +63,13 @@ module Rack
61
63
  expires = "; expires=" + value[:expires].clone.gmtime.
62
64
  strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
63
65
  secure = "; secure" if value[:secure]
66
+ httponly = "; HttpOnly" if value[:httponly]
64
67
  value = value[:value]
65
68
  end
66
69
  value = [value] unless Array === value
67
70
  cookie = Utils.escape(key) + "=" +
68
71
  value.map { |v| Utils.escape v }.join("&") +
69
- "#{domain}#{path}#{expires}#{secure}"
72
+ "#{domain}#{path}#{expires}#{secure}#{httponly}"
70
73
 
71
74
  case self["Set-Cookie"]
72
75
  when Array
@@ -92,6 +95,10 @@ module Rack
92
95
  :expires => Time.at(0) }.merge(value))
93
96
  end
94
97
 
98
+ def redirect(target, status=302)
99
+ self.status = status
100
+ self["Location"] = target
101
+ end
95
102
 
96
103
  def finish(&block)
97
104
  @block = block
@@ -100,7 +107,6 @@ module Rack
100
107
  header.delete "Content-Type"
101
108
  [status.to_i, header.to_hash, []]
102
109
  else
103
- header["Content-Length"] ||= @length.to_s
104
110
  [status.to_i, header.to_hash, self]
105
111
  end
106
112
  end
@@ -112,10 +118,16 @@ module Rack
112
118
  @block.call(self) if @block
113
119
  end
114
120
 
121
+ # Append to body and update Content-Length.
122
+ #
123
+ # NOTE: Do not mix #write and direct #body access!
124
+ #
115
125
  def write(str)
116
126
  s = str.to_s
117
- @length += s.size
127
+ @length += Rack::Utils.bytesize(s)
118
128
  @writer.call s
129
+
130
+ header["Content-Length"] = @length.to_s
119
131
  str
120
132
  end
121
133
 
@@ -0,0 +1,98 @@
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
+ if filesystem_has_posix_semantics?
76
+ @rewindable_io.unlink
77
+ @unlinked = true
78
+ end
79
+
80
+ buffer = ""
81
+ while @io.read(1024 * 4, buffer)
82
+ entire_buffer_written_out = false
83
+ while !entire_buffer_written_out
84
+ written = @rewindable_io.write(buffer)
85
+ entire_buffer_written_out = written == buffer.size
86
+ if !entire_buffer_written_out
87
+ buffer.slice!(0 .. written - 1)
88
+ end
89
+ end
90
+ end
91
+ @rewindable_io.rewind
92
+ end
93
+
94
+ def filesystem_has_posix_semantics?
95
+ RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/
96
+ end
97
+ end
98
+ end