colouringcode-passenger 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. data/NEWS +129 -0
  2. data/Rakefile +2 -2
  3. data/bin/passenger-install-apache2-module +1 -0
  4. data/bin/passenger-install-nginx-module +4 -2
  5. data/ext/apache2/Hooks.cpp +4 -2
  6. data/ext/common/ApplicationPoolServer.h +1 -1
  7. data/ext/common/ApplicationPoolServerExecutable.cpp +1 -1
  8. data/ext/common/MessageChannel.h +48 -4
  9. data/ext/common/StandardApplicationPool.h +4 -2
  10. data/ext/common/Version.h +1 -1
  11. data/ext/nginx/Configuration.c +1 -1
  12. data/ext/nginx/HttpStatusExtractor.h +1 -0
  13. data/ext/nginx/ScgiRequestParser.h +1 -0
  14. data/ext/oxt/system_calls.cpp +11 -0
  15. data/ext/oxt/system_calls.hpp +2 -1
  16. data/ext/oxt/thread.hpp +97 -1
  17. data/ext/phusion_passenger/native_support.c +30 -1
  18. data/lib/phusion_passenger/constants.rb +1 -1
  19. data/lib/phusion_passenger/dependencies.rb +32 -0
  20. data/lib/phusion_passenger/message_channel.rb +45 -3
  21. data/lib/phusion_passenger/platform_info.rb +1 -1
  22. data/lib/phusion_passenger/rack/application_spawner.rb +10 -4
  23. data/lib/phusion_passenger/rack/request_handler.rb +2 -5
  24. data/lib/phusion_passenger/railz/application_spawner.rb +59 -7
  25. data/lib/phusion_passenger/utils.rb +70 -16
  26. data/{vendor/rack-1.0.0-git/lib/rack → lib/phusion_passenger/utils}/rewindable_input.rb +34 -9
  27. data/test/ApplicationPoolTest.cpp +1 -1
  28. data/test/MessageChannelTest.cpp +9 -1
  29. data/test/stub/message_channel.rb +1 -1
  30. data/test/stub/message_channel_2.rb +1 -1
  31. data/test/stub/message_channel_3.rb +2 -2
  32. metadata +43 -155
  33. data/doc/Architectural overview.html +0 -1
  34. data/doc/rdoc/classes/ConditionVariable.html +0 -194
  35. data/doc/rdoc/classes/Exception.html +0 -120
  36. data/doc/rdoc/classes/GC.html +0 -113
  37. data/doc/rdoc/classes/IO.html +0 -169
  38. data/doc/rdoc/classes/PhusionPassenger.html +0 -238
  39. data/doc/rdoc/classes/PhusionPassenger/AbstractInstaller.html +0 -153
  40. data/doc/rdoc/classes/PhusionPassenger/AbstractRequestHandler.html +0 -517
  41. data/doc/rdoc/classes/PhusionPassenger/AbstractServer.html +0 -719
  42. data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerAlreadyStarted.html +0 -97
  43. data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerError.html +0 -96
  44. data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerNotStarted.html +0 -97
  45. data/doc/rdoc/classes/PhusionPassenger/AbstractServer/UnknownMessage.html +0 -96
  46. data/doc/rdoc/classes/PhusionPassenger/AbstractServerCollection.html +0 -598
  47. data/doc/rdoc/classes/PhusionPassenger/AdminTools.html +0 -140
  48. data/doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess.html +0 -317
  49. data/doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess/Instance.html +0 -138
  50. data/doc/rdoc/classes/PhusionPassenger/AppInitError.html +0 -154
  51. data/doc/rdoc/classes/PhusionPassenger/Application.html +0 -283
  52. data/doc/rdoc/classes/PhusionPassenger/ConsoleTextTemplate.html +0 -172
  53. data/doc/rdoc/classes/PhusionPassenger/FrameworkInitError.html +0 -145
  54. data/doc/rdoc/classes/PhusionPassenger/HTMLTemplate.html +0 -181
  55. data/doc/rdoc/classes/PhusionPassenger/InitializationError.html +0 -141
  56. data/doc/rdoc/classes/PhusionPassenger/InvalidPath.html +0 -92
  57. data/doc/rdoc/classes/PhusionPassenger/MessageChannel.html +0 -489
  58. data/doc/rdoc/classes/PhusionPassenger/NativeSupport.html +0 -350
  59. data/doc/rdoc/classes/PhusionPassenger/Rack.html +0 -91
  60. data/doc/rdoc/classes/PhusionPassenger/Rack/ApplicationSpawner.html +0 -188
  61. data/doc/rdoc/classes/PhusionPassenger/Rack/RequestHandler.html +0 -199
  62. data/doc/rdoc/classes/PhusionPassenger/Railz.html +0 -95
  63. data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner.html +0 -438
  64. data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner/Error.html +0 -98
  65. data/doc/rdoc/classes/PhusionPassenger/Railz/CGIFixed.html +0 -200
  66. data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner.html +0 -436
  67. data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner/Error.html +0 -98
  68. data/doc/rdoc/classes/PhusionPassenger/Railz/RequestHandler.html +0 -155
  69. data/doc/rdoc/classes/PhusionPassenger/SpawnManager.html +0 -402
  70. data/doc/rdoc/classes/PhusionPassenger/UnknownError.html +0 -125
  71. data/doc/rdoc/classes/PhusionPassenger/Utils.html +0 -803
  72. data/doc/rdoc/classes/PhusionPassenger/Utils/PseudoIO.html +0 -169
  73. data/doc/rdoc/classes/PhusionPassenger/VersionNotFound.html +0 -140
  74. data/doc/rdoc/classes/PhusionPassenger/WSGI.html +0 -89
  75. data/doc/rdoc/classes/PhusionPassenger/WSGI/ApplicationSpawner.html +0 -188
  76. data/doc/rdoc/classes/PlatformInfo.html +0 -866
  77. data/doc/rdoc/classes/RakeExtensions.html +0 -197
  78. data/doc/rdoc/classes/Signal.html +0 -131
  79. data/doc/rdoc/created.rid +0 -1
  80. data/doc/rdoc/files/DEVELOPERS_TXT.html +0 -255
  81. data/doc/rdoc/files/README.html +0 -175
  82. data/doc/rdoc/files/ext/phusion_passenger/native_support_c.html +0 -92
  83. data/doc/rdoc/files/lib/phusion_passenger/abstract_installer_rb.html +0 -129
  84. data/doc/rdoc/files/lib/phusion_passenger/abstract_request_handler_rb.html +0 -129
  85. data/doc/rdoc/files/lib/phusion_passenger/abstract_server_collection_rb.html +0 -126
  86. data/doc/rdoc/files/lib/phusion_passenger/abstract_server_rb.html +0 -128
  87. data/doc/rdoc/files/lib/phusion_passenger/admin_tools/control_process_rb.html +0 -130
  88. data/doc/rdoc/files/lib/phusion_passenger/admin_tools_rb.html +0 -122
  89. data/doc/rdoc/files/lib/phusion_passenger/application_rb.html +0 -127
  90. data/doc/rdoc/files/lib/phusion_passenger/console_text_template_rb.html +0 -126
  91. data/doc/rdoc/files/lib/phusion_passenger/constants_rb.html +0 -122
  92. data/doc/rdoc/files/lib/phusion_passenger/dependencies_rb.html +0 -134
  93. data/doc/rdoc/files/lib/phusion_passenger/events_rb.html +0 -122
  94. data/doc/rdoc/files/lib/phusion_passenger/exceptions_rb.html +0 -122
  95. data/doc/rdoc/files/lib/phusion_passenger/html_template_rb.html +0 -126
  96. data/doc/rdoc/files/lib/phusion_passenger/message_channel_rb.html +0 -120
  97. data/doc/rdoc/files/lib/phusion_passenger/packaging_rb.html +0 -122
  98. data/doc/rdoc/files/lib/phusion_passenger/platform_info_rb.html +0 -127
  99. data/doc/rdoc/files/lib/phusion_passenger/rack/application_spawner_rb.html +0 -133
  100. data/doc/rdoc/files/lib/phusion_passenger/rack/request_handler_rb.html +0 -125
  101. data/doc/rdoc/files/lib/phusion_passenger/railz/application_spawner_rb.html +0 -140
  102. data/doc/rdoc/files/lib/phusion_passenger/railz/cgi_fixed_rb.html +0 -126
  103. data/doc/rdoc/files/lib/phusion_passenger/railz/framework_spawner_rb.html +0 -145
  104. data/doc/rdoc/files/lib/phusion_passenger/railz/request_handler_rb.html +0 -125
  105. data/doc/rdoc/files/lib/phusion_passenger/simple_benchmarking_rb.html +0 -122
  106. data/doc/rdoc/files/lib/phusion_passenger/spawn_manager_rb.html +0 -159
  107. data/doc/rdoc/files/lib/phusion_passenger/utils_rb.html +0 -174
  108. data/doc/rdoc/files/lib/phusion_passenger/wsgi/application_spawner_rb.html +0 -129
  109. data/doc/rdoc/files/misc/rake/extensions_rb.html +0 -130
  110. data/doc/rdoc/fr_class_index.html +0 -91
  111. data/doc/rdoc/fr_file_index.html +0 -76
  112. data/doc/rdoc/fr_method_index.html +0 -205
  113. data/doc/rdoc/index.html +0 -26
  114. data/doc/rdoc/rdoc-style.css +0 -187
  115. data/vendor/README +0 -13
  116. data/vendor/README_FOR_PACKAGERS +0 -1
  117. data/vendor/rack-1.0.0-git/COPYING +0 -18
  118. data/vendor/rack-1.0.0-git/KNOWN-ISSUES +0 -18
  119. data/vendor/rack-1.0.0-git/README +0 -353
  120. data/vendor/rack-1.0.0-git/Rakefile +0 -164
  121. data/vendor/rack-1.0.0-git/lib/rack.rb +0 -90
  122. data/vendor/rack-1.0.0-git/lib/rack/adapter/camping.rb +0 -22
  123. data/vendor/rack-1.0.0-git/lib/rack/auth/abstract/handler.rb +0 -37
  124. data/vendor/rack-1.0.0-git/lib/rack/auth/abstract/request.rb +0 -37
  125. data/vendor/rack-1.0.0-git/lib/rack/auth/basic.rb +0 -58
  126. data/vendor/rack-1.0.0-git/lib/rack/auth/digest/md5.rb +0 -124
  127. data/vendor/rack-1.0.0-git/lib/rack/auth/digest/nonce.rb +0 -51
  128. data/vendor/rack-1.0.0-git/lib/rack/auth/digest/params.rb +0 -55
  129. data/vendor/rack-1.0.0-git/lib/rack/auth/digest/request.rb +0 -40
  130. data/vendor/rack-1.0.0-git/lib/rack/auth/openid.rb +0 -487
  131. data/vendor/rack-1.0.0-git/lib/rack/builder.rb +0 -63
  132. data/vendor/rack-1.0.0-git/lib/rack/cascade.rb +0 -41
  133. data/vendor/rack-1.0.0-git/lib/rack/chunked.rb +0 -49
  134. data/vendor/rack-1.0.0-git/lib/rack/commonlogger.rb +0 -52
  135. data/vendor/rack-1.0.0-git/lib/rack/conditionalget.rb +0 -47
  136. data/vendor/rack-1.0.0-git/lib/rack/content_length.rb +0 -29
  137. data/vendor/rack-1.0.0-git/lib/rack/content_type.rb +0 -23
  138. data/vendor/rack-1.0.0-git/lib/rack/deflater.rb +0 -96
  139. data/vendor/rack-1.0.0-git/lib/rack/directory.rb +0 -153
  140. data/vendor/rack-1.0.0-git/lib/rack/file.rb +0 -88
  141. data/vendor/rack-1.0.0-git/lib/rack/handler.rb +0 -69
  142. data/vendor/rack-1.0.0-git/lib/rack/handler/cgi.rb +0 -61
  143. data/vendor/rack-1.0.0-git/lib/rack/handler/evented_mongrel.rb +0 -8
  144. data/vendor/rack-1.0.0-git/lib/rack/handler/fastcgi.rb +0 -88
  145. data/vendor/rack-1.0.0-git/lib/rack/handler/lsws.rb +0 -55
  146. data/vendor/rack-1.0.0-git/lib/rack/handler/mongrel.rb +0 -84
  147. data/vendor/rack-1.0.0-git/lib/rack/handler/scgi.rb +0 -59
  148. data/vendor/rack-1.0.0-git/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  149. data/vendor/rack-1.0.0-git/lib/rack/handler/thin.rb +0 -18
  150. data/vendor/rack-1.0.0-git/lib/rack/handler/webrick.rb +0 -67
  151. data/vendor/rack-1.0.0-git/lib/rack/head.rb +0 -19
  152. data/vendor/rack-1.0.0-git/lib/rack/lint.rb +0 -537
  153. data/vendor/rack-1.0.0-git/lib/rack/lobster.rb +0 -65
  154. data/vendor/rack-1.0.0-git/lib/rack/lock.rb +0 -16
  155. data/vendor/rack-1.0.0-git/lib/rack/methodoverride.rb +0 -27
  156. data/vendor/rack-1.0.0-git/lib/rack/mime.rb +0 -204
  157. data/vendor/rack-1.0.0-git/lib/rack/mock.rb +0 -184
  158. data/vendor/rack-1.0.0-git/lib/rack/recursive.rb +0 -57
  159. data/vendor/rack-1.0.0-git/lib/rack/reloader.rb +0 -106
  160. data/vendor/rack-1.0.0-git/lib/rack/request.rb +0 -248
  161. data/vendor/rack-1.0.0-git/lib/rack/response.rb +0 -183
  162. data/vendor/rack-1.0.0-git/lib/rack/session/abstract/id.rb +0 -142
  163. data/vendor/rack-1.0.0-git/lib/rack/session/cookie.rb +0 -91
  164. data/vendor/rack-1.0.0-git/lib/rack/session/memcache.rb +0 -109
  165. data/vendor/rack-1.0.0-git/lib/rack/session/pool.rb +0 -100
  166. data/vendor/rack-1.0.0-git/lib/rack/showexceptions.rb +0 -349
  167. data/vendor/rack-1.0.0-git/lib/rack/showstatus.rb +0 -106
  168. data/vendor/rack-1.0.0-git/lib/rack/static.rb +0 -38
  169. data/vendor/rack-1.0.0-git/lib/rack/urlmap.rb +0 -55
  170. data/vendor/rack-1.0.0-git/lib/rack/utils.rb +0 -522
