mirror of
https://github.com/Sarsoo/Spotify.NET.git
synced 2024-12-23 22:56:25 +00:00
Implemented util class for base64url, start of #490
This commit is contained in:
parent
d8e9386f62
commit
f8b2787154
44
SpotifyAPI.Web.Tests/UtilTests/Base64UtilTest.cs
Normal file
44
SpotifyAPI.Web.Tests/UtilTests/Base64UtilTest.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System.Text;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using SpotifyAPI.Web;
|
||||||
|
|
||||||
|
namespace SpotifyAPI.Web.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class Base64UtilTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Base64UrlDecode_Works()
|
||||||
|
{
|
||||||
|
var encoded = "SGVsbG9Xb3JsZA==";
|
||||||
|
|
||||||
|
Assert.AreEqual("HelloWorld", Encoding.UTF8.GetString(Base64Util.UrlDecode(encoded)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Base64UrlEncode_Works()
|
||||||
|
{
|
||||||
|
var decoded = "HelloWorld";
|
||||||
|
|
||||||
|
Assert.AreEqual("SGVsbG9Xb3JsZA==", Base64Util.UrlEncode(Encoding.UTF8.GetBytes(decoded)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Base64UrlEncode_WorksSpecialChars()
|
||||||
|
{
|
||||||
|
var bytes = new byte[] { 0x04, 0x9f, 0x9c, 0xff, 0x3f, 0x0a };
|
||||||
|
|
||||||
|
// normal base64: BJ+c/z8K
|
||||||
|
Assert.AreEqual("BJ-c_z8K", Base64Util.UrlEncode(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Base64UrlDecode_WorksSpecialChars()
|
||||||
|
{
|
||||||
|
var bytes = new byte[] { 0x04, 0x9f, 0x9c, 0xff, 0x3f, 0x0a };
|
||||||
|
|
||||||
|
// normal base64: BJ+c/z8K
|
||||||
|
Assert.AreEqual(bytes, Base64Util.UrlDecode("BJ-c_z8K"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
SpotifyAPI.Web/Assembly.cs
Normal file
3
SpotifyAPI.Web/Assembly.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("SpotifyAPI.Web.Tests")]
|
123
SpotifyAPI.Web/Util/Base64Util.cs
Normal file
123
SpotifyAPI.Web/Util/Base64Util.cs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace SpotifyAPI.Web
|
||||||
|
{
|
||||||
|
internal class Base64Util
|
||||||
|
{
|
||||||
|
internal const string WebEncoders_InvalidCountOffsetOrLength = "Invalid {0}, {1} or {2} length.";
|
||||||
|
internal const string WebEncoders_MalformedInput = "Malformed input: {0} is an invalid input length.";
|
||||||
|
|
||||||
|
public static string UrlEncode(byte[] input)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special-case empty input
|
||||||
|
if (input.Length == 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = new char[GetArraySizeRequiredToEncode(input.Length)];
|
||||||
|
var numBase64Chars = Convert.ToBase64CharArray(input, 0, input.Length, buffer, 0);
|
||||||
|
|
||||||
|
return new string(buffer, startIndex: 0, length: numBase64Chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] UrlDecode(string input)
|
||||||
|
{
|
||||||
|
var buffer = new char[GetArraySizeRequiredToDecode(input.Length)];
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumption: input is base64url encoded without padding and contains no whitespace.
|
||||||
|
|
||||||
|
var paddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
|
||||||
|
var arraySizeRequired = checked(count + paddingCharsToAdd);
|
||||||
|
|
||||||
|
// Copy input into buffer, fixing up '-' -> '+' and '_' -> '/'.
|
||||||
|
var i = 0;
|
||||||
|
for (var j = 0; i < count; i++, j++)
|
||||||
|
{
|
||||||
|
var ch = input[j];
|
||||||
|
if (ch == '-')
|
||||||
|
{
|
||||||
|
buffer[i] = '+';
|
||||||
|
}
|
||||||
|
else if (ch == '_')
|
||||||
|
{
|
||||||
|
buffer[i] = '/';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer[i] = ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the padding characters back.
|
||||||
|
for (; paddingCharsToAdd > 0; i++, paddingCharsToAdd--)
|
||||||
|
{
|
||||||
|
buffer[i] = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode.
|
||||||
|
// If the caller provided invalid base64 chars, they'll be caught here.
|
||||||
|
return Convert.FromBase64CharArray(buffer, 0, arraySizeRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetArraySizeRequiredToEncode(int count)
|
||||||
|
{
|
||||||
|
var numWholeOrPartialInputBlocks = checked(count + 2) / 3;
|
||||||
|
return checked(numWholeOrPartialInputBlocks * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetArraySizeRequiredToDecode(int count)
|
||||||
|
{
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var numPaddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
|
||||||
|
|
||||||
|
return checked(count + numPaddingCharsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetNumBase64PaddingCharsToAddForDecode(int inputLength)
|
||||||
|
{
|
||||||
|
switch (inputLength % 4)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
case 2:
|
||||||
|
return 2;
|
||||||
|
case 3:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
throw new FormatException(
|
||||||
|
string.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
WebEncoders_MalformedInput,
|
||||||
|
inputLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user