2.0.0-alpha.21
This commit is contained in:
parent
b0ecac3232
commit
ed989984a2
8 changed files with 91 additions and 19 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'));
|
||||||
|
|
|
@ -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),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue