#ifndef HV_CHANNEL_HPP_ #define HV_CHANNEL_HPP_ #include #include #include #include "hloop.h" #include "hsocket.h" #include "Buffer.h" namespace hv { class Channel { public: Channel(hio_t* io = NULL) { io_ = io; fd_ = -1; id_ = 0; ctx_ = NULL; status = CLOSED; if (io) { fd_ = hio_fd(io); id_ = hio_id(io); ctx_ = hio_context(io); hio_set_context(io, this); if (hio_is_opened(io)) { status = OPENED; } if (hio_getcb_read(io) == NULL) { hio_setcb_read(io_, on_read); } if (hio_getcb_write(io) == NULL) { hio_setcb_write(io_, on_write); } if (hio_getcb_close(io) == NULL) { hio_setcb_close(io_, on_close); } } } virtual ~Channel() { close(); } hio_t* io() { return io_; } int fd() { return fd_; } uint32_t id() { return id_; } int error() { return hio_error(io_); } // context void* context() { return ctx_; } void setContext(void* ctx) { ctx_ = ctx; } template T* newContext() { ctx_ = new T; return (T*)ctx_; } template T* getContext() { return (T*)ctx_; } template void deleteContext() { if (ctx_) { delete (T*)ctx_; ctx_ = NULL; } } bool isOpened() { if (io_ == NULL || status >= DISCONNECTED) return false; return id_ == hio_id(io_) && hio_is_opened(io_); } bool isClosed() { return !isOpened(); } int startRead() { if (!isOpened()) return -1; return hio_read_start(io_); } int stopRead() { if (!isOpened()) return -1; return hio_read_stop(io_); } int readOnce() { if (!isOpened()) return -1; return hio_read_once(io_); } int readString() { if (!isOpened()) return -1; return hio_readstring(io_); } int readLine() { if (!isOpened()) return -1; return hio_readline(io_); } int readBytes(int len) { if (!isOpened() || len <= 0) return -1; return hio_readbytes(io_, len); } // write thread-safe int write(const void* data, int size) { if (!isOpened()) return -1; return hio_write(io_, data, size); } int write(Buffer* buf) { return write(buf->data(), buf->size()); } int write(const std::string& str) { return write(str.data(), str.size()); } // iobuf setting void setMaxReadBufsize(uint32_t size) { if (io_ == NULL) return; return hio_set_max_read_bufsize(io_, size); } void setMaxWriteBufsize(uint32_t size) { if (io_ == NULL) return; return hio_set_max_write_bufsize(io_, size); } size_t writeBufsize() { if (io_ == NULL) return 0; return hio_write_bufsize(io_); } bool isWriteComplete() { return writeBufsize() == 0; } // close thread-safe int close(bool async = false) { if (!isOpened()) return -1; if (async) { return hio_close_async(io_); } return hio_close(io_); } public: hio_t* io_; int fd_; uint32_t id_; void* ctx_; enum Status { OPENED, CONNECTING, CONNECTED, DISCONNECTED, CLOSED, } status; std::function onread; // NOTE: Use Channel::isWriteComplete in onwrite callback to determine whether all data has been written. std::function onwrite; std::function onclose; private: static void on_read(hio_t* io, void* data, int readbytes) { Channel* channel = (Channel*)hio_context(io); if (channel && channel->onread) { Buffer buf(data, readbytes); channel->onread(&buf); } } static void on_write(hio_t* io, const void* data, int writebytes) { Channel* channel = (Channel*)hio_context(io); if (channel && channel->onwrite) { Buffer buf((void*)data, writebytes); channel->onwrite(&buf); } } static void on_close(hio_t* io) { Channel* channel = (Channel*)hio_context(io); if (channel) { channel->status = CLOSED; if (channel->onclose) { channel->onclose(); } } } }; class SocketChannel : public Channel { public: std::function onconnect; // only for TcpClient std::function heartbeat; SocketChannel(hio_t* io) : Channel(io) { } virtual ~SocketChannel() {} // SSL/TLS int enableSSL() { if (io_ == NULL) return -1; return hio_enable_ssl(io_); } bool isSSL() { if (io_ == NULL) return false; return hio_is_ssl(io_); } int setSSL(hssl_t ssl) { if (io_ == NULL) return -1; return hio_set_ssl(io_, ssl); } int setSslCtx(hssl_ctx_t ssl_ctx) { if (io_ == NULL) return -1; return hio_set_ssl_ctx(io_, ssl_ctx); } int newSslCtx(hssl_ctx_opt_t* opt) { if (io_ == NULL) return -1; return hio_new_ssl_ctx(io_, opt); } // for hssl_set_sni_hostname int setHostname(const std::string& hostname) { if (io_ == NULL) return -1; return hio_set_hostname(io_, hostname.c_str()); } // timeout void setConnectTimeout(int timeout_ms) { if (io_ == NULL) return; hio_set_connect_timeout(io_, timeout_ms); } void setCloseTimeout(int timeout_ms) { if (io_ == NULL) return; hio_set_close_timeout(io_, timeout_ms); } void setReadTimeout(int timeout_ms) { if (io_ == NULL) return; hio_set_read_timeout(io_, timeout_ms); } void setWriteTimeout(int timeout_ms) { if (io_ == NULL) return; hio_set_write_timeout(io_, timeout_ms); } void setKeepaliveTimeout(int timeout_ms) { if (io_ == NULL) return; hio_set_keepalive_timeout(io_, timeout_ms); } // heartbeat void setHeartbeat(int interval_ms, std::function fn) { if (io_ == NULL) return; heartbeat = std::move(fn); hio_set_heartbeat(io_, interval_ms, send_heartbeat); } // unpack void setUnpack(unpack_setting_t* setting) { if (io_ == NULL) return; hio_set_unpack(io_, setting); } int startConnect(int port, const char* host = "127.0.0.1") { sockaddr_u peeraddr; memset(&peeraddr, 0, sizeof(peeraddr)); int ret = sockaddr_set_ipport(&peeraddr, host, port); if (ret != 0) { // hloge("unknown host %s", host); return ret; } return startConnect(&peeraddr.sa); } int startConnect(struct sockaddr* peeraddr) { if (io_ == NULL) return -1; hio_set_peeraddr(io_, peeraddr, SOCKADDR_LEN(peeraddr)); return startConnect(); } int startConnect() { if (io_ == NULL) return -1; status = CONNECTING; hio_setcb_connect(io_, on_connect); return hio_connect(io_); } bool isConnected() { return status == CONNECTED && isOpened(); } std::string localaddr() { if (io_ == NULL) return ""; struct sockaddr* addr = hio_localaddr(io_); char buf[SOCKADDR_STRLEN] = {0}; return SOCKADDR_STR(addr, buf); } std::string peeraddr() { if (io_ == NULL) return ""; struct sockaddr* addr = hio_peeraddr(io_); char buf[SOCKADDR_STRLEN] = {0}; return SOCKADDR_STR(addr, buf); } private: static void on_connect(hio_t* io) { SocketChannel* channel = (SocketChannel*)hio_context(io); if (channel) { channel->status = CONNECTED; if (channel->onconnect) { channel->onconnect(); } } } static void send_heartbeat(hio_t* io) { SocketChannel* channel = (SocketChannel*)hio_context(io); if (channel && channel->heartbeat) { channel->heartbeat(); } } }; typedef std::shared_ptr ChannelPtr; typedef std::shared_ptr SocketChannelPtr; } #endif // HV_CHANNEL_HPP_