[common] stop crashing with range requests in the browser (#468)
Remove check that cannot be done in the browser
Also remove invalid header sets for HTTP requests in the browser
Fixes https://github.com/google/googleapis.dart/issues/462
diff --git a/discoveryapis_commons/CHANGELOG.md b/discoveryapis_commons/CHANGELOG.md
index 41bffe1..0db79e2 100644
--- a/discoveryapis_commons/CHANGELOG.md
+++ b/discoveryapis_commons/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.0.4
+
+- Eliminate the invalid header warning in the browser.
+- Fix issue with range requests from the browser.
+ ([#462](https://github.com/google/googleapis.dart/issues/462))
+
## 1.0.3
- Throw a more helpful error message when a resumable upload fails.
diff --git a/discoveryapis_commons/lib/src/api_requester.dart b/discoveryapis_commons/lib/src/api_requester.dart
index 67bc494..444a77f 100644
--- a/discoveryapis_commons/lib/src/api_requester.dart
+++ b/discoveryapis_commons/lib/src/api_requester.dart
@@ -108,14 +108,19 @@
'Content length of response does not match requested range length.',
);
}
- final contentRange = response.headers['content-range'];
- final expected = 'bytes ${downloadRange.start}-${downloadRange.end}/';
- if (contentRange == null || !contentRange.startsWith(expected)) {
- throw client_requests.ApiRequestError(
- 'Attempting partial '
- "download but got invalid 'Content-Range' header "
- '(was: $contentRange, expected: $expected).',
- );
+
+ if (!isWeb) {
+ // TODO(kevmoo) on the web, should check access-control-expose-headers
+ // but this is easy for now
+ final contentRange = response.headers[_contentRangeHeader];
+ final expected = 'bytes ${downloadRange.start}-${downloadRange.end}/';
+ if (contentRange == null || !contentRange.startsWith(expected)) {
+ throw client_requests.ApiRequestError(
+ 'Attempting partial '
+ "download but got invalid '$_contentRangeHeader' header "
+ '(was: $contentRange, expected: $expected).',
+ );
+ }
}
}
@@ -183,12 +188,16 @@
Future<http.StreamedResponse> simpleUpload() {
final bodyStream = uploadMedia!.stream;
- final request = RequestImpl(method, uri, bodyStream);
- request.headers.addAll({
- ..._requestHeaders,
- 'content-type': uploadMedia.contentType,
- 'content-length': '${uploadMedia.length}'
- });
+ final request = RequestImpl(
+ method,
+ uri,
+ stream: bodyStream,
+ headers: {
+ ..._requestHeaders,
+ 'content-type': uploadMedia.contentType,
+ 'content-length': '${uploadMedia.length}'
+ },
+ );
return _httpClient.send(request);
}
@@ -210,13 +219,12 @@
'range': 'bytes=${downloadRange.start}-${downloadRange.end}',
};
- // Filter out headers forbidden in the browser (in calling in browser).
- // If we don't do this, the browser will complain that we're attempting
- // to set a header that we're not allowed to set.
- headers.removeWhere((key, value) => _forbiddenHeaders.contains(key));
-
- final request = RequestImpl(method, uri, bodyController.stream);
- request.headers.addAll(headers);
+ final request = RequestImpl(
+ method,
+ uri,
+ stream: bodyController.stream,
+ headers: headers,
+ );
return _httpClient.send(request);
}
@@ -318,10 +326,4 @@
}
}
-/// List of headers that is forbidden in current execution context.
-///
-/// In a browser context we're not allowed to set `user-agent` and
-/// `content-length` headers.
-const _forbiddenHeaders = bool.fromEnvironment('dart.library.html')
- ? <String>{'user-agent', 'content-length'}
- : <String>{};
+const _contentRangeHeader = 'content-range';
diff --git a/discoveryapis_commons/lib/src/multipart_media_uploader.dart b/discoveryapis_commons/lib/src/multipart_media_uploader.dart
index 91dae1b..248f266 100644
--- a/discoveryapis_commons/lib/src/multipart_media_uploader.dart
+++ b/discoveryapis_commons/lib/src/multipart_media_uploader.dart
@@ -73,8 +73,8 @@
'content-length': '$totalLength'
};
final bodyStream = bodyController.stream;
- final request = RequestImpl(_method, _uri, bodyStream);
- request.headers.addAll(headers);
+ final request =
+ RequestImpl(_method, _uri, stream: bodyStream, headers: headers);
return _httpClient.send(request);
}
}
diff --git a/discoveryapis_commons/lib/src/request_impl.dart b/discoveryapis_commons/lib/src/request_impl.dart
index 9ff7df4..825086c 100644
--- a/discoveryapis_commons/lib/src/request_impl.dart
+++ b/discoveryapis_commons/lib/src/request_impl.dart
@@ -6,11 +6,25 @@
import 'package:http/http.dart' as http;
+import 'utils.dart';
+
class RequestImpl extends http.BaseRequest {
final Stream<List<int>> _stream;
- RequestImpl(super.method, super.url, [Stream<List<int>>? stream])
- : _stream = stream ?? const Stream.empty();
+ RequestImpl(
+ super.method,
+ super.url, {
+ Stream<List<int>>? stream,
+ Map<String, String>? headers,
+ }) : _stream = stream ?? const Stream.empty() {
+ if (headers != null) {
+ for (var entry in headers.entries) {
+ if (!_forbiddenHeaders.contains(entry.key)) {
+ this.headers[entry.key] = entry.value;
+ }
+ }
+ }
+ }
@override
http.ByteStream finalize() {
@@ -18,3 +32,10 @@
return http.ByteStream(_stream);
}
}
+
+/// List of headers that is forbidden in current execution context.
+///
+/// In a browser context we're not allowed to set `user-agent` and
+/// `content-length` headers.
+const _forbiddenHeaders =
+ isWeb ? <String>{'user-agent', 'content-length'} : <String>{};
diff --git a/discoveryapis_commons/lib/src/resumable_media_uploader.dart b/discoveryapis_commons/lib/src/resumable_media_uploader.dart
index e275a50..dd9deb8 100644
--- a/discoveryapis_commons/lib/src/resumable_media_uploader.dart
+++ b/discoveryapis_commons/lib/src/resumable_media_uploader.dart
@@ -139,14 +139,16 @@
final bodyStream =
bytes == null ? const Stream<List<int>>.empty() : Stream.value(bytes);
- final request = RequestImpl(_method, _uri, bodyStream);
- request.headers.addAll({
+ final headers = {
..._requestHeaders,
'content-type': contentTypeJsonUtf8,
'content-length': '$length',
'x-upload-content-type': _uploadMedia.contentType,
'x-upload-content-length': '${_uploadMedia.length}',
- });
+ };
+
+ final request =
+ RequestImpl(_method, _uri, stream: bodyStream, headers: headers);
final response = await _httpClient.send(request);
@@ -248,8 +250,7 @@
};
final stream = Stream.fromIterable(chunk.byteArrays);
- final request = RequestImpl('PUT', uri, stream);
- request.headers.addAll(headers);
+ final request = RequestImpl('PUT', uri, stream: stream, headers: headers);
return _httpClient.send(request);
}
}
diff --git a/discoveryapis_commons/lib/src/utils.dart b/discoveryapis_commons/lib/src/utils.dart
index ab6498a..da1e404 100644
--- a/discoveryapis_commons/lib/src/utils.dart
+++ b/discoveryapis_commons/lib/src/utils.dart
@@ -17,3 +17,5 @@
String escapeVariable(String name) =>
Uri.encodeQueryComponent(name).replaceAll('+', '%20');
+
+const isWeb = bool.fromEnvironment('dart.library.html');
diff --git a/discoveryapis_commons/pubspec.yaml b/discoveryapis_commons/pubspec.yaml
index 8aa6738..739ca71 100644
--- a/discoveryapis_commons/pubspec.yaml
+++ b/discoveryapis_commons/pubspec.yaml
@@ -1,5 +1,5 @@
name: _discoveryapis_commons
-version: 1.0.3
+version: 1.0.4
description: Library for use by client APIs generated from Discovery Documents.
repository: https://github.com/google/googleapis.dart/tree/master/discoveryapis_commons
diff --git a/test_integration/web/drive_demo.dart b/test_integration/web/drive_demo.dart
index bd07ab5..b73620c 100644
--- a/test_integration/web/drive_demo.dart
+++ b/test_integration/web/drive_demo.dart
@@ -72,6 +72,17 @@
);
logToTextArea(jsonEncode(newFile));
+
+ logToTextArea('try to read part of the file back - asking for 11 bytes!');
+
+ // regression check for https://github.com/google/googleapis.dart/issues/462
+
+ final partialRequest = await api.get(
+ newFile.id!,
+ downloadOptions: drive.PartialDownloadOptions(drive.ByteRange(0, 10)),
+ ) as drive.Media;
+
+ logToTextArea('bytes received: ${partialRequest.length}');
} finally {
client.close();
}