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

Dio Web: Migration from XMLHttpRequest to new fetch api #2376

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

Binozo
Copy link

@Binozo Binozo commented Feb 6, 2025

Hello 👋

Since I wanted to make use of SSE but since #1740 was in the way I migrated the dio web adapter from XMLHttpRequest to the new fetch api

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

@Binozo Binozo requested a review from a team as a code owner February 6, 2025 20:34
@Zekfad
Copy link

Zekfad commented Feb 7, 2025

Part of this code was likely copied from here: https://github.com/Zekfad/fetch_api/blob/dee32249d9ecb4bf6b4eb05062930b5d704423c9/lib/src/js_promise_or.dart
I believe there must be a copyright notice as per license: https://github.com/Zekfad/fetch_api/blob/master/LICENSE#L4C48-L5C65
Besides for this PR I think bindings from package:web is now should be sufficient, fetch_api bindings were made when js_interop were just becoming a thing.

@Binozo
Copy link
Author

Binozo commented Feb 7, 2025

Indeed, what would you suggest? Either rewriting js_promise_or.dart or adding the license. Where should I add the license?

Additionally, the bindings from package:web are not sufficient. ReadableStream is not fully implemented in the standard package:web package. I wanted to fully support Dio's api, so I needed those auto generated apis for stream upload. To be more precise I needed those for this part:

      final streamReader = ReadableStream(
        ReadableStreamSource.fromStream(
          requestStream.map((e) {

           [...]

            sentBytes += e.lengthInBytes;
            if (options.onSendProgress != null) {
              options.onSendProgress!(sentBytes, -1);
            }
            return e.toJS;
          }),
        ),
      );

The dart doc https://pub.dev/documentation/web/latest/web/ReadableStream-extension-type.html shows the following method signature: ReadableStream([JSObject underlyingSource, QueuingStrategy strategy]) .

The mdn webdocs show this example: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#examples:

    return new ReadableStream({
      start(controller) {
           [...]
      },
    });

Sadly I was not able to implement this this way. That's way I chose those auto generated files.

@Zekfad
Copy link

Zekfad commented Feb 7, 2025

Just add short notice in header in borrowed files, that's sufficient enough.

I thought by now package:web would have more coverage, since it's generated from webdl as opposed to fetch_api which was made by hand.

Also good job for bringing fetch implementation for Dio, hope they'll accept your PR!

@Binozo
Copy link
Author

Binozo commented Feb 7, 2025

Yes, also hope for an accept of my pr because I actually need this feature myself 😄

@Binozo
Copy link
Author

Binozo commented Feb 8, 2025

I think I need some help.

I can't get my head around with the tests, they are so confusing. As an example:

cors_tests.dart:92 asks for an Dio connectionError exception:

      test('POST with onSendProgress is preflighted', () async {
        // If there is a preflight (OPTIONS) request, the server fails it
        // by responding with status 418. This fails CORS, so the browser
        // never sends the main request and this code throws.
        expect(
          () async {
            final _ = await dio.post(
              '/status/418',
              data: 'body text',
              options: Options(
                validateStatus: (status) => true,
                contentType: Headers.textPlainContentType,
              ),
              onSendProgress: (_, __) {},
            );
          },
          throwsDioException(
            DioExceptionType.connectionError,
            stackTraceContains: 'test/test_suite_test.dart',
          ),
        );
      });

Which gets thrown in the code:

    // Check CORS
    if (response.status == 418) {
      if (options.onSendProgress != null || sendTimeout > Duration.zero || (request.method != 'GET' && options.contentType != Headers.textPlainContentType)) {
        completer.completeError(
          DioException.connectionError(
            requestOptions: options,
            error: 'CORS preflight request failed',
            reason: 'The preflight request responded with status 418',
          ),
          StackTrace.current,
        );
      }
    }

Which the test logs confirm:

[dio]: 00:45 +93 -4: test/test_suite_test.dart: CORS preflight POST with onSendProgress is preflighted [E]                                                                                                    
[dio]:   DioException [connection error]: The connection errored: The preflight request responded with status 418 This indicates an error which most likely cannot be solved by the library.
[dio]:   Error: CORS preflight request failed
[dio]:   org-dartlang-sdk:///lib/_internal/js_runtime/lib/core_patch.dart 788:58            StackTrace.current
[dio]:   package:dio_web_adapter/src/adapter.dart 238:22                                    BrowserHttpClientAdapter.<fn>
[dio]:   org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 311:19           _wrapJsFunctionForAsync.<fn>
[dio]:   org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 336:23           call
[dio]:   package:stack_trace                                                                _run
[dio]:   org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 287:19           call
[dio]:   org-dartlang-sdk:///lib/async/zone.dart 1422:46                                    _rootRunUnary
[dio]:   org-dartlang-sdk:///lib/async/zone.dart 1323:34                                    runUnary
[dio]:   org-dartlang-sdk:///lib/async/future_impl.dart 169:29                              call
[dio]:   org-dartlang-sdk:///lib/async/future_impl.dart 931:13                              _Future._propagateToListeners
[dio]:   org-dartlang-sdk:///lib/async/future_impl.dart 707:5                               _completeWithValue
[dio]:   org-dartlang-sdk:///lib/async/future_impl.dart 777:7                               call
[dio]:   package:stack_trace                                                                call
[dio]:   org-dartlang-sdk:///lib/async/zone.dart 1414:12                                    _rootRun
[dio]:   org-dartlang-sdk:///lib/async/zone.dart 1316:34                                    run
[dio]:   org-dartlang-sdk:///lib/async/zone.dart 1225:7                                     runGuarded
[dio]:   org-dartlang-sdk:///lib/async/zone.dart 1265:18                                    call
[dio]:   org-dartlang-sdk:///lib/async/schedule_microtask.dart 40:12                        _microtaskLoop
[dio]:   org-dartlang-sdk:///lib/async/schedule_microtask.dart 49:5                         _startMicrotaskLoop
[dio]:   org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 56:10            call
[dio]:   ===== asynchronous gap ===========================

But the test still fails 😕

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.

2 participants