addressable 2.2.8 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of addressable might be problematic. Click here for more details.

@@ -120,7 +120,11 @@ module Addressable
120
120
  user = userinfo.strip[/^([^:]*):?/, 1]
121
121
  password = userinfo.strip[/:(.*)$/, 1]
122
122
  end
123
- host = authority.gsub(/^([^\[\]]*)@/, EMPTY_STR).gsub(/:([^:@\[\]]*?)$/, EMPTY_STR)
123
+ host = authority.gsub(
124
+ /^([^\[\]]*)@/, EMPTY_STR
125
+ ).gsub(
126
+ /:([^:@\[\]]*?)$/, EMPTY_STR
127
+ )
124
128
  port = authority[/:([^:@\[\]]*?)$/, 1]
125
129
  end
126
130
  if port == EMPTY_STR
@@ -177,6 +181,8 @@ module Addressable
177
181
  uri.gsub!(/^feed:\/+/, "feed://")
178
182
  when /^file:\/+/
179
183
  uri.gsub!(/^file:\/+/, "file:///")
184
+ when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
185
+ uri.gsub!(/^/, hints[:scheme] + "://")
180
186
  end
181
187
  parsed = self.parse(uri)
182
188
  if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
@@ -365,7 +371,7 @@ module Addressable
365
371
  # @param [String, Addressable::URI, #to_str] uri
366
372
  # The URI or component to unencode.
367
373
  #
368
- # @param [Class] returning
374
+ # @param [Class] return_type
369
375
  # The type of object to return.
370
376
  # This value may only be set to <code>String</code> or
371
377
  # <code>Addressable::URI</code>. All other values are invalid. Defaults
@@ -373,8 +379,9 @@ module Addressable
373
379
  #
374
380
  # @return [String, Addressable::URI]
375
381
  # The unencoded component or URI.
376
- # The return type is determined by the <code>returning</code> parameter.
377
- def self.unencode(uri, returning=String)
382
+ # The return type is determined by the <code>return_type</code>
383
+ # parameter.
384
+ def self.unencode(uri, return_type=String)
378
385
  return nil if uri.nil?
379
386
 
380
387
  begin
@@ -382,18 +389,18 @@ module Addressable
382
389
  rescue NoMethodError, TypeError
383
390
  raise TypeError, "Can't convert #{uri.class} into String."
384
391
  end if !uri.is_a? String
385
- if ![String, ::Addressable::URI].include?(returning)
392
+ if ![String, ::Addressable::URI].include?(return_type)
386
393
  raise TypeError,
387
394
  "Expected Class (String or Addressable::URI), " +
388
- "got #{returning.inspect}"
395
+ "got #{return_type.inspect}"
389
396
  end
390
397
  result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence|
391
398
  sequence[1..3].to_i(16).chr
392
399
  end
393
400
  result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
394
- if returning == String
401
+ if return_type == String
395
402
  return result
396
- elsif returning == ::Addressable::URI
403
+ elsif return_type == ::Addressable::URI
397
404
  return ::Addressable::URI.parse(result)
398
405
  end
399
406
  end
@@ -478,7 +485,7 @@ module Addressable
478
485
  # @param [String, Addressable::URI, #to_str] uri
479
486
  # The URI to encode.
480
487
  #
481
- # @param [Class] returning
488
+ # @param [Class] return_type
482
489
  # The type of object to return.
483
490
  # This value may only be set to <code>String</code> or
484
491
  # <code>Addressable::URI</code>. All other values are invalid. Defaults
@@ -486,8 +493,9 @@ module Addressable
486
493
  #
487
494
  # @return [String, Addressable::URI]
488
495
  # The encoded URI.
489
- # The return type is determined by the <code>returning</code> parameter.
490
- def self.encode(uri, returning=String)
496
+ # The return type is determined by the <code>return_type</code>
497
+ # parameter.
498
+ def self.encode(uri, return_type=String)
491
499
  return nil if uri.nil?
492
500
 
493
501
  begin
@@ -496,10 +504,10 @@ module Addressable
496
504
  raise TypeError, "Can't convert #{uri.class} into String."
497
505
  end if !uri.is_a? String
498
506
 
499
- if ![String, ::Addressable::URI].include?(returning)
507
+ if ![String, ::Addressable::URI].include?(return_type)
500
508
  raise TypeError,
