blob: e813918e982cb70de5d1b97952a8d4f7593e5431 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:async/async.dart';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as shelf_io;
import "package:stream_channel/stream_channel.dart";
/// The list of headers to ignore when sending back confirmation.
final _ignoreHeaders = <String>[
// Browser headers (Chrome)
'accept',
'accept-language',
'accept-encoding',
'connection',
'origin',
'referer',
// Dart IO headers
'cookie',
'host',
];
/// Creates a server used to test a `http` client.
///
/// On startup the server will bind to `localhost`. Then it will send the url
/// as a string back through the [channel].
///
/// The server has the following explicit endpoints used to test individual
/// functionality.
/// * /error - Will return a 400 status code.
/// * /loop - Which is used to check for max redirects.
/// * /redirect - Which is used to test that a redirect works.
/// * /no-content-length - Which returns a body with no content.
///
/// All other requests will be responded to. This is used to test the
/// individual HTTP methods. The server will return back the following
/// information in a string.
///
/// {
/// method: 'METHOD_NAME',
/// path: 'ENDPOINT_PATH',
/// headers: {
/// KEY VALUE STORE OF INDIVIDUAL HEADERS
/// },
/// body: OPTIONAL
/// }
hybridMain(StreamChannel channel) async {
Uri serverUrl;
var server = await shelf_io.serve((request) async {
if (request.url.path == 'error') return new shelf.Response(400);
if (request.url.path == 'loop') {
var n = int.parse(request.url.query);
return new shelf.Response.found(serverUrl.resolve('/loop?${n + 1}'));
}
if (request.url.path == 'redirect') {
return new shelf.Response.found(serverUrl.resolve('/'));
}
if (request.url.path == 'no-content-length') {
return new shelf.Response.ok(
new Stream.fromIterable([ascii.encode('body')]));
}
var requestBody;
if (request.encoding != null) {
requestBody = await request.readAsString();
} else {
requestBody = await collectBytes(request.read());
}
var content = {
'method': request.method,
'path': request.url.path,
'headers': {}
};
if (requestBody.isNotEmpty) content['body'] = requestBody;
request.headers.forEach((name, value) {
// Ignore headers that are generated by the client
if (_ignoreHeaders.contains(name)) return;
(content['headers'] as Map)[name] = value;
});
var encodingName = request.url.queryParameters['response-encoding'];
var outputEncoding =
encodingName == null ? ascii : Encoding.getByName(encodingName);
return new shelf.Response.ok(jsonEncode(content), headers: {
"content-type": "application/json; charset=${outputEncoding.name}",
// CORS headers for browser testing
'access-control-allow-origin': '*',
'access-control-allow-headers':
'X-Random-Header,X-Other-Header,User-Agent,Content-Type',
'access-control-allow-methods': 'GET, PUT, POST, DELETE, PATCH, HEAD'
});
}, 'localhost', 0);
serverUrl = Uri.parse('http://localhost:${server.port}');
channel.sink.add(serverUrl.toString());
}