2.0.0-alpha.15
This commit is contained in:
parent
d3c2192042
commit
f95de91bf5
15 changed files with 203 additions and 169 deletions
|
@ -1,3 +1,10 @@
|
|||
# 2.0.0-alpha.15
|
||||
* Remove dependency on `body_parser`.
|
||||
* `RequestContext` now exposes a `Stream<List<int>> get body` getter.
|
||||
* Calling `RequestContext.parseBody()` parses its contents.
|
||||
* Added `bodyAsMap`, `bodyAsList`, `bodyAsObject`, and `uploadedFiles` to `RequestContext`.
|
||||
* Removed `Angel.keepRawRequestBuffers` and anything that had to do with buffering request bodies.
|
||||
|
||||
# 2.0.0-alpha.14
|
||||
* Patch `HttpResponseContext._openStream` to send content-length.
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ main() async {
|
|||
|
||||
app.get('/', (req, res) => res.streamFile(indexHtml));
|
||||
|
||||
app.post('/', (req, res) => req.parseBody());
|
||||
app.post('/', (req, res) => req.parseBody().then((_) => req.bodyAsMap));
|
||||
|
||||
var ctx = new SecurityContext()
|
||||
..useCertificateChain('dev.pem')
|
||||
|
|
|
@ -16,7 +16,7 @@ main() async {
|
|||
app.get('/', (req, res) => 'Hello HTTP/2!!!');
|
||||
|
||||
app.fallback((req, res) => throw new AngelHttpException.notFound(
|
||||
message: 'No file exists at ${req.uri.path}'));
|
||||
message: 'No file exists at ${req.uri}'));
|
||||
|
||||
var ctx = new SecurityContext()
|
||||
..useCertificateChain('dev.pem')
|
||||
|
|
|
@ -10,7 +10,7 @@ main() async {
|
|||
var app = new Angel();
|
||||
app.logger = new Logger('angel')..onRecord.listen(prettyLog);
|
||||
|
||||
var publicDir = new Directory('example/public');
|
||||
var publicDir = new Directory('example/http2/public');
|
||||
var indexHtml =
|
||||
const LocalFileSystem().file(publicDir.uri.resolve('index.html'));
|
||||
var styleCss =
|
||||
|
|
|
@ -32,7 +32,8 @@ main() async {
|
|||
|
||||
serverMain(_) async {
|
||||
var app = new Angel();
|
||||
var http = new AngelHttp.custom(app, startShared, useZone: false); // Run a cluster
|
||||
var http =
|
||||
new AngelHttp.custom(app, startShared, useZone: false); // Run a cluster
|
||||
|
||||
app.get('/', (req, res) {
|
||||
return res.serialize({
|
||||
|
|
|
@ -2,12 +2,12 @@ library angel_framework.http.request_context;
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io' show Cookie, HttpHeaders, HttpSession, InternetAddress;
|
||||
import 'dart:io'
|
||||
show Cookie, HeaderValue, HttpHeaders, HttpSession, InternetAddress;
|
||||
|
||||
import 'package:angel_container/angel_container.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:http_server/http_server.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
|
@ -21,11 +21,11 @@ part 'injection.dart';
|
|||
/// A convenience wrapper around an incoming [RawRequest].
|
||||
abstract class RequestContext<RawRequest> {
|
||||
String _acceptHeaderCache, _extensionCache;
|
||||
bool _acceptsAllCache, _hasParsedBody;
|
||||
bool _acceptsAllCache, _hasParsedBody = false;
|
||||
Map<String, dynamic> _bodyFields, _queryParameters;
|
||||
List _bodyList;
|
||||
Object _bodyObject;
|
||||
List<HttpMultipartFormData> _bodyFiles;
|
||||
List<UploadedFile> _uploadedFiles;
|
||||
MediaType _contentType;
|
||||
|
||||
/// The underlying [RawRequest] provided by the driver.
|
||||
|
@ -97,7 +97,7 @@ abstract class RequestContext<RawRequest> {
|
|||
/// Returns a *mutable* [Map] of the fields parsed from the request [body].
|
||||
///
|
||||
/// Note that [parseBody] must be called first.
|
||||
Map<String, dynamic> get bodyFields {
|
||||
Map<String, dynamic> get bodyAsMap {
|
||||
if (!hasParsedBody) {
|
||||
throw new StateError('The request body has not been parsed yet.');
|
||||
} else if (_bodyFields == null) {
|
||||
|
@ -110,7 +110,7 @@ abstract class RequestContext<RawRequest> {
|
|||
/// Returns a *mutable* [List] parsed from the request [body].
|
||||
///
|
||||
/// Note that [parseBody] must be called first.
|
||||
List get bodyList {
|
||||
List get bodyAsList {
|
||||
if (!hasParsedBody) {
|
||||
throw new StateError('The request body has not been parsed yet.');
|
||||
} else if (_bodyList == null) {
|
||||
|
@ -123,7 +123,7 @@ abstract class RequestContext<RawRequest> {
|
|||
/// Returns the parsed request body, whatever it may be (typically a [Map] or [List]).
|
||||
///
|
||||
/// Note that [parseBody] must be called first.
|
||||
Object get bodyObject {
|
||||
Object get bodyAsObject {
|
||||
if (!hasParsedBody) {
|
||||
throw new StateError('The request body has not been parsed yet.');
|
||||
}
|
||||
|
@ -134,12 +134,12 @@ abstract class RequestContext<RawRequest> {
|
|||
/// Returns a *mutable* map of the files parsed from the request [body].
|
||||
///
|
||||
/// Note that [parseBody] must be called first.
|
||||
List<HttpMultipartFormData> get bodyFiles {
|
||||
List<UploadedFile> get uploadedFiles {
|
||||
if (!hasParsedBody) {
|
||||
throw new StateError('The request body has not been parsed yet.');
|
||||
}
|
||||
|
||||
return _bodyFiles;
|
||||
return _uploadedFiles;
|
||||
}
|
||||
|
||||
/// Returns a *mutable* map of the fields contained in the query.
|
||||
|
@ -189,7 +189,7 @@ abstract class RequestContext<RawRequest> {
|
|||
_hasParsedBody = true;
|
||||
|
||||
if (contentType.type == 'application' && contentType.subtype == 'json') {
|
||||
_bodyFiles = [];
|
||||
_uploadedFiles = [];
|
||||
|
||||
var parsed = _bodyObject =
|
||||
await body.transform(encoding.decoder).join().then(json.decode);
|
||||
|
@ -199,6 +199,14 @@ abstract class RequestContext<RawRequest> {
|
|||
} else if (parsed is List) {
|
||||
_bodyList = parsed;
|
||||
}
|
||||
} else if (contentType.type == 'application' &&
|
||||
contentType.subtype == 'x-www-form-urlencoded') {
|
||||
_uploadedFiles = [];
|
||||
var parsed = await body
|
||||
.transform(encoding.decoder)
|
||||
.join()
|
||||
.then((s) => Uri.splitQueryString(s, encoding: encoding));
|
||||
_bodyFields = new Map<String, dynamic>.from(parsed);
|
||||
} else if (contentType.type == 'multipart' &&
|
||||
contentType.subtype == 'form-data' &&
|
||||
contentType.parameters.containsKey('boundary')) {
|
||||
|
@ -207,11 +215,11 @@ abstract class RequestContext<RawRequest> {
|
|||
var parts = body.transform(transformer).map((part) =>
|
||||
HttpMultipartFormData.parse(part, defaultEncoding: encoding));
|
||||
_bodyFields = {};
|
||||
_bodyFiles = [];
|
||||
_uploadedFiles = [];
|
||||
|
||||
await for (var part in parts) {
|
||||
if (part.isBinary) {
|
||||
_bodyFiles.add(part);
|
||||
_uploadedFiles.add(new UploadedFile(part));
|
||||
} else if (part.isText &&
|
||||
part.contentDisposition.parameters.containsKey('name')) {
|
||||
// If there is no name, then don't parse it.
|
||||
|
@ -222,7 +230,7 @@ abstract class RequestContext<RawRequest> {
|
|||
}
|
||||
} else {
|
||||
_bodyFields = {};
|
||||
_bodyFiles = [];
|
||||
_uploadedFiles = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,3 +244,35 @@ abstract class RequestContext<RawRequest> {
|
|||
return new Future.value();
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads information about a binary chunk uploaded to the server.
|
||||
class UploadedFile {
|
||||
/// The underlying `form-data` item.
|
||||
final HttpMultipartFormData formData;
|
||||
|
||||
MediaType _contentType;
|
||||
|
||||
UploadedFile(this.formData);
|
||||
|
||||
/// Returns the binary stream from [formData].
|
||||
Stream<List<int>> get data => formData.cast<List<int>>();
|
||||
|
||||
/// The filename associated with the data on the user's system.
|
||||
/// Returns [:null:] if not present.
|
||||
String get filename => formData.contentDisposition.parameters['filename'];
|
||||
|
||||
/// The name of the field associated with this data.
|
||||
/// Returns [:null:] if not present.
|
||||
String get name => formData.contentDisposition.parameters['name'];
|
||||
|
||||
/// The parsed [:Content-Type:] header of the [:HttpMultipartFormData:].
|
||||
/// Returns [:null:] if not present.
|
||||
MediaType get contentType => _contentType ??= (formData.contentType == null
|
||||
? null
|
||||
: new MediaType.parse(formData.contentType.toString()));
|
||||
|
||||
/// The parsed [:Content-Transfer-Encoding:] header of the
|
||||
/// [:HttpMultipartFormData:]. This field is used to determine how to decode
|
||||
/// the data. Returns [:null:] if not present.
|
||||
HeaderValue get contentTransferEncoding => formData.contentTransferEncoding;
|
||||
}
|
||||
|
|
|
@ -114,11 +114,6 @@ class Angel extends Routable {
|
|||
/// for you.
|
||||
final Map configuration = {};
|
||||
|
||||
/// When set to `true` (default: `false`), the request body will be parsed
|
||||
/// automatically; otherwise, you must call [RequestContext].parseBody() manually,
|
||||
/// or use `lazyBody()`.
|
||||
bool eagerParseRequestBodies = false;
|
||||
|
||||
/// A function that renders views.
|
||||
///
|
||||
/// Called by [ResponseContext]@`render`.
|
||||
|
@ -358,7 +353,6 @@ class Angel extends Routable {
|
|||
Angel(
|
||||
{Reflector reflector: const EmptyReflector(),
|
||||
this.logger,
|
||||
this.eagerParseRequestBodies: false,
|
||||
this.allowMethodOverrides: true,
|
||||
this.serializer,
|
||||
this.viewGenerator})
|
||||
|
|
|
@ -200,13 +200,11 @@ class Service<Id, Data> extends Routable {
|
|||
Middleware indexMiddleware =
|
||||
getAnnotation(service.index, Middleware, app.container.reflector);
|
||||
get('/', (req, res) {
|
||||
return req.parseQuery().then((query) {
|
||||
return this.index(mergeMap([
|
||||
{'query': query},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
return this.index(mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
},
|
||||
middleware: <RequestHandler>[]
|
||||
..addAll(handlers)
|
||||
|
@ -215,20 +213,18 @@ class Service<Id, Data> extends Routable {
|
|||
Middleware createMiddleware =
|
||||
getAnnotation(service.create, Middleware, app.container.reflector);
|
||||
post('/', (req, ResponseContext res) {
|
||||
return req.parseQuery().then((query) {
|
||||
return req.parseBody().then((body) {
|
||||
return this
|
||||
.create(
|
||||
body as Data,
|
||||
mergeMap([
|
||||
{'query': query},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]))
|
||||
.then((r) {
|
||||
res.statusCode = 201;
|
||||
return r;
|
||||
});
|
||||
return req.parseBody().then((_) {
|
||||
return this
|
||||
.create(
|
||||
req.bodyAsMap as Data,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]))
|
||||
.then((r) {
|
||||
res.statusCode = 201;
|
||||
return r;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -241,15 +237,13 @@ class Service<Id, Data> extends Routable {
|
|||
getAnnotation(service.read, Middleware, app.container.reflector);
|
||||
|
||||
get('/:id', (req, res) {
|
||||
return req.parseQuery().then((query) {
|
||||
return this.read(
|
||||
parseId<Id>(req.params['id']),
|
||||
mergeMap([
|
||||
{'query': query},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
return this.read(
|
||||
parseId<Id>(req.params['id']),
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
|
@ -257,20 +251,18 @@ class Service<Id, Data> extends Routable {
|
|||
|
||||
Middleware modifyMiddleware =
|
||||
getAnnotation(service.modify, Middleware, app.container.reflector);
|
||||
patch(
|
||||
'/:id',
|
||||
(req, res) => req.parseBody().then((body) {
|
||||
return req.parseQuery().then((query) {
|
||||
return this.modify(
|
||||
parseId<Id>(req.params['id']),
|
||||
body as Data,
|
||||
mergeMap([
|
||||
{'query': query},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
}),
|
||||
patch('/:id', (req, res) {
|
||||
return req.parseBody().then((_) {
|
||||
return this.modify(
|
||||
parseId<Id>(req.params['id']),
|
||||
req.bodyAsMap as Data,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
|
@ -278,38 +270,34 @@ class Service<Id, Data> extends Routable {
|
|||
|
||||
Middleware updateMiddleware =
|
||||
getAnnotation(service.update, Middleware, app.container.reflector);
|
||||
post(
|
||||
'/:id',
|
||||
(req, res) => req.parseBody().then((body) {
|
||||
return req.parseQuery().then((query) {
|
||||
return this.update(
|
||||
parseId<Id>(req.params['id']),
|
||||
body as Data,
|
||||
mergeMap([
|
||||
{'query': query},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
}),
|
||||
post('/:id', (req, res) {
|
||||
return req.parseBody().then((_) {
|
||||
return this.update(
|
||||
parseId<Id>(req.params['id']),
|
||||
req.bodyAsMap as Data,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
(updateMiddleware == null) ? [] : updateMiddleware.handlers));
|
||||
put(
|
||||
'/:id',
|
||||
(req, res) => req.parseBody().then((body) {
|
||||
return req.parseQuery().then((query) {
|
||||
return this.update(
|
||||
parseId<Id>(req.params['id']),
|
||||
body as Data,
|
||||
mergeMap([
|
||||
{'query': query},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
}),
|
||||
put('/:id', (req, res) {
|
||||
return req.parseBody().then((_) {
|
||||
return this.update(
|
||||
parseId<Id>(req.params['id']),
|
||||
req.bodyAsMap as Data,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
|
@ -318,30 +306,26 @@ class Service<Id, Data> extends Routable {
|
|||
Middleware removeMiddleware =
|
||||
getAnnotation(service.remove, Middleware, app.container.reflector);
|
||||
delete('/', (req, res) {
|
||||
return req.parseQuery().then((query) {
|
||||
return this.remove(
|
||||
null,
|
||||
mergeMap([
|
||||
{'query': query},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
return this.remove(
|
||||
null,
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
..addAll(
|
||||
(removeMiddleware == null) ? [] : removeMiddleware.handlers));
|
||||
delete('/:id', (req, res) {
|
||||
return req.parseQuery().then((query) {
|
||||
return this.remove(
|
||||
parseId<Id>(req.params['id']),
|
||||
mergeMap([
|
||||
{'query': query},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
});
|
||||
return this.remove(
|
||||
parseId<Id>(req.params['id']),
|
||||
mergeMap([
|
||||
{'query': req.queryParameters},
|
||||
restProvider,
|
||||
req.serviceParams
|
||||
]));
|
||||
},
|
||||
middleware: []
|
||||
..addAll(handlers)
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:angel_container/angel_container.dart';
|
||||
import 'package:body_parser/body_parser.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
|
||||
import '../core/core.dart';
|
||||
|
@ -40,6 +39,9 @@ class HttpRequestContext extends RequestContext<HttpRequest> {
|
|||
/// The underlying [HttpRequest] instance underneath this context.
|
||||
HttpRequest get rawRequest => _io;
|
||||
|
||||
@override
|
||||
Stream<List<int>> get body => _io;
|
||||
|
||||
@override
|
||||
String get method {
|
||||
return _override ?? originalMethod;
|
||||
|
@ -120,10 +122,6 @@ class HttpRequestContext extends RequestContext<HttpRequest> {
|
|||
ctx._path = path;
|
||||
ctx._io = request;
|
||||
|
||||
if (app.eagerParseRequestBodies == true) {
|
||||
return ctx.parse().then((_) => ctx);
|
||||
}
|
||||
|
||||
return new Future.value(ctx);
|
||||
}
|
||||
|
||||
|
@ -134,15 +132,4 @@ class HttpRequestContext extends RequestContext<HttpRequest> {
|
|||
_override = _path = null;
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BodyParseResult> parseOnce() {
|
||||
return parseBodyFromStream(
|
||||
rawRequest,
|
||||
rawRequest.headers.contentType != null
|
||||
? new MediaType.parse(rawRequest.headers.contentType.toString())
|
||||
: null,
|
||||
rawRequest.uri,
|
||||
storeOriginalBuffer: app.keepRawRequestBuffers == true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'package:angel_container/src/container.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:body_parser/body_parser.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:http2/transport.dart';
|
||||
import 'package:mock_request/mock_request.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
@ -13,8 +11,8 @@ final RegExp _comma = new RegExp(r',\s*');
|
|||
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
||||
|
||||
class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
||||
final StreamController<List<int>> _body = new StreamController();
|
||||
final Container container;
|
||||
BytesBuilder _buf;
|
||||
List<Cookie> _cookies;
|
||||
HttpHeaders _headers;
|
||||
String _method, _override, _path;
|
||||
|
@ -25,27 +23,48 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
|
||||
Http2RequestContext._(this.container);
|
||||
|
||||
@override
|
||||
Stream<List<int>> get body => _body.stream;
|
||||
|
||||
static Future<Http2RequestContext> from(
|
||||
ServerTransportStream stream,
|
||||
Socket socket,
|
||||
Angel app,
|
||||
Map<String, MockHttpSession> sessions,
|
||||
Uuid uuid) async {
|
||||
var c = new Completer<Http2RequestContext>();
|
||||
var req = new Http2RequestContext._(app.container.createChild())
|
||||
..app = app
|
||||
.._socket = socket
|
||||
.._stream = stream;
|
||||
|
||||
var buf = req._buf = new BytesBuilder();
|
||||
var headers = req._headers = new MockHttpHeaders();
|
||||
String scheme = 'https',
|
||||
authority = '${socket.address.address}:${socket.port}',
|
||||
path = '';
|
||||
String scheme = 'https', host = socket.address.address, path = '';
|
||||
int port = socket.port;
|
||||
var cookies = <Cookie>[];
|
||||
|
||||
await for (var msg in stream.incomingMessages) {
|
||||
void finalize() {
|
||||
req
|
||||
.._cookies = new List.unmodifiable(cookies)
|
||||
.._uri = new Uri(scheme: scheme, host: host, port: port, path: path);
|
||||
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;
|
||||
|
||||
if (uri.hasAuthority) {
|
||||
host = uri.host;
|
||||
port = uri.hasPort ? uri.port : null;
|
||||
}
|
||||
}
|
||||
|
||||
stream.incomingMessages.listen((msg) {
|
||||
if (msg is DataStreamMessage) {
|
||||
buf.add(msg.bytes);
|
||||
if (!c.isCompleted) finalize();
|
||||
req._body.add(msg.bytes);
|
||||
} else if (msg is HeadersStreamMessage) {
|
||||
for (var header in msg.headers) {
|
||||
var name = ascii.decode(header.name).toLowerCase();
|
||||
|
@ -64,7 +83,7 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
scheme = value;
|
||||
break;
|
||||
case ':authority':
|
||||
authority = value;
|
||||
parseHost(value);
|
||||
break;
|
||||
case 'cookie':
|
||||
var cookieStrings = value.split(';').map((s) => s.trim());
|
||||
|
@ -78,18 +97,22 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
headers.add(ascii.decode(header.name), value.split(_comma));
|
||||
var name = ascii.decode(header.name).toLowerCase();
|
||||
|
||||
if (name == 'host') {
|
||||
parseHost(value);
|
||||
}
|
||||
|
||||
headers.add(name, value.split(_comma));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.endStream && !c.isCompleted) finalize();
|
||||
}
|
||||
|
||||
//if (msg.endStream) break;
|
||||
}
|
||||
|
||||
req
|
||||
.._cookies = new List.unmodifiable(cookies)
|
||||
.._uri = Uri.parse('$scheme://$authority').replace(path: path);
|
||||
}, onDone: () {
|
||||
if (!c.isCompleted) finalize();
|
||||
}, cancelOnError: true, onError: c.completeError);
|
||||
|
||||
// Apply session
|
||||
var dartSessId =
|
||||
|
@ -104,7 +127,7 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
() => new MockHttpSession(id: dartSessId.value),
|
||||
);
|
||||
|
||||
return req;
|
||||
return c.future;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -147,19 +170,10 @@ class Http2RequestContext extends RequestContext<ServerTransportStream> {
|
|||
|
||||
@override
|
||||
Future close() {
|
||||
_body.close();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BodyParseResult> parseOnce() {
|
||||
return parseBodyFromStream(
|
||||
new Stream.fromIterable([_buf.takeBytes()]),
|
||||
contentType == null ? null : new MediaType.parse(contentType.toString()),
|
||||
uri,
|
||||
storeOriginalBuffer: app.keepRawRequestBuffers == true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
ServerTransportStream get rawRequest => _stream;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel_framework
|
||||
version: 2.0.0-alpha.14
|
||||
version: 2.0.0-alpha.15
|
||||
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
|
||||
|
|
|
@ -25,9 +25,7 @@ void main() {
|
|||
Uri serverRoot;
|
||||
|
||||
setUp(() async {
|
||||
app = new Angel()
|
||||
..keepRawRequestBuffers = true
|
||||
..encoders['gzip'] = gzip.encoder;
|
||||
app = new Angel()..encoders['gzip'] = gzip.encoder;
|
||||
|
||||
app.get('/', (req, res) async {
|
||||
res.write('Hello world');
|
||||
|
@ -50,13 +48,17 @@ void main() {
|
|||
await res.close();
|
||||
});
|
||||
|
||||
app.post('/body', (req, res) => req.parseBody());
|
||||
app.post('/body', (req, res) => req.parseBody().then((_) => req.bodyAsMap));
|
||||
|
||||
app.post('/upload', (req, res) async {
|
||||
var body = await req.parseBody(), files = await req.parseUploadedFiles();
|
||||
stdout.add(await req.parseRawRequestBuffer());
|
||||
await req.parseBody();
|
||||
var body = req.bodyAsMap, files = req.uploadedFiles;
|
||||
var file = files.firstWhere((f) => f.name == 'file');
|
||||
return [file.data.length, file.mimeType, body];
|
||||
return [
|
||||
await file.data.map((l) => l.length).reduce((a, b) => a + b),
|
||||
file.contentType.mimeType,
|
||||
body
|
||||
];
|
||||
});
|
||||
|
||||
app.get('/push', (req, res) async {
|
||||
|
|
|
@ -38,7 +38,7 @@ class Http2Client extends BaseClient {
|
|||
headers.add(new Header.ascii(k, v));
|
||||
});
|
||||
|
||||
var stream = await connection.makeRequest(headers);
|
||||
var stream = await connection.makeRequest(headers, endStream: body.isEmpty);
|
||||
|
||||
if (body.isNotEmpty) {
|
||||
stream.sendData(body, endStream: true);
|
||||
|
|
|
@ -75,7 +75,10 @@ main() {
|
|||
middleware: [interceptor]);
|
||||
app.get('/hello', (req, res) => 'world');
|
||||
app.get('/name/:first/last/:last', (req, res) => req.params);
|
||||
app.post('/lambda', (RequestContext req, res) => req.parseBody());
|
||||
app.post(
|
||||
'/lambda',
|
||||
(RequestContext req, res) =>
|
||||
req.parseBody().then((_) => req.bodyAsMap));
|
||||
app.mount('/todos/:id', todos);
|
||||
app
|
||||
.get('/greet/:name',
|
||||
|
@ -85,7 +88,7 @@ main() {
|
|||
res.redirectTo('Named routes', {'name': 'tests'});
|
||||
});
|
||||
app.get('/log', (RequestContext req, res) async {
|
||||
print("Query: ${await req.parseQuery()}");
|
||||
print("Query: ${req.queryParameters}");
|
||||
return "Logged";
|
||||
});
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ main() {
|
|||
await client.post("$url/todos",
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
postData = json.encode({'text': 'modified'});
|
||||
|
||||
var response = await client.patch("$url/todos/0",
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
expect(response.statusCode, 200);
|
||||
|
@ -90,6 +91,7 @@ main() {
|
|||
await client.post("$url/todos",
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
postData = json.encode({'over': 'write'});
|
||||
|
||||
var response = await client.post("$url/todos/0",
|
||||
headers: headers as Map<String, String>, body: postData);
|
||||
expect(response.statusCode, 200);
|
||||
|
|
Loading…
Reference in a new issue