| /* |
| * 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. |
| * |
| */ |
| |
| #pragma once |
| |
| #include <event2/buffer.h> |
| |
| #include <deque> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "commands/commander.h" |
| #include "event_util.h" |
| #include "redis_request.h" |
| #include "server/redis_reply.h" |
| |
| class Worker; |
| |
| namespace redis { |
| |
| class Connection : public EvbufCallbackBase<Connection> { |
| public: |
| enum Flag { |
| kSlave = 1 << 4, |
| kMonitor = 1 << 5, |
| kCloseAfterReply = 1 << 6, |
| kCloseAsync = 1 << 7, |
| kMultiExec = 1 << 8, |
| kReadOnly = 1 << 9, |
| kAsking = 1 << 10, |
| }; |
| |
| enum class ReplyMode { |
| ON, // Always reply to every command (default) |
| OFF, // Never reply to any command |
| SKIP // Skip reply for the next command, then automatically switch back to ON |
| }; |
| |
| explicit Connection(bufferevent *bev, Worker *owner); |
| ~Connection(); |
| |
| Connection(const Connection &) = delete; |
| Connection &operator=(const Connection &) = delete; |
| |
| void Close(); |
| void Detach(); |
| void OnRead(bufferevent *bev); |
| void OnWrite(bufferevent *bev); |
| void OnEvent(bufferevent *bev, int16_t events); |
| void SendFile(int fd); |
| std::string ToString(); |
| |
| void Reply(const std::string &msg); |
| const std::vector<std::string> &GetQueuedReplies() const; |
| void ClearQueuedReplies() { queued_replies_.clear(); } |
| RESP GetProtocolVersion() const { return protocol_version_; } |
| void SetProtocolVersion(RESP version) { protocol_version_ = version; } |
| std::string Bool(bool b) const { return redis::Bool(protocol_version_, b); } |
| std::string BigNumber(const std::string &n) const { return redis::BigNumber(protocol_version_, n); } |
| std::string Double(double d) const { return redis::Double(protocol_version_, d); } |
| std::string VerbatimString(std::string ext, const std::string &data) const { |
| return redis::VerbatimString(protocol_version_, std::move(ext), data); |
| } |
| std::string NilString() const { return redis::NilString(protocol_version_); } |
| std::string NilArray() const { return redis::NilArray(protocol_version_); } |
| std::string MultiBulkString(const std::vector<std::string> &values) const { |
| return redis::MultiBulkString(protocol_version_, values); |
| } |
| std::string MultiBulkString(const std::vector<std::string> &values, |
| const std::vector<rocksdb::Status> &statuses) const { |
| return redis::MultiBulkString(protocol_version_, values, statuses); |
| } |
| template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0> |
| std::string HeaderOfSet(T len) const { |
| return redis::HeaderOfSet(protocol_version_, len); |
| } |
| std::string SetOfBulkStrings(const std::vector<std::string> &elems) const { |
| return redis::SetOfBulkStrings(protocol_version_, elems); |
| } |
| template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0> |
| std::string HeaderOfMap(T len) const { |
| return redis::HeaderOfMap(protocol_version_, len); |
| } |
| std::string MapOfBulkStrings(const std::vector<std::string> &elems) const { |
| return redis::MapOfBulkStrings(protocol_version_, elems); |
| } |
| std::string Map(const std::map<std::string, std::string> &map) const { return redis::Map(protocol_version_, map); } |
| template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0> |
| std::string HeaderOfAttribute(T len) const { |
| return redis::HeaderOfAttribute(len); |
| } |
| std::string HeaderOfPush(int64_t len) const { return redis::HeaderOfPush(protocol_version_, len); } |
| |
| using UnsubscribeCallback = std::function<void(std::string, int)>; |
| void SubscribeChannel(const std::string &channel); |
| void UnsubscribeChannel(const std::string &channel); |
| void UnsubscribeAll(const UnsubscribeCallback &reply = nullptr); |
| int SubscriptionsCount(); |
| void PSubscribeChannel(const std::string &pattern); |
| void PUnsubscribeChannel(const std::string &pattern); |
| void PUnsubscribeAll(const UnsubscribeCallback &reply = nullptr); |
| int PSubscriptionsCount(); |
| void SSubscribeChannel(const std::string &channel, uint16_t slot); |
| void SUnsubscribeChannel(const std::string &channel, uint16_t slot); |
| void SUnsubscribeAll(const UnsubscribeCallback &reply = nullptr); |
| int SSubscriptionsCount(); |
| |
| uint64_t GetAge() const; |
| uint64_t GetIdleTime() const; |
| void SetLastInteraction(); |
| std::string GetFlags() const; |
| void EnableFlag(Flag flag); |
| void DisableFlag(Flag flag); |
| bool IsFlagEnabled(Flag flag) const; |
| |
| uint64_t GetID() const { return id_; } |
| void SetID(uint64_t id) { id_ = id; } |
| std::string GetName() const { return name_; } |
| void SetName(std::string name) { name_ = std::move(name); } |
| struct SetInfo { |
| std::string lib_name; |
| std::string lib_ver; |
| }; |
| |
| const SetInfo &GetSetInfo() const { return set_info_; } |
| void SetLibName(std::string lib_name) { set_info_.lib_name = std::move(lib_name); } |
| void SetLibVer(std::string lib_ver) { set_info_.lib_ver = std::move(lib_ver); } |
| std::string GetAddr() const { return addr_; } |
| void SetAddr(std::string ip, uint32_t port); |
| void SetLastCmd(std::string cmd) { last_cmd_ = std::move(cmd); } |
| std::string GetIP() const { return ip_; } |
| uint32_t GetPort() const { return port_; } |
| void SetListeningPort(int port) { listening_port_ = port; } |
| int GetListeningPort() const { return listening_port_; } |
| void SetAnnounceIP(std::string ip) { announce_ip_ = std::move(ip); } |
| std::string GetAnnounceIP() const { return !announce_ip_.empty() ? announce_ip_ : ip_; } |
| uint32_t GetAnnouncePort() const { return listening_port_ != 0 ? listening_port_ : port_; } |
| std::string GetAnnounceAddr() const { return GetAnnounceIP() + ":" + std::to_string(GetAnnouncePort()); } |
| uint64_t GetClientType() const; |
| Server *GetServer() { return srv_; } |
| |
| bool IsAdmin() const { return is_admin_; } |
| void BecomeAdmin() { is_admin_ = true; } |
| void BecomeUser() { is_admin_ = false; } |
| std::string GetNamespace() const { return ns_; } |
| void SetNamespace(std::string ns) { ns_ = std::move(ns); } |
| |
| void NeedFreeBufferEvent(bool need_free = true) { need_free_bev_ = need_free; } |
| void NeedNotFreeBufferEvent() { NeedFreeBufferEvent(false); } |
| bool IsNeedFreeBufferEvent() const { return need_free_bev_; } |
| |
| Worker *Owner() { return owner_; } |
| void SetOwner(Worker *new_owner) { owner_ = new_owner; }; |
| int GetFD() { return bufferevent_getfd(bev_); } |
| evbuffer *Input() { return bufferevent_get_input(bev_); } |
| evbuffer *Output() { return bufferevent_get_output(bev_); } |
| bufferevent *GetBufferEvent() { return bev_; } |
| void ExecuteCommands(std::deque<CommandTokens> *to_process_cmds); |
| Status ExecuteCommand(engine::Context &ctx, const std::string &cmd_name, const std::vector<std::string> &cmd_tokens, |
| Commander *current_cmd, std::string *reply); |
| bool IsProfilingEnabled(const std::string &cmd); |
| void RecordProfilingSampleIfNeed(const std::string &cmd, uint64_t duration); |
| void SetImporting() { importing_ = true; } |
| bool IsImporting() const { return importing_; } |
| bool CanMigrate() const; |
| |
| // CLIENT PAUSE async suspend/resume |
| void Pause(); |
| void Unpause(); |
| bool IsPaused() const { return is_paused_; } |
| |
| // Multi exec |
| void SetInExec() { in_exec_ = true; } |
| bool IsInExec() const { return in_exec_; } |
| bool IsInScript() const { return in_script_; } |
| bool IsMultiError() const { return multi_error_; } |
| void ResetMultiExec(); |
| std::deque<redis::CommandTokens> *GetMultiExecCommands() { return &multi_cmds_; } |
| |
| std::function<void(int)> close_cb = nullptr; |
| |
| std::set<std::string> watched_keys; |
| std::atomic<bool> watched_keys_modified = false; |
| |
| // Reply mode getter/setter |
| void SetReplyMode(ReplyMode mode) { reply_mode_ = mode; } |
| ReplyMode GetReplyMode() const { return reply_mode_; } |
| |
| private: |
| uint64_t id_ = 0; |
| std::atomic<int> flags_ = 0; |
| std::string ns_; |
| std::string name_; |
| SetInfo set_info_; |
| std::string ip_; |
| std::string announce_ip_; |
| uint32_t port_ = 0; |
| std::string addr_; |
| int listening_port_ = 0; |
| bool is_admin_ = false; |
| bool need_free_bev_ = true; |
| std::string last_cmd_; |
| int64_t create_time_; |
| int64_t last_interaction_; |
| |
| bufferevent *bev_; |
| Request req_; |
| Worker *owner_; |
| std::unique_ptr<Commander> saved_current_command_; |
| |
| std::vector<std::string> subscribe_channels_; |
| std::vector<std::string> subscribe_patterns_; |
| std::vector<std::string> subscribe_shard_channels_; |
| |
| Server *srv_; |
| bool in_exec_ = false; |
| bool multi_error_ = false; |
| std::atomic<bool> is_running_ = false; |
| std::deque<redis::CommandTokens> multi_cmds_; |
| bool in_script_ = false; |
| |
| bool importing_ = false; |
| RESP protocol_version_ = RESP::v2; |
| |
| ReplyMode reply_mode_ = ReplyMode::ON; |
| std::vector<std::string> queued_replies_; |
| |
| bool is_paused_ = false; |
| }; |
| |
| } // namespace redis |