Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set FusedTransformer as default #2250

Merged
merged 14 commits into from
Jun 18, 2024
Merged

Conversation

knaeckeKami
Copy link
Contributor

@knaeckeKami knaeckeKami commented Jun 17, 2024

New Pull Request Checklist

  • I have read the Documentation
  • I have searched for a similar pull request in the project and found none
  • I have updated this branch with the latest main branch to avoid conflicts (via merge from master or rebase)
  • I have added the required tests to prove the fix/feature I'm adding
  • I have updated the documentation (if necessary)
  • I have run the tests without failures
  • I have updated the CHANGELOG.md in the corresponding package

Additional context and info (if any)

Also fix three oversights in the transformer

  • Fix parsing content as JSON when Content-Type is like application/json but ResponseType == .plain

  • HEAD requests have an empty body, even when Content-Length is >0. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD . I did not take this into account. This is also fixed now.

  • Respect custom decoders returning null

@knaeckeKami knaeckeKami requested a review from a team as a code owner June 17, 2024 20:21
Copy link
Contributor

@ueman ueman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again! I have no objections to changing the default transformer, but I'd like to hear the opinions on it from the others

@@ -41,7 +41,8 @@ abstract class DioMixin implements Dio {
/// The default [Transformer] that transfers requests and responses
/// into corresponding content to send.
@override
Transformer transformer = BackgroundTransformer();
Transformer transformer =
FusedTransformer(contentLengthIsolateThreshold: 50 * 1024);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment explaining this specific number? Is it chosen rather arbitrarily or an educated guess?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to be configurable?

Copy link
Contributor Author

@knaeckeKami knaeckeKami Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

50 * 1024 is the hardcoded default in BackgroundTransformer, I chose the same to keep the behavior as close as possible to the old one.
I did not measure how long it takes to decode on smartphones with the new approach. I copied the rationale for the 50 KiB threshold from BackgroundTransformer now.

IMO it makes sense to make it configurable, for example, when running the whole HTTP client in a background isolate already, users might not care about blocking the isolate for a few milliseconds, so they could set a higher (or -1 to disable spawning new isolates completely).

return decodedResponse;
} else {
if (responseBytes.isEmpty) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any references?

Copy link
Contributor Author

@knaeckeKami knaeckeKami Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, did not mean to commit that. Is unnecessary, utf8.decode returns an empty string anyway on an empty bytes list. removed.

Comment on lines 121 to 123
// Successful HEAD requests don't have a response body, even if the content-length header
// present.
final contentLengthHeaderImpliesContent = options.method != 'HEAD';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • With CONNECT too although it's an edge case?
  • Also the MDN reference would be helpful to be placed here.
  • Could the name be neverHasResponseBody and then flip all conditions to the opposite?
final neverHasResponseBody = options.method == 'CONNECT' || options.method == 'HEAD';

if (neverHasResponseBody || !hasContentLengthHeader) {
  // ...
}

Copy link
Contributor Author

@knaeckeKami knaeckeKami Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue only occurs when the ResponseType is .json, the response body has a Content-Type of application/json, and a Content-Length header with a value > 0, but no response body.

A valid CONNECT response will have no Content-Type and no Content-Length header, so the transformer won't try to parse it as json.

A server MUST NOT send any Transfer-Encoding or Content-Length header fields in a 2xx (Successful) response to CONNECT. A client MUST ignore any Content-Length or Transfer-Encoding header fields received in a successful response to CONNECT.

I mean sure, we could also support invalid responses to CONNECT, but I'd argue if a user want's that, they should just set the ResponseType to .plain, .bytes or .stream. or use their own decoder / transformer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks for the explanation. But the flag should be flipped because if you are using its opposite result always you should simply flip it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true. done

dio/test/transformer_test.dart Outdated Show resolved Hide resolved
knaeckeKami and others added 2 commits June 18, 2024 13:00
Co-authored-by: Alex Li <github@alexv525.com>
Signed-off-by: Martin Kamleithner <martin.kamleithner@gmail.com>
Copy link
Contributor

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
dio/lib/src/transformers/fused_transformer.dart 🟢 84.09% 🟢 91.3% 🟢 7.21%
dio/lib/src/transformers/sync_transformer.dart 🟢 100% 🟢 84.21% 🔴 -15.79%
Overall Coverage 🟢 82.45% 🟢 82.48% 🟢 0.03%

Minimum allowed coverage is 0%, this run produced 82.48%

Copy link
Member

@AlexV525 AlexV525 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ueman ueman merged commit 47abf18 into cfug:main Jun 18, 2024
3 checks passed
ueman added a commit that referenced this pull request Sep 3, 2024
fixes #2279 

This improves handling of the following case:

- ResponseType is json
- Server Content-Type is json
- Server Content-Length is nonzero
- Response Body is empty

This can happen in some cases according to HTTP spec (e.g. HEAD
requests), but some servers return responses like this (see #2279) also
in other cases, even if it violates the HTTP spec.

With #2250, in some of these cases dio would throw an exception, which
caused a regression for some users; see #2279
I do believe that dio should handle these cases for gracefully,

This PR brings back the previous behavior of returning null.  

### New Pull Request Checklist

- [x] I have read the
[Documentation](https://pub.dev/documentation/dio/latest/)
- [x] I have searched for a similar pull request in the
[project](https://github.com/cfug/dio/pulls) and found none
- [x] I have updated this branch with the latest `main` branch to avoid
conflicts (via merge from master or rebase)
- [x] I have added the required tests to prove the fix/feature I'm
adding
- [x] I have updated the documentation (if necessary)
- [x] I have run the tests without failures
- [x] I have updated the `CHANGELOG.md` in the corresponding package

### Additional context and info (if any)

<!-- Provide more context and info about the PR. -->

---------

Signed-off-by: Martin Kamleithner <martin.kamleithner@gmail.com>
Co-authored-by: Jonas Uekötter <ueman@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants