grpc / grpc-dart

The Dart language implementation of gRPC.
https://pub.dev/packages/grpc
Apache License 2.0
861 stars 271 forks source link

I dont know why dart change code to 14 when get success return #734

Closed Uwanggood closed 2 months ago

Uwanggood commented 2 months ago

grpc 4.0.1 Flutter 3.24.2 • Dart 3.5.2 DevTools 2.37.2

I'm using Flutter Web WASM and encountering an unusual issue with gRPC calls. Here's the setup and problem description:

Environment:

The Issue:

  1. The Go server correctly processes the request and returns a response.
  2. For example, in a login scenario, it correctly returns PermissionDenied (Code = 7) for incorrect passwords.
  3. The problem occurs when the server returns OK (Code = 0).
  4. Mysteriously, the OK status is changed to UNAVAILABLE (Code = 14) somewhere in the process.
  5. This happens after receiving a 200 HTTP status and the response, making it more perplexing.

Attempted Solutions:

Steps to Reproduce:

  1. Build with: flutter build web --wasm --no-tree-shake-icons
  2. Call login gRPC API (works normally on local)
  3. Receive normal response and token
  4. Console shows error 14:
    Uncaught Error: gRPC Error (code: 14, codeName: UNAVAILABLE, message: Missing trailers, details: null, rawResponse: null, trailers: {})

Relevant Code:

  1. adaptive_web.dart:
    
    import 'package:grpc/grpc_web.dart';
    import 'package:web/web.dart';

const https = 'https://'; const http = 'http://';

String getBaseUrl() { String? hostname = window.location.hostname;

return hostname.contains('localhost') || hostname.contains('127.0.0.1') ? '$http$hostname:50051/' : '$https$hostname/'; }

GrpcWebClientChannel getGrpcChannel() { return GrpcWebClientChannel.xhr(Uri.parse(getBaseUrl())); }


2. api_providers.dart:
```dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frontend/main.dart';
import 'package:frontend/notifier/message/message_notifier.dart';
import 'package:frontend/notifier/session/session_notifier.dart';
import 'package:frontend/notifier/tenant/tenant_notifier.dart';
import 'package:frontend/src/generated/academy_user.pbgrpc.dart';
import 'package:frontend/src/generated/expense.pbgrpc.dart';
import 'package:frontend/src/generated/lesson.pbgrpc.dart';
import 'package:frontend/src/generated/login.pbgrpc.dart';
import 'package:frontend/src/generated/product.pbgrpc.dart';
import 'package:frontend/src/generated/sales.pbgrpc.dart';
import 'package:frontend/src/generated/stats.pbgrpc.dart';
import 'package:frontend/src/generated/teacher.pbgrpc.dart';
import 'package:frontend/src/generated/tenant.pbgrpc.dart';
import 'package:frontend/src/generated/user.pbgrpc.dart';
import 'package:grpc/grpc.dart';

import 'package:frontend/core/platform/adaptive_view.dart';

final gRpcChannel = getGrpcChannel();

class GrpcClients {
  static final login = LoginServiceClient(gRpcChannel);
  static final au = AuServiceClient(gRpcChannel);
  static final user = UserServiceClient(gRpcChannel);
  static final tenant = TenantServiceClient(gRpcChannel);
  static final sales = SalesServiceClient(gRpcChannel);
  static final stats = StatsServiceClient(gRpcChannel);
  static final expense = ExpenseServiceClient(gRpcChannel);
  static final lesson = LessonServiceClient(gRpcChannel);
  static final product = ProductServiceClient(gRpcChannel);
  static final teacher = TeacherServiceClient(gRpcChannel);
}

Future<CallOptions> getCallOption() async {
  final accessToken = await sessionManager.getAccessToken();
  final tenantId = await sessionManager.getSelectedTenantId();

  if (accessToken == null || tenantId == null) {
    throw Exception('accessToken or tenantId is null');
  }

  return CallOptions(metadata: {
    'Authorization': 'Bearer $accessToken',
    'TenantId': tenantId,
  });
}

class ApiException implements Exception {
  final String message;
  final dynamic originalError;

  ApiException(this.message, this.originalError);

  @override
  String toString() => 'StatsApiException: $message';
}
  1. login_api.dart:
    
    import 'package:frontend/core/api/api_providers.dart';
    import 'package:frontend/notifier/message/message_notifier.dart';
    import 'package:frontend/src/generated/login.pbgrpc.dart';
    import 'package:grpc/grpc.dart';
    import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'login_api.g.dart';

@riverpod Future<UserLoginResponse?> login(LoginRef ref, {required String email, required String password}) async { final messageNotifier = ref.read(messageNotifierProvider.notifier);

if (email.isEmpty || !email.contains('@') || password.isEmpty) { messageNotifier.showError('이메일이나 비밀번호가 올바르게 입력되지 않았습니다.'); return null; }

print(email); print(password); final request = UserLoginRequest() ..email = email ..password = password; final response = await GrpcClients.login.userLogin(request); print(response); return response; }



Any insights or suggestions on why the successful response (Code 0) is being changed to UNAVAILABLE (Code 14) would be greatly appreciated. The bundled nature of the code makes it challenging to pinpoint the exact location of this transformation.
![image](https://github.com/user-attachments/assets/b743118e-1ed7-4e77-b69d-343a308364bb)
![image](https://github.com/user-attachments/assets/7669d482-5cde-46c3-8e6e-91ae4d2b585e)
![image](https://github.com/user-attachments/assets/1341d923-390e-4c09-a559-8b616e0e5467)

--additional--

i got successful reponse when i use postman 
![image](https://github.com/user-attachments/assets/7585d4ad-82c5-43e5-81cf-cd8a4c41f933)

--additional 2--
![image](https://github.com/user-attachments/assets/6d199a11-fa50-4af9-ad34-a4065332f842)
process data and  _hasReceivedResponses change to true

![image](https://github.com/user-attachments/assets/a894af17-d216-40c7-ab37-47df774aae64)

if _hasReceivedResponses is true then occurs error every time 

t seems we need to set _trailers.isCompleted to true after inserting the data, but there's no logic in between to actually change it to true.

-- additional 3 --
![image](https://github.com/user-attachments/assets/31969b2c-c8c0-48e3-8eec-0773fc0f4909)

add temp trailer compllete 

then it works 
Uwanggood commented 2 months ago

I was wondering if there's been any progress on this issue?

Uwanggood commented 2 months ago

It seems that Nginx does not support trailers when using gRPC-Web. Therefore, I will close this issue.