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

[C++][FlightRPC] arrow::flight::FlightClient::AuthenticateBasicToken is blocked against Ballista #36369

Closed
kou opened this issue Jun 29, 2023 · 3 comments · Fixed by #36372
Closed

Comments

@kou
Copy link
Member

kou commented Jun 29, 2023

Describe the bug, including details regarding any error messages, version, and platform.

Ballista uses admin/password for (Basic) authentication: https://arrow.apache.org/ballista/user-guide/flightsql.html#a-name-tool-use-the-driver-in-your-favorite-data-tool

The following Apache Arrow C++ Flight RPC client is blocked:

#include <iostream>

#include <arrow/flight/client.h>

static arrow::Status
run(void)
{
  ARROW_ASSIGN_OR_RAISE(auto location,
                        arrow::flight::Location::Parse("grpc://127.0.0.1:50050"));
  arrow::flight::FlightClientOptions options;
  ARROW_ASSIGN_OR_RAISE(auto client,
                        arrow::flight::FlightClient::Connect(location, options));
  arrow::flight::FlightCallOptions call_options;
  ARROW_ASSIGN_OR_RAISE(auto bearer_token,
                        client->AuthenticateBasicToken(call_options, "admin", "password"));
  return arrow::Status::OK();
}

extern "C" int
main(void)
{
  auto status = run();
  if (!status.ok()) {
    std::cout << status.ToString() << std::endl;
    return 1;
  }
  return 0;
}

stream->Finish() in GrpcClientImpl::AuthenticateBasicToken() is blocked:

https://github.com/apache/arrow/blob/main/cpp/src/arrow/flight/transport/grpc/grpc_client.cc#L762

grpc::ClientReaderWriter< W, R >::Finish document refers grpc::internal::ClientStreamingInterface::Finish() document and it says:

It is appropriate to call this method exactly once when both:

We call stream->WritesDone() explicitly for the former. But we don't call stream->Read() until it returns false. Should we call stream->Read() before we call stream->Finish() like the following?

diff --git a/cpp/src/arrow/flight/transport/grpc/grpc_client.cc b/cpp/src/arrow/flight/transport/grpc/grpc_client.cc
index 5abecd91a..b382d1e11 100644
--- a/cpp/src/arrow/flight/transport/grpc/grpc_client.cc
+++ b/cpp/src/arrow/flight/transport/grpc/grpc_client.cc
@@ -740,6 +740,15 @@ class GrpcClientImpl : public internal::ClientTransport {
     RETURN_NOT_OK(auth_handler_->Authenticate(&outgoing, &incoming));
     // Explicitly close our side of the connection
     bool finished_writes = stream->WritesDone();
+    pb::HandshakeResponse response;
+    if (!stream->Read(&response)) {
+      return MakeFlightError(FlightStatusCode::Internal,
+                             "No handshake response from server");
+    }
+    if (stream->Read(&response)) {
+      return MakeFlightError(FlightStatusCode::Internal,
+                             "Too much handshake response from server");
+    }
     RETURN_NOT_OK(FromGrpcStatus(stream->Finish(), &rpc.context));
     if (!finished_writes) {
       return MakeFlightError(FlightStatusCode::Internal,
@@ -759,6 +768,15 @@ class GrpcClientImpl : public internal::ClientTransport {
         stream = stub_->Handshake(&rpc.context);
     // Explicitly close our side of the connection.
     bool finished_writes = stream->WritesDone();
+    pb::HandshakeResponse response;
+    if (!stream->Read(&response)) {
+      return MakeFlightError(FlightStatusCode::Internal,
+                             "No handshake response from server");
+    }
+    if (stream->Read(&response)) {
+      return MakeFlightError(FlightStatusCode::Internal,
+                             "Too much handshake response from server");
+    }
     RETURN_NOT_OK(FromGrpcStatus(stream->Finish(), &rpc.context));
     if (!finished_writes) {
       return MakeFlightError(FlightStatusCode::Internal,

If we call stream->Read() before stream->Finish(), the test program isn't blocked.

@lidavidm What do you think about this?

Component(s)

C++, FlightRPC

@lidavidm
Copy link
Member

Yes, we need to drain all reads. The rest of the client code is careful to ensure this, but not here apparently.

@lidavidm
Copy link
Member

Thanks for digging through this and figuring it out...

@kou
Copy link
Member Author

kou commented Jun 29, 2023

OK. I'll open a pull request for this.

kou added a commit to kou/arrow that referenced this issue Jun 29, 2023
…enticate*()

We need to drain all responses from server before we call gRPC's
Finish() to avoid hanging.
kou added a commit to kou/arrow that referenced this issue Jun 30, 2023
…enticate*()

We need to drain all responses from server before we call gRPC's
Finish() to avoid hanging.
@kou kou closed this as completed in #36372 Jul 1, 2023
kou added a commit that referenced this issue Jul 1, 2023
…te*() (#36372)

### Rationale for this change

We need to drain all responses from server before we call gRPC's Finish() to avoid hanging.

### What changes are included in this PR?

Read all responses before calling Finish().

### Are these changes tested?

Yes.

### Are there any user-facing changes?

Yes.
* Closes: #36369

Authored-by: Sutou Kouhei <kou@clear-code.com>
Signed-off-by: Sutou Kouhei <kou@clear-code.com>
@kou kou added this to the 13.0.0 milestone Jul 1, 2023
westonpace pushed a commit to westonpace/arrow that referenced this issue Jul 7, 2023
…enticate*() (apache#36372)

### Rationale for this change

We need to drain all responses from server before we call gRPC's Finish() to avoid hanging.

### What changes are included in this PR?

Read all responses before calling Finish().

### Are these changes tested?

Yes.

### Are there any user-facing changes?

Yes.
* Closes: apache#36369

Authored-by: Sutou Kouhei <kou@clear-code.com>
Signed-off-by: Sutou Kouhei <kou@clear-code.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants