diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 6983a17f..33521cff 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,11 +2,10 @@ - - - + - + + @@ -28,44 +27,14 @@ - + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -73,80 +42,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + @@ -161,13 +65,13 @@ - path handleRequest flatten pipeline @@ -197,6 +101,7 @@ app handleRe instead. + io\b _isClosed @@ -246,7 +151,6 @@ @@ -710,14 +615,7 @@ - - - - 1495888785100 - 1496752953708 @@ -1055,7 +953,14 @@ - @@ -1091,7 +996,7 @@ - @@ -1126,7 +1031,7 @@ - + @@ -1150,7 +1055,6 @@ @@ -1184,21 +1089,6 @@ - - - - - - - - - - - - - - - @@ -1329,13 +1219,6 @@ - - - - - - - @@ -1512,6 +1395,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1530,7 +1447,7 @@ - + @@ -1538,58 +1455,60 @@ - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/src/http/angel_http.dart b/lib/src/http/angel_http.dart index 5579d0d1..1b4debbb 100644 --- a/lib/src/http/angel_http.dart +++ b/lib/src/http/angel_http.dart @@ -7,6 +7,7 @@ import 'package:combinator/combinator.dart'; import 'package:json_god/json_god.dart' as god; import 'package:pool/pool.dart'; import 'package:tuple/tuple.dart'; +import 'http_request_context.dart'; import 'request_context.dart'; import 'response_context.dart'; import 'server.dart'; @@ -278,7 +279,7 @@ class AngelHttp { Future createRequestContext(HttpRequest request) { var path = request.uri.path.replaceAll(_straySlashes, ''); if (path.length == 0) path = '/'; - return RequestContext.from(request, app, path).then((req) async { + return HttpRequestContextImpl.from(request, app, path).then((req) async { if (_pool != null) req.inject(PoolResource, await _pool.request()); if (app.injections.isNotEmpty) app.injections.forEach(req.inject); return req; diff --git a/lib/src/http/http_request_context.dart b/lib/src/http/http_request_context.dart new file mode 100644 index 00000000..096a4718 --- /dev/null +++ b/lib/src/http/http_request_context.dart @@ -0,0 +1,139 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:body_parser/body_parser.dart'; +import 'request_context.dart'; +import 'server.dart'; + +/// An implementation of [RequestContext] that wraps a [HttpRequest]. +class HttpRequestContextImpl extends RequestContext { + ContentType _contentType; + HttpRequest _io; + String _override, _path; + + @override + ContentType get contentType { + return _contentType; + } + + @override + List get cookies { + return io.cookies; + } + + @override + HttpHeaders get headers { + return io.headers; + } + + @override + String get hostname { + return io.headers.value(HttpHeaders.HOST); + } + + /// The underlying [HttpRequest] instance underneath this context. + HttpRequest get io => _io; + + @override + String get method { + return _override ?? originalMethod; + } + + @override + String get originalMethod { + return io.method; + } + + @override + String get path { + return _path; + } + + @override + InternetAddress get remoteAddress { + return io.connectionInfo.remoteAddress; + } + + @override + HttpSession get session { + return io.session; + } + + @override + Uri get uri { + return io.uri; + } + + @override + bool get xhr { + return io.headers.value("X-Requested-With")?.trim()?.toLowerCase() == + 'xmlhttprequest'; + } + + /// Magically transforms an [HttpRequest] into a [RequestContext]. + static Future from( + HttpRequest request, Angel app, String path) async { + HttpRequestContextImpl ctx = new HttpRequestContextImpl(); + + String override = request.method; + + if (app.allowMethodOverrides == true) + override = + request.headers.value('x-http-method-override')?.toUpperCase() ?? + request.method; + + ctx.app = app; + ctx._contentType = request.headers.contentType; + ctx._override = override; + + /* + // Faster way to get path + List _path = []; + + // Go up until we reach a ? + for (int ch in request.uri.toString().codeUnits) { + if (ch != $question) + _path.add(ch); + else + break; + } + + // Remove trailing slashes + int lastSlash = -1; + + for (int i = _path.length - 1; i >= 0; i--) { + if (_path[i] == $slash) + lastSlash = i; + else + break; + } + + if (lastSlash > -1) + ctx._path = new String.fromCharCodes(_path.take(lastSlash)); + else + ctx._path = new String.fromCharCodes(_path); + */ + + ctx._path = path; + ctx._io = request; + + if (app.lazyParseBodies != true) { + await ctx.parse(); + } + + return ctx; + } + + @override + Future close() { + _contentType = null; + _io = null; + _override = _path = null; + return super.close(); + } + + @override + Future parseOnce() async { + return await parseBody(io, + storeOriginalBuffer: app.storeOriginalBuffer == true); + } +} diff --git a/lib/src/http/request_context.dart b/lib/src/http/request_context.dart index 41fbe2ca..4cb6d625 100644 --- a/lib/src/http/request_context.dart +++ b/lib/src/http/request_context.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:mirrors'; import 'package:body_parser/body_parser.dart'; +import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'metadata.dart'; import 'response_context.dart'; @@ -12,13 +13,10 @@ import 'server.dart' show Angel; part 'injection.dart'; /// A convenience wrapper around an incoming HTTP request. -class RequestContext { +abstract class RequestContext { String _acceptHeaderCache, _extensionCache; bool _acceptsAllCache; BodyParseResult _body; - ContentType _contentType; - HttpRequest _io; - String _override, _path; Map _provisionalQuery; final Map properties = {}; @@ -30,13 +28,13 @@ class RequestContext { Angel app; /// Any cookies sent with this request. - List get cookies => io.cookies; + List get cookies; /// All HTTP headers sent with this request. - HttpHeaders get headers => io.headers; + HttpHeaders get headers; /// The requested hostname. - String get hostname => io.headers.value(HttpHeaders.HOST); + String get hostname; final Map _injections = {}; Map _injectionsCache; @@ -44,8 +42,9 @@ class RequestContext { /// A [Map] of singletons injected via [inject]. *Read-only*. Map get injections => _injectionsCache ??= new Map.unmodifiable(_injections); - /// The underlying [HttpRequest] instance underneath this context. - HttpRequest get io => _io; + /// This feature does not map to other adapters (i.e. HTTP/2), so it will be removed in a future version. + @deprecated + HttpRequest get io; /// The user's IP. String get ip => remoteAddress.address; @@ -53,10 +52,10 @@ class RequestContext { /// This request's HTTP method. /// /// This may have been processed by an override. See [originalMethod] to get the real method. - String get method => _override ?? originalMethod; + String get method; /// The original HTTP verb sent to the server. - String get originalMethod => io.method; + String get originalMethod; StateError _unparsed(String type, String caps) => new StateError( 'Cannot get the $type of an unparsed request. Use lazy${caps}() instead.'); @@ -75,7 +74,7 @@ class RequestContext { } /// The content type of an incoming request. - ContentType get contentType => _contentType; + ContentType get contentType; /// Any and all files sent to the server with this request. /// @@ -107,7 +106,7 @@ class RequestContext { Map params = {}; /// The requested path. - String get path => _path; + String get path; /// The parsed request query string. /// @@ -123,80 +122,22 @@ class RequestContext { } /// The remote address requesting this resource. - InternetAddress get remoteAddress => io.connectionInfo.remoteAddress; + InternetAddress get remoteAddress; /// The user's HTTP session. - HttpSession get session => io.session; + HttpSession get session; /// The [Uri] instance representing the path this request is responding to. - Uri get uri => io.uri; + Uri get uri; /// Is this an **XMLHttpRequest**? - bool get xhr => - io.headers.value("X-Requested-With")?.trim()?.toLowerCase() == - 'xmlhttprequest'; + bool get xhr; /// Returns the file extension of the requested path, if any. /// /// Includes the leading `.`, if there is one. String get extension => _extensionCache ??= p.extension(uri.path); - /// Magically transforms an [HttpRequest] into a [RequestContext]. - static Future from( - HttpRequest request, Angel app, String path) async { - RequestContext ctx = new RequestContext(); - - String override = request.method; - - if (app.allowMethodOverrides == true) - override = - request.headers.value('x-http-method-override')?.toUpperCase() ?? - request.method; - - ctx.app = app; - ctx._contentType = request.headers.contentType; - ctx._override = override; - - /* - // Faster way to get path - List _path = []; - - // Go up until we reach a ? - for (int ch in request.uri.toString().codeUnits) { - if (ch != $question) - _path.add(ch); - else - break; - } - - // Remove trailing slashes - int lastSlash = -1; - - for (int i = _path.length - 1; i >= 0; i--) { - if (_path[i] == $slash) - lastSlash = i; - else - break; - } - - if (lastSlash > -1) - ctx._path = new String.fromCharCodes(_path.take(lastSlash)); - else - ctx._path = new String.fromCharCodes(_path); - */ - - ctx._path = path; - ctx._io = request; - - if (app.lazyParseBodies != true) { - ctx._body = (await parseBody(request, - storeOriginalBuffer: app.storeOriginalBuffer == true)) ?? - {}; - } - - return ctx; - } - /// Grabs an object by key or type from [params], [_injections], or /// [app].container. Use this to perform dependency injection /// within a service hook. @@ -248,6 +189,7 @@ class RequestContext { ? contentType.mimeType : contentType?.toString(); + // Change to assert if (contentTypeString == null) throw new ArgumentError( 'RequestContext.accepts expects the `contentType` parameter to NOT be null.'); @@ -293,18 +235,18 @@ class RequestContext { return _body; else _provisionalQuery = null; - return _body = await parseBody(io, - storeOriginalBuffer: app.storeOriginalBuffer == true); + return _body = await parseOnce(); } + /// Override this method to one-time parse an incoming request. + @virtual + Future parseOnce(); + /// Disposes of all resources. Future close() async { _body = null; _acceptsAllCache = null; _acceptHeaderCache = null; - _io = null; - _override = _path = null; - _contentType = null; _provisionalQuery?.clear(); properties.clear(); _injections.clear();