Skip to content

Commit 3bf3bbd

Browse files
authored
#94 UTF-8 as default charset for response body decoding (#95)
* UTF-8 as default charset for response body decoding * make encodingForCharset private add opportunity to configure default encoding * add tests * refactor
1 parent 10c9583 commit 3bf3bbd

File tree

3 files changed

+92
-3
lines changed

3 files changed

+92
-3
lines changed

lib/src/client/dart_http.dart

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,57 @@
1+
import 'dart:convert';
2+
13
import 'package:http/http.dart';
4+
import 'package:http_parser/http_parser.dart';
25
import 'package:json_api/http.dart';
36

47
/// A handler using the Dart's built-in http client
58
class DartHttp implements HttpHandler {
6-
DartHttp(this._client);
9+
DartHttp(this._client, [this._defaultEncoding = utf8])
10+
: assert(_defaultEncoding != null, "Default encoding can't be null");
711

812
@override
913
Future<HttpResponse> call(HttpRequest request) async {
1014
final response = await _send(Request(request.method, request.uri)
1115
..headers.addAll(request.headers)
1216
..body = request.body);
13-
return HttpResponse(response.statusCode,
14-
body: response.body, headers: response.headers);
17+
final responseBody =
18+
_encodingForHeaders(response.headers).decode(response.bodyBytes);
19+
return HttpResponse(
20+
response.statusCode,
21+
body: responseBody,
22+
headers: response.headers,
23+
);
1524
}
1625

1726
final Client _client;
27+
final Encoding _defaultEncoding;
1828

1929
Future<Response> _send(Request dartRequest) async =>
2030
Response.fromStream(await _client.send(dartRequest));
31+
32+
/// Returns the encoding to use for a response with the given headers.
33+
///
34+
/// Defaults to [_defaultEncoding] if the headers don't specify a charset or if that
35+
/// charset is unknown.
36+
Encoding _encodingForHeaders(Map<String, String> headers) =>
37+
_encodingForCharset(
38+
_contentTypeForHeaders(headers).parameters['charset']);
39+
40+
/// Returns the [Encoding] that corresponds to [charset].
41+
///
42+
/// Returns [_defaultEncoding] if [charset] is null or if no [Encoding] was found that
43+
/// corresponds to [charset].
44+
Encoding _encodingForCharset(String charset) {
45+
if (charset == null) return _defaultEncoding;
46+
return Encoding.getByName(charset) ?? _defaultEncoding;
47+
}
48+
49+
/// Returns the [MediaType] object for the given headers's content-type.
50+
///
51+
/// Defaults to `application/octet-stream`.
52+
MediaType _contentTypeForHeaders(Map<String, String> headers) {
53+
var contentType = headers['content-type'];
54+
if (contentType != null) return MediaType.parse(contentType);
55+
return MediaType('application', 'octet-stream');
56+
}
2157
}

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ environment:
66
sdk: '>=2.6.0 <3.0.0'
77
dependencies:
88
http: ^0.12.0
9+
http_parser: ^3.1.4
910
dev_dependencies:
1011
args: ^1.5.2
1112
pedantic: ^1.9.0

test/unit/client/dart_http_test.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import 'dart:convert';
2+
3+
import 'package:http/http.dart' as http;
4+
import 'package:http/testing.dart';
5+
import 'package:json_api/client.dart';
6+
import 'package:json_api/http.dart';
7+
import 'package:test/test.dart';
8+
9+
void main() {
10+
group('Decode body with', () {
11+
final stringBodyRu = 'йцукен';
12+
final bytesBodyRu = utf8.encode(stringBodyRu);
13+
final stringBodyEn = 'qwerty';
14+
final bytesBodyEn = utf8.encode(stringBodyEn);
15+
16+
final buildResponse = (
17+
List<int> bytesBody,
18+
Encoding encoding,
19+
) async {
20+
final dartHttp = DartHttp(
21+
MockClient(
22+
(request) async {
23+
return http.Response.bytes(bytesBody, 200);
24+
},
25+
),
26+
encoding,
27+
);
28+
29+
return dartHttp.call(HttpRequest('', Uri.parse('http://test.com')));
30+
};
31+
32+
test('UTF-8 ru', () async {
33+
final response = await buildResponse(bytesBodyRu, utf8);
34+
expect(response.body, equals(stringBodyRu));
35+
});
36+
37+
test('latin1 ru', () async {
38+
final response = await buildResponse(bytesBodyRu, latin1);
39+
expect(response.body, isNot(equals(stringBodyRu)));
40+
});
41+
42+
test('UTF-8 en', () async {
43+
final response = await buildResponse(bytesBodyEn, utf8);
44+
expect(response.body, equals(stringBodyEn));
45+
});
46+
47+
test('latin1 en', () async {
48+
final response = await buildResponse(bytesBodyEn, latin1);
49+
expect(response.body, equals(stringBodyEn));
50+
});
51+
});
52+
}

0 commit comments

Comments
 (0)