diff --git a/lib/src/minio.dart b/lib/src/minio.dart index b203ff359985ddce671eebd4c233130619feab81..a1d1f6e21f0b1c59b08a6f2a7eeffa2da16c8077 100644 --- a/lib/src/minio.dart +++ b/lib/src/minio.dart @@ -75,10 +75,13 @@ class Minio { MinioInvalidBucketNameError.check(bucket); try { final response = await _client.request(method: 'HEAD', bucket: bucket); + validate(response); return (response.statusCode == 200 || response.statusCode == 403); } on MinioS3Error catch (e) { final code = e.error.code; - if (code == 'NoSuchBucket' || code == 'NotFound') return false; + if (code == 'NoSuchBucket' || code == 'NotFound' || code == 'Not Found') { + return false; + } rethrow; } on StateError catch (e) { // Insight from testing: in most cases, AWS S3 returns the HTTP status code @@ -87,11 +90,11 @@ class Minio { // status code 404 as officially documented. Then, this redirect response // lacks the HTTP header `location` which causes this exception in Dart's // HTTP library (`http_impl.dart`). - if (e.message == 'Response has no Location header for redirect') + if (e.message == 'Response has no Location header for redirect') { return false; + } rethrow; } - return true; } int _calculatePartSize(int size) { @@ -456,6 +459,7 @@ class Minio { method: 'GET', region: region ?? 'us-east-1', ); + validate(resp); final bucketsNode = xml.XmlDocument.parse(resp.body).findAllElements('Buckets').first; return bucketsNode.children.map((n) => Bucket.fromXml(n)).toList(); diff --git a/lib/src/minio_helpers.dart b/lib/src/minio_helpers.dart index b8d2e381b4993c16fd4936aef317a1b9fd49aa47..4cd2fdc13e97ece4aabecf0e65aacda27eae296d 100644 --- a/lib/src/minio_helpers.dart +++ b/lib/src/minio_helpers.dart @@ -217,9 +217,17 @@ Future<void> validateStreamed( void validate(Response response, {int expect}) { if (response.statusCode >= 400) { - final body = xml.XmlDocument.parse(response.body); - final error = Error.fromXml(body.rootElement); - throw MinioS3Error(error.message, error, response); + var error; + + // Parse HTTP response body as XML only when not empty + if (response.body == null || response.body.isEmpty) { + error = Error(response.reasonPhrase, null, response.reasonPhrase, null); + } else { + final body = xml.XmlDocument.parse(response.body); + error = Error.fromXml(body.rootElement); + } + + throw MinioS3Error(error?.message, error, response); } if (expect != null && response.statusCode != expect) { diff --git a/test/minio_dart_test.dart b/test/minio_dart_test.dart index cc066cf05713e9b39ede297e02d059a569471ea3..e8626bcee58a4aaa0effa6be67cd70482564e658 100644 --- a/test/minio_dart_test.dart +++ b/test/minio_dart_test.dart @@ -39,6 +39,58 @@ void main() { ); }); }); + + group('bucketExists', () { + final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + + setUpAll(() async { + final minio = _getClient(); + await minio.makeBucket(bucketName); + }); + + tearDownAll(() async { + final minio = _getClient(); + await minio.removeBucket(bucketName); + }); + + test('bucketExists() returns true for an existing bucket', () async { + final minio = _getClient(); + expect(await minio.bucketExists(bucketName), equals(true)); + }); + + test('bucketExists() returns false for a non-existent bucket', () async { + final minio = _getClient(); + expect(await minio.bucketExists('non-existing-bucket-name'), equals(false)); + }); + + test('bucketExists() fails due to wrong access key', () async { + final minio = _getClient(accessKey: 'incorrect-access-key'); + expect( + () async => await minio.bucketExists(bucketName), + throwsA( + isA<MinioError>().having( + (e) => e.message, + 'message', + 'Forbidden', + ), + ), + ); + }); + + test('bucketExists() fails due to wrong secret key', () async { + final minio = _getClient(secretKey: 'incorrect-secret-key'); + expect( + () async => await minio.bucketExists(bucketName), + throwsA( + isA<MinioError>().having( + (e) => e.message, + 'message', + 'Forbidden', + ), + ), + ); + }); + }); } /// Initializes an instance of [Minio] with per default valid configuration.