2.0.0-alpha.21

This commit is contained in:
Tobe O 2019-02-03 13:00:42 -05:00
parent b0ecac3232
commit ed989984a2
8 changed files with 91 additions and 19 deletions

View file

@ -1,3 +1,8 @@
# 2.0.0-alpha.21
* Update for `angel_route@3.0.4` compatibility.
* Add `readAsBytes` and `readAsString` to `UploadedFile`.
* URI-decode path components in HTTP2.
# 2.0.0-alpha.20 # 2.0.0-alpha.20
* Inject the `MiddlewarePipeline` into requests. * Inject the `MiddlewarePipeline` into requests.

View file

@ -97,7 +97,7 @@ abstract class Driver<
var path = req.path; var path = req.path;
if (path == '/') path = ''; if (path == '/') path = '';
Tuple4<List, Map<String, dynamic>, ParseResult<Map<String, dynamic>>, Tuple4<List, Map<String, dynamic>, ParseResult<RouteResult>,
MiddlewarePipeline> resolveTuple() { MiddlewarePipeline> resolveTuple() {
Router r = app.optimizedRouter; Router r = app.optimizedRouter;
var resolved = var resolved =
@ -121,7 +121,7 @@ abstract class Driver<
req.container req.container
..registerSingleton<MiddlewarePipeline>(tuple.item4) ..registerSingleton<MiddlewarePipeline>(tuple.item4)
..registerSingleton<ParseResult<Map<String, dynamic>>>(tuple.item3) ..registerSingleton<ParseResult<RouteResult>>(tuple.item3)
..registerSingleton<ParseResult>(tuple.item3); ..registerSingleton<ParseResult>(tuple.item3);
if (!app.isProduction && app.logger != null) { if (!app.isProduction && app.logger != null) {
@ -129,6 +129,11 @@ abstract class Driver<
.registerSingleton<Stopwatch>(new Stopwatch()..start()); .registerSingleton<Stopwatch>(new Stopwatch()..start());
} }
if (tuple.item1.isEmpty) {
print(req.uri);
print('${req.path} => ${tuple.item1}');
}
var pipeline = tuple.item1; var pipeline = tuple.item1;
var it = pipeline.iterator; var it = pipeline.iterator;

View file

@ -3,7 +3,13 @@ library angel_framework.http.request_context;
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io' import 'dart:io'
show Cookie, HeaderValue, HttpHeaders, HttpSession, InternetAddress; show
BytesBuilder,
Cookie,
HeaderValue,
HttpHeaders,
HttpSession,
InternetAddress;
import 'package:angel_container/angel_container.dart'; import 'package:angel_container/angel_container.dart';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
@ -279,4 +285,19 @@ class UploadedFile {
/// [:HttpMultipartFormData:]. This field is used to determine how to decode /// [:HttpMultipartFormData:]. This field is used to determine how to decode
/// the data. Returns [:null:] if not present. /// the data. Returns [:null:] if not present.
HeaderValue get contentTransferEncoding => formData.contentTransferEncoding; HeaderValue get contentTransferEncoding => formData.contentTransferEncoding;
/// Reads the contents of the file into a single linear buffer.
///
/// Note that this leads to holding the whole file in memory, which might
/// not be ideal for large files.w
Future<List<int>> readAsBytes() {
return data
.fold<BytesBuilder>(BytesBuilder(), (bb, out) => bb..add(out))
.then((bb) => bb.takeBytes());
}
/// Reads the contents of the file as [String], using the given [encoding].
Future<String> readAsString({Encoding encoding: utf8}) {
return data.transform(encoding.decoder).join();
}
} }

View file

@ -38,7 +38,7 @@ class Angel extends Routable {
final List<Angel> _children = []; final List<Angel> _children = [];
final Map< final Map<
String, String,
Tuple4<List, Map<String, dynamic>, ParseResult<Map<String, dynamic>>, Tuple4<List, Map<String, dynamic>, ParseResult<RouteResult>,
MiddlewarePipeline>> handlerCache = new HashMap(); MiddlewarePipeline>> handlerCache = new HashMap();
Router _flattened; Router _flattened;

View file

@ -39,26 +39,30 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
.._stream = stream; .._stream = stream;
var headers = req._headers = new MockHttpHeaders(); var headers = req._headers = new MockHttpHeaders();
String scheme = 'https', host = socket.address.address, path = ''; // String scheme = 'https', host = socket.address.address, path = '';
int port = socket.port; var uri =
Uri(scheme: 'https', host: socket.address.address, port: socket.port);
var cookies = <Cookie>[]; var cookies = <Cookie>[];
void finalize() { void finalize() {
req req
.._cookies = new List.unmodifiable(cookies) .._cookies = new List.unmodifiable(cookies)
.._uri = new Uri(scheme: scheme, host: host, port: port, path: path); .._uri = uri;
if (!c.isCompleted) c.complete(req); if (!c.isCompleted) c.complete(req);
} }
void parseHost(String value) { void parseHost(String value) {
var uri = Uri.tryParse(value); var inUri = Uri.tryParse(value);
if (uri == null || uri.scheme == 'localhost') return; if (inUri == null) return;
scheme = uri.hasScheme ? uri.scheme : scheme; // if (uri == null || uri.scheme == 'localhost') return;
if (uri.hasAuthority) { if (inUri.hasScheme) uri = uri.replace(scheme: inUri.scheme);
host = uri.host;
port = uri.hasPort ? uri.port : null; if (inUri.hasAuthority) {
uri = uri.replace(host: inUri.host, userInfo: inUri.userInfo);
} }
if (inUri.hasPort) uri = uri.replace(port: inUri.port);
} }
stream.incomingMessages.listen((msg) { stream.incomingMessages.listen((msg) {
@ -68,19 +72,22 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
} else if (msg is HeadersStreamMessage) { } else if (msg is HeadersStreamMessage) {
for (var header in msg.headers) { for (var header in msg.headers) {
var name = ascii.decode(header.name).toLowerCase(); var name = ascii.decode(header.name).toLowerCase();
var value = ascii.decode(header.value); var value = Uri.decodeComponent(ascii.decode(header.value));
switch (name) { switch (name) {
case ':method': case ':method':
req._method = value; req._method = value;
break; break;
case ':path': case ':path':
path = value.replaceAll(_straySlashes, ''); var inUri = Uri.parse(value);
uri = uri.replace(path: inUri.path);
if (inUri.hasQuery) uri = uri.replace(query: inUri.query);
var path = uri.path.replaceAll(_straySlashes, '');
req._path = path; req._path = path;
if (path.isEmpty) req._path = '/'; if (path.isEmpty) req._path = '/';
break; break;
case ':scheme': case ':scheme':
scheme = value; uri = uri.replace(scheme: value);
break; break;
case ':authority': case ':authority':
parseHost(value); parseHost(value);

View file

@ -1,5 +1,5 @@
name: angel_framework name: angel_framework
version: 2.0.0-alpha.20 version: 2.0.0-alpha.21
description: A high-powered HTTP server with dependency injection, routing and much more. description: A high-powered HTTP server with dependency injection, routing and much more.
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_framework homepage: https://github.com/angel-dart/angel_framework

View file

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:angel_container/mirrors.dart';
import 'package:angel_framework/angel_framework.dart' hide Header; import 'package:angel_framework/angel_framework.dart' hide Header;
import 'package:angel_framework/http2.dart'; import 'package:angel_framework/http2.dart';
import 'package:http/src/multipart_file.dart' as http; import 'package:http/src/multipart_file.dart' as http;
@ -8,6 +9,7 @@ import 'package:http/src/multipart_request.dart' as http;
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:http2/transport.dart'; import 'package:http2/transport.dart';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
import 'package:logging/logging.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'http2_client.dart'; import 'http2_client.dart';
@ -25,7 +27,16 @@ void main() {
Uri serverRoot; Uri serverRoot;
setUp(() async { setUp(() async {
app = new Angel()..encoders['gzip'] = gzip.encoder; app = new Angel(reflector: MirrorsReflector())
..encoders['gzip'] = gzip.encoder;
hierarchicalLoggingEnabled = true;
app.logger = Logger.detached('angel.http2')
..onRecord.listen((rec) {
print(rec);
if (rec.error == null) return;
print(rec.error);
if (rec.stackTrace != null) print(rec.stackTrace);
});
app.get('/', (req, res) async { app.get('/', (req, res) async {
res.write('Hello world'); res.write('Hello world');
@ -77,6 +88,13 @@ void main() {
await res.close(); await res.close();
}); });
app.get('/param/:name', (req, res) => req.params);
app.get('/query', (req, res) {
print('incoming URI: ${req.uri}');
return req.queryParameters;
});
var ctx = new SecurityContext() var ctx = new SecurityContext()
..useCertificateChain('dev.pem') ..useCertificateChain('dev.pem')
..usePrivateKey('dev.key', password: 'dartdart') ..usePrivateKey('dev.key', password: 'dartdart')
@ -121,6 +139,19 @@ void main() {
}); });
}); });
test('query uri decoded', () async {
var uri =
serverRoot.replace(path: '/query', queryParameters: {'foo!': 'bar?'});
var response = await client.get(uri);
print('Sent $uri');
expect(response.body, json.encode({'foo!': 'bar?'}));
});
test('params uri decoded', () async {
var response = await client.get(serverRoot.replace(path: '/param/foo!'));
expect(response.body, json.encode({'name': 'foo!'}));
});
test('method parsed', () async { test('method parsed', () async {
var response = await client.delete(serverRoot.replace(path: '/method')); var response = await client.delete(serverRoot.replace(path: '/method'));
expect(response.body, json.encode('DELETE')); expect(response.body, json.encode('DELETE'));

View file

@ -21,7 +21,10 @@ class Http2Client extends BaseClient {
var headers = <Header>[ var headers = <Header>[
new Header.ascii(':authority', request.url.authority), new Header.ascii(':authority', request.url.authority),
new Header.ascii(':method', request.method), new Header.ascii(':method', request.method),
new Header.ascii(':path', request.url.path), new Header.ascii(
':path',
request.url.path +
(request.url.hasQuery ? ('?' + request.url.query) : '')),
new Header.ascii(':scheme', request.url.scheme), new Header.ascii(':scheme', request.url.scheme),
]; ];