@@ -1,106 +0,0 @@
1
- require 'erb'
2
- require 'rack/request'
3
- require 'rack/utils'
4
-
5
- module Rack
6
- # Rack::ShowStatus catches all empty responses the app it wraps and
7
- # replaces them with a site explaining the error.
8
- #
9
- # Additional details can be put into <tt>rack.showstatus.detail</tt>
10
- # and will be shown as HTML. If such details exist, the error page
11
- # is always rendered, even if the reply was not empty.
12
-
13
- class ShowStatus
14
- def initialize(app)
15
- @app = app
16
- @template = ERB.new(TEMPLATE)
17
- end
18
-
19
- def call(env)
20
- status, headers, body = @app.call(env)
21
- headers = Utils::HeaderHash.new(headers)
22
- empty = headers['Content-Length'].to_i <= 0
23
-
24
- # client or server error, or explicit message
25
- if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"]
26
- req = Rack::Request.new(env)
27
- message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
28
- detail = env["rack.showstatus.detail"] || message
29
- body = @template.result(binding)
30
- size = Rack::Utils.bytesize(body)
31
- [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]]
32
- else
33
- [status, headers, body]
34
- end
35
- end
36
-
37
- def h(obj) # :nodoc:
38
- case obj
39
- when String
40
- Utils.escape_html(obj)
41
- else
42
- Utils.escape_html(obj.inspect)
43
- end
44
- end
45
-
46
- # :stopdoc:
47
-
48
- # adapted from Django <djangoproject.com>
49
- # Copyright (c) 2005, the Lawrence Journal-World
50
- # Used under the modified BSD license:
51
- # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
52
- TEMPLATE = <<'HTML'
53
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
54
- <html lang="en">
55
- <head>
56
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
57
- <title><%=h message %> at <%=h req.script_name + req.path_info %></title>
58
- <meta name="robots" content="NONE,NOARCHIVE" />
59
- <style type="text/css">
60
- html * { padding:0; margin:0; }
61
- body * { padding:10px 20px; }
62
- body * * { padding:0; }
63
- body { font:small sans-serif; background:#eee; }
64
- body>div { border-bottom:1px solid #ddd; }
65
- h1 { font-weight:normal; margin-bottom:.4em; }
66
- h1 span { font-size:60%; color:#666; font-weight:normal; }
67
- table { border:none; border-collapse: collapse; width:100%; }
68
- td, th { vertical-align:top; padding:2px 3px; }
69
- th { width:12em; text-align:right; color:#666; padding-right:.5em; }
70
- #info { background:#f6f6f6; }
71
- #info ol { margin: 0.5em 4em; }
72
- #info ol li { font-family: monospace; }
73
- #summary { background: #ffc; }
74
- #explanation { background:#eee; border-bottom: 0px none; }
75
- </style>
76
- </head>
77
- <body>
78
- <div id="summary">
79
- <h1><%=h message %> <span>(<%= status.to_i %>)</span></h1>
80
- <table class="meta">
81
- <tr>
82
- <th>Request Method:</th>
83
- <td><%=h req.request_method %></td>
84
- </tr>
85
- <tr>
86
- <th>Request URL:</th>
87
- <td><%=h req.url %></td>
88
- </tr>
89
- </table>
90
- </div>
91
- <div id="info">
92
- <p><%= detail %></p>
93
- </div>
94
-
95
- <div id="explanation">
96
- <p>
97
- You're seeing this error because you use <code>Rack::ShowStatus</code>.
98
- </p>
99
- </div>
100
- </body>
101
- </html>
102
- HTML
103
-
104
- # :startdoc:
105
- end
106
- end
@@ -1,38 +0,0 @@
1
- module Rack
2
-
3
- # The Rack::Static middleware intercepts requests for static files
4
- # (javascript files, images, stylesheets, etc) based on the url prefixes
5
- # passed in the options, and serves them using a Rack::File object. This
6
- # allows a Rack stack to serve both static and dynamic content.
7
- #
8
- # Examples:
9
- # use Rack::Static, :urls => ["/media"]
10
- # will serve all requests beginning with /media from the "media" folder
11
- # located in the current directory (ie media/*).
12
- #
13
- # use Rack::Static, :urls => ["/css", "/images"], :root => "public"
14
- # will serve all requests beginning with /css or /images from the folder
15
- # "public" in the current directory (ie public/css/* and public/images/*)
16
-
17
- class Static
18
-
19
- def initialize(app, options={})
20
- @app = app
21
- @urls = options[:urls] || ["/favicon.ico"]
22
- root = options[:root] || Dir.pwd
23
- @file_server = Rack::File.new(root)
24
- end
25
-
26
- def call(env)
27
- path = env["PATH_INFO"]
28
- can_serve = @urls.any? { |url| path.index(url) == 0 }
29
-
30
- if can_serve
31
- @file_server.call(env)
32
- else
33
- @app.call(env)
34
- end
35
- end
36
-
37
- end
38
- end
@@ -1,55 +0,0 @@
1
- module Rack
2
- # Rack::URLMap takes a hash mapping urls or paths to apps, and
3
- # dispatches accordingly. Support for HTTP/1.1 host names exists if
4
- # the URLs start with <tt>http://</tt> or <tt>https://</tt>.
5
- #
6
- # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part
7
- # relevant for dispatch is in the SCRIPT_NAME, and the rest in the
8
- # PATH_INFO. This should be taken care of when you need to
9
- # reconstruct the URL in order to create links.
10
- #
11
- # URLMap dispatches in such a way that the longest paths are tried
12
- # first, since they are most specific.
13
-
14
- class URLMap
15
- def initialize(map = {})
16
- remap(map)
17
- end
18
-
19
- def remap(map)
20
- @mapping = map.map { |location, app|
21
- if location =~ %r{\Ahttps?://(.*?)(/.*)}
22
- host, location = $1, $2
23
- else
24
- host = nil
25
- end
26
-
27
- unless location[0] == ?/
28
- raise ArgumentError, "paths need to start with /"
29
- end
30
- location = location.chomp('/')
31
-
32
- [host, location, app]
33
- }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first
34
- end
35
-
36
- def call(env)
37
- path = env["PATH_INFO"].to_s.squeeze("/")
38
- script_name = env['SCRIPT_NAME']
39
- hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
40
- @mapping.each { |host, location, app|
41
- next unless (hHost == host || sName == host \
42
- || (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
43
- next unless location == path[0, location.size]
44
- next unless path[location.size] == nil || path[location.size] == ?/
45
-
46
- return app.call(
47
- env.merge(
48
- 'SCRIPT_NAME' => (script_name + location),
49
- 'PATH_INFO' => path[location.size..-1]))
50
- }
51
- [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]]
52
- end
53
- end
54
- end
55
-
@@ -1,522 +0,0 @@
1
- # -*- encoding: binary -*-
2
-
3
- require 'set'
4
- require 'tempfile'
5
-
6
- module Rack
7
- # Rack::Utils contains a grab-bag of useful methods for writing web
8
- # applications adopted from all kinds of Ruby libraries.
9
-
10
- module Utils
11
- # Performs URI escaping so that you can construct proper
12
- # query strings faster. Use this rather than the cgi.rb
13
- # version since it's faster. (Stolen from Camping).
14
- def escape(s)
15
- s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
16
- '%'+$1.unpack('H2'*$1.size).join('%').upcase
17
- }.tr(' ', '+')
18
- end
19
- module_function :escape
20
-
21
- # Unescapes a URI escaped string. (Stolen from Camping).
22
- def unescape(s)
23
- s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
24
- [$1.delete('%')].pack('H*')
25
- }
26
- end
27
- module_function :unescape
28
-
29
- DEFAULT_SEP = /[&;] */n
30
-
31
- # Stolen from Mongrel, with some small modifications:
32
- # Parses a query string by breaking it up at the '&'
33
- # and ';' characters. You can also use this to parse
34
- # cookies by changing the characters used in the second
35
- # parameter (which defaults to '&;').
36
- def parse_query(qs, d = nil)
37
- params = {}
38
-
39
- (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
40
- k, v = p.split('=', 2).map { |x| unescape(x) }
41
-
42
- if cur = params[k]
43
- if cur.class == Array
44
- params[k] << v
45
- else
46
- params[k] = [cur, v]
47
- end
48
- else
49
- params[k] = v
50
- end
51
- end
52
-
53
- return params
54
- end
55
- module_function :parse_query
56
-
57
- def parse_nested_query(qs, d = nil)
58
- params = {}
59
-
60
- (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
61
- k, v = unescape(p).split('=', 2)
62
- normalize_params(params, k, v)
63
- end
64
-
65
- return params
66
- end
67
- module_function :parse_nested_query
68
-
69
- def normalize_params(params, name, v = nil)
70
- name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
71
- k = $1 || ''
72
- after = $' || ''
73
-
74
- return if k.empty?
75
-
76
- if after == ""
77
- params[k] = v
78
- elsif after == "[]"
79
- params[k] ||= []
80
- raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
81
- params[k] << v
82
- elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
83
- child_key = $1
84
- params[k] ||= []
85
- raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
86
- if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
87
- normalize_params(params[k].last, child_key, v)
88
- else
89
- params[k] << normalize_params({}, child_key, v)
90
- end
91
- else
92
- params[k] ||= {}
93
- raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
94
- params[k] = normalize_params(params[k], after, v)
95
- end
96
-
97
- return params
98
- end
99
- module_function :normalize_params
100
-
101
- def build_query(params)
102
- params.map { |k, v|
103
- if v.class == Array
104
- build_query(v.map { |x| [k, x] })
105
- else
106
- "#{escape(k)}=#{escape(v)}"
107
- end
108
- }.join("&")
109
- end
110
- module_function :build_query
111
-
112
- def build_nested_query(value, prefix = nil)
113
- case value
114
- when Array
115
- value.map { |v|
116
- build_nested_query(v, "#{prefix}[]")
117
- }.join("&")
118
- when Hash
119
- value.map { |k, v|
120
- build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
121
- }.join("&")
122
- when String
123
- raise ArgumentError, "value must be a Hash" if prefix.nil?
124
- "#{prefix}=#{escape(value)}"
125
- else
126
- prefix
127
- end
128
- end
129
- module_function :build_nested_query
130
-
131
- # Escape ampersands, brackets and quotes to their HTML/XML entities.
132
- def escape_html(string)
133
- string.to_s.gsub("&", "&amp;").
134
- gsub("<", "&lt;").
135
- gsub(">", "&gt;").
136
- gsub("'", "&#39;").
137
- gsub('"', "&quot;")
138
- end
139
- module_function :escape_html
140
-
141
- def select_best_encoding(available_encodings, accept_encoding)
142
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
143
-
144
- expanded_accept_encoding =
145
- accept_encoding.map { |m, q|
146
- if m == "*"
147
- (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
148
- else
149
- [[m, q]]
150
- end
151
- }.inject([]) { |mem, list|
152
- mem + list
153
- }
154
-
155
- encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
156
-
157
- unless encoding_candidates.include?("identity")
158
- encoding_candidates.push("identity")
159
- end
160
-
161
- expanded_accept_encoding.find_all { |m, q|
162
- q == 0.0
163
- }.each { |m, _|
164
- encoding_candidates.delete(m)
165
- }
166
-
167
- return (encoding_candidates & available_encodings)[0]
168
- end
169
- module_function :select_best_encoding
170
-
171
- # Return the bytesize of String; uses String#length under Ruby 1.8 and
172
- # String#bytesize under 1.9.
173
- if ''.respond_to?(:bytesize)
174
- def bytesize(string)
175
- string.bytesize
176
- end
177
- else
178
- def bytesize(string)
179
- string.size
180
- end
181
- end
182
- module_function :bytesize
183
-
184
- # Context allows the use of a compatible middleware at different points
185
- # in a request handling stack. A compatible middleware must define
186
- # #context which should take the arguments env and app. The first of which
187
- # would be the request environment. The second of which would be the rack
188
- # application that the request would be forwarded to.
189
- class Context
190
- attr_reader :for, :app
191
-
192
- def initialize(app_f, app_r)
193
- raise 'running context does not respond to #context' unless app_f.respond_to? :context
194
- @for, @app = app_f, app_r
195
- end
196
-
197
- def call(env)
198
- @for.context(env, @app)
199
- end
200
-
201
- def recontext(app)
202
- self.class.new(@for, app)
203
- end
204
-
205
- def context(env, app=@app)
206
- recontext(app).call(env)
207
- end
208
- end
209
-
210
- # A case-insensitive Hash that preserves the original case of a
211
- # header when set.
212
- class HeaderHash < Hash
213
- def initialize(hash={})
214
- super()
215
- @names = {}
216
- hash.each { |k, v| self[k] = v }
217
- end
218
-
219
- def to_hash
220
- inject({}) do |hash, (k,v)|
221
- if v.respond_to? :to_ary
222
- hash[k] = v.to_ary.join("\n")
223
- else
224
- hash[k] = v
225
- end
226
- hash
227
- end
228
- end
229
-
230
- def [](k)
231
- super(@names[k] ||= @names[k.downcase])
232
- end
233
-
234
- def []=(k, v)
235
- delete k
236
- @names[k] = @names[k.downcase] = k
237
- super k, v
238
- end
239
-
240
- def delete(k)
241
- canonical = k.downcase
242
- result = super @names.delete(canonical)
243
- @names.delete_if { |name,| name.downcase == canonical }
244
- result
245
- end
246
-
247
- def include?(k)
248
- @names.include?(k) || @names.include?(k.downcase)
249
- end
250
-
251
- alias_method :has_key?, :include?
252
- alias_method :member?, :include?
253
- alias_method :key?, :include?
254
-
255
- def merge!(other)
256
- other.each { |k, v| self[k] = v }
257
- self
258
- end
259
-
260
- def merge(other)
261
- hash = dup
262
- hash.merge! other
263
- end
264
- end
265
-
266
- # Every standard HTTP code mapped to the appropriate message.
267
- # Stolen from Mongrel.
268
- HTTP_STATUS_CODES = {
269
- 100 => 'Continue',
270
- 101 => 'Switching Protocols',
271
- 200 => 'OK',
272
- 201 => 'Created',
273
- 202 => 'Accepted',
274
- 203 => 'Non-Authoritative Information',
275
- 204 => 'No Content',
276
- 205 => 'Reset Content',
277
- 206 => 'Partial Content',
278
- 300 => 'Multiple Choices',
279
- 301 => 'Moved Permanently',
280
- 302 => 'Found',
281
- 303 => 'See Other',
282
- 304 => 'Not Modified',
283
- 305 => 'Use Proxy',
284
- 307 => 'Temporary Redirect',
285
- 400 => 'Bad Request',
286
- 401 => 'Unauthorized',
287
- 402 => 'Payment Required',
288
- 403 => 'Forbidden',
289
- 404 => 'Not Found',
290
- 405 => 'Method Not Allowed',
291
- 406 => 'Not Acceptable',
292
- 407 => 'Proxy Authentication Required',
293
- 408 => 'Request Timeout',
294
- 409 => 'Conflict',
295
- 410 => 'Gone',
296
- 411 => 'Length Required',
297
- 412 => 'Precondition Failed',
298
- 413 => 'Request Entity Too Large',
299
- 414 => 'Request-URI Too Large',
300
- 415 => 'Unsupported Media Type',
301
- 416 => 'Requested Range Not Satisfiable',
302
- 417 => 'Expectation Failed',
303
- 500 => 'Internal Server Error',
304
- 501 => 'Not Implemented',
305
- 502 => 'Bad Gateway',
306
- 503 => 'Service Unavailable',
307
- 504 => 'Gateway Timeout',
308
- 505 => 'HTTP Version Not Supported'
309
- }
310
-
311
- # Responses with HTTP status codes that should not have an entity body
312
- STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
313
-
314
- # A multipart form data parser, adapted from IOWA.
315
- #
316
- # Usually, Rack::Request#POST takes care of calling this.
317
-
318
- module Multipart
319
- class UploadedFile
320
- # The filename, *not* including the path, of the "uploaded" file
321
- attr_reader :original_filename
322
-
323
- # The content type of the "uploaded" file
324
- attr_accessor :content_type
325
-
326
- def initialize(path, content_type = "text/plain", binary = false)
327
- raise "#{path} file does not exist" unless ::File.exist?(path)
328
- @content_type = content_type
329
- @original_filename = ::File.basename(path)
330
- @tempfile = Tempfile.new(@original_filename)
331
- @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
332
- @tempfile.binmode if binary
333
- FileUtils.copy_file(path, @tempfile.path)
334
- end
335
-
336
- def path
337
- @tempfile.path
338
- end
339
- alias_method :local_path, :path
340
-
341
- def method_missing(method_name, *args, &block) #:nodoc:
342
- @tempfile.__send__(method_name, *args, &block)
343
- end
344
- end
345
-
346
- EOL = "\r\n"
347
- MULTIPART_BOUNDARY = "AaB03x"
348
-
349
- def self.parse_multipart(env)
350
- unless env['CONTENT_TYPE'] =~
351
- %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
352
- nil
353
- else
354
- boundary = "--#{$1}"
355
-
356
- params = {}
357
- buf = ""
358
- content_length = env['CONTENT_LENGTH'].to_i
359
- input = env['rack.input']
360
- input.rewind
361
-
362
- boundary_size = Utils.bytesize(boundary) + EOL.size
363
- bufsize = 16384
364
-
365
- content_length -= boundary_size
366
-
367
- read_buffer = ''
368
-
369
- status = input.read(boundary_size, read_buffer)
370
- raise EOFError, "bad content body" unless status == boundary + EOL
371
-
372
- rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
373
-
374
- loop {
375
- head = nil
376
- body = ''
377
- filename = content_type = name = nil
378
-
379
- until head && buf =~ rx
380
- if !head && i = buf.index(EOL+EOL)
381
- head = buf.slice!(0, i+2) # First \r\n
382
- buf.slice!(0, 2) # Second \r\n
383
-
384
- filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
385
- content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
386
- name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
387
-
388
- if content_type || filename
389
- body = Tempfile.new("RackMultipart")
390
- body.binmode if body.respond_to?(:binmode)
391
- end
392
-
393
- next
394
- end
395
-
396
- # Save the read body part.
397
- if head && (boundary_size+4 < buf.size)
398
- body << buf.slice!(0, buf.size - (boundary_size+4))
399
- end
400
-
401
- c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
402
- raise EOFError, "bad content body" if c.nil? || c.empty?
403
- buf << c
404
- content_length -= c.size
405
- end
406
-
407
- # Save the rest.
408
- if i = buf.index(rx)
409
- body << buf.slice!(0, i)
410
- buf.slice!(0, boundary_size+2)
411
-
412
- content_length = -1 if $1 == "--"
413
- end
414
-
415
- if filename == ""
416
- # filename is blank which means no file has been selected
417
- data = nil
418
- elsif filename
419
- body.rewind
420
-
421
- # Take the basename of the upload's original filename.
422
- # This handles the full Windows paths given by Internet Explorer
423
- # (and perhaps other broken user agents) without affecting
424
- # those which give the lone filename.
425
- filename =~ /^(?:.*[:\\\/])?(.*)/m
426
- filename = $1
427
-
428
- data = {:filename => filename, :type => content_type,
429
- :name => name, :tempfile => body, :head => head}
430
- elsif !filename && content_type
431
- body.rewind
432
-
433
- # Generic multipart cases, not coming from a form
434
- data = {:type => content_type,
435
- :name => name, :tempfile => body, :head => head}
436
- else
437
- data = body
438
- end
439
-
440
- Utils.normalize_params(params, name, data) unless data.nil?
441
-
442
- break if buf.empty? || content_length == -1
443
- }
444
-
445
- input.rewind
446
-
447
- params
448
- end
449
- end
450
-
451
- def self.build_multipart(params, first = true)
452
- if first
453
- unless params.is_a?(Hash)
454
- raise ArgumentError, "value must be a Hash"
455
- end
456
-
457
- multipart = false
458
- query = lambda { |value|
459
- case value
460
- when Array
461
- value.each(&query)
462
- when Hash
463
- value.values.each(&query)
464
- when UploadedFile
465
- multipart = true
466
- end
467
- }
468
- params.values.each(&query)
469
- return nil unless multipart
470
- end
471
-
472
- flattened_params = Hash.new
473
-
474
- params.each do |key, value|
475
- k = first ? key.to_s : "[#{key}]"
476
-
477
- case value
478
- when Array
479
- value.map { |v|
480
- build_multipart(v, false).each { |subkey, subvalue|
481
- flattened_params["#{k}[]#{subkey}"] = subvalue
482
- }
483
- }
484
- when Hash
485
- build_multipart(value, false).each { |subkey, subvalue|
486
- flattened_params[k + subkey] = subvalue
487
- }
488
- else
489
- flattened_params[k] = value
490
- end
491
- end
492
-
493
- if first
494
- flattened_params.map { |name, file|
495
- if file.respond_to?(:original_filename)
496
- ::File.open(file.path, "rb") do |f|
497
- f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
498
- <<-EOF
499
- --#{MULTIPART_BOUNDARY}\r
500
- Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r
501
- Content-Type: #{file.content_type}\r
502
- Content-Length: #{::File.stat(file.path).size}\r
503
- \r
504
- #{f.read}\r
505
- EOF
506
- end
507
- else
508
- <<-EOF
509
- --#{MULTIPART_BOUNDARY}\r
510
- Content-Disposition: form-data; name="#{name}"\r
511
- \r
512
- #{file}\r
513
- EOF
514
- end
515
- }.join + "--#{MULTIPART_BOUNDARY}--\r"
516
- else
517
- flattened_params
518
- end
519
- end
520
- end
521
- end
522
- end