From ed989984a2b1e7bb035aa42a01582c371e8cde20 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Sun, 3 Feb 2019 13:00:42 -0500 Subject: [PATCH] 2.0.0-alpha.21 --- CHANGELOG.md | 5 ++++ lib/src/core/driver.dart | 9 +++++-- lib/src/core/request_context.dart | 23 ++++++++++++++++- lib/src/core/server.dart | 2 +- lib/src/http2/http2_request_context.dart | 31 +++++++++++++--------- pubspec.yaml | 2 +- test/http2/adapter_test.dart | 33 +++++++++++++++++++++++- test/http2/http2_client.dart | 5 +++- 8 files changed, 91 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96ce9aa9..f54e3b1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 * Inject the `MiddlewarePipeline` into requests. diff --git a/lib/src/core/driver.dart b/lib/src/core/driver.dart index e7045aaa..5e43226d 100644 --- a/lib/src/core/driver.dart +++ b/lib/src/core/driver.dart @@ -97,7 +97,7 @@ abstract class Driver< var path = req.path; if (path == '/') path = ''; - Tuple4, ParseResult>, + Tuple4, ParseResult, MiddlewarePipeline> resolveTuple() { Router r = app.optimizedRouter; var resolved = @@ -121,7 +121,7 @@ abstract class Driver< req.container ..registerSingleton(tuple.item4) - ..registerSingleton>>(tuple.item3) + ..registerSingleton>(tuple.item3) ..registerSingleton(tuple.item3); if (!app.isProduction && app.logger != null) { @@ -129,6 +129,11 @@ abstract class Driver< .registerSingleton(new Stopwatch()..start()); } + if (tuple.item1.isEmpty) { + print(req.uri); + print('${req.path} => ${tuple.item1}'); + } + var pipeline = tuple.item1; var it = pipeline.iterator; diff --git a/lib/src/core/request_context.dart b/lib/src/core/request_context.dart index 6ac8f4a8..1f9a81a9 100644 --- a/lib/src/core/request_context.dart +++ b/lib/src/core/request_context.dart @@ -3,7 +3,13 @@ library angel_framework.http.request_context; import 'dart:async'; import 'dart:convert'; 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:http_parser/http_parser.dart'; @@ -279,4 +285,19 @@ class UploadedFile { /// [:HttpMultipartFormData:]. This field is used to determine how to decode /// the data. Returns [:null:] if not present. 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> readAsBytes() { + return data + .fold(BytesBuilder(), (bb, out) => bb..add(out)) + .then((bb) => bb.takeBytes()); + } + + /// Reads the contents of the file as [String], using the given [encoding]. + Future readAsString({Encoding encoding: utf8}) { + return data.transform(encoding.decoder).join(); + } } diff --git a/lib/src/core/server.dart b/lib/src/core/server.dart index ccf20ccb..c404d077 100644 --- a/lib/src/core/server.dart +++ b/lib/src/core/server.dart @@ -38,7 +38,7 @@ class Angel extends Routable { final List _children = []; final Map< String, - Tuple4, ParseResult>, + Tuple4, ParseResult, MiddlewarePipeline>> handlerCache = new HashMap(); Router _flattened; diff --git a/lib/src/http2/http2_request_context.dart b/lib/src/http2/http2_request_context.dart index aa1f8889..4c61286a 100644 --- a/lib/src/http2/http2_request_context.dart +++ b/lib/src/http2/http2_request_context.dart @@ -39,26 +39,30 @@ class Http2RequestContext extends RequestContext { .._stream = stream; var headers = req._headers = new MockHttpHeaders(); - String scheme = 'https', host = socket.address.address, path = ''; - int port = socket.port; + // String scheme = 'https', host = socket.address.address, path = ''; + var uri = + Uri(scheme: 'https', host: socket.address.address, port: socket.port); var cookies = []; void finalize() { req .._cookies = new List.unmodifiable(cookies) - .._uri = new Uri(scheme: scheme, host: host, port: port, path: path); + .._uri = uri; if (!c.isCompleted) c.complete(req); } void parseHost(String value) { - var uri = Uri.tryParse(value); - if (uri == null || uri.scheme == 'localhost') return; - scheme = uri.hasScheme ? uri.scheme : scheme; + var inUri = Uri.tryParse(value); + if (inUri == null) return; + // if (uri == null || uri.scheme == 'localhost') return; - if (uri.hasAuthority) { - host = uri.host; - port = uri.hasPort ? uri.port : null; + if (inUri.hasScheme) uri = uri.replace(scheme: inUri.scheme); + + 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) { @@ -68,19 +72,22 @@ class Http2RequestContext extends RequestContext { } else if (msg is HeadersStreamMessage) { for (var header in msg.headers) { var name = ascii.decode(header.name).toLowerCase(); - var value = ascii.decode(header.value); + var value = Uri.decodeComponent(ascii.decode(header.value)); switch (name) { case ':method': req._method = value; break; 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; if (path.isEmpty) req._path = '/'; break; case ':scheme': - scheme = value; + uri = uri.replace(scheme: value); break; case ':authority': parseHost(value); diff --git a/pubspec.yaml b/pubspec.yaml index 743db819..b3d19163 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ 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. author: Tobe O homepage: https://github.com/angel-dart/angel_framework diff --git a/test/http2/adapter_test.dart b/test/http2/adapter_test.dart index d16b5a09..ea625790 100644 --- a/test/http2/adapter_test.dart +++ b/test/http2/adapter_test.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:angel_container/mirrors.dart'; import 'package:angel_framework/angel_framework.dart' hide Header; import 'package:angel_framework/http2.dart'; 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:http2/transport.dart'; import 'package:http_parser/http_parser.dart'; +import 'package:logging/logging.dart'; import 'package:test/test.dart'; import 'http2_client.dart'; @@ -25,7 +27,16 @@ void main() { Uri serverRoot; 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 { res.write('Hello world'); @@ -77,6 +88,13 @@ void main() { 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() ..useCertificateChain('dev.pem') ..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 { var response = await client.delete(serverRoot.replace(path: '/method')); expect(response.body, json.encode('DELETE')); diff --git a/test/http2/http2_client.dart b/test/http2/http2_client.dart index ebf46bab..8a63e145 100644 --- a/test/http2/http2_client.dart +++ b/test/http2/http2_client.dart @@ -21,7 +21,10 @@ class Http2Client extends BaseClient { var headers =
[ new Header.ascii(':authority', request.url.authority), 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), ];