Add 'packages/cors/' from commit 'b5c0635952fe9d863f6190495ca99b3f5a8ce378'
git-subtree-dir: packages/cors git-subtree-mainline:3a14263a6f
git-subtree-split:b5c0635952
This commit is contained in:
commit
12aa791a8e
11 changed files with 522 additions and 0 deletions
74
packages/cors/.gitignore
vendored
Normal file
74
packages/cors/.gitignore
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# See https://www.dartlang.org/tools/private-files.html
|
||||||
|
|
||||||
|
# Files and directories created by pub
|
||||||
|
.buildlog
|
||||||
|
.packages
|
||||||
|
.project
|
||||||
|
.pub/
|
||||||
|
.scripts-bin/
|
||||||
|
build/
|
||||||
|
**/packages/
|
||||||
|
|
||||||
|
# Files created by dart2js
|
||||||
|
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||||
|
# rules if you intend to use dart2js directly
|
||||||
|
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||||
|
# differentiate from explicit Javascript files)
|
||||||
|
*.dart.js
|
||||||
|
*.part.js
|
||||||
|
*.js.deps
|
||||||
|
*.js.map
|
||||||
|
*.info.json
|
||||||
|
|
||||||
|
# Directory created by dartdoc
|
||||||
|
doc/api/
|
||||||
|
|
||||||
|
# Don't commit pubspec lock file
|
||||||
|
# (Library packages only! Remove pattern if developing an application package)
|
||||||
|
pubspec.lock
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.xml
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
.dart_tool
|
1
packages/cors/.travis.yml
Normal file
1
packages/cors/.travis.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
language: dart
|
2
packages/cors/CHANGELOG.md
Normal file
2
packages/cors/CHANGELOG.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# 2.0.0
|
||||||
|
* Updates for Dart 2 and Angel 2.
|
21
packages/cors/LICENSE
Normal file
21
packages/cors/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 The Angel Framework
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
8
packages/cors/README.md
Normal file
8
packages/cors/README.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# cors
|
||||||
|
[![Pub](https://img.shields.io/pub/v/angel_cors.svg)](https://pub.dartlang.org/packages/angel_cors)
|
||||||
|
[![build status](https://travis-ci.org/angel-dart/cors.svg)](https://travis-ci.org/angel-dart/cors)
|
||||||
|
|
||||||
|
Angel CORS middleware.
|
||||||
|
Port of [the original Express CORS middleware](https://github.com/expressjs/cors).
|
||||||
|
|
||||||
|
For complete example usage, see the [example file](example/example.dart).
|
4
packages/cors/analysis_options.yaml
Normal file
4
packages/cors/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:pedantic/analysis_options.yaml
|
||||||
|
analyzer:
|
||||||
|
strong-mode:
|
||||||
|
implicit-casts: false
|
51
packages/cors/example/example.dart
Normal file
51
packages/cors/example/example.dart
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:angel_cors/angel_cors.dart';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
|
||||||
|
Future configureServer(Angel app) async {
|
||||||
|
// The default options will allow CORS for any request.
|
||||||
|
// Combined with `fallback`, you can enable CORS application-wide.
|
||||||
|
app.fallback(cors());
|
||||||
|
|
||||||
|
// You can also enable CORS for a single route.
|
||||||
|
app.get(
|
||||||
|
'/my_api',
|
||||||
|
chain([
|
||||||
|
cors(),
|
||||||
|
(req, res) {
|
||||||
|
// Request handling logic here...
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Likewise, you can apply CORS to a group.
|
||||||
|
app.chain([cors()]).group('/api', (router) {
|
||||||
|
router.get('/version', (req, res) => 'v0');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Of course, you can configure CORS.
|
||||||
|
// The following is just a subset of the available options;
|
||||||
|
app.fallback(cors(
|
||||||
|
CorsOptions(
|
||||||
|
origin: 'https://pub.dartlang.org', successStatus: 200, // default 204
|
||||||
|
allowedHeaders: ['POST'],
|
||||||
|
preflightContinue: false, // default false
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
// You can specify the origin in different ways:
|
||||||
|
CorsOptions(origin: 'https://pub.dartlang.org');
|
||||||
|
CorsOptions(origin: ['https://example.org', 'http://foo.bar']);
|
||||||
|
CorsOptions(origin: RegExp(r'^foo\.[^$]+'));
|
||||||
|
CorsOptions(origin: (String s) => s.length == 4);
|
||||||
|
|
||||||
|
// Lastly, you can dynamically configure CORS:
|
||||||
|
app.fallback(dynamicCors((req, res) {
|
||||||
|
return CorsOptions(
|
||||||
|
origin: [
|
||||||
|
req.headers.value('origin') ?? 'https://pub.dartlang.org',
|
||||||
|
RegExp(r'\.com$'),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
}
|
100
packages/cors/lib/angel_cors.dart
Normal file
100
packages/cors/lib/angel_cors.dart
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/// Angel CORS middleware.
|
||||||
|
library angel_cors;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'src/cors_options.dart';
|
||||||
|
export 'src/cors_options.dart';
|
||||||
|
|
||||||
|
/// Determines if a request origin is CORS-able.
|
||||||
|
typedef bool _CorsFilter(String origin);
|
||||||
|
|
||||||
|
bool _isOriginAllowed(String origin, [allowedOrigin]) {
|
||||||
|
allowedOrigin ??= [];
|
||||||
|
if (allowedOrigin is Iterable) {
|
||||||
|
return allowedOrigin.any((x) => _isOriginAllowed(origin, x));
|
||||||
|
} else if (allowedOrigin is String) {
|
||||||
|
return origin == allowedOrigin;
|
||||||
|
} else if (allowedOrigin is RegExp) {
|
||||||
|
return origin != null && allowedOrigin.hasMatch(origin);
|
||||||
|
} else if (origin != null && allowedOrigin is _CorsFilter) {
|
||||||
|
return allowedOrigin(origin);
|
||||||
|
} else {
|
||||||
|
return allowedOrigin != false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// On-the-fly configures the [cors] handler. Use this when the context of the surrounding request
|
||||||
|
/// is necessary to decide how to handle an incoming request.
|
||||||
|
Future<bool> Function(RequestContext, ResponseContext) dynamicCors(
|
||||||
|
FutureOr<CorsOptions> Function(RequestContext, ResponseContext) f) {
|
||||||
|
return (req, res) async {
|
||||||
|
var opts = await f(req, res);
|
||||||
|
var handler = cors(opts);
|
||||||
|
return await handler(req, res);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies the given [CorsOptions].
|
||||||
|
Future<bool> Function(RequestContext, ResponseContext) cors(
|
||||||
|
[CorsOptions options]) {
|
||||||
|
options ??= CorsOptions();
|
||||||
|
|
||||||
|
return (req, res) async {
|
||||||
|
// access-control-allow-credentials
|
||||||
|
if (options.credentials == true) {
|
||||||
|
res.headers['access-control-allow-credentials'] = 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
// access-control-allow-headers
|
||||||
|
if (req.method == 'OPTIONS' && options.allowedHeaders.isNotEmpty) {
|
||||||
|
res.headers['access-control-allow-headers'] =
|
||||||
|
options.allowedHeaders.join(',');
|
||||||
|
} else if (req.headers['access-control-request-headers'] != null) {
|
||||||
|
res.headers['access-control-allow-headers'] =
|
||||||
|
req.headers.value('access-control-request-headers');
|
||||||
|
}
|
||||||
|
|
||||||
|
// access-control-expose-headers
|
||||||
|
if (options.exposedHeaders.isNotEmpty) {
|
||||||
|
res.headers['access-control-expose-headers'] =
|
||||||
|
options.exposedHeaders.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// access-control-allow-methods
|
||||||
|
if (req.method == 'OPTIONS' && options.methods.isNotEmpty) {
|
||||||
|
res.headers['access-control-allow-methods'] = options.methods.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// access-control-max-age
|
||||||
|
if (req.method == 'OPTIONS' && options.maxAge != null) {
|
||||||
|
res.headers['access-control-max-age'] = options.maxAge.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// access-control-allow-origin
|
||||||
|
if (options.origin == false || options.origin == '*') {
|
||||||
|
res.headers['access-control-allow-origin'] = '*';
|
||||||
|
} else if (options.origin is String) {
|
||||||
|
res
|
||||||
|
..headers['access-control-allow-origin'] = options.origin as String
|
||||||
|
..headers['vary'] = 'origin';
|
||||||
|
} else {
|
||||||
|
bool isAllowed =
|
||||||
|
_isOriginAllowed(req.headers.value('origin'), options.origin);
|
||||||
|
|
||||||
|
res.headers['access-control-allow-origin'] =
|
||||||
|
isAllowed ? req.headers.value('origin') : false.toString();
|
||||||
|
|
||||||
|
if (isAllowed) {
|
||||||
|
res.headers['vary'] = 'origin';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method != 'OPTIONS') return true;
|
||||||
|
res.statusCode = options.successStatus ?? 204;
|
||||||
|
res.contentLength = 0;
|
||||||
|
await res.close();
|
||||||
|
return options.preflightContinue;
|
||||||
|
};
|
||||||
|
}
|
73
packages/cors/lib/src/cors_options.dart
Normal file
73
packages/cors/lib/src/cors_options.dart
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/// CORS configuration options.
|
||||||
|
///
|
||||||
|
/// The default configuration is the equivalent of:
|
||||||
|
///
|
||||||
|
///```json
|
||||||
|
///{
|
||||||
|
/// "origin": "*",
|
||||||
|
/// "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
||||||
|
/// "preflightContinue": false
|
||||||
|
///}
|
||||||
|
/// ```
|
||||||
|
class CorsOptions {
|
||||||
|
/// Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header.
|
||||||
|
final List<String> allowedHeaders = [];
|
||||||
|
|
||||||
|
/// Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted.
|
||||||
|
final bool credentials;
|
||||||
|
|
||||||
|
/// Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed.
|
||||||
|
final List<String> exposedHeaders = [];
|
||||||
|
|
||||||
|
/// Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted.
|
||||||
|
///
|
||||||
|
/// Default: `null`
|
||||||
|
final int maxAge;
|
||||||
|
|
||||||
|
/// The status code to be sent on successful `OPTIONS` requests, if [preflightContinue] is `false`.
|
||||||
|
final int successStatus;
|
||||||
|
|
||||||
|
/// Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`).
|
||||||
|
///
|
||||||
|
/// Default: `['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE˝']`
|
||||||
|
final List<String> methods = [];
|
||||||
|
|
||||||
|
/// Configures the **Access-Control-Allow-Origin** CORS header.
|
||||||
|
/// Possible values:
|
||||||
|
/// - `Boolean` - set `origin` to `true` to reflect the [request origin](http://tools.ietf.org/html/draft-abarth-origin-09), as defined by `req.header('Origin')`, or set it to `false` to disable CORS.
|
||||||
|
/// - `String` - set `origin` to a specific origin. For example if you set it to `"http://example.com"` only requests from "http://example.com" will be allowed.
|
||||||
|
/// - `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com".
|
||||||
|
/// - `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com".
|
||||||
|
/// - `bool Function(String)` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and returns a [bool].
|
||||||
|
///
|
||||||
|
/// Default: `'*'`
|
||||||
|
final origin;
|
||||||
|
|
||||||
|
/// If `false`, then the [cors] handler will terminate the response after performing its logic.
|
||||||
|
///
|
||||||
|
/// Default: `false`
|
||||||
|
final bool preflightContinue;
|
||||||
|
|
||||||
|
CorsOptions(
|
||||||
|
{Iterable<String> allowedHeaders = const [],
|
||||||
|
this.credentials,
|
||||||
|
this.maxAge,
|
||||||
|
Iterable<String> methods = const [
|
||||||
|
'GET',
|
||||||
|
'HEAD',
|
||||||
|
'PUT',
|
||||||
|
'PATCH',
|
||||||
|
'POST',
|
||||||
|
'DELETE'
|
||||||
|
],
|
||||||
|
this.origin = '*',
|
||||||
|
this.successStatus = 204,
|
||||||
|
this.preflightContinue = false,
|
||||||
|
Iterable<String> exposedHeaders = const []}) {
|
||||||
|
if (allowedHeaders != null) this.allowedHeaders.addAll(allowedHeaders);
|
||||||
|
|
||||||
|
if (methods != null) this.methods.addAll(methods);
|
||||||
|
|
||||||
|
if (exposedHeaders != null) this.exposedHeaders.addAll(exposedHeaders);
|
||||||
|
}
|
||||||
|
}
|
14
packages/cors/pubspec.yaml
Normal file
14
packages/cors/pubspec.yaml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
|
description: Angel CORS middleware. Port of expressjs/cors to the Angel framework.
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.0.0 <3.0.0"
|
||||||
|
homepage: https://github.com/angel-dart/cors.git
|
||||||
|
name: angel_cors
|
||||||
|
version: 2.0.0
|
||||||
|
dependencies:
|
||||||
|
angel_framework: ^2.0.0-alpha
|
||||||
|
dev_dependencies:
|
||||||
|
angel_test: ^2.0.0
|
||||||
|
http: ^0.12.0
|
||||||
|
pedantic: ^1.0.0
|
||||||
|
test: ^1.0.0
|
174
packages/cors/test/cors_test.dart
Normal file
174
packages/cors/test/cors_test.dart
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:angel_framework/http.dart';
|
||||||
|
import 'package:angel_cors/angel_cors.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
main() {
|
||||||
|
Angel app;
|
||||||
|
AngelHttp server;
|
||||||
|
http.Client client;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
app = Angel()
|
||||||
|
..options('/credentials', cors(CorsOptions(credentials: true)))
|
||||||
|
..options('/credentials_d',
|
||||||
|
dynamicCors((req, res) => CorsOptions(credentials: true)))
|
||||||
|
..options(
|
||||||
|
'/headers', cors(CorsOptions(exposedHeaders: ['x-foo', 'x-bar'])))
|
||||||
|
..options('/max_age', cors(CorsOptions(maxAge: 250)))
|
||||||
|
..options('/methods', cors(CorsOptions(methods: ['GET', 'POST'])))
|
||||||
|
..get(
|
||||||
|
'/originl',
|
||||||
|
chain([
|
||||||
|
cors(CorsOptions(
|
||||||
|
origin: ['foo.bar', 'baz.quux'],
|
||||||
|
)),
|
||||||
|
(req, res) => req.headers['origin']
|
||||||
|
]))
|
||||||
|
..get(
|
||||||
|
'/origins',
|
||||||
|
chain([
|
||||||
|
cors(CorsOptions(
|
||||||
|
origin: 'foo.bar',
|
||||||
|
)),
|
||||||
|
(req, res) => req.headers['origin']
|
||||||
|
]))
|
||||||
|
..get(
|
||||||
|
'/originr',
|
||||||
|
chain([
|
||||||
|
cors(CorsOptions(
|
||||||
|
origin: RegExp(r'^foo\.[^x]+$'),
|
||||||
|
)),
|
||||||
|
(req, res) => req.headers['origin']
|
||||||
|
]))
|
||||||
|
..get(
|
||||||
|
'/originp',
|
||||||
|
chain([
|
||||||
|
cors(CorsOptions(
|
||||||
|
origin: (String s) => s.endsWith('.bar'),
|
||||||
|
)),
|
||||||
|
(req, res) => req.headers['origin']
|
||||||
|
]))
|
||||||
|
..options('/status', cors(CorsOptions(successStatus: 418)))
|
||||||
|
..fallback(cors(CorsOptions()))
|
||||||
|
..post('/', (req, res) async {
|
||||||
|
res.write('hello world');
|
||||||
|
})
|
||||||
|
..fallback((req, res) => throw AngelHttpException.notFound());
|
||||||
|
|
||||||
|
server = AngelHttp(app);
|
||||||
|
await server.startServer('127.0.0.1', 0);
|
||||||
|
client = http.Client();
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
await server.close();
|
||||||
|
app = null;
|
||||||
|
client = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('status 204 by default', () async {
|
||||||
|
var rq = http.Request('OPTIONS', server.uri.replace(path: '/max_age'));
|
||||||
|
var response = await client.send(rq).then(http.Response.fromStream);
|
||||||
|
expect(response.statusCode, 204);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('content length 0 by default', () async {
|
||||||
|
var rq = http.Request('OPTIONS', server.uri.replace(path: '/max_age'));
|
||||||
|
var response = await client.send(rq).then(http.Response.fromStream);
|
||||||
|
expect(response.contentLength, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('custom successStatus', () async {
|
||||||
|
var rq = http.Request('OPTIONS', server.uri.replace(path: '/status'));
|
||||||
|
var response = await client.send(rq).then(http.Response.fromStream);
|
||||||
|
expect(response.statusCode, 418);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('max age', () async {
|
||||||
|
var rq = http.Request('OPTIONS', server.uri.replace(path: '/max_age'));
|
||||||
|
var response = await client.send(rq).then(http.Response.fromStream);
|
||||||
|
expect(response.headers['access-control-max-age'], '250');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('methods', () async {
|
||||||
|
var rq = http.Request('OPTIONS', server.uri.replace(path: '/methods'));
|
||||||
|
var response = await client.send(rq).then(http.Response.fromStream);
|
||||||
|
expect(response.headers['access-control-allow-methods'], 'GET,POST');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dynamicCors.credentials', () async {
|
||||||
|
var rq =
|
||||||
|
http.Request('OPTIONS', server.uri.replace(path: '/credentials_d'));
|
||||||
|
var response = await client.send(rq).then(http.Response.fromStream);
|
||||||
|
expect(response.headers['access-control-allow-credentials'], 'true');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('credentials', () async {
|
||||||
|
var rq = http.Request('OPTIONS', server.uri.replace(path: '/credentials'));
|
||||||
|
var response = await client.send(rq).then(http.Response.fromStream);
|
||||||
|
expect(response.headers['access-control-allow-credentials'], 'true');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('exposed headers', () async {
|
||||||
|
var rq = http.Request('OPTIONS', server.uri.replace(path: '/headers'));
|
||||||
|
var response = await client.send(rq).then(http.Response.fromStream);
|
||||||
|
expect(response.headers['access-control-expose-headers'], 'x-foo,x-bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('invalid origin', () async {
|
||||||
|
var response = await client.get(server.uri.replace(path: '/originl'),
|
||||||
|
headers: {'origin': 'foreign'});
|
||||||
|
expect(response.headers['access-control-allow-origin'], 'false');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('list origin', () async {
|
||||||
|
var response = await client.get(server.uri.replace(path: '/originl'),
|
||||||
|
headers: {'origin': 'foo.bar'});
|
||||||
|
expect(response.headers['access-control-allow-origin'], 'foo.bar');
|
||||||
|
expect(response.headers['vary'], 'origin');
|
||||||
|
response = await client.get(server.uri.replace(path: '/originl'),
|
||||||
|
headers: {'origin': 'baz.quux'});
|
||||||
|
expect(response.headers['access-control-allow-origin'], 'baz.quux');
|
||||||
|
expect(response.headers['vary'], 'origin');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('string origin', () async {
|
||||||
|
var response = await client.get(server.uri.replace(path: '/origins'),
|
||||||
|
headers: {'origin': 'foo.bar'});
|
||||||
|
expect(response.headers['access-control-allow-origin'], 'foo.bar');
|
||||||
|
expect(response.headers['vary'], 'origin');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('regex origin', () async {
|
||||||
|
var response = await client.get(server.uri.replace(path: '/originr'),
|
||||||
|
headers: {'origin': 'foo.bar'});
|
||||||
|
expect(response.headers['access-control-allow-origin'], 'foo.bar');
|
||||||
|
expect(response.headers['vary'], 'origin');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('predicate origin', () async {
|
||||||
|
var response = await client.get(server.uri.replace(path: '/originp'),
|
||||||
|
headers: {'origin': 'foo.bar'});
|
||||||
|
expect(response.headers['access-control-allow-origin'], 'foo.bar');
|
||||||
|
expect(response.headers['vary'], 'origin');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('POST works', () async {
|
||||||
|
final response = await client.post(server.uri);
|
||||||
|
expect(response.statusCode, equals(200));
|
||||||
|
print('Response: ${response.body}');
|
||||||
|
print('Headers: ${response.headers}');
|
||||||
|
expect(response.headers['access-control-allow-origin'], equals('*'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mirror headers', () async {
|
||||||
|
final response = await client
|
||||||
|
.post(server.uri, headers: {'access-control-request-headers': 'foo'});
|
||||||
|
expect(response.statusCode, equals(200));
|
||||||
|
print('Response: ${response.body}');
|
||||||
|
print('Headers: ${response.headers}');
|
||||||
|
expect(response.headers['access-control-allow-headers'], equals('foo'));
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue