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
|
||||
* Inject the `MiddlewarePipeline` into requests.
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ abstract class Driver<
|
|||
var path = req.path;
|
||||
if (path == '/') path = '';
|
||||
|
||||
Tuple4<List, Map<String, dynamic>, ParseResult<Map<String, dynamic>>,
|
||||
Tuple4<List, Map<String, dynamic>, ParseResult<RouteResult>,
|
||||
MiddlewarePipeline> resolveTuple() {
|
||||
Router r = app.optimizedRouter;
|
||||
var resolved =
|
||||
|
@ -121,7 +121,7 @@ abstract class Driver<
|
|||
|
||||
req.container
|
||||
..registerSingleton<MiddlewarePipeline>(tuple.item4)
|
||||
..registerSingleton<ParseResult<Map<String, dynamic>>>(tuple.item3)
|
||||
..registerSingleton<ParseResult<RouteResult>>(tuple.item3)
|
||||
..registerSingleton<ParseResult>(tuple.item3);
|
||||
|
||||
if (!app.isProduction && app.logger != null) {
|
||||
|
@ -129,6 +129,11 @@ abstract class Driver<
|
|||
.registerSingleton<Stopwatch>(new Stopwatch()..start());
|
||||
}
|
||||
|
||||
if (tuple.item1.isEmpty) {
|
||||
print(req.uri);
|
||||
print('${req.path} => ${tuple.item1}');
|
||||
}
|
||||
|
||||
var pipeline = tuple.item1;
|
||||
var it = pipeline.iterator;
|
||||
|
||||
|
|
|
@ -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<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 Map<
|
||||
String,
|
||||
Tuple4<List, Map<String, dynamic>, ParseResult<Map<String, dynamic>>,
|
||||
Tuple4<List, Map<String, dynamic>, ParseResult<RouteResult>,
|
||||
MiddlewarePipeline>> handlerCache = new HashMap();
|
||||
|
||||
Router _flattened;
|
||||
|
|
|
@ -39,26 +39,30 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
.._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 = <Cookie>[];
|
||||
|
||||
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<ServerTransportStream> {
|
|||
} 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);
|
||||
|
|
|
@ -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 <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_framework
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -21,7 +21,10 @@ class Http2Client extends BaseClient {
|
|||
var headers = <Header>[
|
||||
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),
|
||||
];
|
||||
|
||||
|
|
Loading…
Reference in a new issue