Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/storage_client/lib/src/storage_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,25 @@ class SupabaseStorageClient extends StorageBucketApi {
/// 8. 30000 ms +/- 25%
///
/// Anything beyond the 8th try will have 30 second delay.
///
/// [useNewHostname] controls whether legacy storage URLs are rewritten to use
/// the dedicated storage host (`<ref>.storage.supabase.co`). Set to `true`
/// only if your project has the dedicated storage host enabled; otherwise
/// every storage request will fail with an `Invalid Storage request` error.
/// Defaults to `false` (opt-in).
SupabaseStorageClient(
String url,
Map<String, String> headers, {
Client? httpClient,
int retryAttempts = 0,
bool useNewHostname = false,
}) : assert(
retryAttempts >= 0,
'retryAttempts has to be greater than or equal to 0',
),
_defaultRetryAttempts = retryAttempts,
super(
_transformStorageUrl(url),
useNewHostname ? _transformStorageUrl(url) : url,
{...Constants.defaultHeaders, ...headers},
httpClient: httpClient,
) {
Expand Down
150 changes: 110 additions & 40 deletions packages/storage_client/test/basic_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -308,58 +308,128 @@ void main() {
});

group('URL Construction', () {
test('should update legacy prod host to new host', () {
const inputUrl = 'https://blah.supabase.co/storage/v1';
const expectedUrl = 'https://blah.storage.supabase.co/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
group('default behavior (useNewHostname: false)', () {
test('should NOT transform legacy prod host by default', () {
const inputUrl = 'https://blah.supabase.co/storage/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
});
expect(client.url, inputUrl);
});
expect(client.url, expectedUrl);
});

test('should update legacy staging host to new host', () {
const inputUrl = 'https://blah.supabase.red/storage/v1';
const expectedUrl = 'https://blah.storage.supabase.red/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
test('should NOT transform legacy staging host by default', () {
const inputUrl = 'https://blah.supabase.red/storage/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
});
expect(client.url, inputUrl);
});
expect(client.url, expectedUrl);
});

test('should accept new host without modification', () {
const inputUrl = 'https://blah.storage.supabase.co/v1';
const expectedUrl = 'https://blah.storage.supabase.co/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
test('should NOT transform legacy supabase.in host by default', () {
const inputUrl = 'https://blah.supabase.in/storage/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
});
expect(client.url, inputUrl);
});
expect(client.url, expectedUrl);
});

test('should not modify non-platform hosts', () {
const inputUrl = 'https://blah.supabase.co.example.com/storage/v1';
const expectedUrl = 'https://blah.supabase.co.example.com/storage/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
test('should accept new host without modification', () {
const inputUrl = 'https://blah.storage.supabase.co/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
});
expect(client.url, inputUrl);
});
expect(client.url, expectedUrl);
});

test('should support local host with port without modification', () {
const inputUrl = 'http://localhost:1234/storage/v1';
const expectedUrl = 'http://localhost:1234/storage/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
test('should not modify non-platform hosts', () {
const inputUrl = 'https://blah.supabase.co.example.com/storage/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
});
expect(client.url, inputUrl);
});

test('should support local host with port without modification', () {
const inputUrl = 'http://localhost:1234/storage/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
});
expect(client.url, inputUrl);
});
expect(client.url, expectedUrl);
});

test('should update legacy supabase.in host to new host', () {
const inputUrl = 'https://blah.supabase.in/storage/v1';
const expectedUrl = 'https://blah.storage.supabase.in/v1';
client = SupabaseStorageClient(inputUrl, {
'Authorization': 'Bearer $supabaseKey',
group('opt-in behavior (useNewHostname: true)', () {
test('should update legacy prod host to new host', () {
const inputUrl = 'https://blah.supabase.co/storage/v1';
const expectedUrl = 'https://blah.storage.supabase.co/v1';
client = SupabaseStorageClient(
inputUrl,
{
'Authorization': 'Bearer $supabaseKey',
},
useNewHostname: true);
expect(client.url, expectedUrl);
});

test('should update legacy staging host to new host', () {
const inputUrl = 'https://blah.supabase.red/storage/v1';
const expectedUrl = 'https://blah.storage.supabase.red/v1';
client = SupabaseStorageClient(
inputUrl,
{
'Authorization': 'Bearer $supabaseKey',
},
useNewHostname: true);
expect(client.url, expectedUrl);
});

test('should accept new host without modification', () {
const inputUrl = 'https://blah.storage.supabase.co/v1';
const expectedUrl = 'https://blah.storage.supabase.co/v1';
client = SupabaseStorageClient(
inputUrl,
{
'Authorization': 'Bearer $supabaseKey',
},
useNewHostname: true);
expect(client.url, expectedUrl);
});

test('should not modify non-platform hosts', () {
const inputUrl = 'https://blah.supabase.co.example.com/storage/v1';
const expectedUrl = 'https://blah.supabase.co.example.com/storage/v1';
client = SupabaseStorageClient(
inputUrl,
{
'Authorization': 'Bearer $supabaseKey',
},
useNewHostname: true);
expect(client.url, expectedUrl);
});

test('should support local host with port without modification', () {
const inputUrl = 'http://localhost:1234/storage/v1';
const expectedUrl = 'http://localhost:1234/storage/v1';
client = SupabaseStorageClient(
inputUrl,
{
'Authorization': 'Bearer $supabaseKey',
},
useNewHostname: true);
expect(client.url, expectedUrl);
});

test('should update legacy supabase.in host to new host', () {
const inputUrl = 'https://blah.supabase.in/storage/v1';
const expectedUrl = 'https://blah.storage.supabase.in/v1';
client = SupabaseStorageClient(
inputUrl,
{
'Authorization': 'Bearer $supabaseKey',
},
useNewHostname: true);
expect(client.url, expectedUrl);
});
expect(client.url, expectedUrl);
});
});
}
7 changes: 5 additions & 2 deletions packages/supabase/lib/src/supabase_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ class SupabaseClient {
AuthHttpClient(_supabaseKey, httpClient ?? Client(), _getAccessToken);
rest = _initRestClient();
functions = _initFunctionsClient();
storage = _initStorageClient(storageOptions.retryAttempts);
storage = _initStorageClient(
storageOptions.retryAttempts, storageOptions.useNewHostname);
realtime = _initRealtimeClient(options: realtimeClientOptions);
if (accessToken == null) {
_log.config(
Expand Down Expand Up @@ -316,12 +317,14 @@ class SupabaseClient {
);
}

SupabaseStorageClient _initStorageClient(int storageRetryAttempts) {
SupabaseStorageClient _initStorageClient(
int storageRetryAttempts, bool useNewHostname) {
return SupabaseStorageClient(
_storageUrl,
{...headers},
httpClient: _authHttpClient,
retryAttempts: storageRetryAttempts,
useNewHostname: useNewHostname,
);
}

Expand Down
12 changes: 11 additions & 1 deletion packages/supabase/lib/src/supabase_client_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ class AuthClientOptions {
class StorageClientOptions {
final int retryAttempts;

const StorageClientOptions({this.retryAttempts = 0});
/// Whether to rewrite legacy storage URLs to use the dedicated storage host
/// (`<ref>.storage.supabase.co`). Enables uploads larger than 50 GB by
/// bypassing proxy buffering limits.
///
/// Set to `true` only if your project has the dedicated storage host
/// enabled; otherwise every storage request will fail with an
/// `Invalid Storage request` error. Defaults to `false` (opt-in).
final bool useNewHostname;

const StorageClientOptions(
{this.retryAttempts = 0, this.useNewHostname = false});
}

class FunctionsClientOptions {
Expand Down
Loading