homura-runtime 0.3.2 → 0.3.4

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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/exe/compile-assets +2 -2
  4. data/exe/compile-erb +5 -7
  5. data/lib/homura/runtime/build_support.rb +19 -2
  6. data/lib/homura/runtime/version.rb +1 -1
  7. data/vendor/rack/auth/abstract/handler.rb +41 -0
  8. data/vendor/rack/auth/abstract/request.rb +51 -0
  9. data/vendor/rack/auth/basic.rb +58 -0
  10. data/vendor/rack/bad_request.rb +8 -0
  11. data/vendor/rack/body_proxy.rb +63 -0
  12. data/vendor/rack/builder.rb +315 -0
  13. data/vendor/rack/cascade.rb +67 -0
  14. data/vendor/rack/common_logger.rb +94 -0
  15. data/vendor/rack/conditional_get.rb +87 -0
  16. data/vendor/rack/config.rb +22 -0
  17. data/vendor/rack/constants.rb +68 -0
  18. data/vendor/rack/content_length.rb +34 -0
  19. data/vendor/rack/content_type.rb +33 -0
  20. data/vendor/rack/deflater.rb +159 -0
  21. data/vendor/rack/directory.rb +210 -0
  22. data/vendor/rack/etag.rb +71 -0
  23. data/vendor/rack/events.rb +172 -0
  24. data/vendor/rack/files.rb +224 -0
  25. data/vendor/rack/head.rb +25 -0
  26. data/vendor/rack/headers.rb +238 -0
  27. data/vendor/rack/lint.rb +1000 -0
  28. data/vendor/rack/lock.rb +29 -0
  29. data/vendor/rack/media_type.rb +42 -0
  30. data/vendor/rack/method_override.rb +56 -0
  31. data/vendor/rack/mime.rb +694 -0
  32. data/vendor/rack/mock.rb +3 -0
  33. data/vendor/rack/mock_request.rb +161 -0
  34. data/vendor/rack/mock_response.rb +147 -0
  35. data/vendor/rack/multipart/generator.rb +99 -0
  36. data/vendor/rack/multipart/parser.rb +586 -0
  37. data/vendor/rack/multipart/uploaded_file.rb +82 -0
  38. data/vendor/rack/multipart.rb +77 -0
  39. data/vendor/rack/null_logger.rb +48 -0
  40. data/vendor/rack/protection/authenticity_token.rb +256 -0
  41. data/vendor/rack/protection/base.rb +140 -0
  42. data/vendor/rack/protection/content_security_policy.rb +80 -0
  43. data/vendor/rack/protection/cookie_tossing.rb +77 -0
  44. data/vendor/rack/protection/escaped_params.rb +93 -0
  45. data/vendor/rack/protection/form_token.rb +25 -0
  46. data/vendor/rack/protection/frame_options.rb +39 -0
  47. data/vendor/rack/protection/http_origin.rb +43 -0
  48. data/vendor/rack/protection/ip_spoofing.rb +27 -0
  49. data/vendor/rack/protection/json_csrf.rb +60 -0
  50. data/vendor/rack/protection/path_traversal.rb +45 -0
  51. data/vendor/rack/protection/referrer_policy.rb +27 -0
  52. data/vendor/rack/protection/remote_referrer.rb +22 -0
  53. data/vendor/rack/protection/remote_token.rb +24 -0
  54. data/vendor/rack/protection/session_hijacking.rb +37 -0
  55. data/vendor/rack/protection/strict_transport.rb +41 -0
  56. data/vendor/rack/protection/version.rb +7 -0
  57. data/vendor/rack/protection/xss_header.rb +27 -0
  58. data/vendor/rack/protection.rb +58 -0
  59. data/vendor/rack/query_parser.rb +261 -0
  60. data/vendor/rack/recursive.rb +66 -0
  61. data/vendor/rack/reloader.rb +112 -0
  62. data/vendor/rack/request.rb +818 -0
  63. data/vendor/rack/response.rb +403 -0
  64. data/vendor/rack/rewindable_input.rb +116 -0
  65. data/vendor/rack/runtime.rb +35 -0
  66. data/vendor/rack/sendfile.rb +197 -0
  67. data/vendor/rack/session/abstract/id.rb +533 -0
  68. data/vendor/rack/session/constants.rb +13 -0
  69. data/vendor/rack/session/cookie.rb +292 -0
  70. data/vendor/rack/session/encryptor.rb +415 -0
  71. data/vendor/rack/session/pool.rb +76 -0
  72. data/vendor/rack/session/version.rb +10 -0
  73. data/vendor/rack/session.rb +12 -0
  74. data/vendor/rack/show_exceptions.rb +433 -0
  75. data/vendor/rack/show_status.rb +121 -0
  76. data/vendor/rack/static.rb +188 -0
  77. data/vendor/rack/tempfile_reaper.rb +44 -0
  78. data/vendor/rack/urlmap.rb +99 -0
  79. data/vendor/rack/utils.rb +631 -0
  80. data/vendor/rack/version.rb +17 -0
  81. data/vendor/rack.rb +66 -0
  82. metadata +76 -1
