diff --git a/lib/src/minio_helpers.dart b/lib/src/minio_helpers.dart index bebc4ff5ff8c35bb5617512e3bc18d086d738328..b5cf6a320195c5b47a63d830aa5fbe7e3e790a62 100644 --- a/lib/src/minio_helpers.dart +++ b/lib/src/minio_helpers.dart @@ -270,3 +270,32 @@ String encodePath(Uri uri) { } return result.toString(); } + +final _queryIgnoredChars = { + '-'.codeUnitAt(0), + '_'.codeUnitAt(0), + '.'.codeUnitAt(0), + '~'.codeUnitAt(0), +}; + +/// encode [uri].path to HTML hex escape sequence +String encodeCanonicalQuery(String query) { + final result = StringBuffer(); + for (var char in query.codeUnits) { + if (_A <= char && char <= _Z || + _a <= char && char <= _z || + _0 <= char && char <= _9) { + result.writeCharCode(char); + continue; + } + + if (_queryIgnoredChars.contains(char)) { + result.writeCharCode(char); + continue; + } + + result.write('%'); + result.write(hex.encode([char]).toUpperCase()); + } + return result.toString(); +} diff --git a/lib/src/minio_sign.dart b/lib/src/minio_sign.dart index 9f62262d0f38e17256c3d5a678dbe62aa233f93f..c507ac1c42314bbde0055c5feb1ffaf5c8a41ff3 100644 --- a/lib/src/minio_sign.dart +++ b/lib/src/minio_sign.dart @@ -54,8 +54,8 @@ String getCanonicalRequest( final requestQuery = queryKeys.map((key) { final value = request.url.queryParameters[key]; final hasValue = value != null; - final valuePart = hasValue ? Uri.encodeQueryComponent(value!) : ''; - return Uri.encodeQueryComponent(key) + '=' + valuePart; + final valuePart = hasValue ? encodeCanonicalQuery(value!) : ''; + return encodeCanonicalQuery(key) + '=' + valuePart; }).join('&'); final canonical = []; diff --git a/test/minio_test.dart b/test/minio_test.dart index 7faadbc5adda597406f4fff9b3ee5b38432a0394..481220aa1d2c5c9ec87fa5f6a5c67b56ac8d96c7 100644 --- a/test/minio_test.dart +++ b/test/minio_test.dart @@ -91,6 +91,17 @@ void testListBuckets() { await minio.removeBucket(bucketName2); }); + test('listBuckets() can list buckets with spaces in name', () async { + final minio = getMinioClient(); + final bucketName = uniqueName() + ' folder'; + await minio.makeBucket(bucketName); + + final buckets = await minio.listBuckets(); + expect(buckets.any((b) => b.name == bucketName), isTrue); + + await minio.removeBucket(bucketName); + }); + test('listBuckets() fails due to wrong access key', () async { final minio = getMinioClient(accessKey: 'incorrect-access-key');