ultra-max-sys 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ultra-max-sys might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/roda-3.106.0/MIT-LICENSE +21 -0
- data/roda-3.106.0/lib/roda/cache.rb +42 -0
- data/roda-3.106.0/lib/roda/plugins/Integer_matcher_max.rb +55 -0
- data/roda-3.106.0/lib/roda/plugins/_after_hook.rb +11 -0
- data/roda-3.106.0/lib/roda/plugins/_base64.rb +34 -0
- data/roda-3.106.0/lib/roda/plugins/_before_hook.rb +41 -0
- data/roda-3.106.0/lib/roda/plugins/_optimized_matching.rb +227 -0
- data/roda-3.106.0/lib/roda/plugins/_symbol_class_matchers.rb +108 -0
- data/roda-3.106.0/lib/roda/plugins/_symbol_regexp_matchers.rb +24 -0
- data/roda-3.106.0/lib/roda/plugins/additional_render_engines.rb +61 -0
- data/roda-3.106.0/lib/roda/plugins/additional_view_directories.rb +66 -0
- data/roda-3.106.0/lib/roda/plugins/all_verbs.rb +50 -0
- data/roda-3.106.0/lib/roda/plugins/assets.rb +902 -0
- data/roda-3.106.0/lib/roda/plugins/assets_preloading.rb +89 -0
- data/roda-3.106.0/lib/roda/plugins/assume_ssl.rb +28 -0
- data/roda-3.106.0/lib/roda/plugins/autoload_hash_branches.rb +79 -0
- data/roda-3.106.0/lib/roda/plugins/autoload_named_routes.rb +65 -0
- data/roda-3.106.0/lib/roda/plugins/backtracking_array.rb +88 -0
- data/roda-3.106.0/lib/roda/plugins/bearer_token.rb +28 -0
- data/roda-3.106.0/lib/roda/plugins/branch_locals.rb +76 -0
- data/roda-3.106.0/lib/roda/plugins/break.rb +43 -0
- data/roda-3.106.0/lib/roda/plugins/caching.rb +206 -0
- data/roda-3.106.0/lib/roda/plugins/capture_erb.rb +94 -0
- data/roda-3.106.0/lib/roda/plugins/chunked.rb +333 -0
- data/roda-3.106.0/lib/roda/plugins/class_level_routing.rb +111 -0
- data/roda-3.106.0/lib/roda/plugins/class_matchers.rb +155 -0
- data/roda-3.106.0/lib/roda/plugins/common_logger.rb +82 -0
- data/roda-3.106.0/lib/roda/plugins/conditional_sessions.rb +67 -0
- data/roda-3.106.0/lib/roda/plugins/content_for.rb +105 -0
- data/roda-3.106.0/lib/roda/plugins/content_security_policy.rb +333 -0
- data/roda-3.106.0/lib/roda/plugins/cookie_flags.rb +157 -0
- data/roda-3.106.0/lib/roda/plugins/cookies.rb +51 -0
- data/roda-3.106.0/lib/roda/plugins/csrf.rb +81 -0
- data/roda-3.106.0/lib/roda/plugins/custom_block_results.rb +85 -0
- data/roda-3.106.0/lib/roda/plugins/custom_matchers.rb +89 -0
- data/roda-3.106.0/lib/roda/plugins/default_headers.rb +64 -0
- data/roda-3.106.0/lib/roda/plugins/default_status.rb +35 -0
- data/roda-3.106.0/lib/roda/plugins/delay_build.rb +18 -0
- data/roda-3.106.0/lib/roda/plugins/delegate.rb +77 -0
- data/roda-3.106.0/lib/roda/plugins/delete_empty_headers.rb +36 -0
- data/roda-3.106.0/lib/roda/plugins/direct_call.rb +38 -0
- data/roda-3.106.0/lib/roda/plugins/disallow_file_uploads.rb +38 -0
- data/roda-3.106.0/lib/roda/plugins/drop_body.rb +43 -0
- data/roda-3.106.0/lib/roda/plugins/each_part.rb +87 -0
- data/roda-3.106.0/lib/roda/plugins/early_hints.rb +25 -0
- data/roda-3.106.0/lib/roda/plugins/empty_root.rb +49 -0
- data/roda-3.106.0/lib/roda/plugins/environments.rb +79 -0
- data/roda-3.106.0/lib/roda/plugins/erb_h.rb +43 -0
- data/roda-3.106.0/lib/roda/plugins/error_email.rb +152 -0
- data/roda-3.106.0/lib/roda/plugins/error_handler.rb +133 -0
- data/roda-3.106.0/lib/roda/plugins/error_mail.rb +149 -0
- data/roda-3.106.0/lib/roda/plugins/exception_page.rb +445 -0
- data/roda-3.106.0/lib/roda/plugins/filter_common_logger.rb +46 -0
- data/roda-3.106.0/lib/roda/plugins/flash.rb +119 -0
- data/roda-3.106.0/lib/roda/plugins/h.rb +59 -0
- data/roda-3.106.0/lib/roda/plugins/halt.rb +108 -0
- data/roda-3.106.0/lib/roda/plugins/hash_branch_view_subdir.rb +76 -0
- data/roda-3.106.0/lib/roda/plugins/hash_branches.rb +145 -0
- data/roda-3.106.0/lib/roda/plugins/hash_matcher.rb +39 -0
- data/roda-3.106.0/lib/roda/plugins/hash_paths.rb +128 -0
- data/roda-3.106.0/lib/roda/plugins/hash_public.rb +125 -0
- data/roda-3.106.0/lib/roda/plugins/hash_public_cache.rb +91 -0
- data/roda-3.106.0/lib/roda/plugins/hash_routes.rb +304 -0
- data/roda-3.106.0/lib/roda/plugins/head.rb +99 -0
- data/roda-3.106.0/lib/roda/plugins/header_matchers.rb +88 -0
- data/roda-3.106.0/lib/roda/plugins/heartbeat.rb +41 -0
- data/roda-3.106.0/lib/roda/plugins/hmac_paths.rb +413 -0
- data/roda-3.106.0/lib/roda/plugins/hooks.rb +92 -0
- data/roda-3.106.0/lib/roda/plugins/host_authorization.rb +155 -0
- data/roda-3.106.0/lib/roda/plugins/host_routing.rb +194 -0
- data/roda-3.106.0/lib/roda/plugins/hsts.rb +35 -0
- data/roda-3.106.0/lib/roda/plugins/indifferent_params.rb +126 -0
- data/roda-3.106.0/lib/roda/plugins/inject_erb.rb +33 -0
- data/roda-3.106.0/lib/roda/plugins/invalid_request_body.rb +107 -0
- data/roda-3.106.0/lib/roda/plugins/ip_from_header.rb +34 -0
- data/roda-3.106.0/lib/roda/plugins/json.rb +119 -0
- data/roda-3.106.0/lib/roda/plugins/json_parser.rb +107 -0
- data/roda-3.106.0/lib/roda/plugins/link_to.rb +83 -0
- data/roda-3.106.0/lib/roda/plugins/mail_processor.rb +629 -0
- data/roda-3.106.0/lib/roda/plugins/mailer.rb +285 -0
- data/roda-3.106.0/lib/roda/plugins/map_matcher.rb +48 -0
- data/roda-3.106.0/lib/roda/plugins/match_affix.rb +64 -0
- data/roda-3.106.0/lib/roda/plugins/match_hook.rb +34 -0
- data/roda-3.106.0/lib/roda/plugins/match_hook_args.rb +93 -0
- data/roda-3.106.0/lib/roda/plugins/middleware.rb +225 -0
- data/roda-3.106.0/lib/roda/plugins/middleware_stack.rb +101 -0
- data/roda-3.106.0/lib/roda/plugins/module_include.rb +96 -0
- data/roda-3.106.0/lib/roda/plugins/multi_public.rb +95 -0
- data/roda-3.106.0/lib/roda/plugins/multi_route.rb +156 -0
- data/roda-3.106.0/lib/roda/plugins/multi_run.rb +144 -0
- data/roda-3.106.0/lib/roda/plugins/multi_view.rb +83 -0
- data/roda-3.106.0/lib/roda/plugins/multibyte_string_matcher.rb +57 -0
- data/roda-3.106.0/lib/roda/plugins/named_routes.rb +167 -0
- data/roda-3.106.0/lib/roda/plugins/named_templates.rb +102 -0
- data/roda-3.106.0/lib/roda/plugins/not_allowed.rb +151 -0
- data/roda-3.106.0/lib/roda/plugins/not_found.rb +55 -0
- data/roda-3.106.0/lib/roda/plugins/optimized_segment_matchers.rb +53 -0
- data/roda-3.106.0/lib/roda/plugins/optimized_string_matchers.rb +50 -0
- data/roda-3.106.0/lib/roda/plugins/padrino_render.rb +42 -0
- data/roda-3.106.0/lib/roda/plugins/param_matchers.rb +83 -0
- data/roda-3.106.0/lib/roda/plugins/params_capturing.rb +118 -0
- data/roda-3.106.0/lib/roda/plugins/part.rb +76 -0
- data/roda-3.106.0/lib/roda/plugins/partials.rb +74 -0
- data/roda-3.106.0/lib/roda/plugins/pass.rb +47 -0
- data/roda-3.106.0/lib/roda/plugins/path.rb +255 -0
- data/roda-3.106.0/lib/roda/plugins/path_matchers.rb +54 -0
- data/roda-3.106.0/lib/roda/plugins/path_rewriter.rb +111 -0
- data/roda-3.106.0/lib/roda/plugins/permissions_policy.rb +340 -0
- data/roda-3.106.0/lib/roda/plugins/placeholder_string_matchers.rb +48 -0
- data/roda-3.106.0/lib/roda/plugins/plain_hash_response_headers.rb +32 -0
- data/roda-3.106.0/lib/roda/plugins/precompile_templates.rb +156 -0
- data/roda-3.106.0/lib/roda/plugins/public.rb +182 -0
- data/roda-3.106.0/lib/roda/plugins/r.rb +35 -0
- data/roda-3.106.0/lib/roda/plugins/recheck_precompiled_assets.rb +107 -0
- data/roda-3.106.0/lib/roda/plugins/redirect_http_to_https.rb +99 -0
- data/roda-3.106.0/lib/roda/plugins/redirect_path.rb +59 -0
- data/roda-3.106.0/lib/roda/plugins/relative_path.rb +76 -0
- data/roda-3.106.0/lib/roda/plugins/render.rb +998 -0
- data/roda-3.106.0/lib/roda/plugins/render_coverage.rb +105 -0
- data/roda-3.106.0/lib/roda/plugins/render_each.rb +224 -0
- data/roda-3.106.0/lib/roda/plugins/render_locals.rb +102 -0
- data/roda-3.106.0/lib/roda/plugins/request_aref.rb +77 -0
- data/roda-3.106.0/lib/roda/plugins/request_headers.rb +83 -0
- data/roda-3.106.0/lib/roda/plugins/response_attachment.rb +94 -0
- data/roda-3.106.0/lib/roda/plugins/response_content_type.rb +79 -0
- data/roda-3.106.0/lib/roda/plugins/response_request.rb +36 -0
- data/roda-3.106.0/lib/roda/plugins/route_block_args.rb +50 -0
- data/roda-3.106.0/lib/roda/plugins/route_csrf.rb +388 -0
- data/roda-3.106.0/lib/roda/plugins/run_append_slash.rb +51 -0
- data/roda-3.106.0/lib/roda/plugins/run_handler.rb +51 -0
- data/roda-3.106.0/lib/roda/plugins/run_require_slash.rb +46 -0
- data/roda-3.106.0/lib/roda/plugins/sec_fetch_site_csrf.rb +131 -0
- data/roda-3.106.0/lib/roda/plugins/send_file.rb +127 -0
- data/roda-3.106.0/lib/roda/plugins/sessions.rb +537 -0
- data/roda-3.106.0/lib/roda/plugins/shape_friendly.rb +232 -0
- data/roda-3.106.0/lib/roda/plugins/shared_vars.rb +86 -0
- data/roda-3.106.0/lib/roda/plugins/sinatra_helpers.rb +444 -0
- data/roda-3.106.0/lib/roda/plugins/slash_path_empty.rb +26 -0
- data/roda-3.106.0/lib/roda/plugins/static.rb +42 -0
- data/roda-3.106.0/lib/roda/plugins/static_routing.rb +89 -0
- data/roda-3.106.0/lib/roda/plugins/status_303.rb +34 -0
- data/roda-3.106.0/lib/roda/plugins/status_handler.rb +86 -0
- data/roda-3.106.0/lib/roda/plugins/streaming.rb +170 -0
- data/roda-3.106.0/lib/roda/plugins/strip_path_prefix.rb +33 -0
- data/roda-3.106.0/lib/roda/plugins/symbol_matchers.rb +188 -0
- data/roda-3.106.0/lib/roda/plugins/symbol_status.rb +32 -0
- data/roda-3.106.0/lib/roda/plugins/symbol_views.rb +37 -0
- data/roda-3.106.0/lib/roda/plugins/timestamp_public.rb +78 -0
- data/roda-3.106.0/lib/roda/plugins/type_routing.rb +217 -0
- data/roda-3.106.0/lib/roda/plugins/typecast_params.rb +1185 -0
- data/roda-3.106.0/lib/roda/plugins/typecast_params_sized_integers.rb +108 -0
- data/roda-3.106.0/lib/roda/plugins/unescape_path.rb +41 -0
- data/roda-3.106.0/lib/roda/plugins/view_options.rb +203 -0
- data/roda-3.106.0/lib/roda/plugins/view_subdir_leading_slash.rb +49 -0
- data/roda-3.106.0/lib/roda/plugins.rb +68 -0
- data/roda-3.106.0/lib/roda/request.rb +722 -0
- data/roda-3.106.0/lib/roda/response.rb +221 -0
- data/roda-3.106.0/lib/roda/session_middleware.rb +184 -0
- data/roda-3.106.0/lib/roda/version.rb +18 -0
- data/roda-3.106.0/lib/roda.rb +741 -0
- data/ultra-max-sys.gemspec +12 -0
- metadata +202 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
require 'rack/files'
|
|
7
|
+
rescue LoadError
|
|
8
|
+
require 'rack/file'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
class Roda
|
|
13
|
+
module RodaPlugins
|
|
14
|
+
# The public plugin adds a +r.public+ routing method to serve static files
|
|
15
|
+
# from a directory.
|
|
16
|
+
#
|
|
17
|
+
# The public plugin recognizes the application's :root option, and defaults to
|
|
18
|
+
# using the +public+ subfolder of the application's +:root+ option. If the application's
|
|
19
|
+
# :root option is not set, it defaults to the +public+ folder in the working
|
|
20
|
+
# directory. Additionally, if a relative path is provided as the +:root+
|
|
21
|
+
# option to the plugin, it will be considered relative to the application's
|
|
22
|
+
# +:root+ option.
|
|
23
|
+
#
|
|
24
|
+
# Examples:
|
|
25
|
+
#
|
|
26
|
+
# # Use public folder as location of files
|
|
27
|
+
# plugin :public
|
|
28
|
+
#
|
|
29
|
+
# # Use /path/to/app/static as location of files
|
|
30
|
+
# opts[:root] = '/path/to/app'
|
|
31
|
+
# plugin :public, root: 'static'
|
|
32
|
+
#
|
|
33
|
+
# # Assuming public is the location of files
|
|
34
|
+
# route do
|
|
35
|
+
# # Make GET /images/foo.png look for public/images/foo.png
|
|
36
|
+
# r.public
|
|
37
|
+
#
|
|
38
|
+
# # Make GET /static/images/foo.png look for public/images/foo.png
|
|
39
|
+
# r.on(:static) do
|
|
40
|
+
# r.public
|
|
41
|
+
# end
|
|
42
|
+
# end
|
|
43
|
+
module Public
|
|
44
|
+
SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact)
|
|
45
|
+
RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File
|
|
46
|
+
ENCODING_MAP = {:zstd=>'zstd', :brotli=>'br', :gzip=>'gzip'}.freeze
|
|
47
|
+
ENCODING_EXTENSIONS = {'br'=>'.br', 'gzip'=>'.gz', 'zstd'=>'.zst'}.freeze
|
|
48
|
+
|
|
49
|
+
# :nocov:
|
|
50
|
+
PARSER = defined?(::URI::RFC2396_PARSER) ? ::URI::RFC2396_PARSER : ::URI::DEFAULT_PARSER
|
|
51
|
+
MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
|
|
52
|
+
# :nocov:
|
|
53
|
+
|
|
54
|
+
# Use options given to setup a Rack::File instance for serving files. Options:
|
|
55
|
+
# :brotli :: Whether to serve already brotli-compressed files with a .br extension
|
|
56
|
+
# for clients supporting "br" transfer encoding.
|
|
57
|
+
# :default_mime :: The default mime type to use if the mime type is not recognized.
|
|
58
|
+
# :encodings :: An enumerable of pairs to handle accepted encodings. The first
|
|
59
|
+
# element of the pair is the accepted encoding name (e.g. 'gzip'),
|
|
60
|
+
# and the second element of the pair is the file extension (e.g.
|
|
61
|
+
# '.gz'). This allows configuration of the order in which encodings
|
|
62
|
+
# are tried, to prefer brotli to zstd for example, or to support
|
|
63
|
+
# encodings other than zstd, brotli, and gzip. This takes
|
|
64
|
+
# precedence over the :brotli, :gzip, and :zstd options if given.
|
|
65
|
+
# :gzip :: Whether to serve already gzipped files with a .gz extension for clients
|
|
66
|
+
# supporting "gzip" transfer encoding.
|
|
67
|
+
# :headers :: A hash of headers to use for statically served files
|
|
68
|
+
# :root :: Use this option for the root of the public directory (default: "public")
|
|
69
|
+
# :zstd :: Whether to serve already zstd-compressed files with a .zst extension
|
|
70
|
+
# for clients supporting "zstd" transfer encoding.
|
|
71
|
+
def self.configure(app, opts={})
|
|
72
|
+
if opts[:root]
|
|
73
|
+
app.opts[:public_root] = app.expand_path(opts[:root])
|
|
74
|
+
elsif !app.opts[:public_root]
|
|
75
|
+
app.opts[:public_root] = app.expand_path("public")
|
|
76
|
+
end
|
|
77
|
+
app.opts[:public_server] = RACK_FILES.new(app.opts[:public_root], opts[:headers]||{}, opts[:default_mime] || 'text/plain')
|
|
78
|
+
|
|
79
|
+
unless encodings = opts[:encodings]
|
|
80
|
+
if ENCODING_MAP.any?{|k,| opts.has_key?(k)}
|
|
81
|
+
encodings = ENCODING_MAP.map{|k, v| [v, ENCODING_EXTENSIONS[v]] if opts[k]}.compact
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
encodings = (encodings || app.opts[:public_encodings] || EMPTY_ARRAY).map(&:dup).freeze
|
|
85
|
+
encodings.each do |a|
|
|
86
|
+
a << /\b#{a[0]}\b/
|
|
87
|
+
end
|
|
88
|
+
encodings.each(&:freeze)
|
|
89
|
+
app.opts[:public_encodings] = encodings
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
module RequestMethods
|
|
93
|
+
# Serve files from the public directory if the file exists and this is a GET request.
|
|
94
|
+
def public
|
|
95
|
+
public_serve_with(roda_class.opts[:public_server])
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
# Return an array of segments for the given path, handling ..
|
|
101
|
+
# and . components
|
|
102
|
+
def public_path_segments(path)
|
|
103
|
+
segments = []
|
|
104
|
+
|
|
105
|
+
path.split(SPLIT).each do |seg|
|
|
106
|
+
next if seg.empty? || seg == '.'
|
|
107
|
+
seg == '..' ? segments.pop : segments << seg
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
segments
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Return whether the given path is a readable regular file.
|
|
114
|
+
def public_file_readable?(path)
|
|
115
|
+
::File.file?(path) && ::File.readable?(path)
|
|
116
|
+
rescue SystemCallError
|
|
117
|
+
# :nocov:
|
|
118
|
+
false
|
|
119
|
+
# :nocov:
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def public_serve_with(server)
|
|
123
|
+
return unless is_get?
|
|
124
|
+
path = PARSER.unescape(real_remaining_path)
|
|
125
|
+
return if path.include?("\0")
|
|
126
|
+
|
|
127
|
+
roda_opts = roda_class.opts
|
|
128
|
+
path = ::File.join(server.root, *public_path_segments(path))
|
|
129
|
+
|
|
130
|
+
if accept_encoding = env['HTTP_ACCEPT_ENCODING']
|
|
131
|
+
roda_opts[:public_encodings].each do |enc, ext, regexp|
|
|
132
|
+
if regexp.send(MATCH_METHOD, accept_encoding)
|
|
133
|
+
public_serve_compressed(server, path, ext, enc)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
if public_file_readable?(path)
|
|
139
|
+
s, h, b = public_serve(server, path)
|
|
140
|
+
headers = response.headers
|
|
141
|
+
headers.replace(h)
|
|
142
|
+
halt [s, headers, b]
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Serve the compressed file if it exists. This should only
|
|
147
|
+
# be called if the client will accept the related encoding.
|
|
148
|
+
def public_serve_compressed(server, path, suffix, encoding)
|
|
149
|
+
compressed_path = path + suffix
|
|
150
|
+
|
|
151
|
+
if public_file_readable?(compressed_path)
|
|
152
|
+
s, h, b = public_serve(server, compressed_path)
|
|
153
|
+
headers = response.headers
|
|
154
|
+
headers.replace(h)
|
|
155
|
+
|
|
156
|
+
unless s == 304
|
|
157
|
+
headers[RodaResponseHeaders::CONTENT_TYPE] = ::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
|
|
158
|
+
headers[RodaResponseHeaders::CONTENT_ENCODING] = encoding
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
halt [s, headers, b]
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
if ::Rack.release > '2'
|
|
166
|
+
# Serve the given path using the given Rack::Files server.
|
|
167
|
+
def public_serve(server, path)
|
|
168
|
+
server.serving(self, path)
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
def public_serve(server, path)
|
|
172
|
+
server = server.dup
|
|
173
|
+
server.path = path
|
|
174
|
+
server.serving(env)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
register_plugin(:public, Public)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The r plugin adds an +r+ instance method that will return the request.
|
|
7
|
+
# This allows you to use common Roda idioms such as +r.halt+ and
|
|
8
|
+
# +r.redirect+ even when +r+ isn't a local variable in scope. Example:
|
|
9
|
+
#
|
|
10
|
+
# plugin :r
|
|
11
|
+
#
|
|
12
|
+
# def foo
|
|
13
|
+
# r.redirect "/bar"
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# route do |r|
|
|
17
|
+
# r.get "foo" do
|
|
18
|
+
# foo
|
|
19
|
+
# end
|
|
20
|
+
# r.get "bar" do
|
|
21
|
+
# "bar"
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
module R
|
|
25
|
+
module InstanceMethods
|
|
26
|
+
# The request object.
|
|
27
|
+
def r
|
|
28
|
+
@_request
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
register_plugin(:r, R)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The recheck_precompiled_assets plugin enables checking for the precompiled asset metadata file.
|
|
7
|
+
# You need to have already loaded the assets plugin with the +:precompiled+ option and the file
|
|
8
|
+
# specified by the +:precompiled+ option must already exist in order to use the
|
|
9
|
+
# recheck_precompiled_assets plugin.
|
|
10
|
+
#
|
|
11
|
+
# Any time you want to check whether the precompiled asset metadata file has changed and should be
|
|
12
|
+
# reloaded, you can call the +recheck_precompiled_assets+ class method. This method will check
|
|
13
|
+
# whether the file has changed, and reload it if it has. If you want to check for modifications on
|
|
14
|
+
# every request, you can use +self.class.recheck_precompiled_assets+ inside your route block.
|
|
15
|
+
module RecheckPrecompiledAssets
|
|
16
|
+
# Thread safe wrapper for the compiled asset metadata hash. Does not wrap all
|
|
17
|
+
# hash methods, only a few that are used.
|
|
18
|
+
class CompiledAssetsHash
|
|
19
|
+
include Enumerable
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
@hash = {}
|
|
23
|
+
@mutex = Mutex.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def [](key)
|
|
27
|
+
@mutex.synchronize{@hash[key]}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def []=(key, value)
|
|
31
|
+
@mutex.synchronize{@hash[key] = value}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def replace(hash)
|
|
35
|
+
hash = hash.instance_variable_get(:@hash) if (CompiledAssetsHash === hash)
|
|
36
|
+
@mutex.synchronize{@hash.replace(hash)}
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def each(&block)
|
|
41
|
+
@mutex.synchronize{@hash.dup}.each(&block)
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_json(*args)
|
|
46
|
+
@mutex.synchronize{@hash.dup}.to_json(*args)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.load_dependencies(app)
|
|
51
|
+
unless app.respond_to?(:assets_opts) && app.assets_opts[:precompiled]
|
|
52
|
+
raise RodaError, "must load assets plugin with precompiled option before loading recheck_precompiled_assets plugin"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.configure(app)
|
|
57
|
+
precompiled_file = app.assets_opts[:precompiled]
|
|
58
|
+
prev_mtime = ::File.mtime(precompiled_file)
|
|
59
|
+
app.instance_exec do
|
|
60
|
+
opts[:assets] = opts[:assets].merge(:compiled=>_compiled_assets_initial_hash.replace(assets_opts[:compiled])).freeze
|
|
61
|
+
|
|
62
|
+
define_singleton_method(:recheck_precompiled_assets) do
|
|
63
|
+
new_mtime = ::File.mtime(precompiled_file)
|
|
64
|
+
if new_mtime != prev_mtime
|
|
65
|
+
prev_mtime = new_mtime
|
|
66
|
+
assets_opts[:compiled].replace(_precompiled_asset_metadata(precompiled_file))
|
|
67
|
+
|
|
68
|
+
# Unset the cached asset matchers, so new ones will be generated.
|
|
69
|
+
# This is needed in case the new precompiled metadata uses
|
|
70
|
+
# different files.
|
|
71
|
+
app::RodaRequest.instance_variable_set(:@assets_matchers, nil)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
singleton_class.send(:alias_method, :recheck_precompiled_assets, :recheck_precompiled_assets)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
module ClassMethods
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
# Wrap the precompiled asset metadata in a thread-safe hash.
|
|
82
|
+
def _precompiled_asset_metadata(file)
|
|
83
|
+
CompiledAssetsHash.new.replace(super)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Use a thread-safe wrapper of a hash for the :compiled assets option, since
|
|
87
|
+
# the recheck_precompiled_asset_metadata can modify it at runtime.
|
|
88
|
+
def _compiled_assets_initial_hash
|
|
89
|
+
CompiledAssetsHash.new
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
module RequestClassMethods
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
# Use a regexp that matches any digest. When the precompiled asset metadata
|
|
97
|
+
# file is updated, this allows requests for a previous precompiled asset to
|
|
98
|
+
# still work.
|
|
99
|
+
def _asset_regexp(type, key, _)
|
|
100
|
+
/#{Regexp.escape(key.sub(/\A#{type}/, ''))}\.[0-9a-fA-F]+\.#{type}/
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
register_plugin(:recheck_precompiled_assets, RecheckPrecompiledAssets)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The redirect_http_to_https plugin exposes a +redirect_http_to_https+
|
|
7
|
+
# request method that redirects HTTP requests to HTTPS, helping to ensure
|
|
8
|
+
# that future requests by the same browser will be submitted securely.
|
|
9
|
+
#
|
|
10
|
+
# You should use this plugin if you have an application that can receive
|
|
11
|
+
# requests using both HTTP and HTTPS, and you want to make sure that all
|
|
12
|
+
# or a subset of routes are only handled for HTTPS requests.
|
|
13
|
+
#
|
|
14
|
+
# The reason this exposes a request method is so that you can choose where
|
|
15
|
+
# in your routing tree to do the redirection:
|
|
16
|
+
#
|
|
17
|
+
# route do |r|
|
|
18
|
+
# # routes available via both HTTP and HTTPS
|
|
19
|
+
# r.redirect_http_to_https
|
|
20
|
+
# # routes available only via HTTPS
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# If you want to redirect to HTTPS for all routes in the routing tree, you
|
|
24
|
+
# can have this as the very first method call in the routing tree. Note that
|
|
25
|
+
# in Roda it is possible to handle routing before the normal routing tree
|
|
26
|
+
# using before hooks. The static_routing and heartbeat plugins use this
|
|
27
|
+
# feature. If you would like to handle routes before the normal routing tree,
|
|
28
|
+
# you can setup a before hook:
|
|
29
|
+
#
|
|
30
|
+
# plugin :hooks
|
|
31
|
+
#
|
|
32
|
+
# before do
|
|
33
|
+
# request.redirect_http_to_https
|
|
34
|
+
# end
|
|
35
|
+
module RedirectHttpToHttps
|
|
36
|
+
status_map = Hash.new(307)
|
|
37
|
+
status_map['GET'] = status_map['HEAD'] = 301
|
|
38
|
+
status_map.freeze
|
|
39
|
+
DEFAULTS = {:status_map => status_map}.freeze
|
|
40
|
+
private_constant :DEFAULTS
|
|
41
|
+
|
|
42
|
+
# Configures redirection from HTTP to HTTPS. Available options:
|
|
43
|
+
#
|
|
44
|
+
# :body :: The body used in the redirect. If not set, uses an empty body.
|
|
45
|
+
# :headers :: Any additional headers used in the redirect response. By default,
|
|
46
|
+
# no additional headers are set, the only header used is the Location header.
|
|
47
|
+
# :host :: The host to redirect to. If not set, redirects to the same host as the HTTP
|
|
48
|
+
# requested to. It is highly recommended that you set this if requests with
|
|
49
|
+
# arbitrary Host headers can be submitted to the application.
|
|
50
|
+
# :port :: The port to use in the redirect. By default, will not set an explicit port,
|
|
51
|
+
# so that it will implicitly use the HTTPS default port of 443.
|
|
52
|
+
# :status_map :: A hash mapping request methods to response status codes. By default,
|
|
53
|
+
# uses a hash that redirects GET and HEAD requests with a 301 status,
|
|
54
|
+
# and other request methods with a 307 status.
|
|
55
|
+
def self.configure(app, opts=OPTS)
|
|
56
|
+
previous = app.opts[:redirect_http_to_https] || DEFAULTS
|
|
57
|
+
opts = app.opts[:redirect_http_to_https] = previous.merge(opts)
|
|
58
|
+
opts[:port_string] = opts[:port] ? ":#{opts[:port]}".freeze : "".freeze
|
|
59
|
+
opts[:prefix] = opts[:host] ? "https://#{opts[:host]}#{opts[:port_string]}".freeze : nil
|
|
60
|
+
opts.freeze
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
module RequestMethods
|
|
64
|
+
# Redirect HTTP requests to HTTPS. While this doesn't secure the
|
|
65
|
+
# current request, it makes it more likely that the browser will submit
|
|
66
|
+
# future requests securely via HTTPS.
|
|
67
|
+
def redirect_http_to_https
|
|
68
|
+
return if ssl?
|
|
69
|
+
|
|
70
|
+
opts = roda_class.opts[:redirect_http_to_https]
|
|
71
|
+
|
|
72
|
+
res = response
|
|
73
|
+
|
|
74
|
+
if body = opts[:body]
|
|
75
|
+
res.write(body)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if headers = opts[:headers]
|
|
79
|
+
res.headers.merge!(headers)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
path = if prefix = opts[:prefix]
|
|
83
|
+
prefix + fullpath
|
|
84
|
+
else
|
|
85
|
+
"https://#{host}#{opts[:port_string]}#{fullpath}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
unless status = opts[:status_map][@env['REQUEST_METHOD']]
|
|
89
|
+
raise RodaError, "redirect_http_to_https :status_map provided does not support #{@env['REQUEST_METHOD']}"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
redirect(path, status)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
register_plugin(:redirect_http_to_https, RedirectHttpToHttps)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The redirect_path plugin builds on top of the path plugin,
|
|
7
|
+
# and allows the +r.redirect+ method to be passed a non-string
|
|
8
|
+
# object that will be passed to +path+, and redirect to the
|
|
9
|
+
# result of +path+.
|
|
10
|
+
#
|
|
11
|
+
# In the second argument, you can provide a suffix to the
|
|
12
|
+
# generated path. However, in this case you cannot provide a
|
|
13
|
+
# non-default redirect status in the same call).
|
|
14
|
+
#
|
|
15
|
+
# Example:
|
|
16
|
+
#
|
|
17
|
+
# Foo = Struct.new(:id)
|
|
18
|
+
# foo = Foo.new(1)
|
|
19
|
+
#
|
|
20
|
+
# plugin :redirect_path
|
|
21
|
+
# path Foo do |foo|
|
|
22
|
+
# "/foo/#{foo.id}"
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# route do |r|
|
|
26
|
+
# r.get "example" do
|
|
27
|
+
# # redirects to /foo/1
|
|
28
|
+
# r.redirect(foo)
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# r.get "suffix-example" do
|
|
32
|
+
# # redirects to /foo/1/status
|
|
33
|
+
# r.redirect(foo, "/status")
|
|
34
|
+
# end
|
|
35
|
+
# end
|
|
36
|
+
module RedirectPath
|
|
37
|
+
def self.load_dependencies(app)
|
|
38
|
+
app.plugin :path
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module RequestMethods
|
|
42
|
+
def redirect(path=default_redirect_path, status=default_redirect_status)
|
|
43
|
+
if String === path
|
|
44
|
+
super
|
|
45
|
+
else
|
|
46
|
+
path = scope.path(path)
|
|
47
|
+
if status.is_a?(String)
|
|
48
|
+
super(path + status)
|
|
49
|
+
else
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
register_plugin(:redirect_path, RedirectPath)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The relative_path plugin adds a relative_path method that accepts
|
|
7
|
+
# an absolute path and returns a path relative to the current request
|
|
8
|
+
# by adding an appropriate prefix:
|
|
9
|
+
#
|
|
10
|
+
# plugin :relative_path
|
|
11
|
+
# route do |r|
|
|
12
|
+
# relative_path("/foo")
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# # GET /
|
|
16
|
+
# "./foo"
|
|
17
|
+
#
|
|
18
|
+
# # GET /bar
|
|
19
|
+
# "./foo"
|
|
20
|
+
#
|
|
21
|
+
# # GET /bar/
|
|
22
|
+
# "../foo"
|
|
23
|
+
#
|
|
24
|
+
# # GET /bar/baz/quux
|
|
25
|
+
# "../../foo"
|
|
26
|
+
#
|
|
27
|
+
# It also offers a relative_prefix method that returns a string that can
|
|
28
|
+
# be prepended to an absolute path. This can be more efficient if you
|
|
29
|
+
# need to convert multiple paths.
|
|
30
|
+
#
|
|
31
|
+
# This plugin is mostly designed for applications using Roda as a static
|
|
32
|
+
# site generator, where the generated site can be hosted at any subpath.
|
|
33
|
+
module RelativePath
|
|
34
|
+
SCOPE_INSTANCE_VARIABLES = [:@_relative_prefix].freeze
|
|
35
|
+
|
|
36
|
+
module InstanceMethods
|
|
37
|
+
# Return a relative path for the absolute path based on the current path
|
|
38
|
+
# of the request by adding the appropriate prefix.
|
|
39
|
+
def relative_path(absolute_path)
|
|
40
|
+
relative_prefix + absolute_path
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Return a relative prefix to append to an absolute path to a relative path
|
|
44
|
+
# based on the current path of the request.
|
|
45
|
+
def relative_prefix
|
|
46
|
+
return @_relative_prefix if @_relative_prefix
|
|
47
|
+
env = @_request.env
|
|
48
|
+
script_name = env["SCRIPT_NAME"]
|
|
49
|
+
path_info = env["PATH_INFO"]
|
|
50
|
+
|
|
51
|
+
# Check path begins with slash. All valid paths should, but in case this
|
|
52
|
+
# request is bad, just skip using a relative prefix.
|
|
53
|
+
case script_name.getbyte(0)
|
|
54
|
+
when nil # SCRIPT_NAME empty
|
|
55
|
+
unless path_info.getbyte(0) == 47 # PATH_INFO starts with /
|
|
56
|
+
return(@_relative_prefix = '')
|
|
57
|
+
end
|
|
58
|
+
when 47 # SCRIPT_NAME starts with /
|
|
59
|
+
# nothing
|
|
60
|
+
else
|
|
61
|
+
return(@_relative_prefix = '')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
slash_count = script_name.count('/') + path_info.count('/')
|
|
65
|
+
@_relative_prefix = if slash_count > 1
|
|
66
|
+
("../" * (slash_count - 2)) << ".."
|
|
67
|
+
else
|
|
68
|
+
"."
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
register_plugin(:relative_path, RelativePath)
|
|
75
|
+
end
|
|
76
|
+
end
|