@@ -0,0 +1,818 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+ require_relative 'media_type'
6
+
7
+ module Rack
8
+ # Rack::Request provides a convenient interface to a Rack
9
+ # environment. It is stateless, the environment +env+ passed to the
10
+ # constructor will be directly modified.
11
+ #
12
+ # req = Rack::Request.new(env)
13
+ # req.post?
14
+ # req.params["data"]
15
+
16
+ class Request
17
+ class << self
18
+ attr_accessor :ip_filter
19
+
20
+ # The priority when checking forwarded headers. The default
21
+ # is <tt>[:forwarded, :x_forwarded]</tt>, which means, check the
22
+ # +Forwarded+ header first, followed by the appropriate
23
+ # <tt>X-Forwarded-*</tt> header. You can revert the priority by
24
+ # reversing the priority, or remove checking of either
25
+ # or both headers by removing elements from the array.
26
+ #
27
+ # This should be set as appropriate in your environment
28
+ # based on what reverse proxies are in use. If you are not
29
+ # using reverse proxies, you should probably use an empty
30
+ # array.
31
+ attr_accessor :forwarded_priority
32
+
33
+ # The priority when checking either the <tt>X-Forwarded-Proto</tt>
34
+ # or <tt>X-Forwarded-Scheme</tt> header for the forwarded protocol.
35
+ # The default is <tt>[:proto, :scheme]</tt>, to try the
36
+ # <tt>X-Forwarded-Proto</tt> header before the
37
+ # <tt>X-Forwarded-Scheme</tt> header. Rack 2 had behavior
38
+ # similar to <tt>[:scheme, :proto]</tt>. You can remove either or
39
+ # both of the entries in array to ignore that respective header.
40
+ attr_accessor :x_forwarded_proto_priority
41
+ end
42
+
43
+ @forwarded_priority = [:forwarded, :x_forwarded]
44
+ @x_forwarded_proto_priority = [:proto, :scheme]
45
+
46
+ valid_ipv4_octet = /\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])/
47
+
48
+ # homura patch: Opal compiles to JS regex, which cannot mix
49
+ # case-insensitive (`i`) and case-sensitive members in a Regexp.union.
50
+ # The two `i`-flagged literals are rewritten with explicit character
51
+ # classes so the union is uniform.
52
+ trusted_proxies = Regexp.union(
53
+ /\A127#{valid_ipv4_octet}{3}\z/, # localhost IPv4 range 127.x.x.x, per RFC-3330
54
+ /\A::1\z/, # localhost IPv6 ::1
55
+ /\A[Ff][CcDd][0-9a-fA-F]{2}(?::[0-9a-fA-F]{0,4}){0,7}\z/, # private IPv6 range fc00 .. fdff
56
+ /\A10#{valid_ipv4_octet}{3}\z/, # private IPv4 range 10.x.x.x
57
+ /\A172\.(1[6-9]|2[0-9]|3[01])#{valid_ipv4_octet}{2}\z/, # private IPv4 range 172.16.0.0 .. 172.31.255.255
58
+ /\A192\.168#{valid_ipv4_octet}{2}\z/, # private IPv4 range 192.168.x.x
59
+ /\A[Ll][Oo][Cc][Aa][Ll][Hh][Oo][Ss][Tt]\z|\A[Uu][Nn][Ii][Xx](\z|:)/, # localhost hostname, and unix domain sockets
60
+ ).freeze
61
+
62
+ self.ip_filter = lambda { |ip| trusted_proxies.match?(ip) }
63
+
64
+ ALLOWED_SCHEMES = %w(https http wss ws).freeze
65
+
66
+ def initialize(env)
67
+ @env = env
68
+ @ip = nil
69
+ @params = nil
70
+ end
71
+
72
+ def ip
73
+ @ip ||= super
74
+ end
75
+
76
+ def params
77
+ @params ||= super
78
+ end
79
+
80
+ def update_param(k, v)
81
+ super
82
+ @params = nil
83
+ end
84
+
85
+ def delete_param(k)
86
+ v = super
87
+ @params = nil
88
+ v
89
+ end
90
+
91
+ module Env
92
+ # The environment of the request.
93
+ attr_reader :env
94
+
95
+ def initialize(env)
96
+ @env = env
97
+ # This module is included at least in `ActionDispatch::Request`
98
+ # The call to `super()` allows additional mixed-in initializers are called
99
+ super()
100
+ end
101
+
102
+ # Predicate method to test to see if `name` has been set as request
103
+ # specific data
104
+ def has_header?(name)
105
+ @env.key? name
106
+ end
107
+
108
+ # Get a request specific value for `name`.
109
+ def get_header(name)
110
+ @env[name]
111
+ end
112
+
113
+ # If a block is given, it yields to the block if the value hasn't been set
114
+ # on the request.
115
+ def fetch_header(name, &block)
116
+ @env.fetch(name, &block)
117
+ end
118
+
119
+ # Loops through each key / value pair in the request specific data.
120
+ def each_header(&block)
121
+ @env.each(&block)
122
+ end
123
+
124
+ # Set a request specific value for `name` to `v`
125
+ def set_header(name, v)
126
+ @env[name] = v
127
+ end
128
+
129
+ # Add a header that may have multiple values.
130
+ #
131
+ # Example:
132
+ # request.add_header 'Accept', 'image/png'
133
+ # request.add_header 'Accept', '*/*'
134
+ #
135
+ # assert_equal 'image/png,*/*', request.get_header('Accept')
136
+ #
137
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
138
+ def add_header(key, v)
139
+ if v.nil?
140
+ get_header key
141
+ elsif has_header? key
142
+ set_header key, "#{get_header key},#{v}"
143
+ else
144
+ set_header key, v
145
+ end
146
+ end
147
+
148
+ # Delete a request specific value for `name`.
149
+ def delete_header(name)
150
+ @env.delete name
151
+ end
152
+
153
+ def initialize_copy(other)
154
+ @env = other.env.dup
155
+ end
156
+ end
157
+
158
+ module Helpers
159
+ # The set of form-data media-types. Requests that do not indicate
160
+ # one of the media types present in this list will not be eligible
161
+ # for form-data / param parsing.
162
+ FORM_DATA_MEDIA_TYPES = [
163
+ 'application/x-www-form-urlencoded',
164
+ 'multipart/form-data'
165
+ ]
166
+
167
+ # The set of media-types. Requests that do not indicate
168
+ # one of the media types present in this list will not be eligible
169
+ # for param parsing like soap attachments or generic multiparts
170
+ PARSEABLE_DATA_MEDIA_TYPES = [
171
+ 'multipart/related',
172
+ 'multipart/mixed'
173
+ ]
174
+
175
+ # Default ports depending on scheme. Used to decide whether or not
176
+ # to include the port in a generated URI.
177
+ DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
178
+
179
+ # The address of the client which connected to the proxy.
180
+ HTTP_X_FORWARDED_FOR = 'HTTP_X_FORWARDED_FOR'
181
+
182
+ # The contents of the host/:authority header sent to the proxy.
183
+ HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'
184
+
185
+ HTTP_FORWARDED = 'HTTP_FORWARDED'
186
+
187
+ # The value of the scheme sent to the proxy.
188
+ HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'
189
+
190
+ # The protocol used to connect to the proxy.
191
+ HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
192
+
193
+ # The port used to connect to the proxy.
194
+ HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'
195
+
196
+ # Another way for specifying https scheme was used.
197
+ HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'
198
+
199
+ def body; get_header(RACK_INPUT) end
200
+ def script_name; get_header(SCRIPT_NAME).to_s end
201
+ def script_name=(s); set_header(SCRIPT_NAME, s.to_s) end
202
+
203
+ def path_info; get_header(PATH_INFO).to_s end
204
+ def path_info=(s); set_header(PATH_INFO, s.to_s) end
205
+
206
+ def request_method; get_header(REQUEST_METHOD) end
207
+ def query_string; get_header(QUERY_STRING).to_s end
208
+ def content_length; get_header('CONTENT_LENGTH') end
209
+ def logger; get_header(RACK_LOGGER) end
210
+ def user_agent; get_header('HTTP_USER_AGENT') end
211
+
212
+ # the referer of the client
213
+ def referer; get_header('HTTP_REFERER') end
214
+ alias referrer referer
215
+
216
+ def session
217
+ fetch_header(RACK_SESSION) do |k|
218
+ set_header RACK_SESSION, default_session
219
+ end
220
+ end
221
+
222
+ def session_options
223
+ fetch_header(RACK_SESSION_OPTIONS) do |k|
224
+ set_header RACK_SESSION_OPTIONS, {}
225
+ end
226
+ end
227
+
228
+ # Checks the HTTP request method (or verb) to see if it was of type DELETE
229
+ def delete?; request_method == DELETE end
230
+
231
+ # Checks the HTTP request method (or verb) to see if it was of type GET
232
+ def get?; request_method == GET end
233
+
234
+ # Checks the HTTP request method (or verb) to see if it was of type HEAD
235
+ def head?; request_method == HEAD end
236
+
237
+ # Checks the HTTP request method (or verb) to see if it was of type OPTIONS
238
+ def options?; request_method == OPTIONS end
239
+
240
+ # Checks the HTTP request method (or verb) to see if it was of type LINK
241
+ def link?; request_method == LINK end
242
+
243
+ # Checks the HTTP request method (or verb) to see if it was of type PATCH
244
+ def patch?; request_method == PATCH end
245
+
246
+ # Checks the HTTP request method (or verb) to see if it was of type POST
247
+ def post?; request_method == POST end
248
+
249
+ # Checks the HTTP request method (or verb) to see if it was of type PUT
250
+ def put?; request_method == PUT end
251
+
252
+ # Checks the HTTP request method (or verb) to see if it was of type TRACE
253
+ def trace?; request_method == TRACE end
254
+
255
+ # Checks the HTTP request method (or verb) to see if it was of type UNLINK
256
+ def unlink?; request_method == UNLINK end
257
+
258
+ def scheme
259
+ if get_header(HTTPS) == 'on'
260
+ 'https'
261
+ elsif get_header(HTTP_X_FORWARDED_SSL) == 'on'
262
+ 'https'
263
+ elsif forwarded_scheme
264
+ forwarded_scheme
265
+ else
266
+ get_header(RACK_URL_SCHEME)
267
+ end
268
+ end
269
+
270
+ # The authority of the incoming request as defined by RFC3976.
271
+ # https://tools.ietf.org/html/rfc3986#section-3.2
272
+ #
273
+ # In HTTP/1, this is the `host` header.
274
+ # In HTTP/2, this is the `:authority` pseudo-header.
275
+ def authority
276
+ forwarded_authority || host_authority || server_authority
277
+ end
278
+
279
+ # The authority as defined by the `SERVER_NAME` and `SERVER_PORT`
280
+ # variables.
281
+ def server_authority
282
+ host = self.server_name
283
+ port = self.server_port
284
+
285
+ if host
286
+ if port
287
+ "#{host}:#{port}"
288
+ else
289
+ host
290
+ end
291
+ end
292
+ end
293
+
294
+ def server_name
295
+ get_header(SERVER_NAME)
296
+ end
297
+
298
+ def server_port
299
+ get_header(SERVER_PORT)
300
+ end
301
+
302
+ def cookies
303
+ hash = fetch_header(RACK_REQUEST_COOKIE_HASH) do |key|
304
+ set_header(key, {})
305
+ end
306
+
307
+ string = get_header(HTTP_COOKIE)
308
+
309
+ unless string == get_header(RACK_REQUEST_COOKIE_STRING)
310
+ hash.replace Utils.parse_cookies_header(string)
311
+ set_header(RACK_REQUEST_COOKIE_STRING, string)
312
+ end
313
+
314
+ hash
315
+ end
316
+
317
+ def content_type
318
+ content_type = get_header('CONTENT_TYPE')
319
+ content_type.nil? || content_type.empty? ? nil : content_type
320
+ end
321
+
322
+ def xhr?
323
+ get_header("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest"
324
+ end
325
+
326
+ # The `HTTP_HOST` header.
327
+ def host_authority
328
+ get_header(HTTP_HOST)
329
+ end
330
+
331
+ def host_with_port(authority = self.authority)
332
+ host, _, port = split_authority(authority)
333
+
334
+ if port == DEFAULT_PORTS[self.scheme]
335
+ host
336
+ else
337
+ authority
338
+ end
339
+ end
340
+
341
+ # Returns a formatted host, suitable for being used in a URI.
342
+ def host
343
+ split_authority(self.authority)[0]
344
+ end
345
+
346
+ # Returns an address suitable for being to resolve to an address.
347
+ # In the case of a domain name or IPv4 address, the result is the same
348
+ # as +host+. In the case of IPv6 or future address formats, the square
349
+ # brackets are removed.
350
+ def hostname
351
+ split_authority(self.authority)[1]
352
+ end
353
+
354
+ def port
355
+ if authority = self.authority
356
+ _, _, authority_port = split_authority(authority)
357
+ end
358
+
359
+ authority_port || forwarded_port&.last || DEFAULT_PORTS[scheme] || server_port
360
+ end
361
+
362
+ def forwarded_for
363
+ forwarded_priority.each do |type|
364
+ case type
365
+ when :forwarded
366
+ if forwarded_for = get_http_forwarded(:for)
367
+ return(forwarded_for.map! do |authority|
368
+ split_authority(authority)[1]
369
+ end)
370
+ end
371
+ when :x_forwarded
372
+ if value = get_header(HTTP_X_FORWARDED_FOR)
373
+ return(split_header(value).map do |authority|
374
+ split_authority(wrap_ipv6(authority))[1]
375
+ end)
376
+ end
377
+ end
378
+ end
379
+
380
+ nil
381
+ end
382
+
383
+ def forwarded_port
384
+ forwarded_priority.each do |type|
385
+ case type
386
+ when :forwarded
387
+ if forwarded = get_http_forwarded(:for)
388
+ return(forwarded.map do |authority|
389
+ split_authority(authority)[2]
390
+ end.compact)
391
+ end
392
+ when :x_forwarded
393
+ if value = get_header(HTTP_X_FORWARDED_PORT)
394
+ return split_header(value).map(&:to_i)
395
+ end
396
+ end
397
+ end
398
+
399
+ nil
400
+ end
401
+
402
+ def forwarded_authority
403
+ forwarded_priority.each do |type|
404
+ case type
405
+ when :forwarded
406
+ if forwarded = get_http_forwarded(:host)
407
+ return forwarded.last
408
+ end
409
+ when :x_forwarded
410
+ if (value = get_header(HTTP_X_FORWARDED_HOST)) && (x_forwarded_host = split_header(value).last)
411
+ return wrap_ipv6(x_forwarded_host)
412
+ end
413
+ end
414
+ end
415
+
416
+ nil
417
+ end
418
+
419
+ def ssl?
420
+ scheme == 'https' || scheme == 'wss'
421
+ end
422
+
423
+ def ip
424
+ remote_addresses = split_header(get_header('REMOTE_ADDR'))
425
+
426
+ remote_addresses.reverse_each do |ip|
427
+ return ip unless trusted_proxy?(ip)
428
+ end
429
+
430
+ if (forwarded_for = self.forwarded_for) && !forwarded_for.empty?
431
+ # The forwarded for addresses are ordered: client, proxy1, proxy2.
432
+ # So we reject all the trusted addresses (proxy*) and return the
433
+ # last client. Or if we trust everyone, we just return the first
434
+ # address.
435
+ forwarded_for.reverse_each do |ip|
436
+ return ip unless trusted_proxy?(ip)
437
+ end
438
+ return forwarded_for.first
439
+ end
440
+
441
+ # If all the addresses are trusted, and we aren't forwarded, just return
442
+ # the first remote address, which represents the source of the request.
443
+ remote_addresses.first
444
+ end
445
+
446
+ # The media type (type/subtype) portion of the CONTENT_TYPE header
447
+ # without any media type parameters. e.g., when CONTENT_TYPE is
448
+ # "text/plain;charset=utf-8", the media-type is "text/plain".
449
+ #
450
+ # For more information on the use of media types in HTTP, see:
451
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
452
+ def media_type
453
+ MediaType.type(content_type)
454
+ end
455
+
456
+ # The media type parameters provided in CONTENT_TYPE as a Hash, or
457
+ # an empty Hash if no CONTENT_TYPE or media-type parameters were
458
+ # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
459
+ # this method responds with the following Hash:
460
+ # { 'charset' => 'utf-8' }
461
+ def media_type_params
462
+ MediaType.params(content_type)
463
+ end
464
+
465
+ # The character set of the request body if a "charset" media type
466
+ # parameter was given, or nil if no "charset" was specified. Note
467
+ # that, per RFC2616, text/* media types that specify no explicit
468
+ # charset are to be considered ISO-8859-1.
469
+ def content_charset
470
+ media_type_params['charset']
471
+ end
472
+
473
+ # Determine whether the request body contains form-data by checking
474
+ # the request content-type for one of the media-types:
475
+ # "application/x-www-form-urlencoded" or "multipart/form-data". The
476
+ # list of form-data media types can be modified through the
477
+ # +FORM_DATA_MEDIA_TYPES+ array.
478
+ #
479
+ # A request body is also assumed to contain form-data when no
480
+ # content-type header is provided and the request_method is POST.
481
+ def form_data?
482
+ type = media_type
483
+ meth = get_header(RACK_METHODOVERRIDE_ORIGINAL_METHOD) || get_header(REQUEST_METHOD)
484
+
485
+ (meth == POST && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
486
+ end
487
+
488
+ # Determine whether the request body contains data by checking
489
+ # the request media_type against registered parse-data media-types
490
+ def parseable_data?
491
+ PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
492
+ end
493
+
494
+ # Returns the data received in the query string.
495
+ def GET
496
+ get_header(RACK_REQUEST_QUERY_HASH) || set_header(RACK_REQUEST_QUERY_HASH, parse_query(query_string, '&'))
497
+ end
498
+
499
+ # Returns the form data pairs received in the request body.
500
+ #
501
+ # This method support both application/x-www-form-urlencoded and
502
+ # multipart/form-data.
503
+ def form_pairs
504
+ if pairs = get_header(RACK_REQUEST_FORM_PAIRS)
505
+ return pairs
506
+ elsif error = get_header(RACK_REQUEST_FORM_ERROR)
507
+ raise error.class, error.message, cause: error.cause
508
+ end
509
+
510
+ begin
511
+ rack_input = get_header(RACK_INPUT)
512
+
513
+ # Otherwise, figure out how to parse the input:
514
+ if rack_input.nil?
515
+ set_header(RACK_REQUEST_FORM_PAIRS, [])
516
+ elsif form_data? || parseable_data?
517
+ if pairs = Rack::Multipart.parse_multipart(env, Rack::Multipart::ParamList)
518
+ set_header RACK_REQUEST_FORM_PAIRS, pairs
519
+ else
520
+ # Add 2 bytes. One to check whether it is over the limit, and a second
521
+ # in case the slice! call below removes the last byte
522
+ # If read returns nil, use the empty string
523
+ form_vars = get_header(RACK_INPUT).read(query_parser.bytesize_limit + 2) || ''
524
+
525
+ # Fix for Safari Ajax postings that always append \0
526
+ # form_vars.sub!(/\0\z/, '') # performance replacement:
527
+ form_vars.slice!(-1) if form_vars.end_with?("\0")
528
+
529
+ set_header RACK_REQUEST_FORM_VARS, form_vars
530
+ pairs = query_parser.parse_query_pairs(form_vars, '&')
531
+ set_header(RACK_REQUEST_FORM_PAIRS, pairs)
532
+ end
533
+ else
534
+ set_header(RACK_REQUEST_FORM_PAIRS, [])
535
+ end
536
+ rescue => error
537
+ set_header(RACK_REQUEST_FORM_ERROR, error)
538
+ raise
539
+ end
540
+ end
541
+
542
+ # Returns the data received in the request body.
543
+ #
544
+ # This method support both application/x-www-form-urlencoded and
545
+ # multipart/form-data.
546
+ def POST
547
+ if form_hash = get_header(RACK_REQUEST_FORM_HASH)
548
+ return form_hash
549
+ elsif error = get_header(RACK_REQUEST_FORM_ERROR)
550
+ raise error.class, error.message, cause: error.cause
551
+ end
552
+
553
+ pairs = form_pairs
554
+ set_header RACK_REQUEST_FORM_HASH, expand_param_pairs(pairs)
555
+ end
556
+
557
+ # The union of GET and POST data.
558
+ #
559
+ # Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
560
+ def params
561
+ self.GET.merge(self.POST)
562
+ end
563
+
564
+ # Allow overriding the query parser that the receiver will use.
565
+ # By default Rack::Utils.default_query_parser is used.
566
+ attr_writer :query_parser
567
+
568
+ # Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
569
+ #
570
+ # The parameter is updated wherever it was previous defined, so GET, POST, or both. If it wasn't previously defined, it's inserted into GET.
571
+ #
572
+ # <tt>env['rack.input']</tt> is not touched.
573
+ def update_param(k, v)
574
+ found = false
575
+ if self.GET.has_key?(k)
576
+ found = true
577
+ self.GET[k] = v
578
+ end
579
+ if self.POST.has_key?(k)
580
+ found = true
581
+ self.POST[k] = v
582
+ end
583
+ unless found
584
+ self.GET[k] = v
585
+ end
586
+ end
587
+
588
+ # Destructively delete a parameter, whether it's in GET or POST. Returns the value of the deleted parameter.
589
+ #
590
+ # If the parameter is in both GET and POST, the POST value takes precedence since that's how #params works.
591
+ #
592
+ # <tt>env['rack.input']</tt> is not touched.
593
+ def delete_param(k)
594
+ post_value, get_value = self.POST.delete(k), self.GET.delete(k)
595
+ post_value || get_value
596
+ end
597
+
598
+ def base_url
599
+ "#{scheme}://#{host_with_port}"
600
+ end
601
+
602
+ # Tries to return a remake of the original request URL as a string.
603
+ def url
604
+ base_url + fullpath
605
+ end
606
+
607
+ def path
608
+ script_name + path_info
609
+ end
610
+
611
+ def fullpath
612
+ query_string.empty? ? path : "#{path}?#{query_string}"
613
+ end
614
+
615
+ def accept_encoding
616
+ parse_http_accept_header(get_header("HTTP_ACCEPT_ENCODING"))
617
+ end
618
+
619
+ def accept_language
620
+ parse_http_accept_header(get_header("HTTP_ACCEPT_LANGUAGE"))
621
+ end
622
+
623
+ def trusted_proxy?(ip)
624
+ Rack::Request.ip_filter.call(ip)
625
+ end
626
+
627
+ private
628
+
629
+ def default_session; {}; end
630
+
631
+ # Assist with compatibility when processing `X-Forwarded-For`.
632
+ def wrap_ipv6(host)
633
+ # Even thought IPv6 addresses should be wrapped in square brackets,
634
+ # sometimes this is not done in various legacy/underspecified headers.
635
+ # So we try to fix this situation for compatibility reasons.
636
+
637
+ # Try to detect IPv6 addresses which aren't escaped yet:
638
+ if !host.start_with?('[') && host.count(':') > 1
639
+ "[#{host}]"
640
+ else
641
+ host
642
+ end
643
+ end
644
+
645
+ def parse_http_accept_header(header)
646
+ # It would be nice to use filter_map here, but it's Ruby 2.7+
647
+ parts = header.to_s.split(',')
648
+
649
+ parts.map! do |part|
650
+ part.strip!
651
+ next if part.empty?
652
+
653
+ attribute, parameters = part.split(';', 2)
654
+ attribute.strip!
655
+ parameters&.strip!
656
+ quality = 1.0
657
+ if parameters and /\Aq=([\d.]+)/ =~ parameters
658
+ quality = $1.to_f
659
+ end
660
+ [attribute, quality]
661
+ end
662
+
663
+ parts.compact!
664
+
665
+ parts
666
+ end
667
+
668
+ # Get an array of values set in the RFC 7239 `Forwarded` request header.
669
+ def get_http_forwarded(token)
670
+ Utils.forwarded_values(get_header(HTTP_FORWARDED))&.[](token)
671
+ end
672
+
673
+ def query_parser
674
+ @query_parser || Utils.default_query_parser
675
+ end
676
+
677
+ def parse_query(qs, d = '&')
678
+ query_parser.parse_nested_query(qs, d)
679
+ end
680
+
681
+ def parse_multipart
682
+ warn "Rack::Request#parse_multipart is deprecated and will be removed in a future version of Rack.", uplevel: 1
683
+ Rack::Multipart.extract_multipart(self, query_parser)
684
+ end
685
+
686
+ def expand_param_pairs(pairs, query_parser = query_parser())
687
+ params = query_parser.make_params
688
+
689
+ pairs.each do |k, v|
690
+ query_parser.normalize_params(params, k, v)
691
+ end
692
+
693
+ params.to_params_hash
694
+ end
695
+
696
+ def split_header(value)
697
+ value ? value.strip.split(/[, \t]+/) : []
698
+ end
699
+
700
+ # homura patch: the upstream ipv6 / AUTHORITY regexes use Ruby's
701
+ # `x` (extended) flag and POSIX character classes (`[:graph:]`),
702
+ # neither of which Opal's JS regex backend supports. We collapse
703
+ # the IPv6 alternation into a single whitespace-free regex and
704
+ # rewrite AUTHORITY similarly. The resulting matcher is slightly
705
+ # looser than upstream but covers the cases Phase 2's hello-world
706
+ # actually needs (and is only invoked at request time).
707
+ ipv6 = Regexp.union(
708
+ /(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/,
709
+ /(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?/,
710
+ /(?:[0-9A-Fa-f]{1,4}:){6,6}\d+\.\d+\.\d+\.\d+/,
711
+ /(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?::(?:[0-9A-Fa-f]{1,4}:)*\d+\.\d+\.\d+\.\d+/,
712
+ /[Ff][Ee]80(?::[0-9A-Fa-f]{1,4}){7}%[\-0-9A-Za-z._~]+/,
713
+ /[Ff][Ee]80:(?:(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?|:(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)?:[0-9A-Fa-f]{1,4}%[\-0-9A-Za-z._~]+/
714
+ ).freeze
715
+
716
+ # homura patch: JS regex disallows duplicate named capture groups,
717
+ # so we collapse the two `(?<address>...)` alternatives into a
718
+ # single `(?<host>...)` and re-derive `address` from `host` in
719
+ # `split_authority` below.
720
+ AUTHORITY = /\A(?<host>\[#{ipv6}\]|[^\[\]:]*?)(:(?<port>\d+))?\z/
721
+
722
+ private_constant :AUTHORITY
723
+
724
+ def split_authority(authority)
725
+ return [] if authority.nil?
726
+ authority = authority.to_s
727
+
728
+ host = nil
729
+ port = nil
730
+
731
+ if authority.start_with?('[')
732
+ closing = authority.index(']')
733
+ return [] unless closing
734
+
735
+ host = authority[0..closing]
736
+ rest = authority[(closing + 1)..]
737
+ if rest && !rest.empty?
738
+ return [] unless rest.start_with?(':')
739
+ port_str = rest[1..]
740
+ return [] unless port_str.match?(/\A\d+\z/)
741
+
742
+ port = port_str.to_i
743
+ end
744
+ else
745
+ first_colon = authority.index(':')
746
+ last_colon = authority.rindex(':')
747
+
748
+ if first_colon && first_colon == last_colon
749
+ candidate_host = authority[0...last_colon]
750
+ candidate_port = authority[(last_colon + 1)..]
751
+
752
+ if !candidate_host.empty? && candidate_port.match?(/\A\d+\z/)
753
+ host = candidate_host
754
+ port = candidate_port.to_i
755
+ else
756
+ host = authority
757
+ end
758
+ else
759
+ host = authority
760
+ end
761
+ end
762
+
763
+ return [] if host.nil? || host.empty?
764
+
765
+ address = host.start_with?('[') && host.end_with?(']') ? host[1..-2] : host
766
+ [host, address, port]
767
+ end
768
+
769
+ FORWARDED_SCHEME_HEADERS = {
770
+ proto: HTTP_X_FORWARDED_PROTO,
771
+ scheme: HTTP_X_FORWARDED_SCHEME
772
+ }.freeze
773
+ private_constant :FORWARDED_SCHEME_HEADERS
774
+ def forwarded_scheme
775
+ forwarded_priority.each do |type|
776
+ case type
777
+ when :forwarded
778
+ if (forwarded_proto = get_http_forwarded(:proto)) &&
779
+ (scheme = allowed_scheme(forwarded_proto.last))
780
+ return scheme
781
+ end
782
+ when :x_forwarded
783
+ x_forwarded_proto_priority.each do |x_type|
784
+ if header = FORWARDED_SCHEME_HEADERS[x_type]
785
+ split_header(get_header(header)).reverse_each do |scheme|
786
+ if allowed_scheme(scheme)
787
+ return scheme
788
+ end
789
+ end
790
+ end
791
+ end
792
+ end
793
+ end
794
+
795
+ nil
796
+ end
797
+
798
+ def allowed_scheme(header)
799
+ header if ALLOWED_SCHEMES.include?(header)
800
+ end
801
+
802
+ def forwarded_priority
803
+ Request.forwarded_priority
804
+ end
805
+
806
+ def x_forwarded_proto_priority
807
+ Request.x_forwarded_proto_priority
808
+ end
809
+ end
810
+
811
+ include Env
812
+ include Helpers
813
+ end
814
+ end
815
+
816
+ # :nocov:
817
+ require_relative 'multipart' unless defined?(Rack::Multipart)
818
+ # :nocov: