// 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 #include #include #include #include #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 descriptor; /// Non-length-prefixed Message header as described in format/Message.fbs std::shared_ptr metadata; /// Application-defined metadata std::shared_ptr app_metadata; /// Message body std::shared_ptr body; /// Open IPC message from the metadata and body ::arrow::Result> 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 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* 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 auth_handler); virtual arrow::Result> AuthenticateBasicToken( const FlightCallOptions& options, const std::string& username, const std::string& password); virtual Status DoAction(const FlightCallOptions& options, const Action& action, std::unique_ptr* results); virtual Status ListActions(const FlightCallOptions& options, std::vector* actions); virtual Status GetFlightInfo(const FlightCallOptions& options, const FlightDescriptor& descriptor, std::unique_ptr* info); virtual arrow::Result> GetSchema( const FlightCallOptions& options, const FlightDescriptor& descriptor); virtual Status ListFlights(const FlightCallOptions& options, const Criteria& criteria, std::unique_ptr* listing); virtual Status DoGet(const FlightCallOptions& options, const Ticket& ticket, std::unique_ptr* stream); virtual Status DoPut(const FlightCallOptions& options, std::unique_ptr* stream); virtual Status DoExchange(const FlightCallOptions& options, std::unique_ptr* stream); }; /// A registry of transport implementations. class ARROW_FLIGHT_EXPORT TransportRegistry { public: using ClientFactory = std::function>()>; using ServerFactory = std::function>( FlightServerBase*, std::shared_ptr memory_manager)>; TransportRegistry(); ~TransportRegistry(); arrow::Result> MakeClient( const std::string& scheme) const; arrow::Result> MakeServer( const std::string& scheme, FlightServerBase* base, std::shared_ptr 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_; }; /// \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 message, util::optional detail_message, util::optional detail_bin, std::shared_ptr detail); } // namespace internal } // namespace flight } // namespace arrow