501
509
  "Expected Class (String or Addressable::URI), " +
502
- "got #{returning.inspect}"
510
+ "got #{return_type.inspect}"
503
511
  end
504
512
  uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
505
513
  encoded_uri = Addressable::URI.new(
@@ -514,9 +522,9 @@ module Addressable
514
522
  :fragment => self.encode_component(uri_object.fragment,
515
523
  Addressable::URI::CharacterClasses::FRAGMENT)
516
524
  )
517
- if returning == String
525
+ if return_type == String
518
526
  return encoded_uri.to_s
519
- elsif returning == ::Addressable::URI
527
+ elsif return_type == ::Addressable::URI
520
528
  return encoded_uri
521
529
  end
522
530
  end
@@ -532,7 +540,7 @@ module Addressable
532
540
  # @param [String, Addressable::URI, #to_str] uri
533
541
  # The URI to encode.
534
542
  #
535
- # @param [Class] returning
543
+ # @param [Class] return_type
536
544
  # The type of object to return.
537
545
  # This value may only be set to <code>String</code> or
538
546
  # <code>Addressable::URI</code>. All other values are invalid. Defaults
@@ -540,18 +548,19 @@ module Addressable
540
548
  #
541
549
  # @return [String, Addressable::URI]
542
550
  # The encoded URI.
543
- # The return type is determined by the <code>returning</code> parameter.
544
- def self.normalized_encode(uri, returning=String)
551
+ # The return type is determined by the <code>return_type</code>
552
+ # parameter.
553
+ def self.normalized_encode(uri, return_type=String)
545
554
  begin
546
555
  uri = uri.to_str
547
556
  rescue NoMethodError, TypeError
548
557
  raise TypeError, "Can't convert #{uri.class} into String."
549
558
  end if !uri.is_a? String
550
559
 
551
- if ![String, ::Addressable::URI].include?(returning)
560
+ if ![String, ::Addressable::URI].include?(return_type)
552
561
  raise TypeError,
553
562
  "Expected Class (String or Addressable::URI), " +
554
- "got #{returning.inspect}"
563
+ "got #{return_type.inspect}"
555
564
  end
556
565
  uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
557
566
  components = {
@@ -591,9 +600,9 @@ module Addressable
591
600
  :fragment => self.encode_component(components[:fragment],
592
601
  Addressable::URI::CharacterClasses::FRAGMENT)
593
602
  )
594
- if returning == String
603
+ if return_type == String
595
604
  return encoded_uri.to_s
596
- elsif returning == ::Addressable::URI
605
+ elsif return_type == ::Addressable::URI
597
606
  return encoded_uri
598
607
  end
599
608
  end
@@ -1019,6 +1028,14 @@ module Addressable
1019
1028
  end
1020
1029
  @host = new_host ? new_host.to_str : nil
1021
1030
 
