blob: 736e86e7e785246012a47b7e2a2e0083b777a416 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.io;
class _HttpBodyHandlerTransformer
extends StreamEventTransformer<HttpRequest, HttpRequestBody> {
void handleData(HttpRequest request, EventSink<HttpRequestBody> sink) {
HttpBodyHandler.processRequest(request)
.then(sink.add, onError: sink.addError);
}
}
class _HttpBodyHandler {
static Future<HttpRequestBody> processRequest(HttpRequest request) {
return process(request, request.headers)
.then((body) => new _HttpRequestBody(request, body),
onError: (error) {
// Try to send BAD_REQUEST response.
request.response.statusCode = HttpStatus.BAD_REQUEST;
request.response.close();
request.response.done.catchError((_) {});
throw error;
});
}
static Future<HttpClientResponseBody> processResponse(
HttpClientResponse response) {
return process(response, response.headers)
.then((body) => new _HttpClientResponseBody(response, body));
}
static Future<HttpBody> process(Stream<List<int>> stream,
HttpHeaders headers) {
ContentType contentType = headers.contentType;
Future<HttpBody> asBinary() {
return stream
.fold(new _BufferList(), (buffer, data) => buffer..add(data))
.then((buffer) => new _HttpBody(contentType,
"binary",
buffer.readBytes()));
}
Future<HttpBody> asText(Encoding defaultEncoding) {
var encoding;
var charset = contentType.charset;
if (charset != null) encoding = Encoding.fromName(charset);
if (encoding == null) encoding = defaultEncoding;
return stream
.transform(new StringDecoder(encoding))
.fold(new StringBuffer(), (buffer, data) => buffer..write(data))
.then((buffer) => new _HttpBody(contentType,
"text",
buffer.toString()));
}
Future<HttpBody> asFormData() {
return stream
.transform(new MimeMultipartTransformer(
contentType.parameters['boundary']))
.map((HttpMultipartFormData.parse))
.map((multipart) {
var future;
if (multipart.isText) {
future = multipart
.fold(new StringBuffer(), (b, s) => b..write(s))
.then((b) => b.toString());
} else {
future = multipart
.fold(new _BufferList(), (b, d) => b..add(d))
.then((b) => b.readBytes());
}
return future.then((data) {
var filename =
multipart.contentDisposition.parameters['filename'];
if (filename != null) {
data = new _HttpBodyFileUpload(multipart.contentType,
filename,
data);
}
return [multipart.contentDisposition.parameters['name'], data];
});
})
.fold([], (l, f) => l..add(f))
.then(Future.wait)
.then((parts) {
Map<String, dynamic> map = new Map<String, dynamic>();
for (var part in parts) {
map[part[0]] = part[1]; // Override existing entries.
}
return new _HttpBody(contentType, 'form', map);
});
}
if (contentType == null) {
return asBinary();
}
switch (contentType.primaryType) {
case "text":
return asText(Encoding.ASCII);
case "application":
switch (contentType.subType) {
case "json":
return asText(Encoding.UTF_8)
.then((body) => new _HttpBody(contentType,
"json",
JSON.parse(body.body)));
case "x-www-form-urlencoded":
return asText(Encoding.ASCII)
.then((body) {
var map = _HttpUtils.splitQueryString(body.body);
var result = {};
String parse(String s) {
return _HttpUtils.decodeHttpEntityString(
_HttpUtils.decodeUrlEncodedString(s));
}
for (var key in map.keys) {
result[parse(key)] = parse(map[key]);
}
return new _HttpBody(contentType, "form", result);
});
default:
break;
}
break;
case "multipart":
switch (contentType.subType) {
case "form-data":
return asFormData();
default:
break;
}
break;
default:
break;
}
return asBinary();
}
}
class _HttpBodyFileUpload implements HttpBodyFileUpload {
final ContentType contentType;
final String filename;
final dynamic content;
_HttpBodyFileUpload(this.contentType, this.filename, this.content);
}
class _HttpBody implements HttpBody {
final ContentType contentType;
final String type;
final dynamic body;
_HttpBody(ContentType this.contentType,
String this.type,
dynamic this.body);
}
class _HttpRequestBody extends _HttpBody implements HttpRequestBody {
final String method;
final Uri uri;
final HttpHeaders headers;
final HttpResponse response;
_HttpRequestBody(HttpRequest request, HttpBody body)
: super(body.contentType, body.type, body.body),
method = request.method,
uri = request.uri,
headers = request.headers,
response = request.response;
}
class _HttpClientResponseBody
extends _HttpBody implements HttpClientResponseBody {
final HttpClientResponse response;
_HttpClientResponseBody(HttpClientResponse response, HttpBody body)
: super(body.contentType, body.type, body.body),
this.response = response;
int get statusCode => response.statusCode;
String get reasonPhrase => response.reasonPhrase;
HttpHeaders get headers => response.headers;
}