Skip to content

Commit 0eb481b

Browse files
authored
Make package:http/http.dart support all platforms (#198)
Fixes #22 Adds config specific imports to make `package:http/http.dart` support all platforms, and allow the `Client` factory constructor to return a valid `Client` for the web platform. This should eliminate almost all need for the platform specific imports for consumers, although it does also add the `io_client.dart` public import. Passes presubmit internally, with edits in only 3 files (they use the IoClient constructor directly, and pass in an HttpClient). Externally build_runner now supports config specific imports as well, I am currently working on validating everything works as expected there with this change. ### New Features * The regular `Client` factory constructor is now usable anywhere that `dart:io` or `dart:html` are available, and will give you an `IoClient` or `BrowserClient` respectively. * The `package:http/http.dart` import is now safe to use on the web (or anywhere that either `dart:io` or `dart:html` are available). ### Breaking Changes * In order to use or reference the `IoClient` directly, you will need to import the new `package:http/io_client.dart` import. This is typically only necessary if you are passing a custom `HttpClient` instance to the constructor, in which case you are already giving up support for web.
1 parent 21e02c1 commit 0eb481b

13 files changed

+204
-123
lines changed

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
## 0.12.0-dev
2+
3+
### New Features
4+
5+
* The regular `Client` factory constructor is now usable anywhere that `dart:io`
6+
or `dart:html` are available, and will give you an `IoClient` or
7+
`BrowserClient` respectively.
8+
* The `package:http/http.dart` import is now safe to use on the web (or
9+
anywhere that either `dart:io` or `dart:html` are available).
10+
11+
### Breaking Changes
12+
13+
* In order to use or reference the `IoClient` directly, you will need to import
14+
the new `package:http/io_client.dart` import. This is typically only necessary
15+
if you are passing a custom `HttpClient` instance to the constructor, in which
16+
case you are already giving up support for web.
17+
118
## 0.11.3+17
219

320
* Use new Dart 2 constant names. This branch is only for allowing existing

lib/browser_client.dart

+1-104
Original file line numberDiff line numberDiff line change
@@ -2,107 +2,4 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'dart:async';
6-
import 'dart:html';
7-
import 'dart:typed_data';
8-
9-
import 'src/base_client.dart';
10-
import 'src/base_request.dart';
11-
import 'src/byte_stream.dart';
12-
import 'src/exception.dart';
13-
import 'src/streamed_response.dart';
14-
15-
// TODO(nweiz): Move this under src/, re-export from lib/http.dart, and use this
16-
// automatically from [new Client] once sdk#24581 is fixed.
17-
18-
/// A `dart:html`-based HTTP client that runs in the browser and is backed by
19-
/// XMLHttpRequests.
20-
///
21-
/// This client inherits some of the limitations of XMLHttpRequest. It ignores
22-
/// the [BaseRequest.contentLength], [BaseRequest.persistentConnection],
23-
/// [BaseRequest.followRedirects], and [BaseRequest.maxRedirects] fields. It is
24-
/// also unable to stream requests or responses; a request will only be sent and
25-
/// a response will only be returned once all the data is available.
26-
class BrowserClient extends BaseClient {
27-
/// The currently active XHRs.
28-
///
29-
/// These are aborted if the client is closed.
30-
final _xhrs = new Set<HttpRequest>();
31-
32-
/// Creates a new HTTP client.
33-
BrowserClient();
34-
35-
/// Whether to send credentials such as cookies or authorization headers for
36-
/// cross-site requests.
37-
///
38-
/// Defaults to `false`.
39-
bool withCredentials = false;
40-
41-
/// Sends an HTTP request and asynchronously returns the response.
42-
Future<StreamedResponse> send(BaseRequest request) async {
43-
var bytes = await request.finalize().toBytes();
44-
var xhr = new HttpRequest();
45-
_xhrs.add(xhr);
46-
_openHttpRequest(xhr, request.method, request.url.toString(), asynch: true);
47-
xhr.responseType = 'blob';
48-
xhr.withCredentials = withCredentials;
49-
request.headers.forEach(xhr.setRequestHeader);
50-
51-
var completer = new Completer<StreamedResponse>();
52-
xhr.onLoad.first.then((_) {
53-
// TODO(nweiz): Set the response type to "arraybuffer" when issue 18542
54-
// is fixed.
55-
var blob = xhr.response == null ? new Blob([]) : xhr.response;
56-
var reader = new FileReader();
57-
58-
reader.onLoad.first.then((_) {
59-
var body = reader.result as Uint8List;
60-
completer.complete(new StreamedResponse(
61-
new ByteStream.fromBytes(body), xhr.status,
62-
contentLength: body.length,
63-
request: request,
64-
headers: xhr.responseHeaders,
65-
reasonPhrase: xhr.statusText));
66-
});
67-
68-
reader.onError.first.then((error) {
69-
completer.completeError(
70-
new ClientException(error.toString(), request.url),
71-
StackTrace.current);
72-
});
73-
74-
reader.readAsArrayBuffer(blob);
75-
});
76-
77-
xhr.onError.first.then((_) {
78-
// Unfortunately, the underlying XMLHttpRequest API doesn't expose any
79-
// specific information about the error itself.
80-
completer.completeError(
81-
new ClientException("XMLHttpRequest error.", request.url),
82-
StackTrace.current);
83-
});
84-
85-
xhr.send(bytes);
86-
87-
try {
88-
return await completer.future;
89-
} finally {
90-
_xhrs.remove(xhr);
91-
}
92-
}
93-
94-
// TODO(nweiz): Remove this when sdk#24637 is fixed.
95-
void _openHttpRequest(HttpRequest request, String method, String url,
96-
{bool asynch, String user, String password}) {
97-
request.open(method, url, async: asynch, user: user, password: password);
98-
}
99-
100-
/// Closes the client.
101-
///
102-
/// This terminates all active requests.
103-
void close() {
104-
for (var xhr in _xhrs) {
105-
xhr.abort();
106-
}
107-
}
108-
}
5+
export 'src/browser_client.dart' show BrowserClient;

lib/http.dart

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export 'src/base_response.dart';
1616
export 'src/byte_stream.dart';
1717
export 'src/client.dart';
1818
export 'src/exception.dart';
19-
export 'src/io_client.dart';
2019
export 'src/multipart_file.dart';
2120
export 'src/multipart_request.dart';
2221
export 'src/request.dart';

lib/io_client.dart

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
export 'src/io_client.dart' show IOClient;

lib/src/browser_client.dart

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:html';
7+
import 'dart:typed_data';
8+
9+
import 'base_client.dart';
10+
import 'base_request.dart';
11+
import 'byte_stream.dart';
12+
import 'exception.dart';
13+
import 'streamed_response.dart';
14+
15+
/// Used from conditional imports, matches the definition in `client_stub.dart`.
16+
BaseClient createClient() => BrowserClient();
17+
18+
/// A `dart:html`-based HTTP client that runs in the browser and is backed by
19+
/// XMLHttpRequests.
20+
///
21+
/// This client inherits some of the limitations of XMLHttpRequest. It ignores
22+
/// the [BaseRequest.contentLength], [BaseRequest.persistentConnection],
23+
/// [BaseRequest.followRedirects], and [BaseRequest.maxRedirects] fields. It is
24+
/// also unable to stream requests or responses; a request will only be sent and
25+
/// a response will only be returned once all the data is available.
26+
class BrowserClient extends BaseClient {
27+
/// The currently active XHRs.
28+
///
29+
/// These are aborted if the client is closed.
30+
final _xhrs = new Set<HttpRequest>();
31+
32+
/// Creates a new HTTP client.
33+
BrowserClient();
34+
35+
/// Whether to send credentials such as cookies or authorization headers for
36+
/// cross-site requests.
37+
///
38+
/// Defaults to `false`.
39+
bool withCredentials = false;
40+
41+
/// Sends an HTTP request and asynchronously returns the response.
42+
Future<StreamedResponse> send(BaseRequest request) async {
43+
var bytes = await request.finalize().toBytes();
44+
var xhr = new HttpRequest();
45+
_xhrs.add(xhr);
46+
_openHttpRequest(xhr, request.method, request.url.toString(), asynch: true);
47+
xhr.responseType = 'blob';
48+
xhr.withCredentials = withCredentials;
49+
request.headers.forEach(xhr.setRequestHeader);
50+
51+
var completer = new Completer<StreamedResponse>();
52+
xhr.onLoad.first.then((_) {
53+
// TODO(nweiz): Set the response type to "arraybuffer" when issue 18542
54+
// is fixed.
55+
var blob = xhr.response == null ? new Blob([]) : xhr.response;
56+
var reader = new FileReader();
57+
58+
reader.onLoad.first.then((_) {
59+
var body = reader.result as Uint8List;
60+
completer.complete(new StreamedResponse(
61+
new ByteStream.fromBytes(body), xhr.status,
62+
contentLength: body.length,
63+
request: request,
64+
headers: xhr.responseHeaders,
65+
reasonPhrase: xhr.statusText));
66+
});
67+
68+
reader.onError.first.then((error) {
69+
completer.completeError(
70+
new ClientException(error.toString(), request.url),
71+
StackTrace.current);
72+
});
73+
74+
reader.readAsArrayBuffer(blob);
75+
});
76+
77+
xhr.onError.first.then((_) {
78+
// Unfortunately, the underlying XMLHttpRequest API doesn't expose any
79+
// specific information about the error itself.
80+
completer.completeError(
81+
new ClientException("XMLHttpRequest error.", request.url),
82+
StackTrace.current);
83+
});
84+
85+
xhr.send(bytes);
86+
87+
try {
88+
return await completer.future;
89+
} finally {
90+
_xhrs.remove(xhr);
91+
}
92+
}
93+
94+
// TODO(nweiz): Remove this when sdk#24637 is fixed.
95+
void _openHttpRequest(HttpRequest request, String method, String url,
96+
{bool asynch, String user, String password}) {
97+
request.open(method, url, async: asynch, user: user, password: password);
98+
}
99+
100+
/// Closes the client.
101+
///
102+
/// This terminates all active requests.
103+
void close() {
104+
for (var xhr in _xhrs) {
105+
xhr.abort();
106+
}
107+
}
108+
}

lib/src/client.dart

+10-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import 'dart:typed_data';
88

99
import 'base_client.dart';
1010
import 'base_request.dart';
11-
import 'io_client.dart';
11+
// ignore: uri_does_not_exist
12+
import 'client_stub.dart'
13+
// ignore: uri_does_not_exist
14+
if (dart.library.html) 'browser_client.dart'
15+
// ignore: uri_does_not_exist
16+
if (dart.library.io) 'io_client.dart';
1217
import 'response.dart';
1318
import 'streamed_response.dart';
1419

@@ -24,10 +29,10 @@ import 'streamed_response.dart';
2429
abstract class Client {
2530
/// Creates a new client.
2631
///
27-
/// Currently this will create an [IOClient] if `dart:io` is available and
28-
/// throw an [UnsupportedError] otherwise. In the future, it will create a
29-
/// [BrowserClient] if `dart:html` is available.
30-
factory Client() => new IOClient();
32+
/// Currently this will create an `IOClient` if `dart:io` is available and
33+
/// a `BrowserClient` if `dart:html` is available, otherwise it will throw
34+
/// an unsupported error.
35+
factory Client() => createClient();
3136

3237
/// Sends an HTTP HEAD request with the given headers to the given URL, which
3338
/// can be a [Uri] or a [String].

lib/src/client_stub.dart

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'base_client.dart';
6+
7+
/// Implemented in `browser_client.dart` and `io_client.dart`.
8+
BaseClient createClient() => throw UnsupportedError(
9+
'Cannot create a client without dart:html or dart:io.');

lib/src/io_client.dart

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import 'base_request.dart';
1212
import 'exception.dart';
1313
import 'streamed_response.dart';
1414

15+
/// Used from conditional imports, matches the definition in `client_stub.dart`.
16+
BaseClient createClient() => IOClient();
17+
1518
/// A `dart:io`-based HTTP client.
1619
///
1720
/// This is the default client when running on the command line.

lib/src/multipart_file.dart

+8-11
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44

55
import 'dart:async';
66
import 'dart:convert';
7-
import 'dart:io';
87

9-
import 'package:async/async.dart';
108
import 'package:http_parser/http_parser.dart';
11-
import 'package:path/path.dart' as path;
129

1310
import 'byte_stream.dart';
1411
import 'utils.dart';
1512

13+
// ignore: uri_does_not_exist
14+
import 'multipart_file_stub.dart'
15+
// ignore: uri_does_not_exist
16+
if (dart.library.io) 'multipart_file_io.dart';
17+
1618
/// A file to be uploaded as part of a [MultipartRequest]. This doesn't need to
1719
/// correspond to a physical file.
1820
class MultipartFile {
@@ -87,14 +89,9 @@ class MultipartFile {
8789
/// Throws an [UnsupportedError] if `dart:io` isn't supported in this
8890
/// environment.
8991
static Future<MultipartFile> fromPath(String field, String filePath,
90-
{String filename, MediaType contentType}) async {
91-
if (filename == null) filename = path.basename(filePath);
92-
var file = new File(filePath);
93-
var length = await file.length();
94-
var stream = new ByteStream(DelegatingStream.typed(file.openRead()));
95-
return new MultipartFile(field, stream, length,
96-
filename: filename, contentType: contentType);
97-
}
92+
{String filename, MediaType contentType}) =>
93+
multipartFileFromPath(field, filePath,
94+
filename: filename, contentType: contentType);
9895

9996
// Finalizes the file in preparation for it being sent as part of a
10097
// [MultipartRequest]. This returns a [ByteStream] that should emit the body

lib/src/multipart_file_io.dart

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:io';
7+
8+
import 'package:async/async.dart';
9+
import 'package:http_parser/http_parser.dart';
10+
import 'package:path/path.dart' as p;
11+
12+
import 'byte_stream.dart';
13+
import 'multipart_file.dart';
14+
15+
Future<MultipartFile> multipartFileFromPath(String field, String filePath,
16+
{String filename, MediaType contentType}) async {
17+
if (filename == null) filename = p.basename(filePath);
18+
var file = new File(filePath);
19+
var length = await file.length();
20+
var stream = new ByteStream(DelegatingStream.typed(file.openRead()));
21+
return new MultipartFile(field, stream, length,
22+
filename: filename, contentType: contentType);
23+
}

lib/src/multipart_file_stub.dart

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:http_parser/http_parser.dart';
8+
9+
import 'multipart_file.dart';
10+
11+
Future<MultipartFile> multipartFileFromPath(String field, String filePath,
12+
{String filename, MediaType contentType}) =>
13+
throw UnsupportedError(
14+
'MultipartFile is only supported where dart:io is available.');

pubspec.yaml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: http
2-
version: 0.11.4-dev
2+
version: 0.12.0-dev
33
author: "Dart Team <[email protected]>"
44
homepage: https://github.com/dart-lang/http
55
description: A composable, Future-based API for making HTTP requests.
@@ -14,3 +14,6 @@ dependencies:
1414

1515
dev_dependencies:
1616
test: ^1.3.0
17+
18+
dependency_overrides:
19+
package_resolver: 1.0.4

0 commit comments

Comments
 (0)