1031
+ unreserved = CharacterClasses::UNRESERVED
1032
+ sub_delims = CharacterClasses::SUB_DELIMS
1033
+ if @host != nil && (@host =~ /[<>{}\/\?\#\@]/ ||
1034
+ (@host[/^\[(.*)\]$/, 1] != nil && @host[/^\[(.*)\]$/, 1] !~
1035
+ Regexp.new("^[#{unreserved}#{sub_delims}:]*$")))
1036
+ raise InvalidURIError, "Invalid character in host: '#{@host.to_s}'"
1037
+ end
1038
+
1022
1039
  # Reset dependant values
1023
1040
  @authority = nil
1024
1041
  @normalized_host = nil
@@ -1089,8 +1106,11 @@ module Addressable
1089
1106
  new_user = new_userinfo.strip[/^([^:]*):?/, 1]
1090
1107
  new_password = new_userinfo.strip[/:(.*)$/, 1]
1091
1108
  end
1092
- new_host =
1093
- new_authority.gsub(/^([^\[\]]*)@/, EMPTY_STR).gsub(/:([^:@\[\]]*?)$/, EMPTY_STR)
1109
+ new_host = new_authority.gsub(
1110
+ /^([^\[\]]*)@/, EMPTY_STR
1111
+ ).gsub(
1112
+ /:([^:@\[\]]*?)$/, EMPTY_STR
1113
+ )
1094
1114
  new_port =
1095
1115
  new_authority[/:([^:@\[\]]*?)$/, 1]
1096
1116
  end
@@ -1201,16 +1221,22 @@ module Addressable
1201
1221
  # @return [Integer] The inferred port component.
1202
1222
  def inferred_port
1203
1223
  if self.port.to_i == 0
1204
- if self.scheme
1205
- URI.port_mapping[self.scheme.strip.downcase]
1206
- else
1207
- nil
1208
- end
1224
+ self.default_port
1209
1225
  else
1210
1226
  self.port.to_i
1211
1227
  end
1212
1228
  end
1213
1229
 
1230
+ ##
1231
+ # The default port for this URI's scheme.
1232
+ # This method will always returns the default port for the URI's scheme
1233
+ # regardless of the presence of an explicit port in the URI.
1234
+ #
1235
+ # @return [Integer] The default port.
1236
+ def default_port
1237
+ URI.port_mapping[self.scheme.strip.downcase] if self.scheme
1238
+ end
1239
+
1214
1240
  ##
1215
1241
  # The combination of components that represent a site.
1216
1242
  # Combines the scheme, user, password, host, and port components.
@@ -1391,114 +1417,72 @@ module Addressable
1391
1417
  ##
1392
1418
  # Converts the query component to a Hash value.
1393
1419
  #
1394
- # @option [Symbol] notation
1395
- # May be one of <code>:flat</code>, <code>:dot</code>, or
1396
- # <code>:subscript</code>. The <code>:dot</code> notation is not
1397
- # supported for assignment. Default value is <code>:subscript</code>.
1420
+ # @param [Class] return_type The return type desired. Value must be either
1421
+ # `Hash` or `Array`.
1398
1422
  #
1399
1423
  # @return [Hash, Array] The query string parsed as a Hash or Array object.
1400
1424
  #
1401
1425
  # @example
1402
1426
  # Addressable::URI.parse("?one=1&two=2&three=3").query_values
1403
1427
  # #=> {"one" => "1", "two" => "2", "three" => "3"}
1404
- # Addressable::URI.parse("?one[two][three]=four").query_values
1405
- # #=> {"one" => {"two" => {"three" => "four"}}}
1406
- # Addressable::URI.parse("?one.two.three=four").query_values(
1407
- # :notation => :dot
1408
- # )
1409
- # #=> {"one" => {"two" => {"three" => "four"}}}
1410
- # Addressable::URI.parse("?one[two][three]=four").query_values(
1411
- # :notation => :flat
1412
- # )
1413
- # #=> {"one[two][three]" => "four"}
1414
- # Addressable::URI.parse("?one.two.three=four").query_values(
1415
- # :notation => :flat
1416
- # )
1417
- # #=> {"one.two.three" => "four"}
1418
- # Addressable::URI.parse(
1419
- # "?one[two][three][]=four&one[two][three][]=five"
1420
- # ).query_values
1421
- # #=> {"one" => {"two" => {"three" => ["four", "five"]}}}
1422
- # Addressable::URI.parse(
1423
- # "?one=two&one=three").query_values(:notation => :flat_array)
1424
- # #=> [['one', 'two'], ['one', 'three']]
1425
- def query_values(options={})
1426
- defaults = {:notation => :subscript}
1427
- options = defaults.merge(options)
1428
- if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation])
1429
- raise ArgumentError,
1430
- "Invalid notation. Must be one of: " +
1431
- "[:flat, :dot, :subscript, :flat_array]."
1432
- end
1433
- dehash = lambda do |hash|
1434
- hash.each do |(key, value)|
1435
- if value.kind_of?(Hash)
1436
- hash[key] = dehash.call(value)
1437
- end
1438
- end
1439
- if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
1440
- hash.sort.inject([]) do |accu, (_, value)|
1441
- accu << value; accu
1442
- end
1443
- else
1444
- hash
1445
- end
1428
+ # Addressable::URI.parse("?one=two&one=three").query_values(Array)
1429
+ # #=> [["one", "two"], ["one", "three"]]
1430
+ # Addressable::URI.parse("?one=two&one=three").query_values(Hash)
1431
+ # #=> {"one" => "three"}
1432
+ def query_values(return_type=Hash)
1433
+ empty_accumulator = Array == return_type ? [] : {}
1434
+ if return_type != Hash && return_type != Array
1435
+ raise ArgumentError, "Invalid return type. Must be Hash or Array."
1446
1436
  end
1447
1437
  return nil if self.query == nil
1448
- empty_accumulator = :flat_array == options[:notation] ? [] : {}
1449
- return ((self.query.split("&").map do |pair|
1438
+ split_query = (self.query.split("&").map do |pair|
1450
1439
  pair.split("=", 2) if pair && !pair.empty?
1451
- end).compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
1452
- value = true if value.nil?
1453
- key = URI.unencode_component(key)
1454
- if value != true
1455
- value = URI.unencode_component(value.gsub(/\+/, " "))
1440
+ end).compact
1441
+ return split_query.inject(empty_accumulator.dup) do |accu, pair|
1442
+ # I'd rather use key/value identifiers instead of array lookups,
1443
+ # but in this case I really want to maintain the exact pair structure,
1444
+ # so it's best to make all changes in-place.
1445
+ pair[0] = URI.unencode_component(pair[0])
1446
+ # This looks weird, but it's correct. Handles query values like:
1447
+ # ?data=1&flag&color=blue
1448
+ # In this case, `flag` would evaluate to `true`, which is what you
1449
+ # want. Its absence implies that `flag` resolves to `false`.
1450
+ # value = true if value.nil?
1451
+ if pair[1].respond_to?(:to_str)
1452
+ # I loathe the fact that I have to do this. Stupid HTML 4.01.
1453
+ # Treating '+' as a space was just an unbelievably bad idea.
1454
+ # There was nothing wrong with '%20'!
1455
+ # If it ain't broke, don't fix it!
1456
+ pair[1] = URI.unencode_component(pair[1].to_str.gsub(/\+/, " "))
1456
1457
  end
1457
- if options[:notation] == :flat
1458
- if accumulator[key]
1459
- raise ArgumentError, "Key was repeated: #{key.inspect}"
1460
- end
1461
- accumulator[key] = value
1462
- elsif options[:notation] == :flat_array
1463
- accumulator << [key, value]
1458
+ if return_type == Hash
1459
+ accu[pair[0]] = pair[1]
1464
1460
  else
1465
- if options[:notation] == :dot
1466
- array_value = false
1467
- subkeys = key.split(".")
1468
- elsif options[:notation] == :subscript
1469
- array_value = !!(key =~ /\[\]$/)
1470
- subkeys = key.split(/[\[\]]+/)
1471
- end
1472
- current_hash = accumulator
1473
- for i in 0...(subkeys.size - 1)
1474
- subkey = subkeys[i]
1475
- current_hash[subkey] = {} unless current_hash[subkey]
1476
- current_hash = current_hash[subkey]
1477
- end
1478
- if array_value
1479
- current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
1480
- current_hash[subkeys.last] << value
1481
- else
1482
- current_hash[subkeys.last] = value
1483
- end
1461
+ accu << pair
1484
1462
  end
1485
- accumulator
1486
- end).inject(empty_accumulator.dup) do |accumulator, (key, value)|
1487
- if options[:notation] == :flat_array
1488
- accumulator << [key, value]
1489
- else
1490
- accumulator[key] = value.kind_of?(Hash) ? dehash.call(value) : value
1491
- end
1492
- accumulator
1463
+ accu
1493
1464
  end
1494
1465
  end
1495
1466
 
1496
1467
  ##
1497
1468
  # Sets the query component for this URI from a Hash object.
1498
- # This method produces a query string using the :subscript notation.
1499
- # An empty Hash will result in a nil query.
1469
+ # An empty Hash or Array will result in an empty query string.
1500
1470
  #
1501
1471
  # @param [Hash, #to_hash, Array] new_query_values The new query values.
1472
+ #
1473
+ # @example
1474
+ # uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
1475
+ # uri.query
1476
+ # # => "a=a&b=c&b=d&b=e"
1477
+ # uri.query_values = [['a', 'a'], ['b', 'c'], ['b', 'd'], ['b', 'e']]
1478
+ # uri.query
1479
+ # # => "a=a&b=c&b=d&b=e"
1480
+ # uri.query_values = [['a', 'a'], ['b', ['c', 'd', 'e']]]
1481
+ # uri.query
1482
+ # # => "a=a&b=c&b=d&b=e"
1483
+ # uri.query_values = [['flag'], ['key', 'value']]
1484
+ # uri.query
1485
+ # # => "flag&key=value"
1502
1486
  def query_values=(new_query_values)
