-
-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
5 changed files
with
106 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import 'dart:convert'; | ||
|
||
import 'package:http/http.dart'; | ||
import 'package:http_parser/http_parser.dart'; | ||
import 'package:json_api/http.dart'; | ||
|
||
/// Converts HTTP messages to and from to ones used by the `http` package. | ||
class MessageConverter { | ||
/// Creates an instance of the converter. | ||
/// | ||
/// Pass [defaultResponseEncoding] to use in cases when the server does | ||
/// not specify the encoding. | ||
MessageConverter({Encoding defaultResponseEncoding = utf8}) | ||
: _defaultResponseEncoding = defaultResponseEncoding; | ||
|
||
final Encoding _defaultResponseEncoding; | ||
|
||
/// Converts [HttpRequest] to [Request]. | ||
Request request(HttpRequest request) { | ||
final converted = Request(request.method, request.uri); | ||
final hasBody = !(request.isGet || request.isOptions); | ||
if (hasBody) { | ||
// The Request would set the content-type header if the body is assigned | ||
// a value (even an empty string). We want to avoid this extra header for | ||
// GET and OPTIONS requests. | ||
// See https://github.com/dart-lang/http/issues/841 | ||
converted.body = request.body; | ||
} | ||
converted.headers.addAll(request.headers); | ||
return converted; | ||
} | ||
|
||
/// Converts [Response] to [HttpResponse]. | ||
HttpResponse response(Response response) { | ||
final encoding = _encodingForHeaders(response.headers); | ||
final body = encoding.decode(response.bodyBytes); | ||
return HttpResponse(response.statusCode, body: body) | ||
..headers.addAll(response.headers); | ||
} | ||
|
||
/// Returns the [Encoding] that corresponds to [charset]. | ||
/// | ||
/// Returns [defaultResponseEncoding] if [charset] is null or if no [Encoding] | ||
/// was found that corresponds to [charset]. | ||
Encoding _encodingForCharset(String? charset) { | ||
if (charset == null) return _defaultResponseEncoding; | ||
return Encoding.getByName(charset) ?? _defaultResponseEncoding; | ||
} | ||
|
||
/// Returns the [MediaType] object for the given content-type. | ||
/// | ||
/// Defaults to `application/octet-stream`. | ||
MediaType _mediaType(String? contentType) { | ||
if (contentType != null) return MediaType.parse(contentType); | ||
return MediaType('application', 'octet-stream'); | ||
} | ||
|
||
/// Returns the encoding to use for a response with the given headers. | ||
/// | ||
/// Defaults to [defaultEncoding] if the headers don't specify the charset | ||
/// or if that charset is unknown. | ||
Encoding _encodingForHeaders(Map<String, String> headers) { | ||
final mediaType = _mediaType(headers['content-type']); | ||
final charset = mediaType.parameters['charset']; | ||
return _encodingForCharset(charset); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,35 @@ | ||
import 'dart:convert'; | ||
|
||
import 'package:http/http.dart'; | ||
import 'package:http_parser/http_parser.dart'; | ||
import 'package:json_api/http.dart'; | ||
import 'package:json_api/src/client/message_converter.dart'; | ||
|
||
/// Handler which relies on the built-in Dart HTTP client. | ||
/// It is the developer's responsibility to instantiate the client and | ||
/// call `close()` on it in the end pf the application lifecycle. | ||
class PersistentHandler implements HttpHandler { | ||
/// Creates a new instance of the handler. Do not forget to call `close()` on | ||
/// the [client] when it's not longer needed. | ||
PersistentHandler(this.client, {this.defaultEncoding = utf8}); | ||
/// | ||
/// Use [messageConverter] to fine tune the HTTP request/response conversion. | ||
PersistentHandler( | ||
this.client, | ||
{@Deprecated('Deprecated in favor of MessageConverter.' | ||
' To be removed in version 6.0.0') | ||
this.defaultEncoding = utf8, | ||
MessageConverter? messageConverter}) | ||
: _converter = messageConverter ?? | ||
MessageConverter(defaultResponseEncoding: defaultEncoding); | ||
|
||
final Client client; | ||
final Encoding defaultEncoding; | ||
final MessageConverter _converter; | ||
|
||
@override | ||
Future<HttpResponse> handle(HttpRequest request) async { | ||
final response = await Response.fromStream( | ||
await client.send(Request(request.method, request.uri) | ||
..headers.addAll(request.headers) | ||
..body = request.body)); | ||
final responseBody = | ||
_encodingForHeaders(response.headers).decode(response.bodyBytes); | ||
return HttpResponse(response.statusCode, body: responseBody) | ||
..headers.addAll(response.headers); | ||
} | ||
|
||
/// Returns the encoding to use for a response with the given headers. | ||
/// | ||
/// Defaults to [defaultEncoding] if the headers don't specify a charset or if that | ||
/// charset is unknown. | ||
Encoding _encodingForHeaders(Map<String, String> headers) => | ||
_encodingForCharset( | ||
_contentTypeForHeaders(headers).parameters['charset']); | ||
|
||
/// Returns the [Encoding] that corresponds to [charset]. | ||
/// | ||
/// Returns [defaultEncoding] if [charset] is null or if no [Encoding] was found that | ||
/// corresponds to [charset]. | ||
Encoding _encodingForCharset(String? charset) { | ||
if (charset == null) return defaultEncoding; | ||
return Encoding.getByName(charset) ?? defaultEncoding; | ||
} | ||
|
||
/// Returns the [MediaType] object for the given headers's content-type. | ||
/// | ||
/// Defaults to `application/octet-stream`. | ||
MediaType _contentTypeForHeaders(Map<String, String> headers) { | ||
final contentType = headers['content-type']; | ||
if (contentType != null) return MediaType.parse(contentType); | ||
return MediaType('application', 'octet-stream'); | ||
final convertedRequest = _converter.request(request); | ||
final streamedResponse = await client.send(convertedRequest); | ||
final response = await Response.fromStream(streamedResponse); | ||
return _converter.response(response); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import 'package:json_api/client.dart'; | ||
import 'package:json_api/http.dart'; | ||
import 'package:test/expect.dart'; | ||
import 'package:test/scaffolding.dart'; | ||
|
||
void main() { | ||
final converter = MessageConverter(); | ||
final uri = Uri.parse('https://example.com'); | ||
|
||
test('No headers are set for GET requests', () { | ||
final r = converter.request(HttpRequest('GET', uri)); | ||
expect(r.headers, isEmpty); | ||
}); | ||
|
||
test('No headers are set for OPTIONS requests', () { | ||
final r = converter.request(HttpRequest('OPTIONS', uri)); | ||
expect(r.headers, isEmpty); | ||
}); | ||
} |