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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Future<void> main() async {
final request = Request(
Uri.parse('https://api.example.com/tasks'),
RequestInit(
method: HttpMethod.post,
method: 'POST',
headers: Headers({'content-type': 'application/json; charset=utf-8'}),
body: '{"title":"rewrite ht"}',
),
Expand Down Expand Up @@ -119,7 +119,7 @@ Future<void> main() async {
final body = block.Block(<Object>['hello'], type: 'text/plain');
final request = Request(
Uri.parse('https://example.com'),
RequestInit(method: HttpMethod.post, body: body),
RequestInit(method: 'POST', body: body),
);

print(await request.text()); // hello
Expand Down
2 changes: 1 addition & 1 deletion example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Future<void> main() async {
final request = Request(
Uri.parse('https://api.example.com/tasks'),
RequestInit(
method: HttpMethod.post,
method: 'POST',
headers: Headers({'content-type': 'application/json; charset=utf-8'}),
body: jsonEncode({'title': 'Ship ht', 'priority': 'high'}),
),
Expand Down
3 changes: 2 additions & 1 deletion lib/src/fetch/request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export 'request.native.dart'
RequestCache,
RequestRedirect,
RequestReferrerPolicy,
RequestDuplex;
RequestDuplex,
RequestPriority;
export 'request.native.dart'
if (dart.library.js_interop) 'request.js.dart'
if (dart.library.io) 'request.io.dart'
Expand Down
28 changes: 21 additions & 7 deletions lib/src/fetch/request.io.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:io' as io;
import 'dart:typed_data';

import '../core/http_method.dart';
import 'body.dart';
import 'blob.dart';
import 'form_data.native.dart';
Expand Down Expand Up @@ -122,9 +121,9 @@ class Request implements native.Request {
}

@override
HttpMethod get method {
String get method {
return switch (_host) {
final HttpRequestHost host => HttpMethod.parse(host.value.method),
final HttpRequestHost host => host.value.method,
final NativeRequestHost host => host.value.method,
};
}
Expand All @@ -137,6 +136,14 @@ class Request implements native.Request {
};
}

@override
native.RequestPriority get priority {
return switch (_host) {
final HttpRequestHost _ => native.RequestPriority.auto,
final NativeRequestHost host => host.value.priority,
};
}

@override
native.RequestRedirect get redirect {
return switch (_host) {
Expand Down Expand Up @@ -243,6 +250,7 @@ class Request implements native.Request {
integrity: integrity,
keepalive: keepalive,
duplex: duplex,
priority: priority,
);
}

Expand All @@ -253,7 +261,7 @@ class Request implements native.Request {
HttpRequestHost() => Request(
native.Request(
url,
init(body: method.allowsRequestBody ? body?.clone() : null),
init(body: _allowsRequestBody(method) ? body?.clone() : null),
),
),
};
Expand Down Expand Up @@ -295,6 +303,7 @@ class Request implements native.Request {
integrity: init?.integrity ?? request.integrity,
keepalive: init?.keepalive ?? request.keepalive,
duplex: init?.duplex ?? request.duplex,
priority: init?.priority ?? request.priority,
);
}

Expand All @@ -314,13 +323,18 @@ class Request implements native.Request {
return native.Request(request.url, requestInit(body: init?.body ?? body));
}

static Body? _bodyFromWrappedRequest(Request request, HttpMethod method) {
if (!method.allowsRequestBody &&
static Body? _bodyFromWrappedRequest(Request request, String method) {
if (!_allowsRequestBody(method) &&
request._host is HttpRequestHost &&
!request.method.allowsRequestBody) {
!_allowsRequestBody(request.method)) {
Comment thread
medz marked this conversation as resolved.
return null;
}

return request.body;
}

static bool _allowsRequestBody(String method) {
final upper = method.toUpperCase();
return upper != 'GET' && upper != 'HEAD';
}
}
16 changes: 13 additions & 3 deletions lib/src/fetch/request.js.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import 'package:web/web.dart' as web;

import '../_internal/web_fetch_utils.dart' as web_fetch;
import '../_internal/web_stream_bridge.dart';
import '../core/http_method.dart';
import 'body.dart';
import 'blob.dart';
import 'form_data.native.dart';
Expand Down Expand Up @@ -141,9 +140,9 @@ class Request implements native.Request {
}

@override
HttpMethod get method {
String get method {
return switch (_host) {
final WebRequestHost host => HttpMethod.parse(host.value.method),
final WebRequestHost host => host.value.method,
final NativeRequestHost host => host.value.method,
};
}
Expand All @@ -156,6 +155,14 @@ class Request implements native.Request {
};
}

@override
native.RequestPriority get priority {
return switch (_host) {
final WebRequestHost _ => native.RequestPriority.auto,
final NativeRequestHost host => host.value.priority,
};
}

@override
native.RequestRedirect get redirect {
return switch (_host) {
Expand Down Expand Up @@ -286,6 +293,7 @@ class Request implements native.Request {
integrity: integrity,
keepalive: keepalive,
duplex: duplex,
priority: priority,
);
}

Expand Down Expand Up @@ -332,6 +340,7 @@ class Request implements native.Request {
integrity: init?.integrity ?? request.integrity,
keepalive: init?.keepalive ?? request.keepalive,
duplex: init?.duplex ?? request.duplex,
priority: init?.priority ?? request.priority,
);
}

Expand Down Expand Up @@ -374,6 +383,7 @@ class Request implements native.Request {
integrity: init?.integrity ?? wrapped.integrity,
keepalive: init?.keepalive ?? wrapped.keepalive,
duplex: init?.duplex ?? wrapped.duplex,
priority: init?.priority ?? wrapped.priority,
),
);
}
Expand Down
63 changes: 53 additions & 10 deletions lib/src/fetch/request.native.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:typed_data';

import '../core/http_method.dart';
import 'body.dart';
import 'blob.dart';
import 'form_data.native.dart';
Expand Down Expand Up @@ -73,6 +72,16 @@ enum RequestDuplex {
final String value;
}

enum RequestPriority {
auto('auto'),
high('high'),
low('low');

const RequestPriority(this.value);

final String value;
}

sealed class _RequestInput {
const _RequestInput();
}
Expand Down Expand Up @@ -111,9 +120,10 @@ class RequestInit {
this.integrity,
this.keepalive,
this.duplex,
this.priority,
});

final HttpMethod? method;
final String? method;
final HeadersInit? headers;
final BodyInit? body;
final String? referrer;
Expand All @@ -125,6 +135,7 @@ class RequestInit {
final String? integrity;
final bool? keepalive;
final RequestDuplex? duplex;
final RequestPriority? priority;
}

/// Native request contract shell aligned with the MDN `Request` surface.
Expand All @@ -142,6 +153,7 @@ class Request {
isHistoryNavigation = _isHistoryNavigationFromInput(input),
keepalive = _keepaliveFromInput(input, init?.keepalive),
mode = _modeFromInput(input, init?.mode),
priority = _priorityFromInput(input, init?.priority),
redirect = _redirectFromInput(input, init?.redirect),
referrer = _referrerFromInput(input, init?.referrer),
referrerPolicy = _referrerPolicyFromInput(input, init?.referrerPolicy),
Expand All @@ -167,8 +179,9 @@ class Request {
final String integrity;
final bool isHistoryNavigation;
final bool keepalive;
final HttpMethod method;
final String method;
final RequestMode mode;
final RequestPriority priority;
final RequestRedirect redirect;
final String referrer;
final RequestReferrerPolicy? referrerPolicy;
Expand Down Expand Up @@ -242,6 +255,7 @@ class Request {
integrity: integrity,
keepalive: keepalive,
duplex: duplex,
priority: priority,
),
);
}
Expand All @@ -257,7 +271,7 @@ class Request {
static Body? _bodyFromInput(
_RequestInput input,
BodyInit? init,
HttpMethod method,
String method,
) {
if (init != null) {
_validateRequestBodyMethod(method);
Expand Down Expand Up @@ -343,26 +357,44 @@ class Request {
};
}

static HttpMethod _methodFromInput(_RequestInput input, HttpMethod? init) {
if (init != null) return init;
static String _methodFromInput(_RequestInput input, String? init) {
if (init != null) return _normalizeMethod(init);
return switch (input) {
_RequestRequestInput(:final value) => value.method,
_ => HttpMethod.get,
_ => 'GET',
};
}

static void _validateRequestBodyMethod(HttpMethod method) {
if (method.allowsRequestBody) {
static void _validateRequestBodyMethod(String method) {
if (method != 'GET' && method != 'HEAD') {
return;
}

throw ArgumentError.value(
method,
'method',
'${method.value} requests cannot have a body.',
'$method requests cannot have a body.',
);
}

static String _normalizeMethod(String method) {
if (!_methodPattern.hasMatch(method)) {
throw ArgumentError.value(method, 'method', 'Invalid HTTP method');
}

final upper = method.toUpperCase();
if (upper == 'CONNECT' || upper == 'TRACE' || upper == 'TRACK') {
throw ArgumentError.value(method, 'method', 'Forbidden HTTP method');
}

return switch (upper) {
'DELETE' || 'GET' || 'HEAD' || 'OPTIONS' || 'POST' || 'PUT' => upper,
_ => method,
};
}

static final _methodPattern = RegExp(r"^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$");

static RequestMode _modeFromInput(_RequestInput input, RequestMode? init) {
if (init != null) return init;
return switch (input) {
Expand All @@ -382,6 +414,17 @@ class Request {
};
}

static RequestPriority _priorityFromInput(
_RequestInput input,
RequestPriority? init,
) {
if (init != null) return init;
return switch (input) {
_RequestRequestInput(:final value) => value.priority,
_ => RequestPriority.auto,
};
}

static String _referrerFromInput(_RequestInput input, String? init) {
if (init != null) return init;
return switch (input) {
Expand Down
13 changes: 9 additions & 4 deletions test/public_api_surface_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import 'package:test/test.dart';

void main() {
test('public API symbols are importable and usable', () async {
const method = HttpMethod.post;
const method = 'POST';
const protocolMethod = HttpMethod.post;
const status = HttpStatus.ok;
const version = HttpVersion.http11;
final mime = MimeType.json;
final requestInit = RequestInit(method: method);
final requestInit = RequestInit(
method: method,
priority: RequestPriority.high,
);
final responseInit = ResponseInit(status: status);

final headers = Headers({'content-type': mime.toString()});
Expand All @@ -28,13 +32,14 @@ void main() {

final Object init = 'x';

expect(method.toString(), 'POST');
expect(protocolMethod.toString(), 'POST');
expect(version.value, 'HTTP/1.1');
expect(mime.essence, 'application/json');
expect(params.get('a'), '1');
expect(await blob.text(), 'hello');
expect(file.name, 'hello.txt');
expect(requestInit.method, HttpMethod.post);
expect(requestInit.method, 'POST');
expect(requestInit.priority, RequestPriority.high);
expect(responseInit.status, 200);
expect(request.headers.has('content-type'), isTrue);
expect(await multipart.bytes(), isNotEmpty);
Expand Down
Loading