colouringcode-passenger 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +129 -0
- data/Rakefile +2 -2
- data/bin/passenger-install-apache2-module +1 -0
- data/bin/passenger-install-nginx-module +4 -2
- data/ext/apache2/Hooks.cpp +4 -2
- data/ext/common/ApplicationPoolServer.h +1 -1
- data/ext/common/ApplicationPoolServerExecutable.cpp +1 -1
- data/ext/common/MessageChannel.h +48 -4
- data/ext/common/StandardApplicationPool.h +4 -2
- data/ext/common/Version.h +1 -1
- data/ext/nginx/Configuration.c +1 -1
- data/ext/nginx/HttpStatusExtractor.h +1 -0
- data/ext/nginx/ScgiRequestParser.h +1 -0
- data/ext/oxt/system_calls.cpp +11 -0
- data/ext/oxt/system_calls.hpp +2 -1
- data/ext/oxt/thread.hpp +97 -1
- data/ext/phusion_passenger/native_support.c +30 -1
- data/lib/phusion_passenger/constants.rb +1 -1
- data/lib/phusion_passenger/dependencies.rb +32 -0
- data/lib/phusion_passenger/message_channel.rb +45 -3
- data/lib/phusion_passenger/platform_info.rb +1 -1
- data/lib/phusion_passenger/rack/application_spawner.rb +10 -4
- data/lib/phusion_passenger/rack/request_handler.rb +2 -5
- data/lib/phusion_passenger/railz/application_spawner.rb +59 -7
- data/lib/phusion_passenger/utils.rb +70 -16
- data/{vendor/rack-1.0.0-git/lib/rack → lib/phusion_passenger/utils}/rewindable_input.rb +34 -9
- data/test/ApplicationPoolTest.cpp +1 -1
- data/test/MessageChannelTest.cpp +9 -1
- data/test/stub/message_channel.rb +1 -1
- data/test/stub/message_channel_2.rb +1 -1
- data/test/stub/message_channel_3.rb +2 -2
- metadata +43 -155
- data/doc/Architectural overview.html +0 -1
- data/doc/rdoc/classes/ConditionVariable.html +0 -194
- data/doc/rdoc/classes/Exception.html +0 -120
- data/doc/rdoc/classes/GC.html +0 -113
- data/doc/rdoc/classes/IO.html +0 -169
- data/doc/rdoc/classes/PhusionPassenger.html +0 -238
- data/doc/rdoc/classes/PhusionPassenger/AbstractInstaller.html +0 -153
- data/doc/rdoc/classes/PhusionPassenger/AbstractRequestHandler.html +0 -517
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer.html +0 -719
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerAlreadyStarted.html +0 -97
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerError.html +0 -96
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerNotStarted.html +0 -97
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/UnknownMessage.html +0 -96
- data/doc/rdoc/classes/PhusionPassenger/AbstractServerCollection.html +0 -598
- data/doc/rdoc/classes/PhusionPassenger/AdminTools.html +0 -140
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess.html +0 -317
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess/Instance.html +0 -138
- data/doc/rdoc/classes/PhusionPassenger/AppInitError.html +0 -154
- data/doc/rdoc/classes/PhusionPassenger/Application.html +0 -283
- data/doc/rdoc/classes/PhusionPassenger/ConsoleTextTemplate.html +0 -172
- data/doc/rdoc/classes/PhusionPassenger/FrameworkInitError.html +0 -145
- data/doc/rdoc/classes/PhusionPassenger/HTMLTemplate.html +0 -181
- data/doc/rdoc/classes/PhusionPassenger/InitializationError.html +0 -141
- data/doc/rdoc/classes/PhusionPassenger/InvalidPath.html +0 -92
- data/doc/rdoc/classes/PhusionPassenger/MessageChannel.html +0 -489
- data/doc/rdoc/classes/PhusionPassenger/NativeSupport.html +0 -350
- data/doc/rdoc/classes/PhusionPassenger/Rack.html +0 -91
- data/doc/rdoc/classes/PhusionPassenger/Rack/ApplicationSpawner.html +0 -188
- data/doc/rdoc/classes/PhusionPassenger/Rack/RequestHandler.html +0 -199
- data/doc/rdoc/classes/PhusionPassenger/Railz.html +0 -95
- data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner.html +0 -438
- data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner/Error.html +0 -98
- data/doc/rdoc/classes/PhusionPassenger/Railz/CGIFixed.html +0 -200
- data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner.html +0 -436
- data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner/Error.html +0 -98
- data/doc/rdoc/classes/PhusionPassenger/Railz/RequestHandler.html +0 -155
- data/doc/rdoc/classes/PhusionPassenger/SpawnManager.html +0 -402
- data/doc/rdoc/classes/PhusionPassenger/UnknownError.html +0 -125
- data/doc/rdoc/classes/PhusionPassenger/Utils.html +0 -803
- data/doc/rdoc/classes/PhusionPassenger/Utils/PseudoIO.html +0 -169
- data/doc/rdoc/classes/PhusionPassenger/VersionNotFound.html +0 -140
- data/doc/rdoc/classes/PhusionPassenger/WSGI.html +0 -89
- data/doc/rdoc/classes/PhusionPassenger/WSGI/ApplicationSpawner.html +0 -188
- data/doc/rdoc/classes/PlatformInfo.html +0 -866
- data/doc/rdoc/classes/RakeExtensions.html +0 -197
- data/doc/rdoc/classes/Signal.html +0 -131
- data/doc/rdoc/created.rid +0 -1
- data/doc/rdoc/files/DEVELOPERS_TXT.html +0 -255
- data/doc/rdoc/files/README.html +0 -175
- data/doc/rdoc/files/ext/phusion_passenger/native_support_c.html +0 -92
- data/doc/rdoc/files/lib/phusion_passenger/abstract_installer_rb.html +0 -129
- data/doc/rdoc/files/lib/phusion_passenger/abstract_request_handler_rb.html +0 -129
- data/doc/rdoc/files/lib/phusion_passenger/abstract_server_collection_rb.html +0 -126
- data/doc/rdoc/files/lib/phusion_passenger/abstract_server_rb.html +0 -128
- data/doc/rdoc/files/lib/phusion_passenger/admin_tools/control_process_rb.html +0 -130
- data/doc/rdoc/files/lib/phusion_passenger/admin_tools_rb.html +0 -122
- data/doc/rdoc/files/lib/phusion_passenger/application_rb.html +0 -127
- data/doc/rdoc/files/lib/phusion_passenger/console_text_template_rb.html +0 -126
- data/doc/rdoc/files/lib/phusion_passenger/constants_rb.html +0 -122
- data/doc/rdoc/files/lib/phusion_passenger/dependencies_rb.html +0 -134
- data/doc/rdoc/files/lib/phusion_passenger/events_rb.html +0 -122
- data/doc/rdoc/files/lib/phusion_passenger/exceptions_rb.html +0 -122
- data/doc/rdoc/files/lib/phusion_passenger/html_template_rb.html +0 -126
- data/doc/rdoc/files/lib/phusion_passenger/message_channel_rb.html +0 -120
- data/doc/rdoc/files/lib/phusion_passenger/packaging_rb.html +0 -122
- data/doc/rdoc/files/lib/phusion_passenger/platform_info_rb.html +0 -127
- data/doc/rdoc/files/lib/phusion_passenger/rack/application_spawner_rb.html +0 -133
- data/doc/rdoc/files/lib/phusion_passenger/rack/request_handler_rb.html +0 -125
- data/doc/rdoc/files/lib/phusion_passenger/railz/application_spawner_rb.html +0 -140
- data/doc/rdoc/files/lib/phusion_passenger/railz/cgi_fixed_rb.html +0 -126
- data/doc/rdoc/files/lib/phusion_passenger/railz/framework_spawner_rb.html +0 -145
- data/doc/rdoc/files/lib/phusion_passenger/railz/request_handler_rb.html +0 -125
- data/doc/rdoc/files/lib/phusion_passenger/simple_benchmarking_rb.html +0 -122
- data/doc/rdoc/files/lib/phusion_passenger/spawn_manager_rb.html +0 -159
- data/doc/rdoc/files/lib/phusion_passenger/utils_rb.html +0 -174
- data/doc/rdoc/files/lib/phusion_passenger/wsgi/application_spawner_rb.html +0 -129
- data/doc/rdoc/files/misc/rake/extensions_rb.html +0 -130
- data/doc/rdoc/fr_class_index.html +0 -91
- data/doc/rdoc/fr_file_index.html +0 -76
- data/doc/rdoc/fr_method_index.html +0 -205
- data/doc/rdoc/index.html +0 -26
- data/doc/rdoc/rdoc-style.css +0 -187
- data/vendor/README +0 -13
- data/vendor/README_FOR_PACKAGERS +0 -1
- data/vendor/rack-1.0.0-git/COPYING +0 -18
- data/vendor/rack-1.0.0-git/KNOWN-ISSUES +0 -18
- data/vendor/rack-1.0.0-git/README +0 -353
- data/vendor/rack-1.0.0-git/Rakefile +0 -164
- data/vendor/rack-1.0.0-git/lib/rack.rb +0 -90
- data/vendor/rack-1.0.0-git/lib/rack/adapter/camping.rb +0 -22
- data/vendor/rack-1.0.0-git/lib/rack/auth/abstract/handler.rb +0 -37
- data/vendor/rack-1.0.0-git/lib/rack/auth/abstract/request.rb +0 -37
- data/vendor/rack-1.0.0-git/lib/rack/auth/basic.rb +0 -58
- data/vendor/rack-1.0.0-git/lib/rack/auth/digest/md5.rb +0 -124
- data/vendor/rack-1.0.0-git/lib/rack/auth/digest/nonce.rb +0 -51
- data/vendor/rack-1.0.0-git/lib/rack/auth/digest/params.rb +0 -55
- data/vendor/rack-1.0.0-git/lib/rack/auth/digest/request.rb +0 -40
- data/vendor/rack-1.0.0-git/lib/rack/auth/openid.rb +0 -487
- data/vendor/rack-1.0.0-git/lib/rack/builder.rb +0 -63
- data/vendor/rack-1.0.0-git/lib/rack/cascade.rb +0 -41
- data/vendor/rack-1.0.0-git/lib/rack/chunked.rb +0 -49
- data/vendor/rack-1.0.0-git/lib/rack/commonlogger.rb +0 -52
- data/vendor/rack-1.0.0-git/lib/rack/conditionalget.rb +0 -47
- data/vendor/rack-1.0.0-git/lib/rack/content_length.rb +0 -29
- data/vendor/rack-1.0.0-git/lib/rack/content_type.rb +0 -23
- data/vendor/rack-1.0.0-git/lib/rack/deflater.rb +0 -96
- data/vendor/rack-1.0.0-git/lib/rack/directory.rb +0 -153
- data/vendor/rack-1.0.0-git/lib/rack/file.rb +0 -88
- data/vendor/rack-1.0.0-git/lib/rack/handler.rb +0 -69
- data/vendor/rack-1.0.0-git/lib/rack/handler/cgi.rb +0 -61
- data/vendor/rack-1.0.0-git/lib/rack/handler/evented_mongrel.rb +0 -8
- data/vendor/rack-1.0.0-git/lib/rack/handler/fastcgi.rb +0 -88
- data/vendor/rack-1.0.0-git/lib/rack/handler/lsws.rb +0 -55
- data/vendor/rack-1.0.0-git/lib/rack/handler/mongrel.rb +0 -84
- data/vendor/rack-1.0.0-git/lib/rack/handler/scgi.rb +0 -59
- data/vendor/rack-1.0.0-git/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/vendor/rack-1.0.0-git/lib/rack/handler/thin.rb +0 -18
- data/vendor/rack-1.0.0-git/lib/rack/handler/webrick.rb +0 -67
- data/vendor/rack-1.0.0-git/lib/rack/head.rb +0 -19
- data/vendor/rack-1.0.0-git/lib/rack/lint.rb +0 -537
- data/vendor/rack-1.0.0-git/lib/rack/lobster.rb +0 -65
- data/vendor/rack-1.0.0-git/lib/rack/lock.rb +0 -16
- data/vendor/rack-1.0.0-git/lib/rack/methodoverride.rb +0 -27
- data/vendor/rack-1.0.0-git/lib/rack/mime.rb +0 -204
- data/vendor/rack-1.0.0-git/lib/rack/mock.rb +0 -184
- data/vendor/rack-1.0.0-git/lib/rack/recursive.rb +0 -57
- data/vendor/rack-1.0.0-git/lib/rack/reloader.rb +0 -106
- data/vendor/rack-1.0.0-git/lib/rack/request.rb +0 -248
- data/vendor/rack-1.0.0-git/lib/rack/response.rb +0 -183
- data/vendor/rack-1.0.0-git/lib/rack/session/abstract/id.rb +0 -142
- data/vendor/rack-1.0.0-git/lib/rack/session/cookie.rb +0 -91
- data/vendor/rack-1.0.0-git/lib/rack/session/memcache.rb +0 -109
- data/vendor/rack-1.0.0-git/lib/rack/session/pool.rb +0 -100
- data/vendor/rack-1.0.0-git/lib/rack/showexceptions.rb +0 -349
- data/vendor/rack-1.0.0-git/lib/rack/showstatus.rb +0 -106
- data/vendor/rack-1.0.0-git/lib/rack/static.rb +0 -38
- data/vendor/rack-1.0.0-git/lib/rack/urlmap.rb +0 -55
- 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("&", "&").
|
134
|
-
gsub("<", "<").
|
135
|
-
gsub(">", ">").
|
136
|
-
gsub("'", "'").
|
137
|
-
gsub('"', """)
|
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
|