2022-05-23 00:16:32 +04:00

276 lines
11 KiB
C++

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
/// \file
/// Internal (but not private) interface for implementing
/// alternate network transports in Flight.
///
/// \warning EXPERIMENTAL. Subject to change.
///
/// To implement a transport, implement ServerTransport and
/// ClientTransport, and register the desired URI schemes with
/// TransportRegistry. Flight takes care of most of the per-RPC
/// details; transports only handle connections and providing a I/O
/// stream implementation (TransportDataStream).
///
/// On the server side:
///
/// 1. Applications subclass FlightServerBase and override RPC handlers.
/// 2. FlightServerBase::Init will look up and create a ServerTransport
/// based on the scheme of the Location given to it.
/// 3. The ServerTransport will start the actual server. (For instance,
/// for gRPC, it creates a gRPC server and registers a gRPC service.)
/// That server will handle connections.
/// 4. The transport should forward incoming calls to the server to the RPC
/// handlers defined on ServerTransport, which implements the actual
/// RPC handler using the interfaces here. Any I/O the RPC handler needs
/// to do is managed by transport-specific implementations of
/// TransportDataStream.
/// 5. ServerTransport calls FlightServerBase for the actual application
/// logic.
///
/// On the client side:
///
/// 1. Applications create a FlightClient with a Location.
/// 2. FlightClient will look up and create a ClientTransport based on
/// the scheme of the Location given to it.
/// 3. When calling a method on FlightClient, FlightClient will delegate to
/// the ClientTransport. There is some indirection, e.g. for DoGet,
/// FlightClient only requests that the ClientTransport start the
/// call and provide it with an I/O stream. The "Flight implementation"
/// itself still lives in FlightClient.
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "arrow/flight/type_fwd.h"
#include "arrow/flight/visibility.h"
#include "arrow/type_fwd.h"
#include "arrow/util/optional.h"
namespace arrow {
namespace ipc {
class Message;
}
namespace flight {
class FlightStatusDetail;
namespace internal {
/// Internal, not user-visible type used for memory-efficient reads
struct FlightData {
/// Used only for puts, may be null
std::unique_ptr<FlightDescriptor> descriptor;
/// Non-length-prefixed Message header as described in format/Message.fbs
std::shared_ptr<Buffer> metadata;
/// Application-defined metadata
std::shared_ptr<Buffer> app_metadata;
/// Message body
std::shared_ptr<Buffer> body;
/// Open IPC message from the metadata and body
::arrow::Result<std::unique_ptr<ipc::Message>> OpenMessage();
};
/// \brief A transport-specific interface for reading/writing Arrow data.
///
/// New transports will implement this to read/write IPC payloads to
/// the underlying stream.
class ARROW_FLIGHT_EXPORT TransportDataStream {
public:
virtual ~TransportDataStream() = default;
/// \brief Attempt to read the next FlightData message.
///
/// \return success true if data was populated, false if there was
/// an error. For clients, the error can be retrieved from
/// Finish(Status).
virtual bool ReadData(FlightData* data);
/// \brief Attempt to write a FlightPayload.
///
/// \param[in] payload The data to write.
/// \return true if the message was accepted by the transport, false
/// if not (e.g. due to client/server disconnect), Status if there
/// was an error (e.g. with the payload itself).
virtual arrow::Result<bool> WriteData(const FlightPayload& payload);
/// \brief Indicate that there are no more writes on this stream.
///
/// This is only a hint for the underlying transport and may not
/// actually do anything.
virtual Status WritesDone();
};
/// \brief A transport-specific interface for reading/writing Arrow
/// data for a client.
class ARROW_FLIGHT_EXPORT ClientDataStream : public TransportDataStream {
public:
/// \brief Attempt to read a non-data message.
///
/// Only implemented for DoPut; mutually exclusive with
/// ReadData(FlightData*).
virtual bool ReadPutMetadata(std::shared_ptr<Buffer>* out);
/// \brief Attempt to cancel the call.
///
/// This is only a hint and may not take effect immediately. The
/// client should still finish the call with Finish(Status) as usual.
virtual void TryCancel() {}
/// \brief Finish the call, reporting the server-sent status and/or
/// any client-side errors as appropriate.
///
/// Implies WritesDone() and DoFinish().
///
/// \param[in] st A client-side status to combine with the
/// server-side error. That is, if an error occurs on the
/// client-side, call Finish(Status) to finish the server-side
/// call, get the server-side status, and merge the statuses
/// together so context is not lost.
Status Finish(Status st);
protected:
/// \brief End the call, returning the final server status.
///
/// For implementors: should imply WritesDone() (even if it does not
/// directly call it).
///
/// Implies WritesDone().
virtual Status DoFinish() = 0;
};
/// An implementation of a Flight client for a particular transport.
///
/// Transports should override the methods they are capable of
/// supporting. The default method implementations return an error.
class ARROW_FLIGHT_EXPORT ClientTransport {
public:
virtual ~ClientTransport() = default;
/// Initialize the client.
virtual Status Init(const FlightClientOptions& options, const Location& location,
const arrow::internal::Uri& uri) = 0;
/// Close the client. Once this returns, the client is no longer usable.
virtual Status Close() = 0;
virtual Status Authenticate(const FlightCallOptions& options,
std::unique_ptr<ClientAuthHandler> auth_handler);
virtual arrow::Result<std::pair<std::string, std::string>> AuthenticateBasicToken(
const FlightCallOptions& options, const std::string& username,
const std::string& password);
virtual Status DoAction(const FlightCallOptions& options, const Action& action,
std::unique_ptr<ResultStream>* results);
virtual Status ListActions(const FlightCallOptions& options,
std::vector<ActionType>* actions);
virtual Status GetFlightInfo(const FlightCallOptions& options,
const FlightDescriptor& descriptor,
std::unique_ptr<FlightInfo>* info);
virtual arrow::Result<std::unique_ptr<SchemaResult>> GetSchema(
const FlightCallOptions& options, const FlightDescriptor& descriptor);
virtual Status ListFlights(const FlightCallOptions& options, const Criteria& criteria,
std::unique_ptr<FlightListing>* listing);
virtual Status DoGet(const FlightCallOptions& options, const Ticket& ticket,
std::unique_ptr<ClientDataStream>* stream);
virtual Status DoPut(const FlightCallOptions& options,
std::unique_ptr<ClientDataStream>* stream);
virtual Status DoExchange(const FlightCallOptions& options,
std::unique_ptr<ClientDataStream>* stream);
};
/// A registry of transport implementations.
class ARROW_FLIGHT_EXPORT TransportRegistry {
public:
using ClientFactory = std::function<arrow::Result<std::unique_ptr<ClientTransport>>()>;
using ServerFactory = std::function<arrow::Result<std::unique_ptr<ServerTransport>>(
FlightServerBase*, std::shared_ptr<MemoryManager> memory_manager)>;
TransportRegistry();
~TransportRegistry();
arrow::Result<std::unique_ptr<ClientTransport>> MakeClient(
const std::string& scheme) const;
arrow::Result<std::unique_ptr<ServerTransport>> MakeServer(
const std::string& scheme, FlightServerBase* base,
std::shared_ptr<MemoryManager> memory_manager) const;
Status RegisterClient(const std::string& scheme, ClientFactory factory);
Status RegisterServer(const std::string& scheme, ServerFactory factory);
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
/// \brief Get the registry of transport implementations.
ARROW_FLIGHT_EXPORT
TransportRegistry* GetDefaultTransportRegistry();
//------------------------------------------------------------
// Error propagation helpers
/// \brief Abstract status code as per the Flight specification.
enum class TransportStatusCode {
kOk = 0,
kUnknown = 1,
kInternal = 2,
kInvalidArgument = 3,
kTimedOut = 4,
kNotFound = 5,
kAlreadyExists = 6,
kCancelled = 7,
kUnauthenticated = 8,
kUnauthorized = 9,
kUnimplemented = 10,
kUnavailable = 11,
};
/// \brief Abstract error status.
///
/// Transport implementations may use side channels (e.g. HTTP
/// trailers) to convey additional information to reconstruct the
/// original C++ status for implementations that can use it.
struct ARROW_FLIGHT_EXPORT TransportStatus {
TransportStatusCode code;
std::string message;
/// \brief Convert a C++ status to an abstract transport status.
static TransportStatus FromStatus(const Status& arrow_status);
/// \brief Reconstruct a string-encoded TransportStatus.
static TransportStatus FromCodeStringAndMessage(const std::string& code_str,
std::string message);
/// \brief Convert an abstract transport status to a C++ status.
Status ToStatus() const;
};
/// \brief Convert the string representation of an Arrow status code
/// back to an Arrow status.
ARROW_FLIGHT_EXPORT
Status ReconstructStatus(const std::string& code_str, const Status& current_status,
util::optional<std::string> message,
util::optional<std::string> detail_message,
util::optional<std::string> detail_bin,
std::shared_ptr<FlightStatusDetail> detail);
} // namespace internal
} // namespace flight
} // namespace arrow