1503
1487
  if new_query_values == nil
1504
1488
  self.query = nil
@@ -1520,55 +1504,28 @@ module Addressable
1520
1504
  new_query_values.sort!
1521
1505
  end
1522
1506
 
1523
- ##
1524
- # Joins and converts parent and value into a properly encoded and
1525
- # ordered URL query.
1526
- #
1527
- # @private
1528
- # @param [String] parent an URI encoded component.
1529
- # @param [Array, Hash, Symbol, #to_str] value
1530
- #
1531
- # @return [String] a properly escaped and ordered URL query.
1532
- to_query = lambda do |parent, value|
1533
- if value.is_a?(Hash)
1534
- value = value.map do |key, val|
1535
- [
1536
- URI.encode_component(key, CharacterClasses::UNRESERVED),
1537
- val
1538
- ]
1539
- end
1540
- value.sort!
1541
- buffer = ""
1542
- value.each do |key, val|
1543
- new_parent = "#{parent}[#{key}]"
1544
- buffer << "#{to_query.call(new_parent, val)}&"
1545
- end
1546
- return buffer.chop
1547
- elsif value.is_a?(Array)
1548
- buffer = ""
1549
- value.each_with_index do |val, i|
1550
- new_parent = "#{parent}[#{i}]"
1551
- buffer << "#{to_query.call(new_parent, val)}&"
1507
+ # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
1508
+ buffer = ""
1509
+ new_query_values.each do |key, value|
1510
+ encoded_key = URI.encode_component(
1511
+ key, CharacterClasses::UNRESERVED
1512
+ )
1513
+ if value == nil || value == true
1514
+ buffer << "#{encoded_key}&"
1515
+ elsif value.kind_of?(Array)
1516
+ value.each do |sub_value|
1517
+ encoded_value = URI.encode_component(
1518
+ sub_value, CharacterClasses::UNRESERVED
1519
+ )
1520
+ buffer << "#{encoded_key}=#{encoded_value}&"
1552
1521
  end
1553
- return buffer.chop
1554
- elsif value == true
1555
- return parent
1556
1522
  else
1557
1523
  encoded_value = URI.encode_component(
1558
1524
  value, CharacterClasses::UNRESERVED
1559
1525
  )
1560
- return "#{parent}=#{encoded_value}"
1526
+ buffer << "#{encoded_key}=#{encoded_value}&"
1561
1527
  end
1562
1528
  end
1563
-
1564
- # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
1565
- buffer = ""
1566
- new_query_values.each do |parent, value|
1567
- encoded_parent = URI.encode_component(
1568
- parent, CharacterClasses::UNRESERVED
1569
- )
1570
- buffer << "#{to_query.call(encoded_parent, value)}&"
1571
- end
1572
1529
  self.query = buffer.chop
1573
1530
  end
1574
1531
 
@@ -21,8 +21,8 @@ if !defined?(Addressable::VERSION)
21
21
  module Addressable
22
22
  module VERSION
23
23
  MAJOR = 2
24
- MINOR = 2
25
- TINY = 8
24
+ MINOR = 3
25
+ TINY = 2
26
26
 
27
27
  STRING = [MAJOR, MINOR, TINY].join('.')
28
28
  end
@@ -196,6 +196,21 @@ describe Addressable::IDNA, "when using the pure-Ruby implementation" do
196
196
 
197
197
  it_should_behave_like "converting from unicode to ASCII"
198
198
  it_should_behave_like "converting from ASCII to unicode"
199
+
200
+ begin
201
+ require "fiber"
202
+
203
+ it "should not blow up inside fibers" do
204
+ f = Fiber.new do
205
+ Addressable.send(:remove_const, :IDNA)
206
+ load "addressable/idna/pure.rb"
207
+ end
208
+ f.resume
209
+ end
210
+ rescue LoadError
211
+ # Fibers aren't supported in this version of Ruby, skip this test.
212
+ warn('Fibers unsupported.')
213
+ end
199
214
  end
200
215
 
201
216
  begin