From 81d9428eca0b831d52e42c6ed8ac001b4dc8adff Mon Sep 17 00:00:00 2001 From: thedarkone Date: Thu, 26 Feb 2009 01:23:24 +0100 Subject: [PATCH] Implement url safe "base64url" encoding (RFC 4648). --- .../lib/active_support/core_ext/base64/encoding.rb | 31 ++++++++++++++++++++ activesupport/test/core_ext/base64_ext_test.rb | 14 +++++++++ 2 files changed, 45 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/base64/encoding.rb b/activesupport/lib/active_support/core_ext/base64/encoding.rb index a9656c1..589659e 100644 --- a/activesupport/lib/active_support/core_ext/base64/encoding.rb +++ b/activesupport/lib/active_support/core_ext/base64/encoding.rb @@ -10,6 +10,37 @@ module ActiveSupport #:nodoc: def encode64s(value) encode64(value).gsub(/\n/, '') end + + B2URL_PATTERN = '+/=' + B2URL_REPLACEMENT = '-_ ' + + URL2B_PATTERN = '-_' + URL2B_REPLACEMENT = '+/' + + # Encodes passed data using "base64url" (as defined by RFC4648). This makes the base64 encoding readily usable as URL parameters + # not requiring percent-encoding. No padding '=' or newline breaks are used, and the '+' and '/' characters of standard Base64 + # encoding are respectively replaced by '-' and '_'. + # ==== Examples + # ActiveSupport::Base64.encode64("\377\377\276a") # => "//++YQ==\n" + # ActiveSupport::Base64.encode64_url("\377\377\276a") # => "__--YQ" + # ActiveSupport::Base64.decode64_url("__--YQ") # => "\377\377\276a" + def encode64_url(data) + encode64(data).tr(B2URL_PATTERN, B2URL_REPLACEMENT).strip.delete("\n") + end + + # Decodes a "base64url" encoded string to its original representation. + # See Encoding#encode64_url for more information. + def decode64_url(str) + # add '=' padding + str = case str.length % 4 + when 2 then str + '==' + when 3 then str + '=' + else + str + end + + decode64(str.tr(URL2B_PATTERN, URL2B_REPLACEMENT)) + end end end end diff --git a/activesupport/test/core_ext/base64_ext_test.rb b/activesupport/test/core_ext/base64_ext_test.rb index bd0e9f8..b27a94b 100644 --- a/activesupport/test/core_ext/base64_ext_test.rb +++ b/activesupport/test/core_ext/base64_ext_test.rb @@ -5,4 +5,18 @@ class Base64Test < Test::Unit::TestCase assert_match(/\n/, ActiveSupport::Base64.encode64("oneverylongstringthatwouldnormallybesplitupbynewlinesbytheregularbase64")) assert_no_match(/\n/, ActiveSupport::Base64.encode64s("oneverylongstringthatwouldnormallybesplitupbynewlinesbytheregularbase64")) end + + def test_base64url_works + problematic_input = "\377\377\276a" + assert_equal "//++YQ==\n", ActiveSupport::Base64.encode64(problematic_input) + assert_equal '__--YQ', ActiveSupport::Base64.encode64_url(problematic_input) + assert_equal problematic_input, ActiveSupport::Base64.decode64_url('__--YQ') + assert_equal problematic_input, ActiveSupport::Base64.decode64_url('__--YQ==') + end + + def test_decode64_url_does_not_modify_its_input + str = '//++YQ' + ActiveSupport::Base64.decode64_url(str) + assert_equal '//++YQ', str + end end -- 1.6.0.1