remove hv

main
hkc320 2024-09-10 14:38:23 +08:00
parent 14c7af02bf
commit bac55f5902
80 changed files with 254 additions and 36114 deletions

View File

@ -0,0 +1,74 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
CMakeLists.txt.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe

View File

@ -0,0 +1,56 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
QMAKE_LFLAGS += /ignore:4099
QMAKE_CXXFLAGS_WARN_ON += -wd4100
include("$$PWD/QtSingleApplication/qtsingleapplication.pri")
DEFINES += HAVE_CONFIG_H
DEFINES += _CRT_SECURE_NO_WARNINGS
INCLUDEPATH += $$PWD/../../SDK/include
CONFIG(debug, debug|debug) {
INCLUDEPATH += "D:/Visual Leak Detector/include"
win32:LIBS += "D:/Visual Leak Detector/lib/Win64/vld.lib"
}
CONFIG(debug, debug|release) {
win32:LIBS += $$PWD/..\..\SDK\lib\libdesd.lib
win32:LIBS += $$PWD/..\..\SDK\lib\OpenSSL_VC\libcrypto64MDd.lib
win32:LIBS += $$PWD/..\..\SDK\lib\OpenSSL_VC\libssl64MDd.lib
win32:LIBS += $$PWD/..\..\SDK\lib\hv.lib
win32:LIBS += Ws2_32.lib
}else{
win32:LIBS += $$PWD/..\..\SDK\lib\libdes.lib
win32:LIBS += $$PWD/..\..\SDK\lib\OpenSSL_VC\libcrypto64MD.lib
win32:LIBS += $$PWD/..\..\SDK\lib\OpenSSL_VC\libssl64MD.lib
win32:LIBS += $$PWD/..\..\SDK\lib\hv.lib
win32:LIBS += Ws2_32.lib
}
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp \
cloggermaganer.cpp
HEADERS += \
mainwindow.h \
cloggermaganer.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

View File

@ -0,0 +1,66 @@
#ifdef _DEBUG
#include <vld.h>
#endif
#include "mainwindow.h"
#include "qtsingleapplication.h"
#include <QApplication>
#include <QTranslator>
#include <QMessageBox>
#include <QScreen>
#include <hv/hv.h>
#include <hv/hmain.h>
#include <hv/iniparser.h>
#include <hv/hloop.h>
#include <hv/hsocket.h>
#include <hv/hssl.h>
#include "cloggermaganer.h"
int main(int argc, char *argv[])
{
QString app_unique_name("emsConfigurer_application_name");
QtSingleApplication app(app_unique_name,argc, argv);
if(app.isRunning())
{
QMessageBox::information(nullptr, "EMS Configurer", "Another instance is already running.");
app.sendMessage("raise_window_noop", 1000); //1s后激活前个实例
return EXIT_SUCCESS;
}
main_ctx_init(argc, argv);
QString appDir = QCoreApplication::applicationDirPath();
std::string logFilePath = (appDir + QString::fromStdString("/emsConfigurer.log")).toStdString();
hlog_set_file(logFilePath.c_str());
hlogi("=========--- Welcome to the Earth ---=========\n");
hlogi("%s version: %s\n", argv[0], "1.1.0");
hlog_fsync();
hlogi("%s versionversionversionversionversionversion: %s\n", argv[0], "1.1.0");
QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages)
{
const QString baseName = "emsConfigurer_" + QLocale(locale).name();
if (translator.load(":/i18n/" + baseName))
{
app.installTranslator(&translator);
break;
}
}
MainWindow w;
//获取窗口尺寸并居中
QScreen *scr = app.primaryScreen();
int scr_w = scr->size().width();
int scr_h = scr->size().height();
w.move((scr_w - w.width()) / 2, (scr_h - w.height()) / 2);
w.show();
return app.exec();
}

View File

@ -0,0 +1,15 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}

View File

@ -0,0 +1,21 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar"/>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,15 +0,0 @@
#ifndef HV_BUFFER_HPP_
#define HV_BUFFER_HPP_
#include <memory>
#include "hbuf.h"
namespace hv {
typedef HBuf Buffer;
typedef std::shared_ptr<Buffer> BufferPtr;
}
#endif // HV_BUFFER_HPP_

View File

@ -1,377 +0,0 @@
#ifndef HV_CHANNEL_HPP_
#define HV_CHANNEL_HPP_
#include <string>
#include <functional>
#include <memory>
#include <atomic>
#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() {
if (isOpened()) {
close();
// NOTE: Detach after destructor to avoid triggering onclose
if (io_ && id_ == hio_id(io_)) {
hio_set_context(io_, NULL);
}
}
}
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<class T>
T* newContext() {
ctx_ = new T;
return (T*)ctx_;
}
template<class T>
T* getContext() {
return (T*)ctx_;
}
template<class T>
void deleteContext() {
if (ctx_) {
delete (T*)ctx_;
ctx_ = NULL;
}
}
// contextPtr
std::shared_ptr<void> contextPtr() {
return contextPtr_;
}
void setContextPtr(const std::shared_ptr<void>& ctx) {
contextPtr_ = ctx;
}
void setContextPtr(std::shared_ptr<void>&& ctx) {
contextPtr_ = std::move(ctx);
}
template<class T>
std::shared_ptr<T> newContextPtr() {
contextPtr_ = std::make_shared<T>();
return std::static_pointer_cast<T>(contextPtr_);
}
template<class T>
std::shared_ptr<T> getContextPtr() {
return std::static_pointer_cast<T>(contextPtr_);
}
void deleteContextPtr() {
contextPtr_.reset();
}
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 setReadBuf(void* buf, size_t len) {
if (io_ == NULL) return;
hio_set_readbuf(io_, buf, len);
}
void setMaxReadBufsize(uint32_t size) {
if (io_ == NULL) return;
hio_set_max_read_bufsize(io_, size);
}
void setMaxWriteBufsize(uint32_t size) {
if (io_ == NULL) 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 (isClosed()) return -1;
status = CLOSED;
return async ? hio_close_async(io_) : hio_close(io_);
}
public:
hio_t* io_;
int fd_;
uint32_t id_;
void* ctx_;
enum Status {
OPENED,
CONNECTING,
CONNECTED,
DISCONNECTED,
CLOSED,
};
std::atomic<Status> status;
std::function<void(Buffer*)> onread;
// NOTE: Use Channel::isWriteComplete in onwrite callback to determine whether all data has been written.
std::function<void(Buffer*)> onwrite;
std::function<void()> onclose;
std::shared_ptr<void> contextPtr_;
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<void()> onconnect; // only for TcpClient
std::function<void()> 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
// NOTE: Beware of circular reference problems caused by passing SocketChannelPtr by value.
void setHeartbeat(int interval_ms, std::function<void()> fn) {
if (io_ == NULL) return;
heartbeat = std::move(fn);
hio_set_heartbeat(io_, interval_ms, send_heartbeat);
}
/*
* unpack
*
* NOTE: unpack_setting_t of multiple IOs of the same function also are same,
* so only the pointer of unpack_setting_t is stored in hio_t,
* the life time of unpack_setting_t shoud be guaranteed by caller.
*/
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<Channel> ChannelPtr;
typedef std::shared_ptr<SocketChannel> SocketChannelPtr;
}
#endif // HV_CHANNEL_HPP_

View File

@ -1,47 +0,0 @@
#ifndef HV_EVENT_HPP_
#define HV_EVENT_HPP_
#include <functional>
#include <memory>
#include "hloop.h"
namespace hv {
struct Event;
struct Timer;
typedef uint64_t TimerID;
#define INVALID_TIMER_ID ((hv::TimerID)-1)
typedef std::function<void(Event*)> EventCallback;
typedef std::function<void(TimerID)> TimerCallback;
struct Event {
hevent_t event;
EventCallback cb;
Event(EventCallback cb = NULL) {
memset(&event, 0, sizeof(hevent_t));
this->cb = std::move(cb);
}
};
struct Timer {
htimer_t* timer;
TimerCallback cb;
uint32_t repeat;
Timer(htimer_t* timer = NULL, TimerCallback cb = NULL, uint32_t repeat = INFINITE) {
this->timer = timer;
this->cb = std::move(cb);
this->repeat = repeat;
}
};
typedef std::shared_ptr<Event> EventPtr;
typedef std::shared_ptr<Timer> TimerPtr;
}
#endif // HV_EVENT_HPP_

View File

@ -1,277 +0,0 @@
#ifndef HV_EVENT_LOOP_HPP_
#define HV_EVENT_LOOP_HPP_
#include <functional>
#include <queue>
#include <map>
#include <mutex>
#include "hloop.h"
#include "hthread.h"
#include "Status.h"
#include "Event.h"
#include "ThreadLocalStorage.h"
namespace hv {
class EventLoop : public Status {
public:
typedef std::function<void()> Functor;
// New an EventLoop using an existing hloop_t object,
// so we can embed an EventLoop object into the old application based on hloop.
// NOTE: Be careful to deal with destroy of hloop_t.
EventLoop(hloop_t* loop = NULL) {
setStatus(kInitializing);
if (loop) {
loop_ = loop;
is_loop_owner = false;
} else {
loop_ = hloop_new(HLOOP_FLAG_AUTO_FREE);
is_loop_owner = true;
}
connectionNum = 0;
nextTimerID = 0;
setStatus(kInitialized);
}
~EventLoop() {
stop();
}
hloop_t* loop() {
return loop_;
}
// @brief Run loop forever
void run() {
if (loop_ == NULL) return;
if (status() == kRunning) return;
ThreadLocalStorage::set(ThreadLocalStorage::EVENT_LOOP, this);
setStatus(kRunning);
hloop_run(loop_);
setStatus(kStopped);
}
// stop thread-safe
void stop() {
if (loop_ == NULL) return;
if (status() < kRunning) {
if (is_loop_owner) {
hloop_free(&loop_);
}
loop_ = NULL;
return;
}
setStatus(kStopping);
hloop_stop(loop_);
loop_ = NULL;
}
void pause() {
if (loop_ == NULL) return;
if (isRunning()) {
hloop_pause(loop_);
setStatus(kPause);
}
}
void resume() {
if (loop_ == NULL) return;
if (isPause()) {
hloop_resume(loop_);
setStatus(kRunning);
}
}
// Timer interfaces: setTimer, killTimer, resetTimer
TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
if (loop_ == NULL) return INVALID_TIMER_ID;
assertInLoopThread();
htimer_t* htimer = htimer_add(loop_, onTimer, timeout_ms, repeat);
assert(htimer != NULL);
if (timerID == INVALID_TIMER_ID) {
timerID = generateTimerID();
}
hevent_set_id(htimer, timerID);
hevent_set_userdata(htimer, this);
timers[timerID] = std::make_shared<Timer>(htimer, cb, repeat);
return timerID;
}
// setTimerInLoop thread-safe
TimerID setTimerInLoop(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
if (timerID == INVALID_TIMER_ID) {
timerID = generateTimerID();
}
runInLoop(std::bind(&EventLoop::setTimer, this, timeout_ms, cb, repeat, timerID));
return timerID;
}
// alias javascript setTimeout, setInterval
// setTimeout thread-safe
TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return setTimerInLoop(timeout_ms, cb, 1);
}
// setInterval thread-safe
TimerID setInterval(int interval_ms, TimerCallback cb) {
return setTimerInLoop(interval_ms, cb, INFINITE);
}
// killTimer thread-safe
void killTimer(TimerID timerID) {
runInLoop([timerID, this](){
auto iter = timers.find(timerID);
if (iter != timers.end()) {
htimer_del(iter->second->timer);
timers.erase(iter);
}
});
}
// resetTimer thread-safe
void resetTimer(TimerID timerID, int timeout_ms = 0) {
runInLoop([timerID, timeout_ms, this](){
auto iter = timers.find(timerID);
if (iter != timers.end()) {
htimer_reset(iter->second->timer, timeout_ms);
if (iter->second->repeat == 0) {
iter->second->repeat = 1;
}
}
});
}
long tid() {
if (loop_ == NULL) return hv_gettid();
return hloop_tid(loop_);
}
bool isInLoopThread() {
if (loop_ == NULL) return false;
return hv_gettid() == hloop_tid(loop_);
}
void assertInLoopThread() {
assert(isInLoopThread());
}
void runInLoop(Functor fn) {
if (isRunning() && isInLoopThread()) {
if (fn) fn();
} else {
queueInLoop(std::move(fn));
}
}
void queueInLoop(Functor fn) {
postEvent([fn](Event* ev) {
if (fn) fn();
});
}
void postEvent(EventCallback cb) {
if (loop_ == NULL) return;
EventPtr ev = std::make_shared<Event>(cb);
hevent_set_userdata(&ev->event, this);
ev->event.cb = onCustomEvent;
mutex_.lock();
customEvents.push(ev);
mutex_.unlock();
hloop_post_event(loop_, &ev->event);
}
private:
TimerID generateTimerID() {
return (((TimerID)tid() & 0xFFFFFFFF) << 32) | ++nextTimerID;
}
static void onTimer(htimer_t* htimer) {
EventLoop* loop = (EventLoop*)hevent_userdata(htimer);
TimerID timerID = hevent_id(htimer);
TimerPtr timer = NULL;
auto iter = loop->timers.find(timerID);
if (iter != loop->timers.end()) {
timer = iter->second;
if (timer->repeat != INFINITE) --timer->repeat;
}
if (timer) {
if (timer->cb) timer->cb(timerID);
if (timer->repeat == 0) {
// htimer_t alloc and free by hloop, but timers[timerID] managed by EventLoop.
loop->timers.erase(timerID);
}
}
}
static void onCustomEvent(hevent_t* hev) {
EventLoop* loop = (EventLoop*)hevent_userdata(hev);
loop->mutex_.lock();
EventPtr ev = loop->customEvents.front();
loop->customEvents.pop();
loop->mutex_.unlock();
if (ev && ev->cb) ev->cb(ev.get());
}
public:
std::atomic<uint32_t> connectionNum; // for LB_LeastConnections
private:
hloop_t* loop_;
bool is_loop_owner;
std::mutex mutex_;
std::queue<EventPtr> customEvents; // GUAREDE_BY(mutex_)
std::map<TimerID, TimerPtr> timers;
std::atomic<TimerID> nextTimerID;
};
typedef std::shared_ptr<EventLoop> EventLoopPtr;
// ThreadLocalStorage
static inline EventLoop* tlsEventLoop() {
return (EventLoop*)ThreadLocalStorage::get(ThreadLocalStorage::EVENT_LOOP);
}
#define currentThreadEventLoop tlsEventLoop()
static inline TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return INVALID_TIMER_ID;
return loop->setTimer(timeout_ms, cb, repeat);
}
static inline void killTimer(TimerID timerID) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return;
loop->killTimer(timerID);
}
static inline void resetTimer(TimerID timerID, int timeout_ms) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return;
loop->resetTimer(timerID, timeout_ms);
}
static inline TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return setTimer(timeout_ms, cb, 1);
}
static inline TimerID setInterval(int interval_ms, TimerCallback cb) {
return setTimer(interval_ms, cb, INFINITE);
}
}
#endif // HV_EVENT_LOOP_HPP_

View File

@ -1,115 +0,0 @@
#ifndef HV_EVENT_LOOP_THREAD_HPP_
#define HV_EVENT_LOOP_THREAD_HPP_
#include <thread>
#include "hlog.h"
#include "EventLoop.h"
namespace hv {
class EventLoopThread : public Status {
public:
// Return 0 means OK, other failed.
typedef std::function<int()> Functor;
EventLoopThread(EventLoopPtr loop = NULL) {
setStatus(kInitializing);
loop_ = loop ? loop : std::make_shared<EventLoop>();
setStatus(kInitialized);
}
~EventLoopThread() {
stop();
join();
}
const EventLoopPtr& loop() {
return loop_;
}
hloop_t* hloop() {
return loop_->loop();
}
bool isRunning() {
return loop_->isRunning();
}
// @param wait_thread_started: if ture this method will block until loop_thread started.
// @param pre: This functor will be executed when loop_thread started.
// @param post:This Functor will be executed when loop_thread stopped.
void start(bool wait_thread_started = true,
Functor pre = Functor(),
Functor post = Functor()) {
if (status() >= kStarting && status() < kStopped) return;
setStatus(kStarting);
thread_ = std::make_shared<std::thread>(&EventLoopThread::loop_thread, this, pre, post);
if (wait_thread_started) {
while (loop_->status() < kRunning) {
hv_delay(1);
}
}
}
// @param wait_thread_started: if ture this method will block until loop_thread stopped.
// stop thread-safe
void stop(bool wait_thread_stopped = false) {
if (status() < kStarting || status() >= kStopping) return;
setStatus(kStopping);
long loop_tid = loop_->tid();
loop_->stop();
if (wait_thread_stopped) {
if (hv_gettid() == loop_tid) return;
join();
}
}
// @brief join loop_thread
// @note destructor will join loop_thread if you forget to call this method.
void join() {
if (thread_ && thread_->joinable()) {
thread_->join();
thread_ = NULL;
}
}
private:
void loop_thread(const Functor& pre, const Functor& post) {
hlogi("EventLoopThread started, tid=%ld", hv_gettid());
setStatus(kStarted);
if (pre) {
loop_->queueInLoop([this, pre]{
if (pre() != 0) {
loop_->stop();
}
});
}
loop_->run();
assert(loop_->isStopped());
if (post) {
post();
}
setStatus(kStopped);
hlogi("EventLoopThread stopped, tid=%ld", hv_gettid());
}
private:
EventLoopPtr loop_;
std::shared_ptr<std::thread> thread_;
};
typedef std::shared_ptr<EventLoopThread> EventLoopThreadPtr;
}
#endif // HV_EVENT_LOOP_THREAD_HPP_

View File

@ -1,138 +0,0 @@
#ifndef HV_EVENT_LOOP_THREAD_POOL_HPP_
#define HV_EVENT_LOOP_THREAD_POOL_HPP_
#include "EventLoopThread.h"
#include "hbase.h"
namespace hv {
class EventLoopThreadPool : public Status {
public:
EventLoopThreadPool(int thread_num = std::thread::hardware_concurrency()) {
setStatus(kInitializing);
thread_num_ = thread_num;
next_loop_idx_ = 0;
setStatus(kInitialized);
}
~EventLoopThreadPool() {
stop();
join();
}
int threadNum() {
return thread_num_;
}
void setThreadNum(int num) {
thread_num_ = num;
}
EventLoopPtr nextLoop(load_balance_e lb = LB_RoundRobin) {
size_t numLoops = loop_threads_.size();
if (numLoops == 0) return NULL;
size_t idx = 0;
if (lb == LB_RoundRobin) {
if (++next_loop_idx_ >= numLoops) next_loop_idx_ = 0;
idx = next_loop_idx_ % numLoops;
} else if (lb == LB_Random) {
idx = hv_rand(0, numLoops - 1);
} else if (lb == LB_LeastConnections) {
for (size_t i = 1; i < numLoops; ++i) {
if (loop_threads_[i]->loop()->connectionNum < loop_threads_[idx]->loop()->connectionNum) {
idx = i;
}
}
} else {
// Not Implemented
}
return loop_threads_[idx]->loop();
}
EventLoopPtr loop(int idx = -1) {
if (idx >= 0 && idx < (int)loop_threads_.size()) {
return loop_threads_[idx]->loop();
}
return nextLoop();
}
hloop_t* hloop(int idx = -1) {
EventLoopPtr ptr = loop(idx);
return ptr ? ptr->loop() : NULL;
}
// @param wait_threads_started: if ture this method will block until all loop_threads started.
// @param pre: This functor will be executed when loop_thread started.
// @param post:This Functor will be executed when loop_thread stopped.
void start(bool wait_threads_started = false,
std::function<void(const EventLoopPtr&)> pre = NULL,
std::function<void(const EventLoopPtr&)> post = NULL) {
if (thread_num_ == 0) return;
if (status() >= kStarting && status() < kStopped) return;
setStatus(kStarting);
auto started_cnt = std::make_shared<std::atomic<int>>(0);
auto exited_cnt = std::make_shared<std::atomic<int>>(0);
loop_threads_.clear();
for (int i = 0; i < thread_num_; ++i) {
auto loop_thread = std::make_shared<EventLoopThread>();
const EventLoopPtr& loop = loop_thread->loop();
loop_thread->start(false,
[this, started_cnt, pre, &loop]() {
if (++(*started_cnt) == thread_num_) {
setStatus(kRunning);
}
if (pre) pre(loop);
return 0;
},
[this, exited_cnt, post, &loop]() {
if (post) post(loop);
if (++(*exited_cnt) == thread_num_) {
setStatus(kStopped);
}
return 0;
}
);
loop_threads_.push_back(loop_thread);
}
if (wait_threads_started) {
while (status() < kRunning) {
hv_delay(1);
}
}
}
// @param wait_threads_started: if ture this method will block until all loop_threads stopped.
// stop thread-safe
void stop(bool wait_threads_stopped = false) {
if (status() < kStarting || status() >= kStopping) return;
setStatus(kStopping);
for (auto& loop_thread : loop_threads_) {
loop_thread->stop(false);
}
if (wait_threads_stopped) {
join();
}
}
// @brief join all loop_threads
// @note destructor will join loop_threads if you forget to call this method.
void join() {
for (auto& loop_thread : loop_threads_) {
loop_thread->join();
}
}
private:
int thread_num_;
std::vector<EventLoopThreadPtr> loop_threads_;
std::atomic<unsigned int> next_loop_idx_;
};
}
#endif // HV_EVENT_LOOP_THREAD_POOL_HPP_

View File

@ -1,213 +0,0 @@
#ifndef HV_HTTP_CONTEXT_H_
#define HV_HTTP_CONTEXT_H_
#include "hexport.h"
#include "HttpMessage.h"
#include "HttpResponseWriter.h"
namespace hv {
struct HttpService;
struct HV_EXPORT HttpContext {
HttpService* service;
HttpRequestPtr request;
HttpResponsePtr response;
HttpResponseWriterPtr writer;
void* userdata;
HttpContext() {
service = NULL;
userdata = NULL;
}
// HttpRequest aliases
// return request->xxx
std::string ip() {
return request->client_addr.ip;
}
int port() {
return request->client_addr.port;
}
http_method method() {
return request->method;
}
std::string url() {
return request->url;
}
std::string path() {
return request->Path();
}
std::string fullpath() {
return request->FullPath();
}
std::string host() {
return request->Host();
}
const http_headers& headers() {
return request->headers;
}
std::string header(const char* key, const std::string& defvalue = hv::empty_string) {
return request->GetHeader(key, defvalue);
}
const hv::QueryParams& params() {
return request->query_params;
}
std::string param(const char* key, const std::string& defvalue = hv::empty_string) {
return request->GetParam(key, defvalue);
}
const HttpCookie& cookie(const char* name) {
return request->GetCookie(name);
}
int length() {
return request->ContentLength();
}
http_content_type type() {
return request->ContentType();
}
bool is(http_content_type content_type) {
return request->ContentType() == content_type;
}
bool is(const char* content_type) {
return request->ContentType() == http_content_type_enum(content_type);
}
std::string& body() {
return request->body;
}
#ifndef WITHOUT_HTTP_CONTENT
// Content-Type: application/json
const hv::Json& json() {
return request->GetJson();
}
// Content-Type: multipart/form-data
const hv::MultiPart& form() {
return request->GetForm();
}
std::string form(const char* name, const std::string& defvalue = hv::empty_string) {
return request->GetFormData(name, defvalue);
}
// Content-Type: application/x-www-form-urlencoded
const hv::KeyValue& urlencoded() {
return request->GetUrlEncoded();
}
std::string urlencoded(const char* key, const std::string& defvalue = hv::empty_string) {
return request->GetUrlEncoded(key, defvalue);
}
// T=[bool, int, int64_t, float, double]
template<typename T>
T get(const char* key, T defvalue = 0) {
return request->Get(key, defvalue);
}
std::string get(const char* key, const std::string& defvalue = hv::empty_string) {
return request->GetString(key, defvalue);
}
#endif
// HttpResponse aliases
// response->xxx = xxx
void setStatus(http_status status) {
response->status_code = status;
}
void setContentType(http_content_type type) {
response->content_type = type;
}
void setContentType(const char* type) {
response->content_type = http_content_type_enum(type);
}
void setHeader(const char* key, const std::string& value) {
response->SetHeader(key, value);
if (stricmp(key, "Content-Type") == 0) {
setContentType(value.c_str());
}
}
void setCookie(const HttpCookie& cookie) {
response->AddCookie(cookie);
}
void setBody(const std::string& body) {
response->body = body;
}
// response->sendXxx
int send() {
if (writer) {
writer->End();
}
return response->status_code;
}
int send(const std::string& str, http_content_type type = APPLICATION_JSON) {
response->content_type = type;
response->body = str;
return send();
}
int sendString(const std::string& str) {
response->String(str);
return send();
}
int sendData(void* data, int len, bool nocopy = true) {
response->Data(data, len, nocopy);
return send();
}
int sendFile(const char* filepath) {
response->File(filepath);
return send();
}
#ifndef WITHOUT_HTTP_CONTENT
// T=[bool, int, int64_t, float, double, string]
template<typename T>
void set(const char* key, const T& value) {
response->Set(key, value);
}
// @see HttpMessage::Json
// @usage https://github.com/nlohmann/json
template<typename T>
int sendJson(const T& t) {
response->Json(t);
return send();
}
#endif
int redirect(const std::string& location, http_status status = HTTP_STATUS_FOUND) {
response->Redirect(location, status);
return send();
}
int close() {
return writer ? writer->close(true) : -1;
}
};
} // end namespace hv
typedef std::shared_ptr<hv::HttpContext> HttpContextPtr;
#endif // HV_HTTP_CONTEXT_H_

View File

@ -1,501 +0,0 @@
#ifndef HV_HTTP_MESSAGE_H_
#define HV_HTTP_MESSAGE_H_
/*
* @class HttpMessage
* HttpRequest extends HttpMessage
* HttpResponse extends HttpMessage
*
* @member
* request-line: GET / HTTP/1.1\r\n => method path
* response-line: HTTP/1.1 200 OK\r\n => status_code
* headers, cookies
* body
*
* content, content_length, content_type
* json, form, kv
*
* @function
* Content, ContentLength, ContentType
* ParseUrl, ParseBody
* DumpUrl, DumpHeaders, DumpBody, Dump
* GetHeader, GetParam, GetJson, GetFormData, GetUrlEncoded
* SetHeader, SetParam, SetBody, SetFormData, SetUrlEncoded
* Get<T>, Set<T>
* GetString, GetBool, GetInt, GetFloat
* String, Data, Json, File, FormFile
*
* @example
* examples/http_server_test.cpp
* examples/http_client_test.cpp
* examples/httpd
*
*/
#include <memory>
#include <string>
#include <map>
#include <functional>
#include "hexport.h"
#include "hbase.h"
#include "hstring.h"
#include "hfile.h"
#include "hpath.h"
#include "httpdef.h"
#include "http_content.h"
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
// Cookie: sessionid=1; domain=.example.com; path=/; max-age=86400; secure; httponly
struct HV_EXPORT HttpCookie {
std::string name;
std::string value;
std::string domain;
std::string path;
std::string expires;
int max_age;
bool secure;
bool httponly;
enum SameSite {
Default,
Strict,
Lax,
None
} samesite;
enum Priority {
NotSet,
Low,
Medium,
High,
} priority;
hv::KeyValue kv; // for multiple names
HttpCookie();
void init();
void reset();
bool parse(const std::string& str);
std::string dump() const;
};
typedef std::map<std::string, std::string, hv::StringCaseLess> http_headers;
typedef std::vector<HttpCookie> http_cookies;
typedef std::string http_body;
HV_EXPORT extern http_headers DefaultHeaders;
HV_EXPORT extern http_body NoBody;
HV_EXPORT extern HttpCookie NoCookie;
class HV_EXPORT HttpMessage {
public:
static char s_date[32];
int type;
unsigned short http_major;
unsigned short http_minor;
http_headers headers;
http_cookies cookies;
http_body body;
// http_cb
std::function<void(HttpMessage*, http_parser_state state, const char* data, size_t size)> http_cb;
// structured content
void* content; // DATA_NO_COPY
size_t content_length;
http_content_type content_type;
#ifndef WITHOUT_HTTP_CONTENT
hv::Json json; // APPLICATION_JSON
hv::MultiPart form; // MULTIPART_FORM_DATA
hv::KeyValue kv; // X_WWW_FORM_URLENCODED
// T=[bool, int, int64_t, float, double]
template<typename T>
T Get(const char* key, T defvalue = 0);
std::string GetString(const char* key, const std::string& = "");
bool GetBool(const char* key, bool defvalue = 0);
int64_t GetInt(const char* key, int64_t defvalue = 0);
double GetFloat(const char* key, double defvalue = 0);
template<typename T>
void Set(const char* key, const T& value) {
switch (ContentType()) {
case APPLICATION_JSON:
json[key] = value;
break;
case MULTIPART_FORM_DATA:
form[key] = hv::FormData(value);
break;
case X_WWW_FORM_URLENCODED:
kv[key] = hv::to_string(value);
break;
default:
break;
}
}
/*
* @usage https://github.com/nlohmann/json
*
* null: Json(nullptr);
* boolean: Json(true);
* number: Json(123);
* string: Json("hello");
* object: Json(std::map<string, ValueType>);
* Json(hv::Json::object({
{"k1", "v1"},
{"k2", "v2"}
}));
* array: Json(std::vector<ValueType>);
Json(hv::Json::array(
{1, 2, 3}
));
*/
// Content-Type: application/json
template<typename T>
int Json(const T& t) {
content_type = APPLICATION_JSON;
hv::Json j(t);
body = j.dump(2);
return 200;
}
const hv::Json& GetJson() {
if (json.empty() && ContentType() == APPLICATION_JSON) {
ParseBody();
}
return json;
}
// Content-Type: multipart/form-data
template<typename T>
void SetFormData(const char* name, const T& t) {
form[name] = hv::FormData(t);
}
void SetFormFile(const char* name, const char* filepath) {
form[name] = hv::FormData(NULL, filepath);
}
int FormFile(const char* name, const char* filepath) {
content_type = MULTIPART_FORM_DATA;
form[name] = hv::FormData(NULL, filepath);
return 200;
}
const hv::MultiPart& GetForm() {
if (form.empty() && ContentType() == MULTIPART_FORM_DATA) {
ParseBody();
}
return form;
}
std::string GetFormData(const char* name, const std::string& defvalue = hv::empty_string) {
if (form.empty() && ContentType() == MULTIPART_FORM_DATA) {
ParseBody();
}
auto iter = form.find(name);
return iter == form.end() ? defvalue : iter->second.content;
}
int SaveFormFile(const char* name, const char* path) {
if (ContentType() != MULTIPART_FORM_DATA) {
return HTTP_STATUS_BAD_REQUEST;
}
if (form.empty()) {
ParseBody();
if (form.empty()) return HTTP_STATUS_BAD_REQUEST;
}
auto iter = form.find(name);
if (iter == form.end()) {
return HTTP_STATUS_BAD_REQUEST;
}
const auto& formdata = iter->second;
if (formdata.content.empty()) {
return HTTP_STATUS_BAD_REQUEST;
}
std::string filepath(path);
if (HPath::isdir(path)) {
filepath = HPath::join(filepath, formdata.filename);
}
HFile file;
if (file.open(filepath.c_str(), "wb") != 0) {
return HTTP_STATUS_INTERNAL_SERVER_ERROR;
}
file.write(formdata.content.data(), formdata.content.size());
return 200;
}
// Content-Type: application/x-www-form-urlencoded
template<typename T>
void SetUrlEncoded(const char* key, const T& t) {
kv[key] = hv::to_string(t);
}
const hv::KeyValue& GetUrlEncoded() {
if (kv.empty() && ContentType() == X_WWW_FORM_URLENCODED) {
ParseBody();
}
return kv;
}
std::string GetUrlEncoded(const char* key, const std::string& defvalue = hv::empty_string) {
if (kv.empty() && ContentType() == X_WWW_FORM_URLENCODED) {
ParseBody();
}
auto iter = kv.find(key);
return iter == kv.end() ? defvalue : iter->second;
}
#endif
HttpMessage();
virtual ~HttpMessage();
void Init();
virtual void Reset();
// structured-content -> content_type <-> headers["Content-Type"]
void FillContentType();
// body.size -> content_length <-> headers["Content-Length"]
void FillContentLength();
bool IsChunked();
bool IsKeepAlive();
bool IsUpgrade();
// headers
void SetHeader(const char* key, const std::string& value);
std::string GetHeader(const char* key, const std::string& defvalue = hv::empty_string);
// cookies
void AddCookie(const HttpCookie& cookie);
const HttpCookie& GetCookie(const std::string& name);
// body
void SetBody(const std::string& body);
const std::string& Body();
// headers -> string
void DumpHeaders(std::string& str);
// structured content -> body
void DumpBody();
void DumpBody(std::string& str);
// body -> structured content
// @retval 0:succeed
int ParseBody();
virtual std::string Dump(bool is_dump_headers, bool is_dump_body);
void* Content() {
if (content == NULL && body.size() != 0) {
content = (void*)body.data();
content_length = body.size();
}
return content;
}
size_t ContentLength() {
if (content_length == 0) {
FillContentLength();
}
return content_length;
}
http_content_type ContentType() {
if (content_type == CONTENT_TYPE_NONE) {
FillContentType();
}
return content_type;
}
void SetContentType(http_content_type type) {
content_type = type;
}
void SetContentType(const char* type) {
content_type = http_content_type_enum(type);
}
void SetContentTypeByFilename(const char* filepath) {
const char* suffix = hv_suffixname(filepath);
if (suffix) {
content_type = http_content_type_enum_by_suffix(suffix);
}
if (content_type == CONTENT_TYPE_NONE || content_type == CONTENT_TYPE_UNDEFINED) {
content_type = APPLICATION_OCTET_STREAM;
}
}
int String(const std::string& str) {
content_type = TEXT_PLAIN;
body = str;
return 200;
}
int Data(void* data, int len, bool nocopy = true) {
content_type = APPLICATION_OCTET_STREAM;
if (nocopy) {
content = data;
content_length = len;
} else {
content_length = body.size();
body.resize(content_length + len);
memcpy((void*)(body.data() + content_length), data, len);
content_length += len;
}
return 200;
}
int File(const char* filepath) {
HFile file;
if (file.open(filepath, "rb") != 0) {
return HTTP_STATUS_NOT_FOUND;
}
SetContentTypeByFilename(filepath);
file.readall(body);
return 200;
}
int SaveFile(const char* filepath) {
HFile file;
if (file.open(filepath, "wb") != 0) {
return HTTP_STATUS_NOT_FOUND;
}
file.write(body.data(), body.size());
return 200;
}
};
#define DEFAULT_HTTP_USER_AGENT "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
#define DEFAULT_HTTP_TIMEOUT 60 // s
#define DEFAULT_HTTP_CONNECT_TIMEOUT 10 // s
#define DEFAULT_HTTP_FAIL_RETRY_COUNT 1
#define DEFAULT_HTTP_FAIL_RETRY_DELAY 1000 // ms
class HV_EXPORT HttpRequest : public HttpMessage {
public:
http_method method;
// scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
std::string url;
// structured url
std::string scheme;
std::string host;
int port;
std::string path;
hv::QueryParams query_params;
// client_addr
hv::NetAddr client_addr; // for http server save client addr of request
// for HttpClient
uint16_t timeout; // unit: s
uint16_t connect_timeout;// unit: s
uint32_t retry_count;
uint32_t retry_delay; // unit: ms
unsigned redirect: 1;
unsigned proxy : 1;
unsigned cancel : 1;
HttpRequest();
void Init();
virtual void Reset();
virtual std::string Dump(bool is_dump_headers = true, bool is_dump_body = false);
// method
void SetMethod(const char* method) {
this->method = http_method_enum(method);
}
const char* Method() {
return http_method_str(method);
}
// scheme
bool IsHttps() {
return strncmp(scheme.c_str(), "https", 5) == 0 ||
strncmp(url.c_str(), "https://", 8) == 0;
}
// url
void SetUrl(const char* url) {
this->url = url;
}
const std::string& Url() {
return url;
}
// structed url -> url
void DumpUrl();
// url -> structed url
void ParseUrl();
// /path?query#fragment
std::string FullPath() { return path; }
// /path
std::string Path();
// ?query_params
template<typename T>
void SetParam(const char* key, const T& t) {
query_params[key] = hv::to_string(t);
}
std::string GetParam(const char* key, const std::string& defvalue = hv::empty_string) {
auto iter = query_params.find(key);
return iter == query_params.end() ? defvalue : iter->second;
}
// Host:
std::string Host() {
auto iter = headers.find("Host");
return iter == headers.end() ? host : iter->second;
}
void FillHost(const char* host, int port = DEFAULT_HTTP_PORT);
void SetHost(const char* host, int port = DEFAULT_HTTP_PORT);
void SetProxy(const char* host, int port);
bool IsProxy() { return proxy; }
// Auth
void SetAuth(const std::string& auth);
void SetBasicAuth(const std::string& username, const std::string& password);
void SetBearerTokenAuth(const std::string& token);
void SetTimeout(int sec) { timeout = sec; }
void SetConnectTimeout(int sec) { connect_timeout = sec; }
void AllowRedirect(bool on = true) { redirect = on; }
// NOTE: SetRetry just for AsyncHttpClient
void SetRetry(int count = DEFAULT_HTTP_FAIL_RETRY_COUNT,
int delay = DEFAULT_HTTP_FAIL_RETRY_DELAY) {
retry_count = count;
retry_delay = delay;
}
void Cancel() { cancel = 1; }
bool IsCanceled() { return cancel == 1; }
// Range: bytes=0-4095
void SetRange(long from = 0, long to = -1);
bool GetRange(long& from, long& to);
};
class HV_EXPORT HttpResponse : public HttpMessage {
public:
http_status status_code;
const char* status_message() {
return http_status_str(status_code);
}
HttpResponse();
void Init();
virtual void Reset();
virtual std::string Dump(bool is_dump_headers = true, bool is_dump_body = false);
// Content-Range: bytes 0-4095/10240000
void SetRange(long from, long to, long total);
bool GetRange(long& from, long& to, long& total);
int Redirect(const std::string& location, http_status status = HTTP_STATUS_FOUND) {
status_code = status;
SetHeader("Location", location);
return status_code;
}
};
typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
typedef std::shared_ptr<HttpResponse> HttpResponsePtr;
typedef std::function<void(const HttpResponsePtr&)> HttpResponseCallback;
#endif // HV_HTTP_MESSAGE_H_

View File

@ -1,53 +0,0 @@
#ifndef HV_HTTP_PARSER_H_
#define HV_HTTP_PARSER_H_
#include "hexport.h"
#include "HttpMessage.h"
class HV_EXPORT HttpParser {
public:
http_version version;
http_session_type type;
static HttpParser* New(http_session_type type = HTTP_CLIENT, http_version version = HTTP_V1);
virtual ~HttpParser() {}
virtual int GetSendData(char** data, size_t* len) = 0;
virtual int FeedRecvData(const char* data, size_t len) = 0;
// Http1Parser: http_parser_state
// Http2Parser: http2_session_state
virtual int GetState() = 0;
// Http1Parser: GetState() != HP_MESSAGE_COMPLETE
// Http2Parser: GetState() == H2_WANT_RECV
virtual bool WantRecv() = 0;
// Http1Parser: GetState() == HP_MESSAGE_COMPLETE
// Http2Parser: GetState() == H2_WANT_SEND
virtual bool WantSend() = 0;
// IsComplete: Is recved HttpRequest or HttpResponse complete?
// Http1Parser: GetState() == HP_MESSAGE_COMPLETE
// Http2Parser: (state == H2_RECV_HEADERS || state == H2_RECV_DATA) && stream_closed
virtual bool IsComplete() = 0;
virtual bool IsEof() { return false; }
// client
// SubmitRequest -> while(GetSendData) {send} -> InitResponse -> do {recv -> FeedRecvData} while(WantRecv)
virtual int SubmitRequest(HttpRequest* req) = 0;
virtual int InitResponse(HttpResponse* res) = 0;
// server
// InitRequest -> do {recv -> FeedRecvData} while(WantRecv) -> SubmitResponse -> while(GetSendData) {send}
virtual int InitRequest(HttpRequest* req) = 0;
virtual int SubmitResponse(HttpResponse* res) = 0;
virtual int GetError() = 0;
virtual const char* StrError(int error) = 0;
};
typedef std::shared_ptr<HttpParser> HttpParserPtr;
#endif // HV_HTTP_PARSER_H_

View File

@ -1,100 +0,0 @@
#ifndef HV_HTTP_RESPONSE_WRITER_H_
#define HV_HTTP_RESPONSE_WRITER_H_
#include "Channel.h"
#include "HttpMessage.h"
namespace hv {
class HV_EXPORT HttpResponseWriter : public SocketChannel {
public:
HttpResponsePtr response;
enum State {
SEND_BEGIN = 0,
SEND_HEADER,
SEND_BODY,
SEND_CHUNKED,
SEND_CHUNKED_END,
SEND_END,
} state: 8, end: 8;
HttpResponseWriter(hio_t* io, const HttpResponsePtr& resp)
: SocketChannel(io)
, response(resp)
, state(SEND_BEGIN)
, end(SEND_BEGIN)
{}
~HttpResponseWriter() {}
// Begin -> End
// Begin -> WriteResponse -> End
// Begin -> WriteStatus -> WriteHeader -> WriteBody -> End
// Begin -> EndHeaders("Content-Type", "text/event-stream") -> write -> write -> ... -> close
// Begin -> EndHeaders("Content-Length", content_length) -> WriteBody -> WriteBody -> ... -> End
// Begin -> EndHeaders("Transfer-Encoding", "chunked") -> WriteChunked -> WriteChunked -> ... -> End
int Begin() {
state = end = SEND_BEGIN;
return 0;
}
int WriteStatus(http_status status_codes) {
response->status_code = status_codes;
return 0;
}
int WriteHeader(const char* key, const char* value) {
response->SetHeader(key, value);
return 0;
}
template<typename T>
int WriteHeader(const char* key, T num) {
response->SetHeader(key, hv::to_string(num));
return 0;
}
int WriteCookie(const HttpCookie& cookie) {
response->cookies.push_back(cookie);
return 0;
}
int EndHeaders(const char* key = NULL, const char* value = NULL);
template<typename T>
int EndHeaders(const char* key, T num) {
std::string value = hv::to_string(num);
return EndHeaders(key, value.c_str());
}
int WriteChunked(const char* buf, int len = -1);
int WriteChunked(const std::string& str) {
return WriteChunked(str.c_str(), str.size());
}
int EndChunked() {
return WriteChunked(NULL, 0);
}
int WriteBody(const char* buf, int len = -1);
int WriteBody(const std::string& str) {
return WriteBody(str.c_str(), str.size());
}
int WriteResponse(HttpResponse* resp);
int SSEvent(const std::string& data, const char* event = "message");
int End(const char* buf = NULL, int len = -1);
int End(const std::string& str) {
return End(str.c_str(), str.size());
}
};
}
typedef std::shared_ptr<hv::HttpResponseWriter> HttpResponseWriterPtr;
#endif // HV_HTTP_RESPONSE_WRITER_H_

View File

@ -1,154 +0,0 @@
#ifndef HV_HTTP_SERVER_H_
#define HV_HTTP_SERVER_H_
#include "hexport.h"
#include "hssl.h"
#include "HttpService.h"
// #include "WebSocketServer.h"
namespace hv {
struct WebSocketService;
}
using hv::HttpService;
using hv::WebSocketService;
typedef struct http_server_s {
char host[64];
int port; // http_port
int https_port;
int http_version;
int worker_processes;
int worker_threads;
uint32_t worker_connections; // max_connections = workers * worker_connections
HttpService* service; // http service
WebSocketService* ws; // websocket service
void* userdata;
int listenfd[2]; // 0: http, 1: https
void* privdata;
// hooks
std::function<void()> onWorkerStart;
std::function<void()> onWorkerStop;
// SSL/TLS
hssl_ctx_t ssl_ctx;
unsigned alloced_ssl_ctx: 1;
#ifdef __cplusplus
http_server_s() {
strcpy(host, "0.0.0.0");
// port = DEFAULT_HTTP_PORT;
// https_port = DEFAULT_HTTPS_PORT;
// port = 8080;
// https_port = 8443;
port = https_port = 0;
http_version = 1;
worker_processes = 0;
worker_threads = 0;
worker_connections = 1024;
service = NULL;
ws = NULL;
listenfd[0] = listenfd[1] = -1;
userdata = NULL;
privdata = NULL;
// SSL/TLS
ssl_ctx = NULL;
alloced_ssl_ctx = 0;
}
#endif
} http_server_t;
// @param wait: Whether to occupy current thread
HV_EXPORT int http_server_run(http_server_t* server, int wait = 1);
// NOTE: stop all loops and join all threads
HV_EXPORT int http_server_stop(http_server_t* server);
/*
#include "HttpServer.h"
using namespace hv;
int main() {
HttpService service;
service.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
resp->body = "pong";
return 200;
});
HttpServer server(&service);
server.setThreadNum(4);
server.run(":8080");
return 0;
}
*/
namespace hv {
class HttpServer : public http_server_t {
public:
HttpServer(HttpService* service = NULL)
: http_server_t()
{
this->service = service;
}
~HttpServer() { stop(); }
void registerHttpService(HttpService* service) {
this->service = service;
}
void setHost(const char* host = "0.0.0.0") {
if (host) strcpy(this->host, host);
}
void setPort(int port = 0, int ssl_port = 0) {
if (port >= 0) this->port = port;
if (ssl_port >= 0) this->https_port = ssl_port;
}
void setListenFD(int fd = -1, int ssl_fd = -1) {
if (fd >= 0) this->listenfd[0] = fd;
if (ssl_fd >= 0) this->listenfd[1] = ssl_fd;
}
void setProcessNum(int num) {
this->worker_processes = num;
}
void setThreadNum(int num) {
this->worker_threads = num;
}
// SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx) {
this->ssl_ctx = ssl_ctx;
return 0;
}
int newSslCtx(hssl_ctx_opt_t* opt) {
// NOTE: hssl_ctx_free in http_server_stop
hssl_ctx_t ssl_ctx = hssl_ctx_new(opt);
if (ssl_ctx == NULL) return -1;
this->alloced_ssl_ctx = 1;
return setSslCtx(ssl_ctx);
}
// run(":8080")
// run("0.0.0.0:8080")
// run("[::]:8080")
int run(const char* ip_port = NULL, bool wait = true) {
if (ip_port) {
hv::NetAddr listen_addr(ip_port);
if (listen_addr.ip.size() != 0) setHost(listen_addr.ip.c_str());
if (listen_addr.port != 0) setPort(listen_addr.port);
}
return http_server_run(this, wait);
}
int start(const char* ip_port = NULL) {
return run(ip_port, false);
}
int stop() {
return http_server_stop(this);
}
};
}
#endif // HV_HTTP_SERVER_H_

View File

@ -1,281 +0,0 @@
#ifndef HV_HTTP_SERVICE_H_
#define HV_HTTP_SERVICE_H_
#include <string>
#include <map>
#include <unordered_map>
#include <vector>
#include <list>
#include <memory>
#include <functional>
#include "hexport.h"
#include "HttpMessage.h"
#include "HttpResponseWriter.h"
#include "HttpContext.h"
#define DEFAULT_BASE_URL "/api/v1"
#define DEFAULT_DOCUMENT_ROOT "/var/www/html"
#define DEFAULT_HOME_PAGE "index.html"
#define DEFAULT_ERROR_PAGE "error.html"
#define DEFAULT_INDEXOF_DIR "/downloads/"
#define DEFAULT_KEEPALIVE_TIMEOUT 75000 // ms
// for FileCache
#define MAX_FILE_CACHE_SIZE (1 << 22) // 4M
#define DEFAULT_FILE_CACHE_STAT_INTERVAL 10 // s
#define DEFAULT_FILE_CACHE_EXPIRED_TIME 60 // s
/*
* @param[in] req: parsed structured http request
* @param[out] resp: structured http response
* @return 0: handle next
* http_status_code: handle done
*/
#define HTTP_STATUS_NEXT 0
#define HTTP_STATUS_UNFINISHED 0
// NOTE: http_sync_handler run on IO thread
typedef std::function<int(HttpRequest* req, HttpResponse* resp)> http_sync_handler;
// NOTE: http_async_handler run on hv::async threadpool
typedef std::function<void(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer)> http_async_handler;
// NOTE: http_ctx_handler run on IO thread, you can easily post HttpContextPtr to your consumer thread for processing.
typedef std::function<int(const HttpContextPtr& ctx)> http_ctx_handler;
// NOTE: http_state_handler run on IO thread
typedef std::function<int(const HttpContextPtr& ctx, http_parser_state state, const char* data, size_t size)> http_state_handler;
struct http_handler {
http_sync_handler sync_handler;
http_async_handler async_handler;
http_ctx_handler ctx_handler;
http_state_handler state_handler;
http_handler() {}
http_handler(http_sync_handler fn) : sync_handler(std::move(fn)) {}
http_handler(http_async_handler fn) : async_handler(std::move(fn)) {}
http_handler(http_ctx_handler fn) : ctx_handler(std::move(fn)) {}
http_handler(http_state_handler fn) : state_handler(std::move(fn)) {}
http_handler(const http_handler& rhs)
: sync_handler(std::move(const_cast<http_handler&>(rhs).sync_handler))
, async_handler(std::move(const_cast<http_handler&>(rhs).async_handler))
, ctx_handler(std::move(const_cast<http_handler&>(rhs).ctx_handler))
, state_handler(std::move(const_cast<http_handler&>(rhs).state_handler))
{}
const http_handler& operator=(http_sync_handler fn) {
sync_handler = std::move(fn);
return *this;
}
const http_handler& operator=(http_async_handler fn) {
async_handler = std::move(fn);
return *this;
}
const http_handler& operator=(http_ctx_handler fn) {
ctx_handler = std::move(fn);
return *this;
}
const http_handler& operator=(http_state_handler fn) {
state_handler = std::move(fn);
return *this;
}
bool isNull() {
return sync_handler == NULL &&
async_handler == NULL &&
ctx_handler == NULL;
}
operator bool() {
return !isNull();
}
};
typedef std::vector<http_handler> http_handlers;
struct http_method_handler {
http_method method;
http_handler handler;
http_method_handler() {}
http_method_handler(http_method m, const http_handler& h) : method(m), handler(h) {}
};
// method => http_method_handler
typedef std::list<http_method_handler> http_method_handlers;
// path => http_method_handlers
typedef std::unordered_map<std::string, std::shared_ptr<http_method_handlers>> http_path_handlers;
namespace hv {
struct HV_EXPORT HttpService {
/* handler chain */
// preprocessor -> middleware -> processor -> postprocessor
http_handler preprocessor;
http_handlers middleware;
// processor: pathHandlers -> staticHandler -> errorHandler
http_handler processor;
http_handler postprocessor;
/* API handlers */
std::string base_url;
http_path_handlers pathHandlers;
/* Static file service */
http_handler staticHandler;
http_handler largeFileHandler;
std::string document_root;
std::string home_page;
std::string error_page;
// nginx: location => root
std::map<std::string, std::string, std::greater<std::string>> staticDirs;
/* Indexof directory service */
std::string index_of;
http_handler errorHandler;
/* Proxy service */
/* Reverse proxy service */
// nginx: location => proxy_pass
std::map<std::string, std::string, std::greater<std::string>> proxies;
/* Forward proxy service */
StringList trustProxies;
StringList noProxies;
int proxy_connect_timeout;
int proxy_read_timeout;
int proxy_write_timeout;
// options
int keepalive_timeout;
int max_file_cache_size; // cache small file
int file_cache_stat_interval; // stat file is modified
int file_cache_expired_time; // remove expired file cache
/*
* @test limit_rate
* @build make examples
* @server bin/httpd -c etc/httpd.conf -s restart -d
* @client bin/wget http://127.0.0.1:8080/downloads/test.zip
*/
int limit_rate; // limit send rate, unit: KB/s
unsigned enable_access_log :1;
unsigned enable_forward_proxy :1;
HttpService() {
// base_url = DEFAULT_BASE_URL;
document_root = DEFAULT_DOCUMENT_ROOT;
home_page = DEFAULT_HOME_PAGE;
// error_page = DEFAULT_ERROR_PAGE;
// index_of = DEFAULT_INDEXOF_DIR;
proxy_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
proxy_read_timeout = 0;
proxy_write_timeout = 0;
keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
max_file_cache_size = MAX_FILE_CACHE_SIZE;
file_cache_stat_interval = DEFAULT_FILE_CACHE_STAT_INTERVAL;
file_cache_expired_time = DEFAULT_FILE_CACHE_EXPIRED_TIME;
limit_rate = -1; // unlimited
enable_access_log = 1;
enable_forward_proxy = 0;
}
void AddRoute(const char* path, http_method method, const http_handler& handler);
// @retval 0 OK, else HTTP_STATUS_NOT_FOUND, HTTP_STATUS_METHOD_NOT_ALLOWED
int GetRoute(const char* url, http_method method, http_handler** handler);
// RESTful API /:field/ => req->query_params["field"]
int GetRoute(HttpRequest* req, http_handler** handler);
// Static("/", "/var/www/html")
void Static(const char* path, const char* dir);
// @retval / => /var/www/html/index.html
std::string GetStaticFilepath(const char* path);
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
void AllowCORS();
// proxy
// forward proxy
void EnableForwardProxy() { enable_forward_proxy = 1; }
void AddTrustProxy(const char* host);
void AddNoProxy(const char* host);
bool IsTrustProxy(const char* host);
// reverse proxy
// Proxy("/api/v1/", "http://www.httpbin.org/");
void Proxy(const char* path, const char* url);
// @retval /api/v1/test => http://www.httpbin.org/test
std::string GetProxyUrl(const char* path);
hv::StringList Paths() {
hv::StringList paths;
for (auto& pair : pathHandlers) {
paths.emplace_back(pair.first);
}
return paths;
}
// Handler = [ http_sync_handler, http_ctx_handler ]
template<typename Handler>
void Use(Handler handlerFunc) {
middleware.emplace_back(handlerFunc);
}
// Inspired by github.com/gin-gonic/gin
// Handler = [ http_sync_handler, http_async_handler, http_ctx_handler, http_state_handler ]
template<typename Handler>
void Handle(const char* httpMethod, const char* relativePath, Handler handlerFunc) {
AddRoute(relativePath, http_method_enum(httpMethod), http_handler(handlerFunc));
}
// HEAD
template<typename Handler>
void HEAD(const char* relativePath, Handler handlerFunc) {
Handle("HEAD", relativePath, handlerFunc);
}
// GET
template<typename Handler>
void GET(const char* relativePath, Handler handlerFunc) {
Handle("GET", relativePath, handlerFunc);
}
// POST
template<typename Handler>
void POST(const char* relativePath, Handler handlerFunc) {
Handle("POST", relativePath, handlerFunc);
}
// PUT
template<typename Handler>
void PUT(const char* relativePath, Handler handlerFunc) {
Handle("PUT", relativePath, handlerFunc);
}
// DELETE
// NOTE: Windows <winnt.h> #define DELETE as a macro, we have to replace DELETE with Delete.
template<typename Handler>
void Delete(const char* relativePath, Handler handlerFunc) {
Handle("DELETE", relativePath, handlerFunc);
}
// PATCH
template<typename Handler>
void PATCH(const char* relativePath, Handler handlerFunc) {
Handle("PATCH", relativePath, handlerFunc);
}
// Any
template<typename Handler>
void Any(const char* relativePath, Handler handlerFunc) {
Handle("HEAD", relativePath, handlerFunc);
Handle("GET", relativePath, handlerFunc);
Handle("POST", relativePath, handlerFunc);
Handle("PUT", relativePath, handlerFunc);
Handle("DELETE", relativePath, handlerFunc);
Handle("PATCH", relativePath, handlerFunc);
}
};
}
#endif // HV_HTTP_SERVICE_H_

View File

@ -1,56 +0,0 @@
#ifndef HV_STATUS_HPP_
#define HV_STATUS_HPP_
#include <atomic>
namespace hv {
class Status {
public:
enum KStatus {
kNull = 0,
kInitializing = 1,
kInitialized = 2,
kStarting = 3,
kStarted = 4,
kRunning = 5,
kPause = 6,
kStopping = 7,
kStopped = 8,
kDestroyed = 9,
};
Status() {
status_ = kNull;
}
~Status() {
status_ = kDestroyed;
}
KStatus status() {
return status_;
}
void setStatus(KStatus status) {
status_ = status;
}
bool isRunning() {
return status_ == kRunning;
}
bool isPause() {
return status_ == kPause;
}
bool isStopped() {
return status_ == kStopped;
}
private:
std::atomic<KStatus> status_;
};
}
#endif // HV_STATUS_HPP_

View File

@ -1,308 +0,0 @@
#ifndef HV_TCP_CLIENT_HPP_
#define HV_TCP_CLIENT_HPP_
#include "hsocket.h"
#include "hssl.h"
#include "hlog.h"
#include "EventLoopThread.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class TcpClientEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
TcpClientEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
remote_port = 0;
connect_timeout = HIO_DEFAULT_CONNECT_TIMEOUT;
tls = false;
tls_setting = NULL;
reconn_setting = NULL;
unpack_setting = NULL;
}
virtual ~TcpClientEventLoopTmpl() {
HV_FREE(tls_setting);
HV_FREE(reconn_setting);
HV_FREE(unpack_setting);
}
const EventLoopPtr& loop() {
return loop_;
}
// delete thread-safe
void deleteInLoop() {
loop_->runInLoop([this](){
delete this;
});
}
// NOTE: By default, not bind local port. If necessary, you can call bind() after createsocket().
// @retval >=0 connfd, <0 error
int createsocket(int remote_port, const char* remote_host = "127.0.0.1") {
memset(&remote_addr, 0, sizeof(remote_addr));
int ret = sockaddr_set_ipport(&remote_addr, remote_host, remote_port);
if (ret != 0) {
return NABS(ret);
}
this->remote_host = remote_host;
this->remote_port = remote_port;
return createsocket(&remote_addr.sa);
}
int createsocket(struct sockaddr* remote_addr) {
int connfd = ::socket(remote_addr->sa_family, SOCK_STREAM, 0);
// SOCKADDR_PRINT(remote_addr);
if (connfd < 0) {
perror("socket");
return -2;
}
hio_t* io = hio_get(loop_->loop(), connfd);
assert(io != NULL);
hio_set_peeraddr(io, remote_addr, SOCKADDR_LEN(remote_addr));
channel = std::make_shared<TSocketChannel>(io);
return connfd;
}
int bind(int local_port, const char* local_host = "0.0.0.0") {
sockaddr_u local_addr;
memset(&local_addr, 0, sizeof(local_addr));
int ret = sockaddr_set_ipport(&local_addr, local_host, local_port);
if (ret != 0) {
return NABS(ret);
}
return bind(&local_addr.sa);
}
int bind(struct sockaddr* local_addr) {
if (channel == NULL || channel->isClosed()) {
return -1;
}
int ret = ::bind(channel->fd(), local_addr, SOCKADDR_LEN(local_addr));
if (ret != 0) {
perror("bind");
}
return ret;
}
// closesocket thread-safe
void closesocket() {
if (channel && channel->status != SocketChannel::CLOSED) {
loop_->runInLoop([this](){
if (channel) {
setReconnect(NULL);
channel->close();
}
});
}
}
int startConnect() {
if (channel == NULL || channel->isClosed()) {
int connfd = createsocket(&remote_addr.sa);
if (connfd < 0) {
hloge("createsocket %s:%d return %d!\n", remote_host.c_str(), remote_port, connfd);
return connfd;
}
}
if (channel == NULL || channel->status >= SocketChannel::CONNECTING) {
return -1;
}
if (connect_timeout) {
channel->setConnectTimeout(connect_timeout);
}
if (tls) {
channel->enableSSL();
if (tls_setting) {
int ret = channel->newSslCtx(tls_setting);
if (ret != 0) {
hloge("new SSL_CTX failed: %d", ret);
closesocket();
return ret;
}
}
if (!is_ipaddr(remote_host.c_str())) {
channel->setHostname(remote_host);
}
}
channel->onconnect = [this]() {
if (unpack_setting) {
channel->setUnpack(unpack_setting);
}
channel->startRead();
if (onConnection) {
onConnection(channel);
}
if (reconn_setting) {
reconn_setting_reset(reconn_setting);
}
};
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
channel->onclose = [this]() {
bool reconnect = reconn_setting != NULL;
if (onConnection) {
onConnection(channel);
}
if (reconnect) {
startReconnect();
}
};
return channel->startConnect();
}
int startReconnect() {
if (!reconn_setting) return -1;
if (!reconn_setting_can_retry(reconn_setting)) return -2;
uint32_t delay = reconn_setting_calc_delay(reconn_setting);
hlogi("reconnect... cnt=%d, delay=%d", reconn_setting->cur_retry_cnt, reconn_setting->cur_delay);
loop_->setTimeout(delay, [this](TimerID timerID){
startConnect();
});
return 0;
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&TcpClientEventLoopTmpl::startConnect, this));
}
bool isConnected() {
if (channel == NULL) return false;
return channel->isConnected();
}
// send thread-safe
int send(const void* data, int size) {
if (!isConnected()) return -1;
return channel->write(data, size);
}
int send(Buffer* buf) {
return send(buf->data(), buf->size());
}
int send(const std::string& str) {
return send(str.data(), str.size());
}
int withTLS(hssl_ctx_opt_t* opt = NULL) {
tls = true;
if (opt) {
if (tls_setting == NULL) {
HV_ALLOC_SIZEOF(tls_setting);
}
opt->endpoint = HSSL_CLIENT;
*tls_setting = *opt;
}
return 0;
}
void setConnectTimeout(int ms) {
connect_timeout = ms;
}
void setReconnect(reconn_setting_t* setting) {
if (setting == NULL) {
HV_FREE(reconn_setting);
return;
}
if (reconn_setting == NULL) {
HV_ALLOC_SIZEOF(reconn_setting);
}
*reconn_setting = *setting;
}
bool isReconnect() {
return reconn_setting && reconn_setting->cur_retry_cnt > 0;
}
void setUnpack(unpack_setting_t* setting) {
if (setting == NULL) {
HV_FREE(unpack_setting);
return;
}
if (unpack_setting == NULL) {
HV_ALLOC_SIZEOF(unpack_setting);
}
*unpack_setting = *setting;
}
public:
TSocketChannelPtr channel;
std::string remote_host;
int remote_port;
sockaddr_u remote_addr;
int connect_timeout;
bool tls;
hssl_ctx_opt_t* tls_setting;
reconn_setting_t* reconn_setting;
unpack_setting_t* unpack_setting;
// Callback
std::function<void(const TSocketChannelPtr&)> onConnection;
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class TcpClientTmpl : private EventLoopThread, public TcpClientEventLoopTmpl<TSocketChannel> {
public:
TcpClientTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, TcpClientEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~TcpClientTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
TcpClientEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, [this]() {
TcpClientTmpl::startConnect();
return 0;
});
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
TcpClientEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef TcpClientTmpl<SocketChannel> TcpClient;
}
#endif // HV_TCP_CLIENT_HPP_

View File

@ -1,316 +0,0 @@
#ifndef HV_TCP_SERVER_HPP_
#define HV_TCP_SERVER_HPP_
#include "hsocket.h"
#include "hssl.h"
#include "hlog.h"
#include "EventLoopThreadPool.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class TcpServerEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
TcpServerEventLoopTmpl(EventLoopPtr loop = NULL) {
acceptor_loop = loop ? loop : std::make_shared<EventLoop>();
port = 0;
listenfd = -1;
tls = false;
tls_setting = NULL;
unpack_setting = NULL;
max_connections = 0xFFFFFFFF;
load_balance = LB_RoundRobin;
}
virtual ~TcpServerEventLoopTmpl() {
HV_FREE(tls_setting);
HV_FREE(unpack_setting);
}
EventLoopPtr loop(int idx = -1) {
return worker_threads.loop(idx);
}
//@retval >=0 listenfd, <0 error
int createsocket(int port, const char* host = "0.0.0.0") {
listenfd = Listen(port, host);
if (listenfd < 0) return listenfd;
this->host = host;
this->port = port;
return listenfd;
}
// closesocket thread-safe
void closesocket() {
if (listenfd >= 0) {
hloop_t* loop = acceptor_loop->loop();
if (loop) {
hio_t* listenio = hio_get(loop, listenfd);
assert(listenio != NULL);
hio_close_async(listenio);
}
listenfd = -1;
}
}
void setMaxConnectionNum(uint32_t num) {
max_connections = num;
}
void setLoadBalance(load_balance_e lb) {
load_balance = lb;
}
// NOTE: totalThreadNum = 1 acceptor_thread + N worker_threads (N can be 0)
void setThreadNum(int num) {
worker_threads.setThreadNum(num);
}
int startAccept() {
if (listenfd < 0) {
listenfd = createsocket(port, host.c_str());
if (listenfd < 0) {
hloge("createsocket %s:%d return %d!\n", host.c_str(), port, listenfd);
return listenfd;
}
}
hloop_t* loop = acceptor_loop->loop();
if (loop == NULL) return -2;
hio_t* listenio = haccept(loop, listenfd, onAccept);
assert(listenio != NULL);
hevent_set_userdata(listenio, this);
if (tls) {
hio_enable_ssl(listenio);
if (tls_setting) {
int ret = hio_new_ssl_ctx(listenio, tls_setting);
if (ret != 0) {
hloge("new SSL_CTX failed: %d", ret);
closesocket();
return ret;
}
}
}
return 0;
}
int stopAccept() {
if (listenfd < 0) return -1;
hloop_t* loop = acceptor_loop->loop();
if (loop == NULL) return -2;
hio_t* listenio = hio_get(loop, listenfd);
assert(listenio != NULL);
return hio_del(listenio, HV_READ);
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (worker_threads.threadNum() > 0) {
worker_threads.start(wait_threads_started);
}
acceptor_loop->runInLoop(std::bind(&TcpServerEventLoopTmpl::startAccept, this));
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
closesocket();
if (worker_threads.threadNum() > 0) {
worker_threads.stop(wait_threads_stopped);
}
}
int withTLS(hssl_ctx_opt_t* opt = NULL) {
tls = true;
if (opt) {
if (tls_setting == NULL) {
HV_ALLOC_SIZEOF(tls_setting);
}
opt->endpoint = HSSL_SERVER;
*tls_setting = *opt;
}
return 0;
}
void setUnpack(unpack_setting_t* setting) {
if (setting == NULL) {
HV_FREE(unpack_setting);
return;
}
if (unpack_setting == NULL) {
HV_ALLOC_SIZEOF(unpack_setting);
}
*unpack_setting = *setting;
}
// channel
const TSocketChannelPtr& addChannel(hio_t* io) {
uint32_t id = hio_id(io);
auto channel = std::make_shared<TSocketChannel>(io);
std::lock_guard<std::mutex> locker(mutex_);
channels[id] = channel;
return channels[id];
}
TSocketChannelPtr getChannelById(uint32_t id) {
std::lock_guard<std::mutex> locker(mutex_);
auto iter = channels.find(id);
return iter != channels.end() ? iter->second : NULL;
}
void removeChannel(const TSocketChannelPtr& channel) {
uint32_t id = channel->id();
std::lock_guard<std::mutex> locker(mutex_);
channels.erase(id);
}
size_t connectionNum() {
std::lock_guard<std::mutex> locker(mutex_);
return channels.size();
}
int foreachChannel(std::function<void(const TSocketChannelPtr& channel)> fn) {
std::lock_guard<std::mutex> locker(mutex_);
for (auto& pair : channels) {
fn(pair.second);
}
return channels.size();
}
// broadcast thread-safe
int broadcast(const void* data, int size) {
return foreachChannel([data, size](const TSocketChannelPtr& channel) {
channel->write(data, size);
});
}
int broadcast(const std::string& str) {
return broadcast(str.data(), str.size());
}
private:
static void newConnEvent(hio_t* connio) {
TcpServerEventLoopTmpl* server = (TcpServerEventLoopTmpl*)hevent_userdata(connio);
if (server->connectionNum() >= server->max_connections) {
hlogw("over max_connections");
hio_close(connio);
return;
}
// NOTE: attach to worker loop
EventLoop* worker_loop = currentThreadEventLoop;
assert(worker_loop != NULL);
hio_attach(worker_loop->loop(), connio);
const TSocketChannelPtr& channel = server->addChannel(connio);
channel->status = SocketChannel::CONNECTED;
channel->onread = [server, &channel](Buffer* buf) {
if (server->onMessage) {
server->onMessage(channel, buf);
}
};
channel->onwrite = [server, &channel](Buffer* buf) {
if (server->onWriteComplete) {
server->onWriteComplete(channel, buf);
}
};
channel->onclose = [server, &channel]() {
EventLoop* worker_loop = currentThreadEventLoop;
assert(worker_loop != NULL);
--worker_loop->connectionNum;
channel->status = SocketChannel::CLOSED;
if (server->onConnection) {
server->onConnection(channel);
}
server->removeChannel(channel);
// NOTE: After removeChannel, channel may be destroyed,
// so in this lambda function, no code should be added below.
};
if (server->unpack_setting) {
channel->setUnpack(server->unpack_setting);
}
channel->startRead();
if (server->onConnection) {
server->onConnection(channel);
}
}
static void onAccept(hio_t* connio) {
TcpServerEventLoopTmpl* server = (TcpServerEventLoopTmpl*)hevent_userdata(connio);
// NOTE: detach from acceptor loop
hio_detach(connio);
EventLoopPtr worker_loop = server->worker_threads.nextLoop(server->load_balance);
if (worker_loop == NULL) {
worker_loop = server->acceptor_loop;
}
++worker_loop->connectionNum;
worker_loop->runInLoop(std::bind(&TcpServerEventLoopTmpl::newConnEvent, connio));
}
public:
std::string host;
int port;
int listenfd;
bool tls;
hssl_ctx_opt_t* tls_setting;
unpack_setting_t* unpack_setting;
// Callback
std::function<void(const TSocketChannelPtr&)> onConnection;
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
uint32_t max_connections;
load_balance_e load_balance;
private:
// id => TSocketChannelPtr
std::map<uint32_t, TSocketChannelPtr> channels; // GUAREDE_BY(mutex_)
std::mutex mutex_;
EventLoopPtr acceptor_loop;
EventLoopThreadPool worker_threads;
};
template<class TSocketChannel = SocketChannel>
class TcpServerTmpl : private EventLoopThread, public TcpServerEventLoopTmpl<TSocketChannel> {
public:
TcpServerTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, TcpServerEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~TcpServerTmpl() {
stop(true);
}
EventLoopPtr loop(int idx = -1) {
return TcpServerEventLoopTmpl<TSocketChannel>::loop(idx);
}
// start thread-safe
void start(bool wait_threads_started = true) {
TcpServerEventLoopTmpl<TSocketChannel>::start(wait_threads_started);
EventLoopThread::start(wait_threads_started);
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
TcpServerEventLoopTmpl<TSocketChannel>::stop(wait_threads_stopped);
}
private:
bool is_loop_owner;
};
typedef TcpServerTmpl<SocketChannel> TcpServer;
}
#endif // HV_TCP_SERVER_HPP_

View File

@ -1,67 +0,0 @@
#ifndef HV_THREAD_LOCAL_STORAGE_H_
#define HV_THREAD_LOCAL_STORAGE_H_
#include "hexport.h"
#include "hplatform.h"
#ifdef OS_WIN
#define hthread_key_t DWORD
#define INVALID_HTHREAD_KEY 0xFFFFFFFF
#define hthread_key_create(pkey) *pkey = TlsAlloc()
#define hthread_key_delete TlsFree
#define hthread_get_value TlsGetValue
#define hthread_set_value TlsSetValue
#else
#define hthread_key_t pthread_key_t
#define INVALID_HTHREAD_KEY 0xFFFFFFFF
#define hthread_key_create(pkey) pthread_key_create(pkey, NULL)
#define hthread_key_delete pthread_key_delete
#define hthread_get_value pthread_getspecific
#define hthread_set_value pthread_setspecific
#endif
#ifdef __cplusplus
namespace hv {
class HV_EXPORT ThreadLocalStorage {
public:
enum {
THREAD_NAME = 0,
EVENT_LOOP = 1,
MAX_NUM = 16,
};
ThreadLocalStorage() {
hthread_key_create(&key);
}
~ThreadLocalStorage() {
hthread_key_delete(key);
}
void set(void* val) {
hthread_set_value(key, val);
}
void* get() {
return hthread_get_value(key);
}
static void set(int idx, void* val);
static void* get(int idx);
static void setThreadName(const char* name);
static const char* threadName();
private:
hthread_key_t key;
static ThreadLocalStorage tls[MAX_NUM];
};
}
#endif
#endif // HV_THREAD_LOCAL_STORAGE_H_

View File

@ -1,200 +0,0 @@
#ifndef HV_UDP_CLIENT_HPP_
#define HV_UDP_CLIENT_HPP_
#include "hsocket.h"
#include "EventLoopThread.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class UdpClientEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
UdpClientEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
remote_port = 0;
#if WITH_KCP
kcp_setting = NULL;
#endif
}
virtual ~UdpClientEventLoopTmpl() {
#if WITH_KCP
HV_FREE(kcp_setting);
#endif
}
const EventLoopPtr& loop() {
return loop_;
}
// NOTE: By default, not bind local port. If necessary, you can call bind() after createsocket().
// @retval >=0 sockfd, <0 error
int createsocket(int remote_port, const char* remote_host = "127.0.0.1") {
hio_t* io = hloop_create_udp_client(loop_->loop(), remote_host, remote_port);
if (io == NULL) return -1;
this->remote_host = remote_host;
this->remote_port = remote_port;
channel = std::make_shared<TSocketChannel>(io);
int sockfd = channel->fd();
if (hv_strendswith(remote_host, ".255")) {
udp_broadcast(sockfd, 1);
}
return sockfd;
}
int bind(int local_port, const char* local_host = "0.0.0.0") {
if (channel == NULL || channel->isClosed()) {
return -1;
}
sockaddr_u local_addr;
memset(&local_addr, 0, sizeof(local_addr));
int ret = sockaddr_set_ipport(&local_addr, local_host, local_port);
if (ret != 0) {
return NABS(ret);
}
ret = ::bind(channel->fd(), &local_addr.sa, SOCKADDR_LEN(&local_addr));
if (ret != 0) {
perror("bind");
}
hio_set_localaddr(channel->io(), &local_addr.sa, SOCKADDR_LEN(&local_addr));
return ret;
}
// closesocket thread-safe
void closesocket() {
if (channel) {
channel->close(true);
}
}
int startRecv() {
if (channel == NULL || channel->isClosed()) {
int sockfd = createsocket(remote_port, remote_host.c_str());
if (sockfd < 0) {
hloge("createsocket %s:%d return %d!\n", remote_host.c_str(), remote_port, sockfd);
return sockfd;
}
}
if (channel == NULL || channel->isClosed()) {
return -1;
}
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
#if WITH_KCP
if (kcp_setting) {
hio_set_kcp(channel->io(), kcp_setting);
}
#endif
return channel->startRead();
}
int stopRecv() {
if (channel == NULL) return -1;
return channel->stopRead();
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&UdpClientEventLoopTmpl::startRecv, this));
}
// sendto thread-safe
int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL) {
if (channel == NULL) return -1;
std::lock_guard<std::mutex> locker(sendto_mutex);
if (peeraddr) hio_set_peeraddr(channel->io(), peeraddr, SOCKADDR_LEN(peeraddr));
return channel->write(data, size);
}
int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL) {
return sendto(buf->data(), buf->size(), peeraddr);
}
int sendto(const std::string& str, struct sockaddr* peeraddr = NULL) {
return sendto(str.data(), str.size(), peeraddr);
}
#if WITH_KCP
void setKcp(kcp_setting_t* setting) {
if (setting == NULL) {
HV_FREE(kcp_setting);
return;
}
if (kcp_setting == NULL) {
HV_ALLOC_SIZEOF(kcp_setting);
}
*kcp_setting = *setting;
}
#endif
public:
TSocketChannelPtr channel;
std::string remote_host;
int remote_port;
#if WITH_KCP
kcp_setting_t* kcp_setting;
#endif
// Callback
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
std::mutex sendto_mutex;
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class UdpClientTmpl : private EventLoopThread, public UdpClientEventLoopTmpl<TSocketChannel> {
public:
UdpClientTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, UdpClientEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~UdpClientTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
UdpClientEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, std::bind(&UdpClientTmpl::startRecv, this));
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
UdpClientEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef UdpClientTmpl<SocketChannel> UdpClient;
}
#endif // HV_UDP_CLIENT_HPP_

View File

@ -1,174 +0,0 @@
#ifndef HV_UDP_SERVER_HPP_
#define HV_UDP_SERVER_HPP_
#include "hsocket.h"
#include "EventLoopThreadPool.h"
#include "Channel.h"
namespace hv {
template<class TSocketChannel = SocketChannel>
class UdpServerEventLoopTmpl {
public:
typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
UdpServerEventLoopTmpl(EventLoopPtr loop = NULL) {
loop_ = loop ? loop : std::make_shared<EventLoop>();
port = 0;
#if WITH_KCP
kcp_setting = NULL;
#endif
}
virtual ~UdpServerEventLoopTmpl() {
#if WITH_KCP
HV_FREE(kcp_setting);
#endif
}
const EventLoopPtr& loop() {
return loop_;
}
//@retval >=0 bindfd, <0 error
int createsocket(int port, const char* host = "0.0.0.0") {
hio_t* io = hloop_create_udp_server(loop_->loop(), host, port);
if (io == NULL) return -1;
this->host = host;
this->port = port;
channel = std::make_shared<TSocketChannel>(io);
return channel->fd();
}
// closesocket thread-safe
void closesocket() {
if (channel) {
channel->close(true);
}
}
int startRecv() {
if (channel == NULL || channel->isClosed()) {
int bindfd = createsocket(port, host.c_str());
if (bindfd < 0) {
hloge("createsocket %s:%d return %d!\n", host.c_str(), port, bindfd);
return bindfd;
}
}
if (channel == NULL || channel->isClosed()) {
return -1;
}
channel->onread = [this](Buffer* buf) {
if (onMessage) {
onMessage(channel, buf);
}
};
channel->onwrite = [this](Buffer* buf) {
if (onWriteComplete) {
onWriteComplete(channel, buf);
}
};
#if WITH_KCP
if (kcp_setting) {
hio_set_kcp(channel->io(), kcp_setting);
}
#endif
return channel->startRead();
}
int stopRecv() {
if (channel == NULL) return -1;
return channel->stopRead();
}
// start thread-safe
void start() {
loop_->runInLoop(std::bind(&UdpServerEventLoopTmpl::startRecv, this));
}
// sendto thread-safe
int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL) {
if (channel == NULL) return -1;
std::lock_guard<std::mutex> locker(sendto_mutex);
if (peeraddr) hio_set_peeraddr(channel->io(), peeraddr, SOCKADDR_LEN(peeraddr));
return channel->write(data, size);
}
int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL) {
return sendto(buf->data(), buf->size(), peeraddr);
}
int sendto(const std::string& str, struct sockaddr* peeraddr = NULL) {
return sendto(str.data(), str.size(), peeraddr);
}
#if WITH_KCP
void setKcp(kcp_setting_t* setting) {
if (setting == NULL) {
HV_FREE(kcp_setting);
return;
}
if (kcp_setting == NULL) {
HV_ALLOC_SIZEOF(kcp_setting);
}
*kcp_setting = *setting;
}
#endif
public:
std::string host;
int port;
TSocketChannelPtr channel;
#if WITH_KCP
kcp_setting_t* kcp_setting;
#endif
// Callback
std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage;
// NOTE: Use Channel::isWriteComplete in onWriteComplete callback to determine whether all data has been written.
std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete;
private:
std::mutex sendto_mutex;
EventLoopPtr loop_;
};
template<class TSocketChannel = SocketChannel>
class UdpServerTmpl : private EventLoopThread, public UdpServerEventLoopTmpl<TSocketChannel> {
public:
UdpServerTmpl(EventLoopPtr loop = NULL)
: EventLoopThread(loop)
, UdpServerEventLoopTmpl<TSocketChannel>(EventLoopThread::loop())
, is_loop_owner(loop == NULL)
{}
virtual ~UdpServerTmpl() {
stop(true);
}
const EventLoopPtr& loop() {
return EventLoopThread::loop();
}
// start thread-safe
void start(bool wait_threads_started = true) {
if (isRunning()) {
UdpServerEventLoopTmpl<TSocketChannel>::start();
} else {
EventLoopThread::start(wait_threads_started, std::bind(&UdpServerTmpl::startRecv, this));
}
}
// stop thread-safe
void stop(bool wait_threads_stopped = true) {
UdpServerEventLoopTmpl<TSocketChannel>::closesocket();
if (is_loop_owner) {
EventLoopThread::stop(wait_threads_stopped);
}
}
private:
bool is_loop_owner;
};
typedef UdpServerTmpl<SocketChannel> UdpServer;
}
#endif // HV_UDP_SERVER_HPP_

View File

@ -1,55 +0,0 @@
#ifndef HV_WEBSOCKET_CHANNEL_H_
#define HV_WEBSOCKET_CHANNEL_H_
#include <mutex>
#include "Channel.h"
#include "wsdef.h"
#include "hmath.h"
namespace hv {
class HV_EXPORT WebSocketChannel : public SocketChannel {
public:
ws_session_type type;
WebSocketChannel(hio_t* io, ws_session_type type = WS_CLIENT)
: SocketChannel(io)
, type(type)
, opcode(WS_OPCODE_CLOSE)
{}
~WebSocketChannel() {}
// isConnected, send, close
int send(const std::string& msg, enum ws_opcode opcode = WS_OPCODE_TEXT, bool fin = true) {
return send(msg.c_str(), msg.size(), opcode, fin);
}
int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY, bool fin = true);
// websocket fragment
int send(const char* buf, int len, int fragment, enum ws_opcode opcode = WS_OPCODE_BINARY);
int sendPing();
int sendPong();
int close() {
return SocketChannel::close(type == WS_SERVER);
}
protected:
int sendFrame(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY, bool fin = true);
public:
enum ws_opcode opcode;
private:
Buffer sendbuf_;
std::mutex mutex_;
};
}
typedef std::shared_ptr<hv::WebSocketChannel> WebSocketChannelPtr;
#endif // HV_WEBSOCKET_CHANNEL_H_

View File

@ -1,71 +0,0 @@
#ifndef HV_WEBSOCKET_CLIENT_H_
#define HV_WEBSOCKET_CLIENT_H_
/*
* @demo examples/websocket_client_test.cpp
*/
#include "hexport.h"
#include "TcpClient.h"
#include "WebSocketChannel.h"
#include "HttpParser.h"
#include "WebSocketParser.h"
namespace hv {
class HV_EXPORT WebSocketClient : public TcpClientTmpl<WebSocketChannel> {
public:
std::string url;
std::function<void()> onopen;
std::function<void()> onclose;
std::function<void(const std::string& msg)> onmessage;
// PATCH: onmessage not given opcode
enum ws_opcode opcode() { return channel ? channel->opcode : WS_OPCODE_CLOSE; }
WebSocketClient(EventLoopPtr loop = NULL);
virtual ~WebSocketClient();
// url = ws://ip:port/path
// url = wss://ip:port/path
int open(const char* url, const http_headers& headers = DefaultHeaders);
int close();
int send(const std::string& msg);
int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY);
// setConnectTimeout / setPingInterval / setReconnect
void setPingInterval(int ms) {
ping_interval = ms;
}
// NOTE: call before open
void setHttpRequest(const HttpRequestPtr& req) {
http_req_ = req;
}
// NOTE: call when onopen
const HttpResponsePtr& getHttpResponse() {
return http_resp_;
}
private:
enum State {
CONNECTING,
CONNECTED,
WS_UPGRADING,
WS_OPENED,
WS_CLOSED,
} state;
HttpParserPtr http_parser_;
HttpRequestPtr http_req_;
HttpResponsePtr http_resp_;
WebSocketParserPtr ws_parser_;
// ping/pong
int ping_interval;
int ping_cnt;
};
}
#endif // HV_WEBSOCKET_CLIENT_H_

View File

@ -1,35 +0,0 @@
#ifndef HV_WEBSOCKET_PARSER_H_
#define HV_WEBSOCKET_PARSER_H_
#include "hexport.h"
#include <string>
#include <memory>
#include <functional>
enum websocket_parser_state {
WS_FRAME_BEGIN,
WS_FRAME_HEADER,
WS_FRAME_BODY,
WS_FRAME_END,
WS_FRAME_FIN,
};
struct websocket_parser;
class HV_EXPORT WebSocketParser {
public:
websocket_parser* parser;
websocket_parser_state state;
int opcode;
std::string message;
std::function<void(int opcode, const std::string& msg)> onMessage;
WebSocketParser();
~WebSocketParser();
int FeedRecvData(const char* data, size_t len);
};
typedef std::shared_ptr<WebSocketParser> WebSocketParserPtr;
#endif // HV_WEBSOCKET_PARSER_H_

View File

@ -1,46 +0,0 @@
#ifndef HV_WEBSOCKET_SERVER_H_
#define HV_WEBSOCKET_SERVER_H_
/*
* @demo examples/websocket_server_test.cpp
*/
#include "HttpServer.h"
#include "WebSocketChannel.h"
#define websocket_server_t http_server_t
#define websocket_server_run http_server_run
#define websocket_server_stop http_server_stop
namespace hv {
struct WebSocketService {
std::function<void(const WebSocketChannelPtr&, const HttpRequestPtr&)> onopen;
std::function<void(const WebSocketChannelPtr&, const std::string&)> onmessage;
std::function<void(const WebSocketChannelPtr&)> onclose;
int ping_interval;
WebSocketService() : ping_interval(0) {}
void setPingInterval(int ms) {
ping_interval = ms;
}
};
class WebSocketServer : public HttpServer {
public:
WebSocketServer(WebSocketService* service = NULL)
: HttpServer()
{
this->ws = service;
}
~WebSocketServer() { stop(); }
void registerWebSocketService(WebSocketService* service) {
this->ws = service;
}
};
}
#endif // HV_WEBSOCKET_SERVER_H_

View File

@ -1,193 +0,0 @@
#ifndef HV_AXIOS_H_
#define HV_AXIOS_H_
#include "json.hpp"
#include "requests.h"
/*
* Inspired by js axios
*
* @code
#include "axios.h"
int main() {
const char* strReq = R"(
{
"method": "POST",
"url": "http://127.0.0.1:8080/echo",
"timeout": 10,
"params": {
"page_no": "1",
"page_size": "10"
},
"headers": {
"Content-Type": "application/json"
},
"body": {
"app_id": "123456",
"app_secret": "abcdefg"
}
}
)";
// sync
auto resp = axios::axios(strReq);
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
// async
int finished = 0;
axios::axios(strReq, [&finished](const HttpResponsePtr& resp) {
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
finished = 1;
});
// wait async finished
while (!finished) hv_sleep(1);
return 0;
}
**/
using nlohmann::json;
using requests::Request;
using requests::Response;
using requests::ResponseCallback;
namespace axios {
HV_INLINE Request newRequestFromJson(const json& jreq) {
auto req = std::make_shared<HttpRequest>();
// url
if (jreq.contains("url")) {
req->url = jreq["url"];
}
// params
if (jreq.contains("params")) {
req->query_params = jreq["params"].get<hv::QueryParams>();
}
// headers
if (jreq.contains("headers")) {
req->headers = jreq["headers"].get<http_headers>();
}
// body/data
const char* body_field = nullptr;
if (jreq.contains("body")) {
body_field = "body";
} else if (jreq.contains("data")) {
body_field = "data";
}
if (body_field) {
const json& jbody = jreq[body_field];
if (jbody.is_object() || jbody.is_array()) {
req->json = jbody;
} else if (jbody.is_string()) {
req->body = jbody;
}
}
// method
if (jreq.contains("method")) {
std::string method = jreq["method"];
req->method = http_method_enum(method.c_str());
} else if (body_field) {
req->method = HTTP_POST;
} else {
req->method = HTTP_GET;
}
// timeout
if (jreq.contains("timeout")) {
req->timeout = jreq["timeout"];
}
return req;
}
HV_INLINE Request newRequestFromJsonString(const char* req_str) {
return newRequestFromJson(json::parse(req_str));
}
// sync
HV_INLINE Response axios(const json& jreq, http_method method = HTTP_GET, const char* url = nullptr) {
auto req = newRequestFromJson(jreq);
if (method != HTTP_GET) {
req->method = method;
}
if (url) {
req->url = url;
}
return req ? requests::request(req) : nullptr;
}
HV_INLINE Response axios(const char* req_str, http_method method = HTTP_GET, const char* url = nullptr) {
return req_str ? axios(json::parse(req_str), method, url)
: requests::request(method, url);
}
HV_INLINE Response head(const char* url, const json& jreq) {
return axios(jreq, HTTP_HEAD, url);
}
HV_INLINE Response head(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_HEAD, url);
}
HV_INLINE Response get(const char* url, const json& jreq) {
return axios(jreq, HTTP_GET, url);
}
HV_INLINE Response get(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_GET, url);
}
HV_INLINE Response post(const char* url, const json& jreq) {
return axios(jreq, HTTP_POST, url);
}
HV_INLINE Response post(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_POST, url);
}
HV_INLINE Response put(const char* url, const json& jreq) {
return axios(jreq, HTTP_PUT, url);
}
HV_INLINE Response put(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_PUT, url);
}
HV_INLINE Response patch(const char* url, const json& jreq) {
return axios(jreq, HTTP_PATCH, url);
}
HV_INLINE Response patch(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_PATCH, url);
}
HV_INLINE Response Delete(const char* url, const json& jreq) {
return axios(jreq, HTTP_DELETE, url);
}
HV_INLINE Response Delete(const char* url, const char* req_str = nullptr) {
return axios(req_str, HTTP_DELETE, url);
}
// async
HV_INLINE int axios(const json& jreq, ResponseCallback resp_cb) {
auto req = newRequestFromJson(jreq);
return req ? requests::async(req, std::move(resp_cb)) : -1;
}
HV_INLINE int axios(const char* req_str, ResponseCallback resp_cb) {
return axios(json::parse(req_str), std::move(resp_cb));
}
}
#endif // HV_AXIOS_H_

View File

@ -1,46 +0,0 @@
#ifndef HV_BASE64_H_
#define HV_BASE64_H_
#include "hexport.h"
#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4)
#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3)
BEGIN_EXTERN_C
// @return encoded size
HV_EXPORT int hv_base64_encode(const unsigned char *in, unsigned int inlen, char *out);
// @return decoded size
HV_EXPORT int hv_base64_decode(const char *in, unsigned int inlen, unsigned char *out);
END_EXTERN_C
#ifdef __cplusplus
#include <string.h>
#include <string>
namespace hv {
HV_INLINE std::string Base64Encode(const unsigned char* data, unsigned int len) {
int encoded_size = BASE64_ENCODE_OUT_SIZE(len);
std::string encoded_str(encoded_size + 1, 0);
encoded_size = hv_base64_encode(data, len, (char*)encoded_str.data());
encoded_str.resize(encoded_size);
return encoded_str;
}
HV_INLINE std::string Base64Decode(const char* str, unsigned int len = 0) {
if (len == 0) len = strlen(str);
int decoded_size = BASE64_DECODE_OUT_SIZE(len);
std::string decoded_buf(decoded_size + 1, 0);
decoded_size = hv_base64_decode(str, len, (unsigned char*)decoded_buf.data());
decoded_buf.resize(decoded_size);
return decoded_buf;
}
}
#endif
#endif // HV_BASE64_H_

View File

@ -1,105 +0,0 @@
#ifndef HV_DNS_H_
#define HV_DNS_H_
#include "hexport.h"
#include "hplatform.h"
#define DNS_PORT 53
#define DNS_QUERY 0
#define DNS_RESPONSE 1
#define DNS_TYPE_A 1 // ipv4
#define DNS_TYPE_NS 2
#define DNS_TYPE_CNAME 5
#define DNS_TYPE_SOA 6
#define DNS_TYPE_WKS 11
#define DNS_TYPE_PTR 12
#define DNS_TYPE_HINFO 13
#define DNS_TYPE_MX 15
#define DNS_TYPE_AAAA 28 // ipv6
#define DNS_TYPE_AXFR 252
#define DNS_TYPE_ANY 255
#define DNS_CLASS_IN 1
#define DNS_NAME_MAXLEN 256
// sizeof(dnshdr_t) = 12
typedef struct dnshdr_s {
uint16_t transaction_id;
// flags
#if BYTE_ORDER == LITTLE_ENDIAN
uint8_t rd:1;
uint8_t tc:1;
uint8_t aa:1;
uint8_t opcode:4;
uint8_t qr:1;
uint8_t rcode:4;
uint8_t cd:1;
uint8_t ad:1;
uint8_t res:1;
uint8_t ra:1;
#elif BYTE_ORDER == BIG_ENDIAN
uint8_t qr:1; // DNS_QUERY or DNS_RESPONSE
uint8_t opcode:4;
uint8_t aa:1; // authoritative
uint8_t tc:1; // truncated
uint8_t rd:1; // recursion desired
uint8_t ra:1; // recursion available
uint8_t res:1; // reserved
uint8_t ad:1; // authenticated data
uint8_t cd:1; // checking disable
uint8_t rcode:4;
#else
#error "BYTE_ORDER undefined!"
#endif
uint16_t nquestion;
uint16_t nanswer;
uint16_t nauthority;
uint16_t naddtional;
} dnshdr_t;
typedef struct dns_rr_s {
char name[DNS_NAME_MAXLEN]; // original domain, such as www.example.com
uint16_t rtype;
uint16_t rclass;
uint32_t ttl;
uint16_t datalen;
char* data;
} dns_rr_t;
typedef struct dns_s {
dnshdr_t hdr;
dns_rr_t* questions;
dns_rr_t* answers;
dns_rr_t* authorities;
dns_rr_t* addtionals;
} dns_t;
BEGIN_EXTERN_C
// www.example.com => 3www7example3com
HV_EXPORT int dns_name_encode(const char* domain, char* buf);
// 3www7example3com => www.example.com
HV_EXPORT int dns_name_decode(const char* buf, char* domain);
HV_EXPORT int dns_rr_pack(dns_rr_t* rr, char* buf, int len);
HV_EXPORT int dns_rr_unpack(char* buf, int len, dns_rr_t* rr, int is_question);
HV_EXPORT int dns_pack(dns_t* dns, char* buf, int len);
HV_EXPORT int dns_unpack(char* buf, int len, dns_t* dns);
// NOTE: free dns->rrs
HV_EXPORT void dns_free(dns_t* dns);
// dns_pack -> sendto -> recvfrom -> dns_unpack
HV_EXPORT int dns_query(dns_t* query, dns_t* response, const char* nameserver DEFAULT("127.0.1.1"));
// domain -> dns_t query; -> dns_query -> dns_t response; -> addrs
HV_EXPORT int nslookup(const char* domain, uint32_t* addrs, int naddr, const char* nameserver DEFAULT("127.0.1.1"));
END_EXTERN_C
#endif // HV_DNS_H_

View File

@ -1,96 +0,0 @@
#ifndef HV_FTP_H_
#define HV_FTP_H_
#include "hexport.h"
#define FTP_COMMAND_PORT 21
#define FTP_DATA_PORT 20
// ftp_command
// X(name)
#define FTP_COMMAND_MAP(X) \
X(HELP) \
X(USER) \
X(PASS) \
X(PWD) \
X(CWD) \
X(CDUP) \
X(MKD) \
X(RMD) \
X(STAT) \
X(SIZE) \
X(DELE) \
X(RNFR) \
X(RNTO) \
X(PORT) \
X(PASV) \
X(LIST) \
X(NLST) \
X(APPE) \
X(RETR) \
X(STOR) \
X(QUIT) \
enum ftp_command {
#define X(name) FTP_##name,
FTP_COMMAND_MAP(X)
#undef X
};
// ftp_status
// XXX(code, name, string)
#define FTP_STATUS_MAP(XXX) \
XXX(220, READY, Ready) \
XXX(221, BYE, Bye) \
XXX(226, TRANSFER_COMPLETE, Transfer complete) \
XXX(227, PASV, Entering Passive Mode) \
XXX(331, PASS, Password required) \
XXX(230, LOGIN_OK, Login OK) \
XXX(250, OK, OK) \
XXX(500, BAD_SYNTAX, Bad syntax) \
XXX(530, NOT_LOGIN, Not login) \
enum ftp_status {
#define XXX(code, name, string) FTP_STATUS_##name = code,
FTP_STATUS_MAP(XXX)
#undef XXX
};
// more friendly macros
#define FTP_MKDIR FTP_MKD
#define FTP_RMDIR FTP_RMD
#define FTP_APPEND FTP_APPE
#define FTP_REMOVE FTP_DELE
#define FTP_DOWNLOAD FTP_RETR
#define FTP_UPLOAD FTP_STOR
#define FTP_RECV_BUFSIZE 8192
typedef struct ftp_handle_s {
int sockfd;
char recvbuf[FTP_RECV_BUFSIZE];
void* userdata;
} ftp_handle_t;
BEGIN_EXTERN_C
HV_EXPORT const char* ftp_command_str(enum ftp_command cmd);
HV_EXPORT const char* ftp_status_str(enum ftp_status status);
HV_EXPORT int ftp_connect(ftp_handle_t* hftp, const char* host, int port);
HV_EXPORT int ftp_login(ftp_handle_t* hftp, const char* username, const char* password);
HV_EXPORT int ftp_quit(ftp_handle_t* hftp);
HV_EXPORT int ftp_exec(ftp_handle_t* hftp, const char* cmd, const char* param);
// local => remote
HV_EXPORT int ftp_upload(ftp_handle_t* hftp, const char* local_filepath, const char* remote_filepath);
// remote => local
HV_EXPORT int ftp_download(ftp_handle_t* hftp, const char* remote_filepath, const char* local_filepath);
typedef int (*ftp_download_cb)(ftp_handle_t* hftp, char* buf, int len);
HV_EXPORT int ftp_download_with_cb(ftp_handle_t* hftp, const char* filepath, ftp_download_cb cb);
END_EXTERN_C
#endif // HV_FTP_H_

View File

@ -1,49 +0,0 @@
#ifndef HV_ASYNC_H_
#define HV_ASYNC_H_
#include "hexport.h"
#include "hthreadpool.h"
#include "singleton.h"
namespace hv {
class HV_EXPORT GlobalThreadPool : public HThreadPool {
SINGLETON_DECL(GlobalThreadPool)
protected:
GlobalThreadPool() : HThreadPool() {}
~GlobalThreadPool() {}
};
/*
* return a future, calling future.get() will wait task done and return RetType.
* async(fn, args...)
* async(std::bind(&Class::mem_fn, &obj))
* async(std::mem_fn(&Class::mem_fn, &obj))
*
*/
template<class Fn, class... Args>
auto async(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))> {
return GlobalThreadPool::instance()->commit(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
class async {
public:
static void startup(int min_threads = DEFAULT_THREAD_POOL_MIN_THREAD_NUM,
int max_threads = DEFAULT_THREAD_POOL_MAX_THREAD_NUM,
int max_idle_ms = DEFAULT_THREAD_POOL_MAX_IDLE_TIME) {
GlobalThreadPool* gtp = GlobalThreadPool::instance();
if (gtp->isStarted()) return;
gtp->setMinThreadNum(min_threads);
gtp->setMaxThreadNum(max_threads);
gtp->setMaxIdleTime(max_idle_ms);
gtp->start();
}
static void cleanup() {
GlobalThreadPool::exitInstance();
}
};
} // end namespace hv
#endif // HV_ASYNC_H_

View File

@ -1,130 +0,0 @@
#ifndef HV_ATOMIC_H_
#define HV_ATOMIC_H_
#ifdef __cplusplus
// c++11
#include <atomic>
using std::atomic_flag;
using std::atomic_long;
#define ATOMIC_FLAG_TEST_AND_SET(p) ((p)->test_and_set())
#define ATOMIC_FLAG_CLEAR(p) ((p)->clear())
#else
#include "hplatform.h" // for HAVE_STDATOMIC_H
#if HAVE_STDATOMIC_H
// c11
#include <stdatomic.h>
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
#define ATOMIC_FLAG_CLEAR atomic_flag_clear
#define ATOMIC_ADD atomic_fetch_add
#define ATOMIC_SUB atomic_fetch_sub
#define ATOMIC_INC(p) ATOMIC_ADD(p, 1)
#define ATOMIC_DEC(p) ATOMIC_SUB(p, 1)
#else
typedef volatile bool atomic_bool;
typedef volatile char atomic_char;
typedef volatile unsigned char atomic_uchar;
typedef volatile short atomic_short;
typedef volatile unsigned short atomic_ushort;
typedef volatile int atomic_int;
typedef volatile unsigned int atomic_uint;
typedef volatile long atomic_long;
typedef volatile unsigned long atomic_ulong;
typedef volatile long long atomic_llong;
typedef volatile unsigned long long atomic_ullong;
typedef volatile size_t atomic_size_t;
typedef struct atomic_flag { atomic_bool _Value; } atomic_flag;
#ifdef _WIN32
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
// return InterlockedIncrement((LONG*)&p->_Value, 1);
return InterlockedCompareExchange((LONG*)&p->_Value, 1, 0);
}
#define ATOMIC_ADD InterlockedAdd
#define ATOMIC_SUB(p, n) InterlockedAdd(p, -n)
#define ATOMIC_INC InterlockedIncrement
#define ATOMIC_DEC InterlockedDecrement
#elif defined(__GNUC__)
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
return !__sync_bool_compare_and_swap(&p->_Value, 0, 1);
}
#define ATOMIC_ADD __sync_fetch_and_add
#define ATOMIC_SUB __sync_fetch_and_sub
#define ATOMIC_INC(p) ATOMIC_ADD(p, 1)
#define ATOMIC_DEC(p) ATOMIC_SUB(p, 1)
#endif
#endif // HAVE_STDATOMIC_H
#endif // __cplusplus
#ifndef ATOMIC_FLAG_INIT
#define ATOMIC_FLAG_INIT { 0 }
#endif
#ifndef ATOMIC_VAR_INIT
#define ATOMIC_VAR_INIT(value) (value)
#endif
#ifndef ATOMIC_FLAG_TEST_AND_SET
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set
static inline bool atomic_flag_test_and_set(atomic_flag* p) {
bool ret = p->_Value;
p->_Value = 1;
return ret;
}
#endif
#ifndef ATOMIC_FLAG_CLEAR
#define ATOMIC_FLAG_CLEAR atomic_flag_clear
static inline void atomic_flag_clear(atomic_flag* p) {
p->_Value = 0;
}
#endif
#ifndef ATOMIC_ADD
#define ATOMIC_ADD(p, n) (*(p) += (n))
#endif
#ifndef ATOMIC_SUB
#define ATOMIC_SUB(p, n) (*(p) -= (n))
#endif
#ifndef ATOMIC_INC
#define ATOMIC_INC(p) ((*(p))++)
#endif
#ifndef ATOMIC_DEC
#define ATOMIC_DEC(p) ((*(p))--)
#endif
typedef atomic_flag hatomic_flag_t;
#define HATOMIC_FLAG_INIT ATOMIC_FLAG_INIT
#define hatomic_flag_test_and_set ATOMIC_FLAG_TEST_AND_SET
#define hatomic_flag_clear ATOMIC_FLAG_CLEAR
typedef atomic_long hatomic_t;
#define HATOMIC_VAR_INIT ATOMIC_VAR_INIT
#define hatomic_add ATOMIC_ADD
#define hatomic_sub ATOMIC_SUB
#define hatomic_inc ATOMIC_INC
#define hatomic_dec ATOMIC_DEC
#endif // HV_ATOMIC_H_

View File

@ -1,144 +0,0 @@
#ifndef HV_BASE_H_
#define HV_BASE_H_
#include "hexport.h"
#include "hplatform.h" // for bool
#include "hdef.h" // for printd
BEGIN_EXTERN_C
//--------------------alloc/free---------------------------
HV_EXPORT void* hv_malloc(size_t size);
HV_EXPORT void* hv_realloc(void* oldptr, size_t newsize, size_t oldsize);
HV_EXPORT void* hv_calloc(size_t nmemb, size_t size);
HV_EXPORT void* hv_zalloc(size_t size);
HV_EXPORT void hv_free(void* ptr);
#define HV_ALLOC(ptr, size)\
do {\
*(void**)&(ptr) = hv_zalloc(size);\
printd("alloc(%p, size=%llu)\tat [%s:%d:%s]\n", ptr, (unsigned long long)size, __FILE__, __LINE__, __FUNCTION__);\
} while(0)
#define HV_ALLOC_SIZEOF(ptr) HV_ALLOC(ptr, sizeof(*(ptr)))
#define HV_FREE(ptr)\
do {\
if (ptr) {\
hv_free(ptr);\
printd("free( %p )\tat [%s:%d:%s]\n", ptr, __FILE__, __LINE__, __FUNCTION__);\
ptr = NULL;\
}\
} while(0)
#define STACK_OR_HEAP_ALLOC(ptr, size, stack_size)\
unsigned char _stackbuf_[stack_size] = { 0 };\
if ((size) > (stack_size)) {\
HV_ALLOC(ptr, size);\
} else {\
*(unsigned char**)&(ptr) = _stackbuf_;\
}
#define STACK_OR_HEAP_FREE(ptr)\
if ((unsigned char*)(ptr) != _stackbuf_) {\
HV_FREE(ptr);\
}
#define HV_DEFAULT_STACKBUF_SIZE 1024
#define HV_STACK_ALLOC(ptr, size) STACK_OR_HEAP_ALLOC(ptr, size, HV_DEFAULT_STACKBUF_SIZE)
#define HV_STACK_FREE(ptr) STACK_OR_HEAP_FREE(ptr)
HV_EXPORT long hv_alloc_cnt();
HV_EXPORT long hv_free_cnt();
HV_INLINE void hv_memcheck(void) {
printf("Memcheck => alloc:%ld free:%ld\n", hv_alloc_cnt(), hv_free_cnt());
}
#define HV_MEMCHECK atexit(hv_memcheck);
//--------------------string-------------------------------
HV_EXPORT char* hv_strupper(char* str);
HV_EXPORT char* hv_strlower(char* str);
HV_EXPORT char* hv_strreverse(char* str);
HV_EXPORT bool hv_strstartswith(const char* str, const char* start);
HV_EXPORT bool hv_strendswith(const char* str, const char* end);
HV_EXPORT bool hv_strcontains(const char* str, const char* sub);
HV_EXPORT bool hv_wildcard_match(const char* str, const char* pattern);
// strncpy n = sizeof(dest_buf)-1
// hv_strncpy n = sizeof(dest_buf)
HV_EXPORT char* hv_strncpy(char* dest, const char* src, size_t n);
// strncat n = sizeof(dest_buf)-1-strlen(dest)
// hv_strncpy n = sizeof(dest_buf)
HV_EXPORT char* hv_strncat(char* dest, const char* src, size_t n);
#if !HAVE_STRLCPY
#define strlcpy hv_strncpy
#endif
#if !HAVE_STRLCAT
#define strlcat hv_strncat
#endif
HV_EXPORT char* hv_strnchr(const char* s, char c, size_t n);
#define hv_strrchr_dot(str) strrchr(str, '.')
HV_EXPORT char* hv_strrchr_dir(const char* filepath);
// basename
HV_EXPORT const char* hv_basename(const char* filepath);
HV_EXPORT const char* hv_suffixname(const char* filename);
// mkdir -p
HV_EXPORT int hv_mkdir_p(const char* dir);
// rmdir -p
HV_EXPORT int hv_rmdir_p(const char* dir);
// path
HV_EXPORT bool hv_exists(const char* path);
HV_EXPORT bool hv_isdir(const char* path);
HV_EXPORT bool hv_isfile(const char* path);
HV_EXPORT bool hv_islink(const char* path);
HV_EXPORT size_t hv_filesize(const char* filepath);
HV_EXPORT char* get_executable_path(char* buf, int size);
HV_EXPORT char* get_executable_dir(char* buf, int size);
HV_EXPORT char* get_executable_file(char* buf, int size);
HV_EXPORT char* get_run_dir(char* buf, int size);
// random
HV_EXPORT int hv_rand(int min, int max);
HV_EXPORT char* hv_random_string(char *buf, int len);
// 1 y on yes true enable => true
HV_EXPORT bool hv_getboolean(const char* str);
// 1T2G3M4K5B => ?B
HV_EXPORT size_t hv_parse_size(const char* str);
// 1w2d3h4m5s => ?s
HV_EXPORT time_t hv_parse_time(const char* str);
// scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
typedef enum {
HV_URL_SCHEME,
HV_URL_USERNAME,
HV_URL_PASSWORD,
HV_URL_HOST,
HV_URL_PORT,
HV_URL_PATH,
HV_URL_QUERY,
HV_URL_FRAGMENT,
HV_URL_FIELD_NUM,
} hurl_field_e;
typedef struct hurl_s {
struct {
unsigned short off;
unsigned short len;
} fields[HV_URL_FIELD_NUM];
unsigned short port;
} hurl_t;
HV_EXPORT int hv_parse_url(hurl_t* stURL, const char* strURL);
END_EXTERN_C
#endif // HV_BASE_H_

View File

@ -1,257 +0,0 @@
#ifndef HV_BUF_H_
#define HV_BUF_H_
#include "hdef.h" // for MAX
#include "hbase.h" // for HV_ALLOC, HV_FREE
typedef struct hbuf_s {
char* base;
size_t len;
#ifdef __cplusplus
hbuf_s() {
base = NULL;
len = 0;
}
hbuf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
}
#endif
} hbuf_t;
typedef struct offset_buf_s {
char* base;
size_t len;
size_t offset;
#ifdef __cplusplus
offset_buf_s() {
base = NULL;
len = 0;
offset = 0;
}
offset_buf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
offset = 0;
}
#endif
} offset_buf_t;
typedef struct fifo_buf_s {
char* base;
size_t len;
size_t head;
size_t tail;
#ifdef __cplusplus
fifo_buf_s() {
base = NULL;
len = 0;
head = tail = 0;
}
fifo_buf_s(void* data, size_t len) {
this->base = (char*)data;
this->len = len;
head = tail = 0;
}
#endif
} fifo_buf_t;
#ifdef __cplusplus
class HBuf : public hbuf_t {
public:
HBuf() : hbuf_t() {
cleanup_ = false;
}
HBuf(void* data, size_t len) : hbuf_t(data, len) {
cleanup_ = false;
}
HBuf(size_t cap) { resize(cap); }
virtual ~HBuf() {
cleanup();
}
void* data() { return base; }
size_t size() { return len; }
bool isNull() { return base == NULL || len == 0; }
void cleanup() {
if (cleanup_) {
HV_FREE(base);
len = 0;
cleanup_ = false;
}
}
void resize(size_t cap) {
if (cap == len) return;
if (base == NULL) {
HV_ALLOC(base, cap);
}
else {
base = (char*)hv_realloc(base, cap, len);
}
len = cap;
cleanup_ = true;
}
void copy(void* data, size_t len) {
resize(len);
memcpy(base, data, len);
}
void copy(hbuf_t* buf) {
copy(buf->base, buf->len);
}
private:
bool cleanup_;
};
// VL: Variable-Length
class HVLBuf : public HBuf {
public:
HVLBuf() : HBuf() {_offset = _size = 0;}
HVLBuf(void* data, size_t len) : HBuf(data, len) {_offset = 0; _size = len;}
HVLBuf(size_t cap) : HBuf(cap) {_offset = _size = 0;}
virtual ~HVLBuf() {}
char* data() { return base + _offset; }
size_t size() { return _size; }
void push_front(void* ptr, size_t len) {
if (len > this->len - _size) {
size_t newsize = MAX(this->len, len)*2;
resize(newsize);
}
if (_offset < len) {
// move => end
memmove(base+this->len-_size, data(), _size);
_offset = this->len-_size;
}
memcpy(data()-len, ptr, len);
_offset -= len;
_size += len;
}
void push_back(void* ptr, size_t len) {
if (len > this->len - _size) {
size_t newsize = MAX(this->len, len)*2;
resize(newsize);
}
else if (len > this->len - _offset - _size) {
// move => start
memmove(base, data(), _size);
_offset = 0;
}
memcpy(data()+_size, ptr, len);
_size += len;
}
void pop_front(void* ptr, size_t len) {
if (len <= _size) {
if (ptr) {
memcpy(ptr, data(), len);
}
_offset += len;
if (_offset >= this->len) _offset = 0;
_size -= len;
}
}
void pop_back(void* ptr, size_t len) {
if (len <= _size) {
if (ptr) {
memcpy(ptr, data()+_size-len, len);
}
_size -= len;
}
}
void clear() {
_offset = _size = 0;
}
void prepend(void* ptr, size_t len) {
push_front(ptr, len);
}
void append(void* ptr, size_t len) {
push_back(ptr, len);
}
void insert(void* ptr, size_t len) {
push_back(ptr, len);
}
void remove(size_t len) {
pop_front(NULL, len);
}
private:
size_t _offset;
size_t _size;
};
class HRingBuf : public HBuf {
public:
HRingBuf() : HBuf() {_head = _tail = _size = 0;}
HRingBuf(size_t cap) : HBuf(cap) {_head = _tail = _size = 0;}
virtual ~HRingBuf() {}
char* alloc(size_t len) {
char* ret = NULL;
if (_head < _tail || _size == 0) {
// [_tail, this->len) && [0, _head)
if (this->len - _tail >= len) {
ret = base + _tail;
_tail += len;
if (_tail == this->len) _tail = 0;
}
else if (_head >= len) {
ret = base;
_tail = len;
}
}
else {
// [_tail, _head)
if (_head - _tail >= len) {
ret = base + _tail;
_tail += len;
}
}
_size += ret ? len : 0;
return ret;
}
void free(size_t len) {
_size -= len;
if (len <= this->len - _head) {
_head += len;
if (_head == this->len) _head = 0;
}
else {
_head = len;
}
}
void clear() {_head = _tail = _size = 0;}
size_t size() {return _size;}
private:
size_t _head;
size_t _tail;
size_t _size;
};
#endif
#endif // HV_BUF_H_

View File

@ -1,102 +0,0 @@
#ifndef HV_CONFIG_H_
#define HV_CONFIG_H_
#ifndef HAVE_STDBOOL_H
#define HAVE_STDBOOL_H 1
#endif
#ifndef HAVE_STDINT_H
#define HAVE_STDINT_H 1
#endif
#ifndef HAVE_STDATOMIC_H
#define HAVE_STDATOMIC_H 0
#endif
#ifndef HAVE_SYS_TYPES_H
#define HAVE_SYS_TYPES_H 1
#endif
#ifndef HAVE_SYS_STAT_H
#define HAVE_SYS_STAT_H 1
#endif
#ifndef HAVE_SYS_TIME_H
#define HAVE_SYS_TIME_H 0
#endif
#ifndef HAVE_FCNTL_H
#define HAVE_FCNTL_H 1
#endif
#ifndef HAVE_PTHREAD_H
#define HAVE_PTHREAD_H 0
#endif
#ifndef HAVE_ENDIAN_H
#define HAVE_ENDIAN_H 0
#endif
#ifndef HAVE_SYS_ENDIAN_H
#define HAVE_SYS_ENDIAN_H 0
#endif
#ifndef HAVE_GETTID
#define HAVE_GETTID 0
#endif
#ifndef HAVE_STRLCPY
#define HAVE_STRLCPY 0
#endif
#ifndef HAVE_STRLCAT
#define HAVE_STRLCAT 0
#endif
#ifndef HAVE_CLOCK_GETTIME
#define HAVE_CLOCK_GETTIME 0
#endif
#ifndef HAVE_GETTIMEOFDAY
#define HAVE_GETTIMEOFDAY 0
#endif
#ifndef HAVE_PTHREAD_SPIN_LOCK
#define HAVE_PTHREAD_SPIN_LOCK 0
#endif
#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
#define HAVE_PTHREAD_MUTEX_TIMEDLOCK 0
#endif
#ifndef HAVE_SEM_TIMEDWAIT
#define HAVE_SEM_TIMEDWAIT 0
#endif
#ifndef HAVE_PIPE
#define HAVE_PIPE 0
#endif
#ifndef HAVE_SOCKETPAIR
#define HAVE_SOCKETPAIR 0
#endif
#ifndef HAVE_EVENTFD
#define HAVE_EVENTFD 0
#endif
#ifndef HAVE_SETPROCTITLE
#define HAVE_SETPROCTITLE 0
#endif
#define WITH_OPENSSL 1
/* #undef WITH_GNUTLS */
/* #undef WITH_MBEDTLS */
/* #undef ENABLE_UDS */
/* #undef USE_MULTIMAP */
#define WITH_WEPOLL 1
/* #undef WITH_KCP */
#endif // HV_CONFIG_H_

View File

@ -1,271 +0,0 @@
#ifndef HV_DEF_H_
#define HV_DEF_H_
#include "hplatform.h"
#ifndef ABS
#define ABS(n) ((n) > 0 ? (n) : -(n))
#endif
#ifndef NABS
#define NABS(n) ((n) < 0 ? (n) : -(n))
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
#endif
#ifndef BITSET
#define BITSET(p, n) (*(p) |= (1u << (n)))
#endif
#ifndef BITCLR
#define BITCLR(p, n) (*(p) &= ~(1u << (n)))
#endif
#ifndef BITGET
#define BITGET(i, n) ((i) & (1u << (n)))
#endif
/*
#ifndef CR
#define CR '\r'
#endif
#ifndef LF
#define LF '\n'
#endif
#ifndef CRLF
#define CRLF "\r\n"
#endif
*/
#define FLOAT_PRECISION 1e-6
#define FLOAT_EQUAL_ZERO(f) (ABS(f) < FLOAT_PRECISION)
#ifndef INFINITE
#define INFINITE (uint32_t)-1
#endif
/*
ASCII:
[0, 0x20) control-charaters
[0x20, 0x7F) printable-charaters
0x0A => LF
0x0D => CR
0x20 => SPACE
0x7F => DEL
[0x09, 0x0D] => \t\n\v\f\r
[0x30, 0x39] => 0~9
[0x41, 0x5A] => A~Z
[0x61, 0x7A] => a~z
*/
#ifndef IS_ALPHA
#define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
#endif
// NOTE: IS_NUM conflicts with mysql.h
#ifndef IS_DIGIT
#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
#endif
#ifndef IS_ALPHANUM
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_DIGIT(c))
#endif
#ifndef IS_CNTRL
#define IS_CNTRL(c) ((c) >= 0 && (c) < 0x20)
#endif
#ifndef IS_GRAPH
#define IS_GRAPH(c) ((c) >= 0x20 && (c) < 0x7F)
#endif
#ifndef IS_HEX
#define IS_HEX(c) (IS_DIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
#endif
#ifndef IS_LOWER
#define IS_LOWER(c) (((c) >= 'a' && (c) <= 'z'))
#endif
#ifndef IS_UPPER
#define IS_UPPER(c) (((c) >= 'A' && (c) <= 'Z'))
#endif
#ifndef LOWER
#define LOWER(c) ((c) | 0x20)
#endif
#ifndef UPPER
#define UPPER(c) ((c) & ~0x20)
#endif
// LD, LU, LLD, LLU for explicit conversion of integer
// #ifndef LD
// #define LD(v) ((long)(v))
// #endif
// #ifndef LU
// #define LU(v) ((unsigned long)(v))
// #endif
#ifndef LLD
#define LLD(v) ((long long)(v))
#endif
#ifndef LLU
#define LLU(v) ((unsigned long long)(v))
#endif
#ifndef _WIN32
// MAKEWORD, HIBYTE, LOBYTE
#ifndef MAKEWORD
#define MAKEWORD(h, l) ( (((WORD)h) << 8) | (l & 0xff) )
#endif
#ifndef HIBYTE
#define HIBYTE(w) ( (BYTE)(((WORD)w) >> 8) )
#endif
#ifndef LOBYTE
#define LOBYTE(w) ( (BYTE)(w & 0xff) )
#endif
// MAKELONG, HIWORD, LOWORD
#ifndef MAKELONG
#define MAKELONG(h, l) ( ((int32_t)h) << 16 | (l & 0xffff) )
#endif
#ifndef HIWORD
#define HIWORD(n) ( (WORD)(((int32_t)n) >> 16) )
#endif
#ifndef LOWORD
#define LOWORD(n) ( (WORD)(n & 0xffff) )
#endif
#endif // _WIN32
// MAKEINT64, HIINT, LOINT
#ifndef MAKEINT64
#define MAKEINT64(h, l) ( ((int64_t)h) << 32 | (l & 0xffffffff) )
#endif
#ifndef HIINT
#define HIINT(n) ( (int32_t)(((int64_t)n) >> 32) )
#endif
#ifndef LOINT
#define LOINT(n) ( (int32_t)(n & 0xffffffff) )
#endif
#ifndef MAKE_FOURCC
#define MAKE_FOURCC(a, b, c, d) \
( ((uint32)d) | ( ((uint32)c) << 8 ) | ( ((uint32)b) << 16 ) | ( ((uint32)a) << 24 ) )
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef LIMIT
#define LIMIT(lower, v, upper) ((v) < (lower) ? (lower) : (v) > (upper) ? (upper) : (v))
#endif
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef SAFE_ALLOC
#define SAFE_ALLOC(p, size)\
do {\
void* ptr = malloc(size);\
if (!ptr) {\
fprintf(stderr, "malloc failed!\n");\
exit(-1);\
}\
memset(ptr, 0, size);\
*(void**)&(p) = ptr;\
} while(0)
#endif
#ifndef SAFE_FREE
#define SAFE_FREE(p) do {if (p) {free(p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) do {if (p) {delete (p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) do {if (p) {delete[] (p); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) do {if (p) {(p)->release(); (p) = NULL;}} while(0)
#endif
#ifndef SAFE_CLOSE
#define SAFE_CLOSE(fd) do {if ((fd) >= 0) {close(fd); (fd) = -1;}} while(0)
#endif
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define STRINGIFY_HELPER(x) #x
#define STRINGCAT(x, y) STRINGCAT_HELPER(x, y)
#define STRINGCAT_HELPER(x, y) x##y
#ifndef offsetof
#define offsetof(type, member) \
((size_t)(&((type*)0)->member))
#endif
#ifndef offsetofend
#define offsetofend(type, member) \
(offsetof(type, member) + sizeof(((type*)0)->member))
#endif
#ifndef container_of
#define container_of(ptr, type, member) \
((type*)((char*)(ptr) - offsetof(type, member)))
#endif
#ifdef PRINT_DEBUG
#define printd(...) printf(__VA_ARGS__)
#else
#define printd(...)
#endif
#ifdef PRINT_ERROR
#define printe(...) fprintf(stderr, __VA_ARGS__)
#else
#define printe(...)
#endif
#endif // HV_DEF_H_

View File

@ -1,69 +0,0 @@
#ifndef HV_DIR_H_
#define HV_DIR_H_
/*
*@code
int main(int argc, char* argv[]) {
const char* dir = ".";
if (argc > 1) {
dir = argv[1];
}
std::list<hdir_t> dirs;
listdir(dir, dirs);
for (auto& item : dirs) {
printf("%c%c%c%c%c%c%c%c%c%c\t",
item.type,
item.mode & 0400 ? 'r' : '-',
item.mode & 0200 ? 'w' : '-',
item.mode & 0100 ? 'x' : '-',
item.mode & 0040 ? 'r' : '-',
item.mode & 0020 ? 'w' : '-',
item.mode & 0010 ? 'x' : '-',
item.mode & 0004 ? 'r' : '-',
item.mode & 0002 ? 'w' : '-',
item.mode & 0001 ? 'x' : '-');
float hsize;
if (item.size < 1024) {
printf("%lu\t", item.size);
}
else if ((hsize = item.size/1024.0f) < 1024.0f) {
printf("%.1fK\t", hsize);
}
else if ((hsize /= 1024.0f) < 1024.0f) {
printf("%.1fM\t", hsize);
}
else {
hsize /= 1024.0f;
printf("%.1fG\t", hsize);
}
struct tm* tm = localtime(&item.mtime);
printf("%04d-%02d-%02d %02d:%02d:%02d\t",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
printf("%s%s\n", item.name, item.type == 'd' ? "/" : "");
}
return 0;
}
*/
#include <string.h>
#include <time.h>
#include <list>
#include "hexport.h"
typedef struct hdir_s {
char name[256];
char type; // f:file d:dir l:link b:block c:char s:socket p:pipe
char reserverd;
unsigned short mode;
size_t size;
time_t atime;
time_t mtime;
time_t ctime;
} hdir_t;
// listdir: same as ls on unix, dir on win
HV_EXPORT int listdir(const char* dir, std::list<hdir_t>& dirs);
#endif // HV_DIR_H_

View File

@ -1,244 +0,0 @@
#ifndef HV_ENDIAN_H_
#define HV_ENDIAN_H_
#include "hplatform.h"
#if defined(OS_MAC)
#include <libkern/OSByteOrder.h>
#define htobe16(v) OSSwapHostToBigInt16(v)
#define htobe32(v) OSSwapHostToBigInt32(v)
#define htobe64(v) OSSwapHostToBigInt64(v)
#define be16toh(v) OSSwapBigToHostInt16(v)
#define be32toh(v) OSSwapBigToHostInt32(v)
#define be64toh(v) OSSwapBigToHostInt64(v)
#define htole16(v) OSSwapHostToLittleInt16(v)
#define htole32(v) OSSwapHostToLittleInt32(v)
#define htole64(v) OSSwapHostToLittleInt64(v)
#define le16toh(v) OSSwapLittleToHostInt16(v)
#define le32toh(v) OSSwapLittleToHostInt32(v)
#define le64toh(v) OSSwapLittleToHostInt64(v)
#elif defined(OS_WIN)
#if _WIN32_WINNT < _WIN32_WINNT_WIN8
/*
* Byte order conversion functions for 64-bit integers and 32 + 64 bit
* floating-point numbers. IEEE big-endian format is used for the
* network floating point format.
*/
#define _WS2_32_WINSOCK_SWAP_LONG(l) \
( ( ((l) >> 24) & 0x000000FFL ) | \
( ((l) >> 8) & 0x0000FF00L ) | \
( ((l) << 8) & 0x00FF0000L ) | \
( ((l) << 24) & 0xFF000000L ) )
#define _WS2_32_WINSOCK_SWAP_LONGLONG(l) \
( ( ((l) >> 56) & 0x00000000000000FFLL ) | \
( ((l) >> 40) & 0x000000000000FF00LL ) | \
( ((l) >> 24) & 0x0000000000FF0000LL ) | \
( ((l) >> 8) & 0x00000000FF000000LL ) | \
( ((l) << 8) & 0x000000FF00000000LL ) | \
( ((l) << 24) & 0x0000FF0000000000LL ) | \
( ((l) << 40) & 0x00FF000000000000LL ) | \
( ((l) << 56) & 0xFF00000000000000LL ) )
#ifndef htonll
__inline unsigned __int64 htonll ( unsigned __int64 Value )
{
const unsigned __int64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG (Value);
return Retval;
}
#endif /* htonll */
#ifndef ntohll
__inline unsigned __int64 ntohll ( unsigned __int64 Value )
{
const unsigned __int64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG (Value);
return Retval;
}
#endif /* ntohll */
#ifndef htonf
__inline unsigned __int32 htonf ( float Value )
{
unsigned __int32 Tempval;
unsigned __int32 Retval;
Tempval = *(unsigned __int32*)(&Value);
Retval = _WS2_32_WINSOCK_SWAP_LONG (Tempval);
return Retval;
}
#endif /* htonf */
#ifndef ntohf
__inline float ntohf ( unsigned __int32 Value )
{
const unsigned __int32 Tempval = _WS2_32_WINSOCK_SWAP_LONG (Value);
float Retval;
*((unsigned __int32*)&Retval) = Tempval;
return Retval;
}
#endif /* ntohf */
#ifndef htond
__inline unsigned __int64 htond ( double Value )
{
unsigned __int64 Tempval;
unsigned __int64 Retval;
Tempval = *(unsigned __int64*)(&Value);
Retval = _WS2_32_WINSOCK_SWAP_LONGLONG (Tempval);
return Retval;
}
#endif /* htond */
#ifndef ntohd
__inline double ntohd ( unsigned __int64 Value )
{
const unsigned __int64 Tempval = _WS2_32_WINSOCK_SWAP_LONGLONG (Value);
double Retval;
*((unsigned __int64*)&Retval) = Tempval;
return Retval;
}
#endif /* ntohd */
#endif
#define htobe16(v) htons(v)
#define htobe32(v) htonl(v)
#define htobe64(v) htonll(v)
#define be16toh(v) ntohs(v)
#define be32toh(v) ntohl(v)
#define be64toh(v) ntohll(v)
#if (BYTE_ORDER == LITTLE_ENDIAN)
#define htole16(v) (v)
#define htole32(v) (v)
#define htole64(v) (v)
#define le16toh(v) (v)
#define le32toh(v) (v)
#define le64toh(v) (v)
#elif (BYTE_ORDER == BIG_ENDIAN)
#define htole16(v) __builtin_bswap16(v)
#define htole32(v) __builtin_bswap32(v)
#define htole64(v) __builtin_bswap64(v)
#define le16toh(v) __builtin_bswap16(v)
#define le32toh(v) __builtin_bswap32(v)
#define le64toh(v) __builtin_bswap64(v)
#endif
#elif HAVE_ENDIAN_H
#include <endian.h>
#elif HAVE_SYS_ENDIAN_H
#include <sys/endian.h>
#else
#warning "Not found endian.h!"
#endif
#define PI8(p) *(int8_t*)(p)
#define PI16(p) *(int16_t*)(p)
#define PI32(p) *(int32_t*)(p)
#define PI64(p) *(int64_t*)(p)
#define PU8(p) *(uint8_t*)(p)
#define PU16(p) *(uint16_t*)(p)
#define PU32(p) *(uint32_t*)(p)
#define PU64(p) *(uint64_t*)(p)
#define PF32(p) *(float*)(p)
#define PF64(p) *(double*)(p)
#define GET_BE16(p) be16toh(PU16(p))
#define GET_BE32(p) be32toh(PU32(p))
#define GET_BE64(p) be64toh(PU64(p))
#define GET_LE16(p) le16toh(PU16(p))
#define GET_LE32(p) le32toh(PU32(p))
#define GET_LE64(p) le64toh(PU64(p))
#define PUT_BE16(p, v) PU16(p) = htobe16(v)
#define PUT_BE32(p, v) PU32(p) = htobe32(v)
#define PUT_BE64(p, v) PU64(p) = htobe64(v)
#define PUT_LE16(p, v) PU16(p) = htole16(v)
#define PUT_LE32(p, v) PU32(p) = htole32(v)
#define PUT_LE64(p, v) PU64(p) = htole64(v)
// NOTE: uint8_t* p = (uint8_t*)buf;
#define POP_BE8(p, v) v = *p; ++p
#define POP_BE16(p, v) v = be16toh(PU16(p)); p += 2
#define POP_BE32(p, v) v = be32toh(PU32(p)); p += 4
#define POP_BE64(p, v) v = be64toh(PU64(p)); p += 8
#define POP_LE8(p, v) v= *p; ++p
#define POP_LE16(p, v) v = le16toh(PU16(p)); p += 2
#define POP_LE32(p, v) v = le32toh(PU32(p)); p += 4
#define POP_LE64(p, v) v = le64toh(PU64(p)); p += 8
#define PUSH_BE8(p, v) *p = v; ++p
#define PUSH_BE16(p, v) PU16(p) = htobe16(v); p += 2
#define PUSH_BE32(p, v) PU32(p) = htobe32(v); p += 4
#define PUSH_BE64(p, v) PU64(p) = htobe64(v); p += 8
#define PUSH_LE8(p, v) *p = v; ++p
#define PUSH_LE16(p, v) PU16(p) = htole16(v); p += 2
#define PUSH_LE32(p, v) PU32(p) = htole32(v); p += 4
#define PUSH_LE64(p, v) PU64(p) = htole64(v); p += 8
// NOTE: NET_ENDIAN = BIG_ENDIAN
#define POP8(p, v) POP_BE8(p, v)
#define POP16(p, v) POP_BE16(p, v)
#define POP32(p, v) POP_BE32(p, v)
#define POP64(p, v) POP_BE64(p, v)
#define POP_N(p, v, n) memcpy(v, p, n); p += n
#define PUSH8(p, v) PUSH_BE8(p, v)
#define PUSH16(p, v) PUSH_BE16(p, v)
#define PUSH32(p, v) PUSH_BE32(p, v)
#define PUSH64(p, v) PUSH_BE64(p, v)
#define PUSH_N(p, v, n) memcpy(p, v, n); p += n
static inline int detect_endian() {
union {
char c;
short s;
} u;
u.s = 0x1122;
return u.c ==0x11 ? BIG_ENDIAN : LITTLE_ENDIAN;
}
#ifdef __cplusplus
template <typename T>
uint8_t* serialize(uint8_t* buf, T value, int host_endian = LITTLE_ENDIAN, int buf_endian = BIG_ENDIAN) {
size_t size = sizeof(T);
uint8_t* pDst = buf;
uint8_t* pSrc = (uint8_t*)&value;
if (host_endian == buf_endian) {
memcpy(pDst, pSrc, size);
}
else {
for (int i = 0; i < size; ++i) {
pDst[i] = pSrc[size-i-1];
}
}
return buf+size;
}
template <typename T>
uint8_t* deserialize(uint8_t* buf, T* value, int host_endian = LITTLE_ENDIAN, int buf_endian = BIG_ENDIAN) {
size_t size = sizeof(T);
uint8_t* pSrc = buf;
uint8_t* pDst = (uint8_t*)value;
if (host_endian == buf_endian) {
memcpy(pDst, pSrc, size);
}
else {
for (int i = 0; i < size; ++i) {
pDst[i] = pSrc[size-i-1];
}
}
return buf+size;
}
#endif // __cplusplus
#endif // HV_ENDIAN_H_

View File

@ -1,122 +0,0 @@
#ifndef HV_ERR_H_
#define HV_ERR_H_
#include <errno.h>
#include "hexport.h"
#ifndef SYS_NERR
#define SYS_NERR 133
#endif
// F(errcode, name, errmsg)
// [1, 133]
#define FOREACH_ERR_SYS(F)
// [1xx~5xx]
#define FOREACH_ERR_STATUS(F)
// [1xxx]
#define FOREACH_ERR_COMMON(F) \
F(0, OK, "OK") \
F(1000, UNKNOWN, "Unknown error") \
\
F(1001, NULL_PARAM, "Null parameter") \
F(1002, NULL_POINTER, "Null pointer") \
F(1003, NULL_DATA, "Null data") \
F(1004, NULL_HANDLE, "Null handle") \
\
F(1011, INVALID_PARAM, "Invalid parameter")\
F(1012, INVALID_POINTER, "Invalid pointer") \
F(1013, INVALID_DATA, "Invalid data") \
F(1014, INVALID_HANDLE, "Invalid handle") \
F(1015, INVALID_JSON, "Invalid json") \
F(1016, INVALID_XML, "Invalid xml") \
F(1017, INVALID_FMT, "Invalid format") \
F(1018, INVALID_PROTOCOL, "Invalid protocol") \
F(1019, INVALID_PACKAGE, "Invalid package") \
\
F(1021, OUT_OF_RANGE, "Out of range") \
F(1022, OVER_LIMIT, "Over the limit") \
F(1023, MISMATCH, "Mismatch") \
F(1024, PARSE, "Parse failed") \
\
F(1030, OPEN_FILE, "Open file failed") \
F(1031, SAVE_FILE, "Save file failed") \
F(1032, READ_FILE, "Read file failed") \
F(1033, WRITE_FILE, "Write file failed")\
\
F(1040, SSL, "SSL/TLS error") \
F(1041, NEW_SSL_CTX, "New SSL_CTX failed") \
F(1042, NEW_SSL, "New SSL failed") \
F(1043, SSL_HANDSHAKE, "SSL handshake failed") \
\
F(1100, TASK_TIMEOUT, "Task timeout") \
F(1101, TASK_QUEUE_FULL, "Task queue full") \
F(1102, TASK_QUEUE_EMPTY, "Task queue empty") \
\
F(1400, REQUEST, "Bad request") \
F(1401, RESPONSE, "Bad response") \
// [-1xxx]
#define FOREACH_ERR_FUNC(F) \
F(-1001, MALLOC, "malloc() error") \
F(-1002, REALLOC, "realloc() error") \
F(-1003, CALLOC, "calloc() error") \
F(-1004, FREE, "free() error") \
\
F(-1011, SOCKET, "socket() error") \
F(-1012, BIND, "bind() error") \
F(-1013, LISTEN, "listen() error") \
F(-1014, ACCEPT, "accept() error") \
F(-1015, CONNECT, "connect() error") \
F(-1016, RECV, "recv() error") \
F(-1017, SEND, "send() error") \
F(-1018, RECVFROM, "recvfrom() error") \
F(-1019, SENDTO, "sendto() error") \
F(-1020, SETSOCKOPT, "setsockopt() error") \
F(-1021, GETSOCKOPT, "getsockopt() error") \
// grpc [4xxx]
#define FOREACH_ERR_GRPC(F) \
F(4000, GRPC_FIRST, "grpc no error") \
F(4001, GRPC_STATUS_CANCELLED, "grpc status: cancelled") \
F(4002, GRPC_STATUS_UNKNOWN, "grpc unknown error") \
F(4003, GRPC_STATUS_INVALID_ARGUMENT, "grpc status: invalid argument")\
F(4004, GRPC_STATUS_DEADLINE, "grpc status: deadline") \
F(4005, GRPC_STATUS_NOT_FOUND, "grpc status: not found") \
F(4006, GRPC_STATUS_ALREADY_EXISTS, "grpc status: already exists") \
F(4007, GRPC_STATUS_PERMISSION_DENIED, "grpc status: permission denied") \
F(4008, GRPC_STATUS_RESOURCE_EXHAUSTED, "grpc status: resource exhausted") \
F(4009, GRPC_STATUS_FAILED_PRECONDITION,"grpc status: failed precondition") \
F(4010, GRPC_STATUS_ABORTED, "grpc status: aborted") \
F(4011, GRPC_STATUS_OUT_OF_RANGE, "grpc status: out of range") \
F(4012, GRPC_STATUS_UNIMPLEMENTED, "grpc status: unimplemented") \
F(4013, GRPC_STATUS_INTERNAL, "grpc internal error") \
F(4014, GRPC_STATUS_UNAVAILABLE, "grpc service unavailable") \
F(4015, GRPC_STATUS_DATA_LOSS, "grpc status: data loss") \
#define FOREACH_ERR(F) \
FOREACH_ERR_COMMON(F) \
FOREACH_ERR_FUNC(F) \
FOREACH_ERR_GRPC(F) \
#undef ERR_OK // prevent conflict
enum {
#define F(errcode, name, errmsg) ERR_##name = errcode,
FOREACH_ERR(F)
#undef F
};
#ifdef __cplusplus
extern "C" {
#endif
// errcode => errmsg
HV_EXPORT const char* hv_strerror(int err);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // HV_ERR_H_

View File

@ -1,157 +0,0 @@
#ifndef HV_EXPORT_H_
#define HV_EXPORT_H_
// HV_EXPORT
#if defined(HV_STATICLIB) || defined(HV_SOURCE)
#define HV_EXPORT
#elif defined(_MSC_VER)
#if defined(HV_DYNAMICLIB) || defined(HV_EXPORTS) || defined(hv_EXPORTS)
#define HV_EXPORT __declspec(dllexport)
#else
#define HV_EXPORT __declspec(dllimport)
#endif
#elif defined(__GNUC__)
#define HV_EXPORT __attribute__((visibility("default")))
#else
#define HV_EXPORT
#endif
// HV_INLINE
#define HV_INLINE static inline
// HV_DEPRECATED
#if defined(HV_NO_DEPRECATED)
#define HV_DEPRECATED
#elif defined(__GNUC__) || defined(__clang__)
#define HV_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define HV_DEPRECATED __declspec(deprecated)
#else
#define HV_DEPRECATED
#endif
// HV_UNUSED
#if defined(__GNUC__)
#define HV_UNUSED __attribute__((visibility("unused")))
#else
#define HV_UNUSED
#endif
// @param[IN | OUT | INOUT]
#ifndef IN
#define IN
#endif
#ifndef OUT
#define OUT
#endif
#ifndef INOUT
#define INOUT
#endif
// @field[OPTIONAL | REQUIRED | REPEATED]
#ifndef OPTIONAL
#define OPTIONAL
#endif
#ifndef REQUIRED
#define REQUIRED
#endif
#ifndef REPEATED
#define REPEATED
#endif
#ifdef __cplusplus
#ifndef EXTERN_C
#define EXTERN_C extern "C"
#endif
#ifndef BEGIN_EXTERN_C
#define BEGIN_EXTERN_C extern "C" {
#endif
#ifndef END_EXTERN_C
#define END_EXTERN_C } // extern "C"
#endif
#ifndef BEGIN_NAMESPACE
#define BEGIN_NAMESPACE(ns) namespace ns {
#endif
#ifndef END_NAMESPACE
#define END_NAMESPACE(ns) } // namespace ns
#endif
#ifndef USING_NAMESPACE
#define USING_NAMESPACE(ns) using namespace ns;
#endif
#ifndef DEFAULT
#define DEFAULT(x) = x
#endif
#ifndef ENUM
#define ENUM(e) enum e
#endif
#ifndef STRUCT
#define STRUCT(s) struct s
#endif
#else
#define EXTERN_C extern
#define BEGIN_EXTERN_C
#define END_EXTERN_C
#define BEGIN_NAMESPACE(ns)
#define END_NAMESPACE(ns)
#define USING_NAMESPACE(ns)
#ifndef DEFAULT
#define DEFAULT(x)
#endif
#ifndef ENUM
#define ENUM(e)\
typedef enum e e;\
enum e
#endif
#ifndef STRUCT
#define STRUCT(s)\
typedef struct s s;\
struct s
#endif
#endif // __cplusplus
#define BEGIN_NAMESPACE_HV BEGIN_NAMESPACE(hv)
#define END_NAMESPACE_HV END_NAMESPACE(hv)
#define USING_NAMESPACE_HV USING_NAMESPACE(hv)
// MSVC ports
#ifdef _MSC_VER
#pragma warning (disable: 4251) // STL dll
#pragma warning (disable: 4275) // dll-interface
#if _MSC_VER < 1900 // < VS2015
#ifndef __cplusplus
#ifndef inline
#define inline __inline
#endif
#endif
#ifndef snprintf
#define snprintf _snprintf
#endif
#endif
#endif
#endif // HV_EXPORT_H_

View File

@ -1,134 +0,0 @@
#ifndef HV_FILE_H_
#define HV_FILE_H_
#include <string> // for std::string
#include "hplatform.h" // for stat
#include "hbuf.h" // for HBuf
class HFile {
public:
HFile() {
filepath[0] = '\0';
fp = NULL;
}
~HFile() {
close();
}
int open(const char* filepath, const char* mode) {
close();
strncpy(this->filepath, filepath, MAX_PATH - 1);
fp = fopen(filepath, mode);
return fp ? 0 : errno;
}
void close() {
if (fp) {
fclose(fp);
fp = NULL;
}
}
bool isopen() {
return fp != NULL;
}
int remove() {
close();
return ::remove(filepath);
}
int rename(const char* newpath) {
close();
return ::rename(filepath, newpath);
}
size_t read(void* ptr, size_t len) {
return fread(ptr, 1, len, fp);
}
size_t write(const void* ptr, size_t len) {
return fwrite(ptr, 1, len, fp);
}
size_t write(const std::string& str) {
return write(str.c_str(), str.length());
}
int seek(size_t offset, int whence = SEEK_SET) {
return fseek(fp, offset, whence);
}
int tell() {
return ftell(fp);
}
int flush() {
return fflush(fp);
}
static size_t size(const char* filepath) {
struct stat st;
memset(&st, 0, sizeof(st));
stat(filepath, &st);
return st.st_size;
}
size_t size() {
return HFile::size(filepath);
}
size_t readall(HBuf& buf) {
size_t filesize = size();
if (filesize == 0) return 0;
buf.resize(filesize);
return fread(buf.base, 1, filesize, fp);
}
size_t readall(std::string& str) {
size_t filesize = size();
if (filesize == 0) return 0;
str.resize(filesize);
return fread((void*)str.data(), 1, filesize, fp);
}
bool readline(std::string& str) {
str.clear();
char ch;
while (fread(&ch, 1, 1, fp)) {
if (ch == '\n') {
// unix: LF
return true;
}
if (ch == '\r') {
// dos: CRLF
// read LF
if (fread(&ch, 1, 1, fp) && ch != '\n') {
// mac: CR
fseek(fp, -1, SEEK_CUR);
}
return true;
}
str += ch;
}
return str.length() != 0;
}
int readrange(std::string& str, size_t from = 0, size_t to = 0) {
size_t filesize = size();
if (filesize == 0) return 0;
if (to == 0 || to >= filesize) to = filesize - 1;
size_t readbytes = to - from + 1;
str.resize(readbytes);
fseek(fp, from, SEEK_SET);
return fread((void*)str.data(), 1, readbytes, fp);
}
public:
char filepath[MAX_PATH];
FILE* fp;
};
#endif // HV_FILE_H_

View File

@ -1,176 +0,0 @@
#ifndef HV_LOG_H_
#define HV_LOG_H_
/*
* hlog is thread-safe
*/
#include <string.h>
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
#define DIR_SEPARATOR_STR "\\"
#else
#define DIR_SEPARATOR '/'
#define DIR_SEPARATOR_STR "/"
#endif
#ifndef __FILENAME__
// #define __FILENAME__ (strrchr(__FILE__, DIR_SEPARATOR) ? strrchr(__FILE__, DIR_SEPARATOR) + 1 : __FILE__)
#define __FILENAME__ (strrchr(DIR_SEPARATOR_STR __FILE__, DIR_SEPARATOR) + 1)
#endif
#include "hexport.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CLR_CLR "\033[0m" /* 恢复颜色 */
#define CLR_BLACK "\033[30m" /* 黑色字 */
#define CLR_RED "\033[31m" /* 红色字 */
#define CLR_GREEN "\033[32m" /* 绿色字 */
#define CLR_YELLOW "\033[33m" /* 黄色字 */
#define CLR_BLUE "\033[34m" /* 蓝色字 */
#define CLR_PURPLE "\033[35m" /* 紫色字 */
#define CLR_SKYBLUE "\033[36m" /* 天蓝字 */
#define CLR_WHITE "\033[37m" /* 白色字 */
#define CLR_BLK_WHT "\033[40;37m" /* 黑底白字 */
#define CLR_RED_WHT "\033[41;37m" /* 红底白字 */
#define CLR_GREEN_WHT "\033[42;37m" /* 绿底白字 */
#define CLR_YELLOW_WHT "\033[43;37m" /* 黄底白字 */
#define CLR_BLUE_WHT "\033[44;37m" /* 蓝底白字 */
#define CLR_PURPLE_WHT "\033[45;37m" /* 紫底白字 */
#define CLR_SKYBLUE_WHT "\033[46;37m" /* 天蓝底白字 */
#define CLR_WHT_BLK "\033[47;30m" /* 白底黑字 */
// XXX(id, str, clr)
#define LOG_LEVEL_MAP(XXX) \
XXX(LOG_LEVEL_DEBUG, "DEBUG", CLR_WHITE) \
XXX(LOG_LEVEL_INFO, "INFO ", CLR_GREEN) \
XXX(LOG_LEVEL_WARN, "WARN ", CLR_YELLOW) \
XXX(LOG_LEVEL_ERROR, "ERROR", CLR_RED) \
XXX(LOG_LEVEL_FATAL, "FATAL", CLR_RED_WHT)
typedef enum {
LOG_LEVEL_VERBOSE = 0,
#define XXX(id, str, clr) id,
LOG_LEVEL_MAP(XXX)
#undef XXX
LOG_LEVEL_SILENT
} log_level_e;
#define DEFAULT_LOG_FILE "libhv"
#define DEFAULT_LOG_LEVEL LOG_LEVEL_INFO
#define DEFAULT_LOG_FORMAT "%y-%m-%d %H:%M:%S.%z %L %s"
#define DEFAULT_LOG_REMAIN_DAYS 1
#define DEFAULT_LOG_MAX_BUFSIZE (1<<14) // 16k
#define DEFAULT_LOG_MAX_FILESIZE (1<<24) // 16M
// logger: default file_logger
// network_logger() see event/nlog.h
typedef void (*logger_handler)(int loglevel, const char* buf, int len);
HV_EXPORT void stdout_logger(int loglevel, const char* buf, int len);
HV_EXPORT void stderr_logger(int loglevel, const char* buf, int len);
HV_EXPORT void file_logger(int loglevel, const char* buf, int len);
// network_logger implement see event/nlog.h
// HV_EXPORT void network_logger(int loglevel, const char* buf, int len);
typedef struct logger_s logger_t;
HV_EXPORT logger_t* logger_create();
HV_EXPORT void logger_destroy(logger_t* logger);
HV_EXPORT void logger_set_handler(logger_t* logger, logger_handler fn);
HV_EXPORT void logger_set_level(logger_t* logger, int level);
// level = [VERBOSE,DEBUG,INFO,WARN,ERROR,FATAL,SILENT]
HV_EXPORT void logger_set_level_by_str(logger_t* logger, const char* level);
/*
* format = "%y-%m-%d %H:%M:%S.%z %L %s"
* message = "2020-01-02 03:04:05.067 DEBUG message"
* %y year
* %m month
* %d day
* %H hour
* %M min
* %S sec
* %z ms
* %Z us
* %l First character of level
* %L All characters of level
* %s message
* %% %
*/
HV_EXPORT void logger_set_format(logger_t* logger, const char* format);
HV_EXPORT void logger_set_max_bufsize(logger_t* logger, unsigned int bufsize);
HV_EXPORT void logger_enable_color(logger_t* logger, int on);
HV_EXPORT int logger_print(logger_t* logger, int level, const char* fmt, ...);
// below for file logger
HV_EXPORT void logger_set_file(logger_t* logger, const char* filepath);
HV_EXPORT void logger_set_max_filesize(logger_t* logger, unsigned long long filesize);
// 16, 16M, 16MB
HV_EXPORT void logger_set_max_filesize_by_str(logger_t* logger, const char* filesize);
HV_EXPORT void logger_set_remain_days(logger_t* logger, int days);
HV_EXPORT void logger_enable_fsync(logger_t* logger, int on);
HV_EXPORT void logger_fsync(logger_t* logger);
HV_EXPORT const char* logger_get_cur_file(logger_t* logger);
// hlog: default logger instance
HV_EXPORT logger_t* hv_default_logger();
HV_EXPORT void hv_destroy_default_logger(void);
// macro hlog*
#define hlog hv_default_logger()
#define hlog_destory() hv_destroy_default_logger()
#define hlog_disable() logger_set_level(hlog, LOG_LEVEL_SILENT)
#define hlog_set_file(filepath) logger_set_file(hlog, filepath)
#define hlog_set_level(level) logger_set_level(hlog, level)
#define hlog_set_level_by_str(level) logger_set_level_by_str(hlog, level)
#define hlog_set_handler(fn) logger_set_handler(hlog, fn)
#define hlog_set_format(format) logger_set_format(hlog, format)
#define hlog_set_max_filesize(filesize) logger_set_max_filesize(hlog, filesize)
#define hlog_set_max_filesize_by_str(filesize) logger_set_max_filesize_by_str(hlog, filesize)
#define hlog_set_remain_days(days) logger_set_remain_days(hlog, days)
#define hlog_enable_fsync() logger_enable_fsync(hlog, 1)
#define hlog_disable_fsync() logger_enable_fsync(hlog, 0)
#define hlog_fsync() logger_fsync(hlog)
#define hlog_get_cur_file() logger_get_cur_file(hlog)
#define hlogd(fmt, ...) logger_print(hlog, LOG_LEVEL_DEBUG, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogi(fmt, ...) logger_print(hlog, LOG_LEVEL_INFO, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogw(fmt, ...) logger_print(hlog, LOG_LEVEL_WARN, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hloge(fmt, ...) logger_print(hlog, LOG_LEVEL_ERROR, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
#define hlogf(fmt, ...) logger_print(hlog, LOG_LEVEL_FATAL, fmt " [%s:%d:%s]", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__)
// below for android
#if defined(ANDROID) || defined(__ANDROID__)
#include <android/log.h>
#define LOG_TAG "JNI"
#undef hlogd
#undef hlogi
#undef hlogw
#undef hloge
#undef hlogf
#define hlogd(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define hlogi(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define hlogw(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define hloge(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define hlogf(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#endif
// macro alias
#if !defined(LOGD) && !defined(LOGI) && !defined(LOGW) && !defined(LOGE) && !defined(LOGF)
#define LOGD hlogd
#define LOGI hlogi
#define LOGW hlogw
#define LOGE hloge
#define LOGF hlogf
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // HV_LOG_H_

View File

@ -1,746 +0,0 @@
#ifndef HV_LOOP_H_
#define HV_LOOP_H_
#include "hexport.h"
#include "hplatform.h"
#include "hdef.h"
#include "hssl.h"
typedef struct hloop_s hloop_t;
typedef struct hevent_s hevent_t;
// NOTE: The following structures are subclasses of hevent_t,
// inheriting hevent_t data members and function members.
typedef struct hidle_s hidle_t;
typedef struct htimer_s htimer_t;
typedef struct htimeout_s htimeout_t;
typedef struct hperiod_s hperiod_t;
typedef struct hio_s hio_t;
typedef void (*hevent_cb) (hevent_t* ev);
typedef void (*hidle_cb) (hidle_t* idle);
typedef void (*htimer_cb) (htimer_t* timer);
typedef void (*hio_cb) (hio_t* io);
typedef void (*haccept_cb) (hio_t* io);
typedef void (*hconnect_cb) (hio_t* io);
typedef void (*hread_cb) (hio_t* io, void* buf, int readbytes);
typedef void (*hwrite_cb) (hio_t* io, const void* buf, int writebytes);
typedef void (*hclose_cb) (hio_t* io);
typedef enum {
HLOOP_STATUS_STOP,
HLOOP_STATUS_RUNNING,
HLOOP_STATUS_PAUSE,
HLOOP_STATUS_DESTROY
} hloop_status_e;
typedef enum {
HEVENT_TYPE_NONE = 0,
HEVENT_TYPE_IO = 0x00000001,
HEVENT_TYPE_TIMEOUT = 0x00000010,
HEVENT_TYPE_PERIOD = 0x00000020,
HEVENT_TYPE_TIMER = HEVENT_TYPE_TIMEOUT|HEVENT_TYPE_PERIOD,
HEVENT_TYPE_IDLE = 0x00000100,
HEVENT_TYPE_CUSTOM = 0x00000400, // 1024
} hevent_type_e;
#define HEVENT_LOWEST_PRIORITY (-5)
#define HEVENT_LOW_PRIORITY (-3)
#define HEVENT_NORMAL_PRIORITY 0
#define HEVENT_HIGH_PRIORITY 3
#define HEVENT_HIGHEST_PRIORITY 5
#define HEVENT_PRIORITY_SIZE (HEVENT_HIGHEST_PRIORITY-HEVENT_LOWEST_PRIORITY+1)
#define HEVENT_PRIORITY_INDEX(priority) (priority-HEVENT_LOWEST_PRIORITY)
#define HEVENT_FLAGS \
unsigned destroy :1; \
unsigned active :1; \
unsigned pending :1;
#define HEVENT_FIELDS \
hloop_t* loop; \
hevent_type_e event_type; \
uint64_t event_id; \
hevent_cb cb; \
void* userdata; \
void* privdata; \
struct hevent_s* pending_next; \
int priority; \
HEVENT_FLAGS
// sizeof(struct hevent_s)=64 on x64
struct hevent_s {
HEVENT_FIELDS
};
#define hevent_set_id(ev, id) ((hevent_t*)(ev))->event_id = id
#define hevent_set_cb(ev, cb) ((hevent_t*)(ev))->cb = cb
#define hevent_set_priority(ev, prio) ((hevent_t*)(ev))->priority = prio
#define hevent_set_userdata(ev, udata) ((hevent_t*)(ev))->userdata = (void*)udata
#define hevent_loop(ev) (((hevent_t*)(ev))->loop)
#define hevent_type(ev) (((hevent_t*)(ev))->event_type)
#define hevent_id(ev) (((hevent_t*)(ev))->event_id)
#define hevent_cb(ev) (((hevent_t*)(ev))->cb)
#define hevent_priority(ev) (((hevent_t*)(ev))->priority)
#define hevent_userdata(ev) (((hevent_t*)(ev))->userdata)
typedef enum {
HIO_TYPE_UNKNOWN = 0,
HIO_TYPE_STDIN = 0x00000001,
HIO_TYPE_STDOUT = 0x00000002,
HIO_TYPE_STDERR = 0x00000004,
HIO_TYPE_STDIO = 0x0000000F,
HIO_TYPE_FILE = 0x00000010,
HIO_TYPE_IP = 0x00000100,
HIO_TYPE_SOCK_RAW = 0x00000F00,
HIO_TYPE_UDP = 0x00001000,
HIO_TYPE_KCP = 0x00002000,
HIO_TYPE_DTLS = 0x00010000,
HIO_TYPE_SOCK_DGRAM = 0x000FF000,
HIO_TYPE_TCP = 0x00100000,
HIO_TYPE_SSL = 0x01000000,
HIO_TYPE_TLS = HIO_TYPE_SSL,
HIO_TYPE_SOCK_STREAM= 0x0FF00000,
HIO_TYPE_SOCKET = 0x0FFFFF00,
} hio_type_e;
typedef enum {
HIO_SERVER_SIDE = 0,
HIO_CLIENT_SIDE = 1,
} hio_side_e;
#define HIO_DEFAULT_CONNECT_TIMEOUT 10000 // ms
#define HIO_DEFAULT_CLOSE_TIMEOUT 60000 // ms
#define HIO_DEFAULT_KEEPALIVE_TIMEOUT 75000 // ms
#define HIO_DEFAULT_HEARTBEAT_INTERVAL 10000 // ms
BEGIN_EXTERN_C
// loop
#define HLOOP_FLAG_RUN_ONCE 0x00000001
#define HLOOP_FLAG_AUTO_FREE 0x00000002
#define HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS 0x00000004
HV_EXPORT hloop_t* hloop_new(int flags DEFAULT(HLOOP_FLAG_AUTO_FREE));
// WARN: Forbid to call hloop_free if HLOOP_FLAG_AUTO_FREE set.
HV_EXPORT void hloop_free(hloop_t** pp);
HV_EXPORT int hloop_process_events(hloop_t* loop, int timeout_ms DEFAULT(0));
// NOTE: when no active events, loop will quit if HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS set.
HV_EXPORT int hloop_run(hloop_t* loop);
// NOTE: hloop_stop called in loop-thread just set flag to quit in next loop,
// if called in other thread, it will wakeup loop-thread from blocking poll system call,
// then you should join loop thread to safely exit loop thread.
HV_EXPORT int hloop_stop(hloop_t* loop);
HV_EXPORT int hloop_pause(hloop_t* loop);
HV_EXPORT int hloop_resume(hloop_t* loop);
HV_EXPORT int hloop_wakeup(hloop_t* loop);
HV_EXPORT hloop_status_e hloop_status(hloop_t* loop);
HV_EXPORT void hloop_update_time(hloop_t* loop);
HV_EXPORT uint64_t hloop_now(hloop_t* loop); // s
HV_EXPORT uint64_t hloop_now_ms(hloop_t* loop); // ms
HV_EXPORT uint64_t hloop_now_us(hloop_t* loop); // us
HV_EXPORT uint64_t hloop_now_hrtime(hloop_t* loop); // us
// export some hloop's members
// @return pid of hloop_run
HV_EXPORT long hloop_pid(hloop_t* loop);
// @return tid of hloop_run
HV_EXPORT long hloop_tid(hloop_t* loop);
// @return count of loop
HV_EXPORT uint64_t hloop_count(hloop_t* loop);
// @return number of ios
HV_EXPORT uint32_t hloop_nios(hloop_t* loop);
// @return number of timers
HV_EXPORT uint32_t hloop_ntimers(hloop_t* loop);
// @return number of idles
HV_EXPORT uint32_t hloop_nidles(hloop_t* loop);
// @return number of active events
HV_EXPORT uint32_t hloop_nactives(hloop_t* loop);
// userdata
HV_EXPORT void hloop_set_userdata(hloop_t* loop, void* userdata);
HV_EXPORT void* hloop_userdata(hloop_t* loop);
// custom_event
/*
* hevent_t ev;
* memset(&ev, 0, sizeof(hevent_t));
* ev.event_type = (hevent_type_e)(HEVENT_TYPE_CUSTOM + 1);
* ev.cb = custom_event_cb;
* ev.userdata = userdata;
* hloop_post_event(loop, &ev);
*/
// NOTE: hloop_post_event is thread-safe, used to post event from other thread to loop thread.
HV_EXPORT void hloop_post_event(hloop_t* loop, hevent_t* ev);
// idle
HV_EXPORT hidle_t* hidle_add(hloop_t* loop, hidle_cb cb, uint32_t repeat DEFAULT(INFINITE));
HV_EXPORT void hidle_del(hidle_t* idle);
// timer
HV_EXPORT htimer_t* htimer_add(hloop_t* loop, htimer_cb cb, uint32_t timeout_ms, uint32_t repeat DEFAULT(INFINITE));
/*
* minute hour day week month cb
* 0~59 0~23 1~31 0~6 1~12
* -1 -1 -1 -1 -1 cron.minutely
* 30 -1 -1 -1 -1 cron.hourly
* 30 1 -1 -1 -1 cron.daily
* 30 1 15 -1 -1 cron.monthly
* 30 1 -1 5 -1 cron.weekly
* 30 1 1 -1 10 cron.yearly
*/
HV_EXPORT htimer_t* htimer_add_period(hloop_t* loop, htimer_cb cb,
int8_t minute DEFAULT(0), int8_t hour DEFAULT(-1), int8_t day DEFAULT(-1),
int8_t week DEFAULT(-1), int8_t month DEFAULT(-1), uint32_t repeat DEFAULT(INFINITE));
HV_EXPORT void htimer_del(htimer_t* timer);
HV_EXPORT void htimer_reset(htimer_t* timer, uint32_t timeout_ms DEFAULT(0));
// io
//-----------------------low-level apis---------------------------------------
#define HV_READ 0x0001
#define HV_WRITE 0x0004
#define HV_RDWR (HV_READ|HV_WRITE)
/*
const char* hio_engine() {
#ifdef EVENT_SELECT
return "select";
#elif defined(EVENT_POLL)
return "poll";
#elif defined(EVENT_EPOLL)
return "epoll";
#elif defined(EVENT_KQUEUE)
return "kqueue";
#elif defined(EVENT_IOCP)
return "iocp";
#elif defined(EVENT_PORT)
return "evport";
#else
return "noevent";
#endif
}
*/
HV_EXPORT const char* hio_engine();
HV_EXPORT hio_t* hio_get(hloop_t* loop, int fd);
HV_EXPORT int hio_add(hio_t* io, hio_cb cb, int events DEFAULT(HV_READ));
HV_EXPORT int hio_del(hio_t* io, int events DEFAULT(HV_RDWR));
// NOTE: io detach from old loop and attach to new loop
/* @see examples/multi-thread/one-acceptor-multi-workers.c
void new_conn_event(hevent_t* ev) {
hloop_t* loop = ev->loop;
hio_t* io = (hio_t*)hevent_userdata(ev);
hio_attach(loop, io);
}
void on_accpet(hio_t* io) {
hio_detach(io);
hloop_t* worker_loop = get_one_loop();
hevent_t ev;
memset(&ev, 0, sizeof(ev));
ev.loop = worker_loop;
ev.cb = new_conn_event;
ev.userdata = io;
hloop_post_event(worker_loop, &ev);
}
*/
HV_EXPORT void hio_detach(/*hloop_t* loop,*/ hio_t* io);
HV_EXPORT void hio_attach(hloop_t* loop, hio_t* io);
HV_EXPORT bool hio_exists(hloop_t* loop, int fd);
// hio_t fields
// NOTE: fd cannot be used as unique identifier, so we provide an id.
HV_EXPORT uint32_t hio_id (hio_t* io);
HV_EXPORT int hio_fd (hio_t* io);
HV_EXPORT int hio_error (hio_t* io);
HV_EXPORT int hio_events (hio_t* io);
HV_EXPORT int hio_revents (hio_t* io);
HV_EXPORT hio_type_e hio_type (hio_t* io);
HV_EXPORT struct sockaddr* hio_localaddr(hio_t* io);
HV_EXPORT struct sockaddr* hio_peeraddr (hio_t* io);
HV_EXPORT void hio_set_context(hio_t* io, void* ctx);
HV_EXPORT void* hio_context(hio_t* io);
HV_EXPORT bool hio_is_opened(hio_t* io);
HV_EXPORT bool hio_is_connected(hio_t* io);
HV_EXPORT bool hio_is_closed(hio_t* io);
// iobuf
// #include "hbuf.h"
typedef struct fifo_buf_s hio_readbuf_t;
// NOTE: One loop per thread, one readbuf per loop.
// But you can pass in your own readbuf instead of the default readbuf to avoid memcopy.
HV_EXPORT void hio_set_readbuf(hio_t* io, void* buf, size_t len);
HV_EXPORT hio_readbuf_t* hio_get_readbuf(hio_t* io);
HV_EXPORT void hio_set_max_read_bufsize (hio_t* io, uint32_t size);
HV_EXPORT void hio_set_max_write_bufsize(hio_t* io, uint32_t size);
// NOTE: hio_write is non-blocking, so there is a write queue inside hio_t to cache unwritten data and wait for writable.
// @return current buffer size of write queue.
HV_EXPORT size_t hio_write_bufsize(hio_t* io);
#define hio_write_is_complete(io) (hio_write_bufsize(io) == 0)
HV_EXPORT uint64_t hio_last_read_time(hio_t* io); // ms
HV_EXPORT uint64_t hio_last_write_time(hio_t* io); // ms
// set callbacks
HV_EXPORT void hio_setcb_accept (hio_t* io, haccept_cb accept_cb);
HV_EXPORT void hio_setcb_connect (hio_t* io, hconnect_cb connect_cb);
HV_EXPORT void hio_setcb_read (hio_t* io, hread_cb read_cb);
HV_EXPORT void hio_setcb_write (hio_t* io, hwrite_cb write_cb);
HV_EXPORT void hio_setcb_close (hio_t* io, hclose_cb close_cb);
// get callbacks
HV_EXPORT haccept_cb hio_getcb_accept(hio_t* io);
HV_EXPORT hconnect_cb hio_getcb_connect(hio_t* io);
HV_EXPORT hread_cb hio_getcb_read(hio_t* io);
HV_EXPORT hwrite_cb hio_getcb_write(hio_t* io);
HV_EXPORT hclose_cb hio_getcb_close(hio_t* io);
// Enable SSL/TLS is so easy :)
HV_EXPORT int hio_enable_ssl(hio_t* io);
HV_EXPORT bool hio_is_ssl(hio_t* io);
HV_EXPORT int hio_set_ssl (hio_t* io, hssl_t ssl);
HV_EXPORT int hio_set_ssl_ctx(hio_t* io, hssl_ctx_t ssl_ctx);
// hssl_ctx_new(opt) -> hio_set_ssl_ctx
HV_EXPORT int hio_new_ssl_ctx(hio_t* io, hssl_ctx_opt_t* opt);
HV_EXPORT hssl_t hio_get_ssl(hio_t* io);
HV_EXPORT hssl_ctx_t hio_get_ssl_ctx(hio_t* io);
// for hssl_set_sni_hostname
HV_EXPORT int hio_set_hostname(hio_t* io, const char* hostname);
HV_EXPORT const char* hio_get_hostname(hio_t* io);
// connect timeout => hclose_cb
HV_EXPORT void hio_set_connect_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_CONNECT_TIMEOUT));
// close timeout => hclose_cb
HV_EXPORT void hio_set_close_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_CLOSE_TIMEOUT));
// read timeout => hclose_cb
HV_EXPORT void hio_set_read_timeout(hio_t* io, int timeout_ms);
// write timeout => hclose_cb
HV_EXPORT void hio_set_write_timeout(hio_t* io, int timeout_ms);
// keepalive timeout => hclose_cb
HV_EXPORT void hio_set_keepalive_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_KEEPALIVE_TIMEOUT));
/*
void send_heartbeat(hio_t* io) {
static char buf[] = "PING\r\n";
hio_write(io, buf, 6);
}
hio_set_heartbeat(io, 3000, send_heartbeat);
*/
typedef void (*hio_send_heartbeat_fn)(hio_t* io);
// heartbeat interval => hio_send_heartbeat_fn
HV_EXPORT void hio_set_heartbeat(hio_t* io, int interval_ms, hio_send_heartbeat_fn fn);
// Nonblocking, poll IO events in the loop to call corresponding callback.
// hio_add(io, HV_READ) => accept => haccept_cb
HV_EXPORT int hio_accept (hio_t* io);
// connect => hio_add(io, HV_WRITE) => hconnect_cb
HV_EXPORT int hio_connect(hio_t* io);
// hio_add(io, HV_READ) => read => hread_cb
HV_EXPORT int hio_read (hio_t* io);
#define hio_read_start(io) hio_read(io)
#define hio_read_stop(io) hio_del(io, HV_READ)
// hio_read_start => hread_cb => hio_read_stop
HV_EXPORT int hio_read_once (hio_t* io);
// hio_read_once => hread_cb(len)
HV_EXPORT int hio_read_until_length(hio_t* io, unsigned int len);
// hio_read_once => hread_cb(...delim)
HV_EXPORT int hio_read_until_delim (hio_t* io, unsigned char delim);
HV_EXPORT int hio_read_remain(hio_t* io);
// @see examples/tinyhttpd.c examples/tinyproxyd.c
#define hio_readline(io) hio_read_until_delim(io, '\n')
#define hio_readstring(io) hio_read_until_delim(io, '\0')
#define hio_readbytes(io, len) hio_read_until_length(io, len)
#define hio_read_until(io, len) hio_read_until_length(io, len)
// NOTE: hio_write is thread-safe, locked by recursive_mutex, allow to be called by other threads.
// hio_try_write => hio_add(io, HV_WRITE) => write => hwrite_cb
HV_EXPORT int hio_write (hio_t* io, const void* buf, size_t len);
// NOTE: hio_close is thread-safe, hio_close_async will be called actually in other thread.
// hio_del(io, HV_RDWR) => close => hclose_cb
HV_EXPORT int hio_close (hio_t* io);
// NOTE: hloop_post_event(hio_close_event)
HV_EXPORT int hio_close_async(hio_t* io);
//------------------high-level apis-------------------------------------------
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
HV_EXPORT hio_t* hread (hloop_t* loop, int fd, void* buf, size_t len, hread_cb read_cb);
// hio_get -> hio_setcb_write -> hio_write
HV_EXPORT hio_t* hwrite (hloop_t* loop, int fd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
// hio_get -> hio_close
HV_EXPORT void hclose (hloop_t* loop, int fd);
// tcp
// hio_get -> hio_setcb_accept -> hio_accept
HV_EXPORT hio_t* haccept (hloop_t* loop, int listenfd, haccept_cb accept_cb);
// hio_get -> hio_setcb_connect -> hio_connect
HV_EXPORT hio_t* hconnect (hloop_t* loop, int connfd, hconnect_cb connect_cb);
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
HV_EXPORT hio_t* hrecv (hloop_t* loop, int connfd, void* buf, size_t len, hread_cb read_cb);
// hio_get -> hio_setcb_write -> hio_write
HV_EXPORT hio_t* hsend (hloop_t* loop, int connfd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
// udp
HV_EXPORT void hio_set_type(hio_t* io, hio_type_e type);
HV_EXPORT void hio_set_localaddr(hio_t* io, struct sockaddr* addr, int addrlen);
HV_EXPORT void hio_set_peeraddr (hio_t* io, struct sockaddr* addr, int addrlen);
// NOTE: must call hio_set_peeraddr before hrecvfrom/hsendto
// hio_get -> hio_set_readbuf -> hio_setcb_read -> hio_read
HV_EXPORT hio_t* hrecvfrom (hloop_t* loop, int sockfd, void* buf, size_t len, hread_cb read_cb);
// hio_get -> hio_setcb_write -> hio_write
HV_EXPORT hio_t* hsendto (hloop_t* loop, int sockfd, const void* buf, size_t len, hwrite_cb write_cb DEFAULT(NULL));
//-----------------top-level apis---------------------------------------------
// @hio_create_socket: socket -> bind -> listen
// sockaddr_set_ipport -> socket -> hio_get(loop, sockfd) ->
// side == HIO_SERVER_SIDE ? bind ->
// type & HIO_TYPE_SOCK_STREAM ? listen ->
HV_EXPORT hio_t* hio_create_socket(hloop_t* loop, const char* host, int port,
hio_type_e type DEFAULT(HIO_TYPE_TCP),
hio_side_e side DEFAULT(HIO_SERVER_SIDE));
// @tcp_server: hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_SERVER_SIDE) -> hio_setcb_accept -> hio_accept
// @see examples/tcp_echo_server.c
HV_EXPORT hio_t* hloop_create_tcp_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
// @tcp_client: hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_setcb_close -> hio_connect
// @see examples/nc.c
HV_EXPORT hio_t* hloop_create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb);
// @ssl_server: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_SERVER_SIDE) -> hio_setcb_accept -> hio_accept
// @see examples/tcp_echo_server.c => #define TEST_SSL 1
HV_EXPORT hio_t* hloop_create_ssl_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb);
// @ssl_client: hio_create_socket(loop, host, port, HIO_TYPE_SSL, HIO_CLIENT_SIDE) -> hio_setcb_connect -> hio_setcb_close -> hio_connect
// @see examples/nc.c => #define TEST_SSL 1
HV_EXPORT hio_t* hloop_create_ssl_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb, hclose_cb close_cb);
// @udp_server: hio_create_socket(loop, host, port, HIO_TYPE_UDP, HIO_SERVER_SIDE)
// @see examples/udp_echo_server.c
HV_EXPORT hio_t* hloop_create_udp_server (hloop_t* loop, const char* host, int port);
// @udp_server: hio_create_socket(loop, host, port, HIO_TYPE_UDP, HIO_CLIENT_SIDE)
// @see examples/nc.c
HV_EXPORT hio_t* hloop_create_udp_client (hloop_t* loop, const char* host, int port);
//-----------------upstream---------------------------------------------
// hio_read(io)
// hio_read(io->upstream_io)
HV_EXPORT void hio_read_upstream(hio_t* io);
// on_write(io) -> hio_write_is_complete(io) -> hio_read(io->upstream_io)
HV_EXPORT void hio_read_upstream_on_write_complete(hio_t* io, const void* buf, int writebytes);
// hio_write(io->upstream_io, buf, bytes)
HV_EXPORT void hio_write_upstream(hio_t* io, void* buf, int bytes);
// hio_close(io->upstream_io)
HV_EXPORT void hio_close_upstream(hio_t* io);
// io1->upstream_io = io2;
// io2->upstream_io = io1;
// @see examples/socks5_proxy_server.c
HV_EXPORT void hio_setup_upstream(hio_t* io1, hio_t* io2);
// @return io->upstream_io
HV_EXPORT hio_t* hio_get_upstream(hio_t* io);
// @tcp_upstream: hio_create_socket -> hio_setup_upstream -> hio_connect -> on_connect -> hio_read_upstream
// @return upstream_io
// @see examples/tcp_proxy_server.c
HV_EXPORT hio_t* hio_setup_tcp_upstream(hio_t* io, const char* host, int port, int ssl DEFAULT(0));
#define hio_setup_ssl_upstream(io, host, port) hio_setup_tcp_upstream(io, host, port, 1)
// @udp_upstream: hio_create_socket -> hio_setup_upstream -> hio_read_upstream
// @return upstream_io
// @see examples/udp_proxy_server.c
HV_EXPORT hio_t* hio_setup_udp_upstream(hio_t* io, const char* host, int port);
//-----------------unpack---------------------------------------------
typedef enum {
UNPACK_MODE_NONE = 0,
UNPACK_BY_FIXED_LENGTH = 1, // Not recommended
UNPACK_BY_DELIMITER = 2, // Suitable for text protocol
UNPACK_BY_LENGTH_FIELD = 3, // Suitable for binary protocol
} unpack_mode_e;
#define DEFAULT_PACKAGE_MAX_LENGTH (1 << 21) // 2M
// UNPACK_BY_DELIMITER
#define PACKAGE_MAX_DELIMITER_BYTES 8
// UNPACK_BY_LENGTH_FIELD
typedef enum {
ENCODE_BY_VARINT = 17, // 1 MSB + 7 bits
ENCODE_BY_LITTEL_ENDIAN = LITTLE_ENDIAN, // 1234
ENCODE_BY_BIG_ENDIAN = BIG_ENDIAN, // 4321
} unpack_coding_e;
typedef struct unpack_setting_s {
unpack_mode_e mode;
unsigned int package_max_length;
union {
// UNPACK_BY_FIXED_LENGTH
struct {
unsigned int fixed_length;
};
// UNPACK_BY_DELIMITER
struct {
unsigned char delimiter[PACKAGE_MAX_DELIMITER_BYTES];
unsigned short delimiter_bytes;
};
/*
* UNPACK_BY_LENGTH_FIELD
*
* package_len = head_len + body_len + length_adjustment
*
* if (length_field_coding == ENCODE_BY_VARINT) head_len = body_offset + varint_bytes - length_field_bytes;
* else head_len = body_offset;
*
* length_field stores body length, exclude head length,
* if length_field = head_len + body_len, then length_adjustment should be set to -head_len.
*
*/
struct {
unsigned short body_offset; // Equal to head length usually
unsigned short length_field_offset;
unsigned short length_field_bytes;
short length_adjustment;
unpack_coding_e length_field_coding;
};
};
#ifdef __cplusplus
unpack_setting_s() {
// Recommended setting:
// head = flags:1byte + length:4bytes = 5bytes
mode = UNPACK_BY_LENGTH_FIELD;
package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
fixed_length = 0;
delimiter_bytes = 0;
body_offset = 5;
length_field_offset = 1;
length_field_bytes = 4;
length_field_coding = ENCODE_BY_BIG_ENDIAN;
length_adjustment = 0;
}
#endif
} unpack_setting_t;
/*
* @see examples/jsonrpc examples/protorpc
*
* NOTE: unpack_setting_t of multiple IOs of the same function also are same,
* so only the pointer of unpack_setting_t is stored in hio_t,
* the life time of unpack_setting_t shoud be guaranteed by caller.
*/
HV_EXPORT void hio_set_unpack(hio_t* io, unpack_setting_t* setting);
HV_EXPORT void hio_unset_unpack(hio_t* io);
// unpack examples
/*
unpack_setting_t ftp_unpack_setting;
memset(&ftp_unpack_setting, 0, sizeof(unpack_setting_t));
ftp_unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
ftp_unpack_setting.mode = UNPACK_BY_DELIMITER;
ftp_unpack_setting.delimiter[0] = '\r';
ftp_unpack_setting.delimiter[1] = '\n';
ftp_unpack_setting.delimiter_bytes = 2;
unpack_setting_t mqtt_unpack_setting = {
.mode = UNPACK_BY_LENGTH_FIELD,
.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH,
.body_offset = 2,
.length_field_offset = 1,
.length_field_bytes = 1,
.length_field_coding = ENCODE_BY_VARINT,
};
unpack_setting_t grpc_unpack_setting = {
.mode = UNPACK_BY_LENGTH_FIELD,
.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH,
.body_offset = 5,
.length_field_offset = 1,
.length_field_bytes = 4,
.length_field_coding = ENCODE_BY_BIG_ENDIAN,
};
*/
//-----------------reconnect----------------------------------------
#define DEFAULT_RECONNECT_MIN_DELAY 1000 // ms
#define DEFAULT_RECONNECT_MAX_DELAY 60000 // ms
#define DEFAULT_RECONNECT_DELAY_POLICY 2 // exponential
#define DEFAULT_RECONNECT_MAX_RETRY_CNT INFINITE
typedef struct reconn_setting_s {
uint32_t min_delay; // ms
uint32_t max_delay; // ms
uint32_t cur_delay; // ms
/*
* @delay_policy
* 0: fixed
* min_delay=3s => 3,3,3...
* 1: linear
* min_delay=3s max_delay=10s => 3,6,9,10,10...
* other: exponential
* min_delay=3s max_delay=60s delay_policy=2 => 3,6,12,24,48,60,60...
*/
uint32_t delay_policy;
uint32_t max_retry_cnt;
uint32_t cur_retry_cnt;
#ifdef __cplusplus
reconn_setting_s() {
min_delay = DEFAULT_RECONNECT_MIN_DELAY;
max_delay = DEFAULT_RECONNECT_MAX_DELAY;
cur_delay = 0;
// 1,2,4,8,16,32,60,60...
delay_policy = DEFAULT_RECONNECT_DELAY_POLICY;
max_retry_cnt = DEFAULT_RECONNECT_MAX_RETRY_CNT;
cur_retry_cnt = 0;
}
#endif
} reconn_setting_t;
HV_INLINE void reconn_setting_init(reconn_setting_t* reconn) {
reconn->min_delay = DEFAULT_RECONNECT_MIN_DELAY;
reconn->max_delay = DEFAULT_RECONNECT_MAX_DELAY;
reconn->cur_delay = 0;
// 1,2,4,8,16,32,60,60...
reconn->delay_policy = DEFAULT_RECONNECT_DELAY_POLICY;
reconn->max_retry_cnt = DEFAULT_RECONNECT_MAX_RETRY_CNT;
reconn->cur_retry_cnt = 0;
}
HV_INLINE void reconn_setting_reset(reconn_setting_t* reconn) {
reconn->cur_delay = 0;
reconn->cur_retry_cnt = 0;
}
HV_INLINE bool reconn_setting_can_retry(reconn_setting_t* reconn) {
++reconn->cur_retry_cnt;
return reconn->max_retry_cnt == INFINITE ||
reconn->cur_retry_cnt < reconn->max_retry_cnt;
}
HV_INLINE uint32_t reconn_setting_calc_delay(reconn_setting_t* reconn) {
if (reconn->delay_policy == 0) {
// fixed
reconn->cur_delay = reconn->min_delay;
} else if (reconn->delay_policy == 1) {
// linear
reconn->cur_delay += reconn->min_delay;
} else {
// exponential
reconn->cur_delay *= reconn->delay_policy;
}
reconn->cur_delay = MAX(reconn->cur_delay, reconn->min_delay);
reconn->cur_delay = MIN(reconn->cur_delay, reconn->max_delay);
return reconn->cur_delay;
}
//-----------------LoadBalance-------------------------------------
typedef enum {
LB_RoundRobin,
LB_Random,
LB_LeastConnections,
LB_IpHash,
LB_UrlHash,
} load_balance_e;
//-----------------rudp---------------------------------------------
#if WITH_KCP
#define WITH_RUDP 1
#endif
#if WITH_RUDP
// NOTE: hio_close_rudp is thread-safe.
HV_EXPORT int hio_close_rudp(hio_t* io, struct sockaddr* peeraddr DEFAULT(NULL));
#endif
#if WITH_KCP
typedef struct kcp_setting_s {
// ikcp_create(conv, ...)
unsigned int conv;
// ikcp_nodelay(kcp, nodelay, interval, fastresend, nocwnd)
int nodelay;
int interval;
int fastresend;
int nocwnd;
// ikcp_wndsize(kcp, sndwnd, rcvwnd)
int sndwnd;
int rcvwnd;
// ikcp_setmtu(kcp, mtu)
int mtu;
// ikcp_update
int update_interval;
#ifdef __cplusplus
kcp_setting_s() {
conv = 0x11223344;
// normal mode
nodelay = 0;
interval = 40;
fastresend = 0;
nocwnd = 0;
// fast mode
// nodelay = 1;
// interval = 10;
// fastresend = 2;
// nocwnd = 1;
sndwnd = 0;
rcvwnd = 0;
mtu = 1400;
update_interval = 10; // ms
}
#endif
} kcp_setting_t;
HV_INLINE void kcp_setting_init_with_normal_mode(kcp_setting_t* setting) {
memset(setting, 0, sizeof(kcp_setting_t));
setting->nodelay = 0;
setting->interval = 40;
setting->fastresend = 0;
setting->nocwnd = 0;
}
HV_INLINE void kcp_setting_init_with_fast_mode(kcp_setting_t* setting) {
memset(setting, 0, sizeof(kcp_setting_t));
setting->nodelay = 0;
setting->interval = 30;
setting->fastresend = 2;
setting->nocwnd = 1;
}
HV_INLINE void kcp_setting_init_with_fast2_mode(kcp_setting_t* setting) {
memset(setting, 0, sizeof(kcp_setting_t));
setting->nodelay = 1;
setting->interval = 20;
setting->fastresend = 2;
setting->nocwnd = 1;
}
HV_INLINE void kcp_setting_init_with_fast3_mode(kcp_setting_t* setting) {
memset(setting, 0, sizeof(kcp_setting_t));
setting->nodelay = 1;
setting->interval = 10;
setting->fastresend = 2;
setting->nocwnd = 1;
}
// @see examples/udp_echo_server.c => #define TEST_KCP 1
HV_EXPORT int hio_set_kcp(hio_t* io, kcp_setting_t* setting DEFAULT(NULL));
#endif
END_EXTERN_C
#endif // HV_LOOP_H_

View File

@ -1,117 +0,0 @@
#ifndef HV_MAIN_H_
#define HV_MAIN_H_
#include "hexport.h"
#include "hplatform.h"
#include "hdef.h"
#include "hproc.h"
#ifdef _MSC_VER
#pragma comment(lib, "winmm.lib") // for timeSetEvent
#endif
BEGIN_EXTERN_C
typedef struct main_ctx_s {
char run_dir[MAX_PATH];
char program_name[MAX_PATH];
char confile[MAX_PATH]; // default etc/${program}.conf
char pidfile[MAX_PATH]; // default logs/${program}.pid
char logfile[MAX_PATH]; // default logs/${program}.log
pid_t pid; // getpid
pid_t oldpid; // getpid_from_pidfile
// arg
int argc;
int arg_len;
char** os_argv;
char** save_argv;
char* cmdline;
// parsed arg
int arg_kv_size;
char** arg_kv;
int arg_list_size;
char** arg_list;
// env
int envc;
int env_len;
char** os_envp;
char** save_envp;
// signals
procedure_t reload_fn;
void* reload_userdata;
// master workers model
int worker_processes;
int worker_threads;
procedure_t worker_fn;
void* worker_userdata;
proc_ctx_t* proc_ctxs;
} main_ctx_t;
// arg_type
#define NO_ARGUMENT 0
#define REQUIRED_ARGUMENT 1
#define OPTIONAL_ARGUMENT 2
// option define
#define OPTION_PREFIX '-'
#define OPTION_DELIM '='
#define OPTION_ENABLE "1"
#define OPTION_DISABLE "0"
typedef struct option_s {
char short_opt;
const char* long_opt;
int arg_type;
} option_t;
HV_EXPORT int main_ctx_init(int argc, char** argv);
HV_EXPORT void main_ctx_free(void);
// ls -a -l
// ls -al
// watch -n 10 ls
// watch -n10 ls
HV_EXPORT int parse_opt(int argc, char** argv, const char* opt);
// gcc -g -Wall -O3 -std=cpp main.c
HV_EXPORT int parse_opt_long(int argc, char** argv, const option_t* long_options, int size);
HV_EXPORT const char* get_arg(const char* key);
HV_EXPORT const char* get_env(const char* key);
#if defined(OS_UNIX) && !HAVE_SETPROCTITLE
HV_EXPORT void setproctitle(const char* fmt, ...);
#endif
// pidfile
HV_EXPORT int create_pidfile();
HV_EXPORT void delete_pidfile(void);
HV_EXPORT pid_t getpid_from_pidfile();
// signal=[start,stop,restart,status,reload]
HV_EXPORT int signal_init(procedure_t reload_fn DEFAULT(NULL), void* reload_userdata DEFAULT(NULL));
HV_EXPORT void signal_handle(const char* signal);
#ifdef OS_UNIX
// we use SIGTERM to quit process, SIGUSR1 to reload confile
#define SIGNAL_TERMINATE SIGTERM
#define SIGNAL_RELOAD SIGUSR1
void signal_handler(int signo);
#endif
// global var
#define DEFAULT_WORKER_PROCESSES 4
#define MAXNUM_WORKER_PROCESSES 256
HV_EXPORT extern main_ctx_t g_main_ctx;
// master-workers processes
HV_EXPORT int master_workers_run(
procedure_t worker_fn,
void* worker_userdata DEFAULT(NULL),
int worker_processes DEFAULT(DEFAULT_WORKER_PROCESSES),
int worker_threads DEFAULT(0),
bool wait DEFAULT(true));
END_EXTERN_C
#endif // HV_MAIN_H_

View File

@ -1,55 +0,0 @@
#ifndef HV_MAP_H_
#define HV_MAP_H_
#include "hconfig.h"
#include <map>
#include <string>
// MultiMap
namespace std {
/*
int main() {
std::MultiMap<std::string, std::string> kvs;
kvs["name"] = "hw";
kvs["filename"] = "1.jpg";
kvs["filename"] = "2.jpg";
//kvs.insert(std::pair<std::string,std::string>("name", "hw"));
//kvs.insert(std::pair<std::string,std::string>("filename", "1.jpg"));
//kvs.insert(std::pair<std::string,std::string>("filename", "2.jpg"));
for (auto& pair : kvs) {
printf("%s:%s\n", pair.first.c_str(), pair.second.c_str());
}
auto iter = kvs.find("filename");
if (iter != kvs.end()) {
for (int i = 0; i < kvs.count("filename"); ++i, ++iter) {
printf("%s:%s\n", iter->first.c_str(), iter->second.c_str());
}
}
return 0;
}
*/
template<typename Key,typename Value>
class MultiMap : public multimap<Key, Value> {
public:
Value& operator[](Key key) {
auto iter = this->insert(std::pair<Key,Value>(key,Value()));
return (*iter).second;
}
};
}
#ifdef USE_MULTIMAP
#define HV_MAP std::MultiMap
#else
#define HV_MAP std::map
#endif
// KeyValue
namespace hv {
typedef std::map<std::string, std::string> keyval_t;
typedef std::MultiMap<std::string, std::string> multi_keyval_t;
typedef HV_MAP<std::string, std::string> KeyValue;
}
#endif // HV_MAP_H_

View File

@ -1,68 +0,0 @@
#ifndef HV_MATH_H_
#define HV_MATH_H_
#include <math.h>
static inline unsigned long floor2e(unsigned long num) {
unsigned long n = num;
int e = 0;
while (n>>=1) ++e;
unsigned long ret = 1;
while (e--) ret<<=1;
return ret;
}
static inline unsigned long ceil2e(unsigned long num) {
// 2**0 = 1
if (num == 0 || num == 1) return 1;
unsigned long n = num - 1;
int e = 1;
while (n>>=1) ++e;
unsigned long ret = 1;
while (e--) ret<<=1;
return ret;
}
// varint little-endian
// MSB
static inline int varint_encode(long long value, unsigned char* buf) {
unsigned char ch;
unsigned char *p = buf;
int bytes = 0;
do {
ch = value & 0x7F;
value >>= 7;
*p++ = value == 0 ? ch : (ch | 0x80);
++bytes;
} while (value);
return bytes;
}
// @param[IN|OUT] len: in=>buflen, out=>varint bytesize
static inline long long varint_decode(const unsigned char* buf, int* len) {
long long ret = 0;
int bytes = 0, bits = 0;
const unsigned char *p = buf;
do {
if (len && *len && bytes == *len) {
// Not enough length
*len = 0;
return 0;
}
ret |= ((long long)(*p & 0x7F)) << bits;
++bytes;
if ((*p & 0x80) == 0) {
// Found end
if (len) *len = bytes;
return ret;
}
++p;
bits += 7;
} while(bytes < 10);
// Not found end
if (len) *len = -1;
return ret;
}
#endif // HV_MATH_H_

View File

@ -1,268 +0,0 @@
#ifndef HV_MUTEX_H_
#define HV_MUTEX_H_
#include "hexport.h"
#include "hplatform.h"
#include "htime.h"
BEGIN_EXTERN_C
#ifdef OS_WIN
#define hmutex_t CRITICAL_SECTION
#define hmutex_init InitializeCriticalSection
#define hmutex_destroy DeleteCriticalSection
#define hmutex_lock EnterCriticalSection
#define hmutex_unlock LeaveCriticalSection
#define hrecursive_mutex_t CRITICAL_SECTION
#define hrecursive_mutex_init InitializeCriticalSection
#define hrecursive_mutex_destroy DeleteCriticalSection
#define hrecursive_mutex_lock EnterCriticalSection
#define hrecursive_mutex_unlock LeaveCriticalSection
#define HSPINLOCK_COUNT -1
#define hspinlock_t CRITICAL_SECTION
#define hspinlock_init(pspin) InitializeCriticalSectionAndSpinCount(pspin, HSPINLOCK_COUNT)
#define hspinlock_destroy DeleteCriticalSection
#define hspinlock_lock EnterCriticalSection
#define hspinlock_unlock LeaveCriticalSection
#define hrwlock_t SRWLOCK
#define hrwlock_init InitializeSRWLock
#define hrwlock_destroy(plock)
#define hrwlock_rdlock AcquireSRWLockShared
#define hrwlock_rdunlock ReleaseSRWLockShared
#define hrwlock_wrlock AcquireSRWLockExclusive
#define hrwlock_wrunlock ReleaseSRWLockExclusive
#define htimed_mutex_t HANDLE
#define htimed_mutex_init(pmutex) *(pmutex) = CreateMutex(NULL, FALSE, NULL)
#define htimed_mutex_destroy(pmutex) CloseHandle(*(pmutex))
#define htimed_mutex_lock(pmutex) WaitForSingleObject(*(pmutex), INFINITE)
#define htimed_mutex_unlock(pmutex) ReleaseMutex(*(pmutex))
// true: WAIT_OBJECT_0
// false: WAIT_OBJECT_TIMEOUT
#define htimed_mutex_lock_for(pmutex, ms) ( WaitForSingleObject(*(pmutex), ms) == WAIT_OBJECT_0 )
#define hcondvar_t CONDITION_VARIABLE
#define hcondvar_init InitializeConditionVariable
#define hcondvar_destroy(pcond)
#define hcondvar_wait(pcond, pmutex) SleepConditionVariableCS(pcond, pmutex, INFINITE)
#define hcondvar_wait_for(pcond, pmutex, ms) SleepConditionVariableCS(pcond, pmutex, ms)
#define hcondvar_signal WakeConditionVariable
#define hcondvar_broadcast WakeAllConditionVariable
#define honce_t INIT_ONCE
#define HONCE_INIT INIT_ONCE_STATIC_INIT
typedef void (*honce_fn)();
static inline BOOL WINAPI s_once_func(INIT_ONCE* once, PVOID arg, PVOID* _) {
honce_fn fn = (honce_fn)arg;
fn();
return TRUE;
}
static inline void honce(honce_t* once, honce_fn fn) {
PVOID dummy = NULL;
InitOnceExecuteOnce(once, s_once_func, (PVOID)fn, &dummy);
}
#define hsem_t HANDLE
#define hsem_init(psem, value) *(psem) = CreateSemaphore(NULL, value, value+100000, NULL)
#define hsem_destroy(psem) CloseHandle(*(psem))
#define hsem_wait(psem) WaitForSingleObject(*(psem), INFINITE)
#define hsem_post(psem) ReleaseSemaphore(*(psem), 1, NULL)
// true: WAIT_OBJECT_0
// false: WAIT_OBJECT_TIMEOUT
#define hsem_wait_for(psem, ms) ( WaitForSingleObject(*(psem), ms) == WAIT_OBJECT_0 )
#else
#define hmutex_t pthread_mutex_t
#define hmutex_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define hmutex_destroy pthread_mutex_destroy
#define hmutex_lock pthread_mutex_lock
#define hmutex_unlock pthread_mutex_unlock
#define hrecursive_mutex_t pthread_mutex_t
#define hrecursive_mutex_init(pmutex) \
do {\
pthread_mutexattr_t attr;\
pthread_mutexattr_init(&attr);\
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);\
pthread_mutex_init(pmutex, &attr);\
} while(0)
#define hrecursive_mutex_destroy pthread_mutex_destroy
#define hrecursive_mutex_lock pthread_mutex_lock
#define hrecursive_mutex_unlock pthread_mutex_unlock
#if HAVE_PTHREAD_SPIN_LOCK
#define hspinlock_t pthread_spinlock_t
#define hspinlock_init(pspin) pthread_spin_init(pspin, PTHREAD_PROCESS_PRIVATE)
#define hspinlock_destroy pthread_spin_destroy
#define hspinlock_lock pthread_spin_lock
#define hspinlock_unlock pthread_spin_unlock
#else
#define hspinlock_t pthread_mutex_t
#define hspinlock_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define hspinlock_destroy pthread_mutex_destroy
#define hspinlock_lock pthread_mutex_lock
#define hspinlock_unlock pthread_mutex_unlock
#endif
#define hrwlock_t pthread_rwlock_t
#define hrwlock_init(prwlock) pthread_rwlock_init(prwlock, NULL)
#define hrwlock_destroy pthread_rwlock_destroy
#define hrwlock_rdlock pthread_rwlock_rdlock
#define hrwlock_rdunlock pthread_rwlock_unlock
#define hrwlock_wrlock pthread_rwlock_wrlock
#define hrwlock_wrunlock pthread_rwlock_unlock
#define htimed_mutex_t pthread_mutex_t
#define htimed_mutex_init(pmutex) pthread_mutex_init(pmutex, NULL)
#define htimed_mutex_destroy pthread_mutex_destroy
#define htimed_mutex_lock pthread_mutex_lock
#define htimed_mutex_unlock pthread_mutex_unlock
static inline void timespec_after(struct timespec* ts, unsigned int ms) {
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec + ms / 1000;
ts->tv_nsec = tv.tv_usec * 1000 + ms % 1000 * 1000000;
if (ts->tv_nsec >= 1000000000) {
ts->tv_nsec -= 1000000000;
ts->tv_sec += 1;
}
}
// true: OK
// false: ETIMEDOUT
static inline int htimed_mutex_lock_for(htimed_mutex_t* mutex, unsigned int ms) {
#if HAVE_PTHREAD_MUTEX_TIMEDLOCK
struct timespec ts;
timespec_after(&ts, ms);
return pthread_mutex_timedlock(mutex, &ts) != ETIMEDOUT;
#else
int ret = 0;
unsigned int end = gettick_ms() + ms;
while ((ret = pthread_mutex_trylock(mutex)) != 0) {
if (gettick_ms() >= end) {
break;
}
hv_msleep(1);
}
return ret == 0;
#endif
}
#define hcondvar_t pthread_cond_t
#define hcondvar_init(pcond) pthread_cond_init(pcond, NULL)
#define hcondvar_destroy pthread_cond_destroy
#define hcondvar_wait pthread_cond_wait
#define hcondvar_signal pthread_cond_signal
#define hcondvar_broadcast pthread_cond_broadcast
// true: OK
// false: ETIMEDOUT
static inline int hcondvar_wait_for(hcondvar_t* cond, hmutex_t* mutex, unsigned int ms) {
struct timespec ts;
timespec_after(&ts, ms);
return pthread_cond_timedwait(cond, mutex, &ts) != ETIMEDOUT;
}
#define honce_t pthread_once_t
#define HONCE_INIT PTHREAD_ONCE_INIT
#define honce pthread_once
#include <semaphore.h>
#define hsem_t sem_t
#define hsem_init(psem, value) sem_init(psem, 0, value)
#define hsem_destroy sem_destroy
#define hsem_wait sem_wait
#define hsem_post sem_post
// true: OK
// false: ETIMEDOUT
static inline int hsem_wait_for(hsem_t* sem, unsigned int ms) {
#if HAVE_SEM_TIMEDWAIT
struct timespec ts;
timespec_after(&ts, ms);
return sem_timedwait(sem, &ts) != ETIMEDOUT;
#else
int ret = 0;
unsigned int end = gettick_ms() + ms;
while ((ret = sem_trywait(sem)) != 0) {
if (gettick_ms() >= end) {
break;
}
hv_msleep(1);
}
return ret == 0;
#endif
}
#endif
END_EXTERN_C
#ifdef __cplusplus
#include <mutex>
#include <condition_variable>
// using std::mutex;
// NOTE: test std::timed_mutex incorrect in some platforms, use htimed_mutex_t
// using std::timed_mutex;
using std::condition_variable;
using std::lock_guard;
using std::unique_lock;
BEGIN_NAMESPACE_HV
class MutexLock {
public:
MutexLock() { hmutex_init(&_mutex); }
~MutexLock() { hmutex_destroy(&_mutex); }
void lock() { hmutex_lock(&_mutex); }
void unlock() { hmutex_unlock(&_mutex); }
protected:
hmutex_t _mutex;
};
class SpinLock {
public:
SpinLock() { hspinlock_init(&_spin); }
~SpinLock() { hspinlock_destroy(&_spin); }
void lock() { hspinlock_lock(&_spin); }
void unlock() { hspinlock_unlock(&_spin); }
protected:
hspinlock_t _spin;
};
class RWLock {
public:
RWLock() { hrwlock_init(&_rwlock); }
~RWLock() { hrwlock_destroy(&_rwlock); }
void rdlock() { hrwlock_rdlock(&_rwlock); }
void rdunlock() { hrwlock_rdunlock(&_rwlock); }
void wrlock() { hrwlock_wrlock(&_rwlock); }
void wrunlock() { hrwlock_wrunlock(&_rwlock); }
void lock() { rdlock(); }
void unlock() { rdunlock(); }
protected:
hrwlock_t _rwlock;
};
template<class T>
class LockGuard {
public:
LockGuard(T& t) : _lock(t) { _lock.lock(); }
~LockGuard() { _lock.unlock(); }
protected:
T& _lock;
};
END_NAMESPACE_HV
// same as java synchronized(lock) { ... }
#define synchronized(lock) for (std::lock_guard<std::mutex> _lock_(lock), *p = &_lock_; p != NULL; p = NULL)
#endif // __cplusplus
#endif // HV_MUTEX_H_

View File

@ -1,183 +0,0 @@
#ifndef HV_OBJECT_POOL_H_
#define HV_OBJECT_POOL_H_
/*
* @usage unittest/objectpool_test.cpp
*/
#include <list>
#include <memory>
#include <mutex>
#include <condition_variable>
#define DEFAULT_OBJECT_POOL_INIT_NUM 0
#define DEFAULT_OBJECT_POOL_MAX_NUM 4
#define DEFAULT_OBJECT_POOL_TIMEOUT 3000 // ms
template<class T>
class HObjectFactory {
public:
static T* create() {
return new T;
}
};
template<class T, class TFactory = HObjectFactory<T>>
class HObjectPool {
public:
HObjectPool(
int init_num = DEFAULT_OBJECT_POOL_INIT_NUM,
int max_num = DEFAULT_OBJECT_POOL_MAX_NUM,
int timeout = DEFAULT_OBJECT_POOL_TIMEOUT)
: _max_num(max_num)
, _timeout(timeout)
{
for (int i = 0; i < init_num; ++i) {
T* p = TFactory::create();
if (p) {
objects_.push_back(std::shared_ptr<T>(p));
}
}
_object_num = objects_.size();
}
~HObjectPool() {}
int ObjectNum() { return _object_num; }
int IdleNum() { return objects_.size(); }
int BorrowNum() { return ObjectNum() - IdleNum(); }
std::shared_ptr<T> TryBorrow() {
std::shared_ptr<T> pObj = NULL;
std::lock_guard<std::mutex> locker(mutex_);
if (!objects_.empty()) {
pObj = objects_.front();
objects_.pop_front();
}
return pObj;
}
std::shared_ptr<T> Borrow() {
std::shared_ptr<T> pObj = TryBorrow();
if (pObj) {
return pObj;
}
std::unique_lock<std::mutex> locker(mutex_);
if (_object_num < _max_num) {
++_object_num;
// NOTE: unlock to avoid TFactory::create block
mutex_.unlock();
T* p = TFactory::create();
mutex_.lock();
if (!p) --_object_num;
return std::shared_ptr<T>(p);
}
if (_timeout > 0) {
std::cv_status status = cond_.wait_for(locker, std::chrono::milliseconds(_timeout));
if (status == std::cv_status::timeout) {
return NULL;
}
if (!objects_.empty()) {
pObj = objects_.front();
objects_.pop_front();
return pObj;
}
else {
// WARN: No idle object
}
}
return pObj;
}
void Return(std::shared_ptr<T>& pObj) {
if (!pObj) return;
std::lock_guard<std::mutex> locker(mutex_);
objects_.push_back(pObj);
cond_.notify_one();
}
bool Add(std::shared_ptr<T>& pObj) {
std::lock_guard<std::mutex> locker(mutex_);
if (_object_num >= _max_num) {
return false;
}
objects_.push_back(pObj);
++_object_num;
cond_.notify_one();
return true;
}
bool Remove(std::shared_ptr<T>& pObj) {
std::lock_guard<std::mutex> locker(mutex_);
auto iter = objects_.begin();
while (iter != objects_.end()) {
if (*iter == pObj) {
iter = objects_.erase(iter);
--_object_num;
return true;
}
else {
++iter;
}
}
return false;
}
void Clear() {
std::lock_guard<std::mutex> locker(mutex_);
objects_.clear();
_object_num = 0;
}
int _object_num;
int _max_num;
int _timeout;
private:
std::list<std::shared_ptr<T>> objects_;
std::mutex mutex_;
std::condition_variable cond_;
};
template<class T, class TFactory = HObjectFactory<T>>
class HPoolObject {
public:
typedef HObjectPool<T, TFactory> PoolType;
HPoolObject(PoolType& pool) : pool_(pool)
{
sptr_ = pool_.Borrow();
}
~HPoolObject() {
if (sptr_) {
pool_.Return(sptr_);
}
}
HPoolObject(const HPoolObject<T>&) = delete;
HPoolObject<T>& operator=(const HPoolObject<T>&) = delete;
T* get() {
return sptr_.get();
}
operator bool() {
return sptr_.get() != NULL;
}
T* operator->() {
return sptr_.get();
}
T operator*() {
return *sptr_.get();
}
private:
PoolType& pool_;
std::shared_ptr<T> sptr_;
};
#endif // HV_OBJECT_POOL_H_

View File

@ -1,28 +0,0 @@
#ifndef HV_PATH_H_
#define HV_PATH_H_
#include <string> // for std::string
#include "hexport.h"
class HV_EXPORT HPath {
public:
static bool exists(const char* path);
static bool isdir(const char* path);
static bool isfile(const char* path);
static bool islink(const char* path);
// filepath = /mnt/share/image/test.jpg
// basename = test.jpg
// dirname = /mnt/share/image
// filename = test
// suffixname = jpg
static std::string basename(const std::string& filepath);
static std::string dirname(const std::string& filepath);
static std::string filename(const std::string& filepath);
static std::string suffixname(const std::string& filepath);
static std::string join(const std::string& dir, const std::string& filename);
};
#endif // HV_PATH_H_

View File

@ -1,330 +0,0 @@
#ifndef HV_PLATFORM_H_
#define HV_PLATFORM_H_
#include "hconfig.h"
// OS
#if defined(WIN64) || defined(_WIN64)
#define OS_WIN64
#define OS_WIN32
#elif defined(WIN32)|| defined(_WIN32)
#define OS_WIN32
#elif defined(ANDROID) || defined(__ANDROID__)
#define OS_ANDROID
#define OS_LINUX
#elif defined(linux) || defined(__linux) || defined(__linux__)
#define OS_LINUX
#elif defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))
#include <TargetConditionals.h>
#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
#define OS_MAC
#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
#define OS_IOS
#endif
#define OS_DARWIN
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define OS_FREEBSD
#define OS_BSD
#elif defined(__NetBSD__)
#define OS_NETBSD
#define OS_BSD
#elif defined(__OpenBSD__)
#define OS_OPENBSD
#define OS_BSD
#elif defined(sun) || defined(__sun) || defined(__sun__)
#define OS_SOLARIS
#else
#warning "Untested operating system platform!"
#endif
#if defined(OS_WIN32) || defined(OS_WIN64)
#undef OS_UNIX
#define OS_WIN
#else
#define OS_UNIX
#endif
// ARCH
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
#define ARCH_X64
#define ARCH_X86_64
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
#define ARCH_X86
#define ARCH_X86_32
#elif defined(__aarch64__) || defined(__ARM64__) || defined(_M_ARM64)
#define ARCH_ARM64
#elif defined(__arm__) || defined(_M_ARM)
#define ARCH_ARM
#elif defined(__mips64__)
#define ARCH_MIPS64
#elif defined(__mips__)
#define ARCH_MIPS
#else
#warning "Untested hardware architecture!"
#endif
// COMPILER
#if defined (_MSC_VER)
#define COMPILER_MSVC
#if (_MSC_VER < 1200) // Visual C++ 6.0
#define MSVS_VERSION 1998
#define MSVC_VERSION 60
#elif (_MSC_VER >= 1200) && (_MSC_VER < 1300) // Visual Studio 2002, MSVC++ 7.0
#define MSVS_VERSION 2002
#define MSVC_VERSION 70
#elif (_MSC_VER >= 1300) && (_MSC_VER < 1400) // Visual Studio 2003, MSVC++ 7.1
#define MSVS_VERSION 2003
#define MSVC_VERSION 71
#elif (_MSC_VER >= 1400) && (_MSC_VER < 1500) // Visual Studio 2005, MSVC++ 8.0
#define MSVS_VERSION 2005
#define MSVC_VERSION 80
#elif (_MSC_VER >= 1500) && (_MSC_VER < 1600) // Visual Studio 2008, MSVC++ 9.0
#define MSVS_VERSION 2008
#define MSVC_VERSION 90
#elif (_MSC_VER >= 1600) && (_MSC_VER < 1700) // Visual Studio 2010, MSVC++ 10.0
#define MSVS_VERSION 2010
#define MSVC_VERSION 100
#elif (_MSC_VER >= 1700) && (_MSC_VER < 1800) // Visual Studio 2012, MSVC++ 11.0
#define MSVS_VERSION 2012
#define MSVC_VERSION 110
#elif (_MSC_VER >= 1800) && (_MSC_VER < 1900) // Visual Studio 2013, MSVC++ 12.0
#define MSVS_VERSION 2013
#define MSVC_VERSION 120
#elif (_MSC_VER >= 1900) && (_MSC_VER < 1910) // Visual Studio 2015, MSVC++ 14.0
#define MSVS_VERSION 2015
#define MSVC_VERSION 140
#elif (_MSC_VER >= 1910) && (_MSC_VER < 1920) // Visual Studio 2017, MSVC++ 15.0
#define MSVS_VERSION 2017
#define MSVC_VERSION 150
#elif (_MSC_VER >= 1920) && (_MSC_VER < 2000) // Visual Studio 2019, MSVC++ 16.0
#define MSVS_VERSION 2019
#define MSVC_VERSION 160
#endif
#undef HAVE_STDATOMIC_H
#define HAVE_STDATOMIC_H 0
#undef HAVE_SYS_TIME_H
#define HAVE_SYS_TIME_H 0
#undef HAVE_PTHREAD_H
#define HAVE_PTHREAD_H 0
#pragma warning (disable: 4018) // signed/unsigned comparison
#pragma warning (disable: 4100) // unused param
#pragma warning (disable: 4102) // unreferenced label
#pragma warning (disable: 4244) // conversion loss of data
#pragma warning (disable: 4267) // size_t => int
#pragma warning (disable: 4819) // Unicode
#pragma warning (disable: 4996) // _CRT_SECURE_NO_WARNINGS
#elif defined(__GNUC__)
#define COMPILER_GCC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#elif defined(__clang__)
#define COMPILER_CLANG
#elif defined(__MINGW32__) || defined(__MINGW64__)
#define COMPILER_MINGW
#elif defined(__MSYS__)
#define COMPILER_MSYS
#elif defined(__CYGWIN__)
#define COMPILER_CYGWIN
#else
#warning "Untested compiler!"
#endif
// headers
#ifdef OS_WIN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include <winsock2.h>
#include <ws2tcpip.h> // for inet_pton,inet_ntop
#include <windows.h>
#include <process.h> // for getpid,exec
#include <direct.h> // for mkdir,rmdir,chdir,getcwd
#include <io.h> // for open,close,read,write,lseek,tell
#define hv_sleep(s) Sleep((s) * 1000)
#define hv_msleep(ms) Sleep(ms)
#define hv_usleep(us) Sleep((us) / 1000)
#define hv_delay(ms) hv_msleep(ms)
#define hv_mkdir(dir) mkdir(dir)
// access
#ifndef F_OK
#define F_OK 0 /* test for existence of file */
#endif
#ifndef X_OK
#define X_OK (1<<0) /* test for execute or search permission */
#endif
#ifndef W_OK
#define W_OK (1<<1) /* test for write permission */
#endif
#ifndef R_OK
#define R_OK (1<<2) /* test for read permission */
#endif
// stat
#ifndef S_ISREG
#define S_ISREG(st_mode) (((st_mode) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(st_mode) (((st_mode) & S_IFMT) == S_IFDIR)
#endif
#else
#include <unistd.h>
#include <dirent.h> // for mkdir,rmdir,chdir,getcwd
// socket
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netdb.h> // for gethostbyname
#define hv_sleep(s) sleep(s)
#define hv_msleep(ms) usleep((ms) * 1000)
#define hv_usleep(us) usleep(us)
#define hv_delay(ms) hv_msleep(ms)
#define hv_mkdir(dir) mkdir(dir, 0777)
#endif
#ifdef _MSC_VER
typedef int pid_t;
typedef int gid_t;
typedef int uid_t;
#define strcasecmp stricmp
#define strncasecmp strnicmp
#else
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef void* HANDLE;
#include <strings.h>
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif
// ENDIAN
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 4321
#endif
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 1234
#endif
#ifndef NET_ENDIAN
#define NET_ENDIAN BIG_ENDIAN
#endif
// BYTE_ORDER
#ifndef BYTE_ORDER
#if defined(__BYTE_ORDER)
#define BYTE_ORDER __BYTE_ORDER
#elif defined(__BYTE_ORDER__)
#define BYTE_ORDER __BYTE_ORDER__
#elif defined(ARCH_X86) || defined(ARCH_X86_64) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || \
defined(__MIPSEL) || defined(__MIPS64EL)
#define BYTE_ORDER LITTLE_ENDIAN
#elif defined(__ARMEB__) || defined(__AARCH64EB__) || \
defined(__MIPSEB) || defined(__MIPS64EB)
#define BYTE_ORDER BIG_ENDIAN
#elif defined(OS_WIN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#warning "Unknown byte order!"
#endif
#endif
// ANSI C
#include <assert.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <math.h>
#include <signal.h>
#ifndef __cplusplus
#if HAVE_STDBOOL_H
#include <stdbool.h>
#else
#ifndef bool
#define bool char
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#endif
#endif
#if HAVE_STDINT_H
#include <stdint.h>
#elif defined(_MSC_VER) && _MSC_VER < 1700
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#endif
typedef float float32_t;
typedef double float64_t;
typedef int (*method_t)(void* userdata);
typedef void (*procedure_t)(void* userdata);
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_SYS_TIME_H
#include <sys/time.h> // for gettimeofday
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_PTHREAD_H
#include <pthread.h>
#endif
#endif // HV_PLATFORM_H_

View File

@ -1,69 +0,0 @@
#ifndef HV_PROC_H_
#define HV_PROC_H_
#include "hplatform.h"
typedef struct proc_ctx_s {
pid_t pid; // tid in Windows
time_t start_time;
int spawn_cnt;
procedure_t init;
void* init_userdata;
procedure_t proc;
void* proc_userdata;
procedure_t exit;
void* exit_userdata;
} proc_ctx_t;
static inline void hproc_run(proc_ctx_t* ctx) {
if (ctx->init) {
ctx->init(ctx->init_userdata);
}
if (ctx->proc) {
ctx->proc(ctx->proc_userdata);
}
if (ctx->exit) {
ctx->exit(ctx->exit_userdata);
}
}
#ifdef OS_UNIX
// unix use multi-processes
static inline int hproc_spawn(proc_ctx_t* ctx) {
++ctx->spawn_cnt;
ctx->start_time = time(NULL);
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
} else if (pid == 0) {
// child process
ctx->pid = getpid();
hproc_run(ctx);
exit(0);
} else if (pid > 0) {
// parent process
ctx->pid = pid;
}
return pid;
}
#elif defined(OS_WIN)
// win32 use multi-threads
static void win_thread(void* userdata) {
proc_ctx_t* ctx = (proc_ctx_t*)userdata;
ctx->pid = GetCurrentThreadId(); // tid in Windows
hproc_run(ctx);
}
static inline int hproc_spawn(proc_ctx_t* ctx) {
++ctx->spawn_cnt;
ctx->start_time = time(NULL);
HANDLE h = (HANDLE)_beginthread(win_thread, 0, ctx);
if (h == NULL) {
return -1;
}
ctx->pid = GetThreadId(h); // tid in Windows
return ctx->pid;
}
#endif
#endif // HV_PROC_H_

View File

@ -1,79 +0,0 @@
#ifndef HV_SCOPE_H_
#define HV_SCOPE_H_
#include <functional>
typedef std::function<void()> Function;
#include "hdef.h"
// same as golang defer
class Defer {
public:
Defer(Function&& fn) : _fn(std::move(fn)) {}
~Defer() { if(_fn) _fn();}
private:
Function _fn;
};
#define defer(code) Defer STRINGCAT(_defer_, __LINE__)([&](){code});
class ScopeCleanup {
public:
template<typename Fn, typename... Args>
ScopeCleanup(Fn&& fn, Args&&... args) {
_cleanup = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
~ScopeCleanup() {
_cleanup();
}
private:
Function _cleanup;
};
template<typename T>
class ScopeFree {
public:
ScopeFree(T* p) : _p(p) {}
~ScopeFree() {SAFE_FREE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeDelete {
public:
ScopeDelete(T* p) : _p(p) {}
~ScopeDelete() {SAFE_DELETE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeDeleteArray {
public:
ScopeDeleteArray(T* p) : _p(p) {}
~ScopeDeleteArray() {SAFE_DELETE_ARRAY(_p);}
private:
T* _p;
};
template<typename T>
class ScopeRelease {
public:
ScopeRelease(T* p) : _p(p) {}
~ScopeRelease() {SAFE_RELEASE(_p);}
private:
T* _p;
};
template<typename T>
class ScopeLock {
public:
ScopeLock(T& mutex) : _mutex(mutex) {_mutex.lock();}
~ScopeLock() {_mutex.unlock();}
private:
T& _mutex;
};
#endif // HV_SCOPE_H_

View File

@ -1,285 +0,0 @@
#ifndef HV_SOCKET_H_
#define HV_SOCKET_H_
#include "hexport.h"
#include "hplatform.h"
#ifdef ENABLE_UDS
#ifdef OS_WIN
#include <afunix.h> // import struct sockaddr_un
#else
#include <sys/un.h> // import struct sockaddr_un
#endif
#endif
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#endif
#define LOCALHOST "127.0.0.1"
#define ANYADDR "0.0.0.0"
BEGIN_EXTERN_C
HV_INLINE int socket_errno() {
#ifdef OS_WIN
return WSAGetLastError();
#else
return errno;
#endif
}
HV_EXPORT const char* socket_strerror(int err);
#ifdef OS_WIN
typedef SOCKET hsocket_t;
typedef int socklen_t;
void WSAInit();
void WSADeinit();
HV_INLINE int blocking(int sockfd) {
unsigned long nb = 0;
return ioctlsocket(sockfd, FIONBIO, &nb);
}
HV_INLINE int nonblocking(int sockfd) {
unsigned long nb = 1;
return ioctlsocket(sockfd, FIONBIO, &nb);
}
#undef EAGAIN
#define EAGAIN WSAEWOULDBLOCK
#undef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#undef EINTR
#define EINTR WSAEINTR
#undef ENOTSOCK
#define ENOTSOCK WSAENOTSOCK
#undef EMSGSIZE
#define EMSGSIZE WSAEMSGSIZE
#else
typedef int hsocket_t;
#ifndef SOCKET
#define SOCKET int
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
HV_INLINE int blocking(int s) {
return fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK);
}
HV_INLINE int nonblocking(int s) {
return fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
}
HV_INLINE int closesocket(int sockfd) {
return close(sockfd);
}
#endif
#ifndef SAFE_CLOSESOCKET
#define SAFE_CLOSESOCKET(fd) do {if ((fd) >= 0) {closesocket(fd); (fd) = -1;}} while(0)
#endif
//-----------------------------sockaddr_u----------------------------------------------
typedef union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
#ifdef ENABLE_UDS
struct sockaddr_un sun;
#endif
} sockaddr_u;
HV_EXPORT bool is_ipv4(const char* host);
HV_EXPORT bool is_ipv6(const char* host);
HV_INLINE bool is_ipaddr(const char* host) {
return is_ipv4(host) || is_ipv6(host);
}
// @param host: domain or ip
// @retval 0:succeed
HV_EXPORT int ResolveAddr(const char* host, sockaddr_u* addr);
HV_EXPORT const char* sockaddr_ip(sockaddr_u* addr, char *ip, int len);
HV_EXPORT uint16_t sockaddr_port(sockaddr_u* addr);
HV_EXPORT int sockaddr_set_ip(sockaddr_u* addr, const char* host);
HV_EXPORT void sockaddr_set_port(sockaddr_u* addr, int port);
HV_EXPORT int sockaddr_set_ipport(sockaddr_u* addr, const char* host, int port);
HV_EXPORT socklen_t sockaddr_len(sockaddr_u* addr);
HV_EXPORT const char* sockaddr_str(sockaddr_u* addr, char* buf, int len);
//#define INET_ADDRSTRLEN 16
//#define INET6_ADDRSTRLEN 46
#ifdef ENABLE_UDS
#define SOCKADDR_STRLEN sizeof(((struct sockaddr_un*)(NULL))->sun_path)
HV_INLINE void sockaddr_set_path(sockaddr_u* addr, const char* path) {
addr->sa.sa_family = AF_UNIX;
strncpy(addr->sun.sun_path, path, sizeof(addr->sun.sun_path));
}
#else
#define SOCKADDR_STRLEN 64 // ipv4:port | [ipv6]:port
#endif
HV_INLINE void sockaddr_print(sockaddr_u* addr) {
char buf[SOCKADDR_STRLEN] = {0};
sockaddr_str(addr, buf, sizeof(buf));
puts(buf);
}
#define SOCKADDR_LEN(addr) sockaddr_len((sockaddr_u*)addr)
#define SOCKADDR_STR(addr, buf) sockaddr_str((sockaddr_u*)addr, buf, sizeof(buf))
#define SOCKADDR_PRINT(addr) sockaddr_print((sockaddr_u*)addr)
//=====================================================================================
// socket -> setsockopt -> bind
// @param type: SOCK_STREAM(tcp) SOCK_DGRAM(udp)
// @return sockfd
HV_EXPORT int Bind(int port, const char* host DEFAULT(ANYADDR), int type DEFAULT(SOCK_STREAM));
// Bind -> listen
// @return listenfd
HV_EXPORT int Listen(int port, const char* host DEFAULT(ANYADDR));
// @return connfd
// ResolveAddr -> socket -> nonblocking -> connect
HV_EXPORT int Connect(const char* host, int port, int nonblock DEFAULT(0));
// Connect(host, port, 1)
HV_EXPORT int ConnectNonblock(const char* host, int port);
// Connect(host, port, 1) -> select -> blocking
#define DEFAULT_CONNECT_TIMEOUT 10000 // ms
HV_EXPORT int ConnectTimeout(const char* host, int port, int ms DEFAULT(DEFAULT_CONNECT_TIMEOUT));
#ifdef ENABLE_UDS
HV_EXPORT int BindUnix(const char* path, int type DEFAULT(SOCK_STREAM));
HV_EXPORT int ListenUnix(const char* path);
HV_EXPORT int ConnectUnix(const char* path, int nonblock DEFAULT(0));
HV_EXPORT int ConnectUnixNonblock(const char* path);
HV_EXPORT int ConnectUnixTimeout(const char* path, int ms DEFAULT(DEFAULT_CONNECT_TIMEOUT));
#endif
// Just implement Socketpair(AF_INET, SOCK_STREAM, 0, sv);
HV_EXPORT int Socketpair(int family, int type, int protocol, int sv[2]);
HV_INLINE int tcp_nodelay(int sockfd, int on DEFAULT(1)) {
return setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(int));
}
HV_INLINE int tcp_nopush(int sockfd, int on DEFAULT(1)) {
#ifdef TCP_NOPUSH
return setsockopt(sockfd, IPPROTO_TCP, TCP_NOPUSH, (const char*)&on, sizeof(int));
#elif defined(TCP_CORK)
return setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int tcp_keepalive(int sockfd, int on DEFAULT(1), int delay DEFAULT(60)) {
if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(int)) != 0) {
return socket_errno();
}
#ifdef TCP_KEEPALIVE
return setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, (const char*)&delay, sizeof(int));
#elif defined(TCP_KEEPIDLE)
// TCP_KEEPIDLE => tcp_keepalive_time
// TCP_KEEPCNT => tcp_keepalive_probes
// TCP_KEEPINTVL => tcp_keepalive_intvl
return setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (const char*)&delay, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int udp_broadcast(int sockfd, int on DEFAULT(1)) {
return setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(int));
}
HV_INLINE int ip_v6only(int sockfd, int on DEFAULT(1)) {
#ifdef IPV6_V6ONLY
return setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
// send timeout
HV_INLINE int so_sndtimeo(int sockfd, int timeout) {
#ifdef OS_WIN
return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(int));
#else
struct timeval tv = {timeout/1000, (timeout%1000)*1000};
return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
#endif
}
// recv timeout
HV_INLINE int so_rcvtimeo(int sockfd, int timeout) {
#ifdef OS_WIN
return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(int));
#else
struct timeval tv = {timeout/1000, (timeout%1000)*1000};
return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#endif
}
// send buffer size
HV_INLINE int so_sndbuf(int sockfd, int len) {
return setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char*)&len, sizeof(int));
}
// recv buffer size
HV_INLINE int so_rcvbuf(int sockfd, int len) {
return setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (const char*)&len, sizeof(int));
}
HV_INLINE int so_reuseaddr(int sockfd, int on DEFAULT(1)) {
#ifdef SO_REUSEADDR
// NOTE: SO_REUSEADDR allow to reuse sockaddr of TIME_WAIT status
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int so_reuseport(int sockfd, int on DEFAULT(1)) {
#ifdef SO_REUSEPORT
// NOTE: SO_REUSEPORT allow multiple sockets to bind same port
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&on, sizeof(int));
#else
return 0;
#endif
}
HV_INLINE int so_linger(int sockfd, int timeout DEFAULT(1)) {
#ifdef SO_LINGER
struct linger linger;
if (timeout >= 0) {
linger.l_onoff = 1;
linger.l_linger = timeout;
} else {
linger.l_onoff = 0;
linger.l_linger = 0;
}
// NOTE: SO_LINGER change the default behavior of close, send RST, avoid TIME_WAIT
return setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger, sizeof(linger));
#else
return 0;
#endif
}
END_EXTERN_C
#endif // HV_SOCKET_H_

View File

@ -1,91 +0,0 @@
#ifndef HV_SSL_H_
#define HV_SSL_H_
#include "hexport.h"
#include "hplatform.h"
#if !defined(WITH_OPENSSL) && \
!defined(WITH_GNUTLS) && \
!defined(WITH_MBEDTLS)
#ifdef OS_WIN
#define WITH_WINTLS
#ifdef _MSC_VER
#pragma comment(lib, "secur32.lib")
#pragma comment(lib, "crypt32.lib")
#endif
#elif defined(OS_DARWIN)
#define WITH_APPLETLS
#else
#define HV_WITHOUT_SSL
#endif
#endif
typedef void* hssl_ctx_t; ///> SSL_CTX
typedef void* hssl_t; ///> SSL
enum {
HSSL_SERVER = 0,
HSSL_CLIENT = 1,
};
enum {
HSSL_OK = 0,
HSSL_ERROR = -1,
HSSL_WANT_READ = -2,
HSSL_WANT_WRITE = -3,
HSSL_WOULD_BLOCK = -4,
};
typedef struct {
const char* crt_file;
const char* key_file;
const char* ca_file;
const char* ca_path;
short verify_peer;
short endpoint; // HSSL_SERVER / HSSL_CLIENT
} hssl_ctx_opt_t, hssl_ctx_init_param_t;
BEGIN_EXTERN_C
/*
const char* hssl_backend() {
#ifdef WITH_OPENSSL
return "openssl";
#elif defined(WITH_GNUTLS)
return "gnutls";
#elif defined(WITH_MBEDTLS)
return "mbedtls";
#else
return "nossl";
#endif
}
*/
HV_EXPORT const char* hssl_backend();
#define HV_WITH_SSL (strcmp(hssl_backend(), "nossl") != 0)
HV_EXPORT extern hssl_ctx_t g_ssl_ctx;
HV_EXPORT hssl_ctx_t hssl_ctx_init(hssl_ctx_init_param_t* param);
HV_EXPORT void hssl_ctx_cleanup(hssl_ctx_t ssl_ctx);
HV_EXPORT hssl_ctx_t hssl_ctx_instance();
HV_EXPORT hssl_ctx_t hssl_ctx_new(hssl_ctx_opt_t* opt);
HV_EXPORT void hssl_ctx_free(hssl_ctx_t ssl_ctx);
HV_EXPORT hssl_t hssl_new(hssl_ctx_t ssl_ctx, int fd);
HV_EXPORT void hssl_free(hssl_t ssl);
HV_EXPORT int hssl_accept(hssl_t ssl);
HV_EXPORT int hssl_connect(hssl_t ssl);
HV_EXPORT int hssl_read(hssl_t ssl, void* buf, int len);
HV_EXPORT int hssl_write(hssl_t ssl, const void* buf, int len);
HV_EXPORT int hssl_close(hssl_t ssl);
HV_EXPORT int hssl_set_sni_hostname(hssl_t ssl, const char* hostname);
#ifdef WITH_OPENSSL
HV_EXPORT int hssl_ctx_set_alpn_protos(hssl_ctx_t ssl_ctx, const unsigned char* protos, unsigned int protos_len);
#endif
END_EXTERN_C
#endif // HV_SSL_H_

View File

@ -1,92 +0,0 @@
#ifndef HV_STRING_H_
#define HV_STRING_H_
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include "hexport.h"
#include "hplatform.h"
#include "hmap.h"
#define SPACE_CHARS " \t\r\n"
#define PAIR_CHARS "{}[]()<>\"\"\'\'``"
namespace hv {
HV_EXPORT extern std::string empty_string;
HV_EXPORT extern std::map<std::string, std::string> empty_map;
typedef std::vector<std::string> StringList;
// std::map<std::string, std::string, StringCaseLess>
class StringCaseLess : public std::less<std::string> {
public:
bool operator()(const std::string& lhs, const std::string& rhs) const {
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
}
};
// NOTE: low-version NDK not provide std::to_string
template<typename T>
HV_INLINE std::string to_string(const T& t) {
std::ostringstream oss;
oss << t;
return oss.str();
}
template<typename T>
HV_INLINE T from_string(const std::string& str) {
T t;
std::istringstream iss(str);
iss >> t;
return t;
}
template<typename T>
HV_INLINE void print(const T& t) {
std::cout << t;
}
template<typename T>
HV_INLINE void println(const T& t) {
std::cout << t << std::endl;
}
HV_EXPORT std::string& toupper(std::string& str);
HV_EXPORT std::string& tolower(std::string& str);
HV_EXPORT std::string& reverse(std::string& str);
HV_EXPORT bool startswith(const std::string& str, const std::string& start);
HV_EXPORT bool endswith(const std::string& str, const std::string& end);
HV_EXPORT bool contains(const std::string& str, const std::string& sub);
HV_EXPORT std::string asprintf(const char* fmt, ...);
// x,y,z
HV_EXPORT StringList split(const std::string& str, char delim = ',');
// k1=v1&k2=v2
HV_EXPORT hv::KeyValue splitKV(const std::string& str, char kv_kv = '&', char k_v = '=');
HV_EXPORT std::string trim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string ltrim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string rtrim(const std::string& str, const char* chars = SPACE_CHARS);
HV_EXPORT std::string trim_pairs(const std::string& str, const char* pairs = PAIR_CHARS);
HV_EXPORT std::string replace(const std::string& str, const std::string& find, const std::string& rep);
HV_EXPORT std::string replaceAll(const std::string& str, const std::string& find, const std::string& rep);
struct HV_EXPORT NetAddr {
std::string ip;
int port;
NetAddr() : port(0) {}
NetAddr(const std::string& _ip, int _port) : ip(_ip), port(_port) {}
NetAddr(const std::string& ipport) { from_string(ipport); }
void from_string(const std::string& ipport);
std::string to_string();
};
} // end namespace hv
#endif // HV_STRING_H_

View File

@ -1,68 +0,0 @@
#ifndef HV_SYS_INFO_H_
#define HV_SYS_INFO_H_
#include "hplatform.h"
#ifdef OS_LINUX
#include <sys/sysinfo.h>
#endif
#ifdef OS_DARWIN
#include <mach/mach_host.h>
#include <sys/sysctl.h>
#endif
static inline int get_ncpu() {
#ifdef OS_WIN
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwNumberOfProcessors;
#else
//return get_nprocs();
//return get_nprocs_conf();
//return sysconf(_SC_NPROCESSORS_ONLN); // processors available
return sysconf(_SC_NPROCESSORS_CONF); // processors configured
#endif
}
typedef struct meminfo_s {
unsigned long total; // KB
unsigned long free; // KB
} meminfo_t;
static inline int get_meminfo(meminfo_t* mem) {
#ifdef OS_WIN
MEMORYSTATUSEX memstat;
memset(&memstat, 0, sizeof(memstat));
memstat.dwLength = sizeof(memstat);
GlobalMemoryStatusEx(&memstat);
mem->total = (unsigned long)(memstat.ullTotalPhys >> 10);
mem->free = (unsigned long)(memstat.ullAvailPhys >> 10);
return 0;
#elif defined(OS_LINUX)
struct sysinfo info;
if (sysinfo(&info) < 0) {
return errno;
}
mem->total = info.totalram * info.mem_unit >> 10;
mem->free = info.freeram * info.mem_unit >> 10;
return 0;
#elif defined(OS_DARWIN)
uint64_t memsize = 0;
size_t size = sizeof(memsize);
int which[2] = {CTL_HW, HW_MEMSIZE};
sysctl(which, 2, &memsize, &size, NULL, 0);
mem->total = memsize >> 10;
vm_statistics_data_t info;
mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count);
mem->free = ((uint64_t)info.free_count * sysconf(_SC_PAGESIZE)) >> 10;
return 0;
#else
(void)(mem);
return -10;
#endif
}
#endif // HV_SYS_INFO_H_

View File

@ -1,217 +0,0 @@
#ifndef HV_THREAD_H_
#define HV_THREAD_H_
#include "hplatform.h"
#ifdef OS_WIN
#define hv_getpid (long)GetCurrentProcessId
#else
#define hv_getpid (long)getpid
#endif
#ifdef OS_WIN
#define hv_gettid (long)GetCurrentThreadId
#elif HAVE_GETTID || defined(OS_ANDROID)
#define hv_gettid (long)gettid
#elif defined(OS_LINUX)
#include <sys/syscall.h>
#define hv_gettid() (long)syscall(SYS_gettid)
#elif defined(OS_DARWIN)
static inline long hv_gettid() {
uint64_t tid = 0;
pthread_threadid_np(NULL, &tid);
return tid;
}
#elif HAVE_PTHREAD_H
#define hv_gettid (long)pthread_self
#else
#define hv_gettid hv_getpid
#endif
/*
#include "hthread.h"
HTHREAD_ROUTINE(thread_demo) {
printf("thread[%ld] start\n", hv_gettid());
hv_delay(3000);
printf("thread[%ld] end\n", hv_gettid());
return 0;
}
int main() {
hthread_t th = hthread_create(thread_demo, NULL);
hthread_join(th);
return 0;
}
*/
#ifdef OS_WIN
typedef HANDLE hthread_t;
typedef DWORD (WINAPI *hthread_routine)(void*);
#define HTHREAD_RETTYPE DWORD
#define HTHREAD_ROUTINE(fname) DWORD WINAPI fname(void* userdata)
static inline hthread_t hthread_create(hthread_routine fn, void* userdata) {
return CreateThread(NULL, 0, fn, userdata, 0, NULL);
}
static inline int hthread_join(hthread_t th) {
WaitForSingleObject(th, INFINITE);
CloseHandle(th);
return 0;
}
#else
typedef pthread_t hthread_t;
typedef void* (*hthread_routine)(void*);
#define HTHREAD_RETTYPE void*
#define HTHREAD_ROUTINE(fname) void* fname(void* userdata)
static inline hthread_t hthread_create(hthread_routine fn, void* userdata) {
pthread_t th;
pthread_create(&th, NULL, fn, userdata);
return th;
}
static inline int hthread_join(hthread_t th) {
return pthread_join(th, NULL);
}
#endif
#ifdef __cplusplus
/************************************************
* HThread
* Status: STOP,RUNNING,PAUSE
* Control: start,stop,pause,resume
* first-level virtual: doTask
* second-level virtual: run
************************************************/
#include <thread>
#include <atomic>
#include <chrono>
class HThread {
public:
enum Status {
STOP,
RUNNING,
PAUSE,
};
enum SleepPolicy {
YIELD,
SLEEP_FOR,
SLEEP_UNTIL,
NO_SLEEP,
};
HThread() {
status = STOP;
status_changed = false;
dotask_cnt = 0;
sleep_policy = YIELD;
sleep_ms = 0;
}
virtual ~HThread() {}
void setStatus(Status stat) {
status_changed = true;
status = stat;
}
void setSleepPolicy(SleepPolicy policy, uint32_t ms = 0) {
sleep_policy = policy;
sleep_ms = ms;
setStatus(status);
}
virtual int start() {
if (status == STOP) {
thread = std::thread([this] {
if (!doPrepare()) return;
setStatus(RUNNING);
run();
setStatus(STOP);
if (!doFinish()) return;
});
}
return 0;
}
virtual int stop() {
if (status != STOP) {
setStatus(STOP);
}
if (thread.joinable()) {
thread.join(); // wait thread exit
}
return 0;
}
virtual int pause() {
if (status == RUNNING) {
setStatus(PAUSE);
}
return 0;
}
virtual int resume() {
if (status == PAUSE) {
setStatus(RUNNING);
}
return 0;
}
virtual void run() {
while (status != STOP) {
while (status == PAUSE) {
std::this_thread::yield();
}
doTask();
++dotask_cnt;
HThread::sleep();
}
}
virtual bool doPrepare() {return true;}
virtual void doTask() {}
virtual bool doFinish() {return true;}
std::thread thread;
std::atomic<Status> status;
uint32_t dotask_cnt;
protected:
void sleep() {
switch (sleep_policy) {
case YIELD:
std::this_thread::yield();
break;
case SLEEP_FOR:
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
break;
case SLEEP_UNTIL: {
if (status_changed) {
status_changed = false;
base_tp = std::chrono::steady_clock::now();
}
base_tp += std::chrono::milliseconds(sleep_ms);
std::this_thread::sleep_until(base_tp);
}
break;
default: // donothing, go all out.
break;
}
}
SleepPolicy sleep_policy;
uint32_t sleep_ms;
// for SLEEP_UNTIL
std::atomic<bool> status_changed;
std::chrono::steady_clock::time_point base_tp;
};
#endif
#endif // HV_THREAD_H_

View File

@ -1,249 +0,0 @@
#ifndef HV_THREAD_POOL_H_
#define HV_THREAD_POOL_H_
/*
* @usage unittest/threadpool_test.cpp
*/
#include <time.h>
#include <thread>
#include <list>
#include <queue>
#include <functional>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <future>
#include <memory>
#include <utility>
#include <chrono>
#define DEFAULT_THREAD_POOL_MIN_THREAD_NUM 1
#define DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency()
#define DEFAULT_THREAD_POOL_MAX_IDLE_TIME 60000 // ms
class HThreadPool {
public:
using Task = std::function<void()>;
HThreadPool(int min_threads = DEFAULT_THREAD_POOL_MIN_THREAD_NUM,
int max_threads = DEFAULT_THREAD_POOL_MAX_THREAD_NUM,
int max_idle_ms = DEFAULT_THREAD_POOL_MAX_IDLE_TIME)
: min_thread_num(min_threads)
, max_thread_num(max_threads)
, max_idle_time(max_idle_ms)
, status(STOP)
, cur_thread_num(0)
, idle_thread_num(0)
{}
virtual ~HThreadPool() {
stop();
}
void setMinThreadNum(int min_threads) {
min_thread_num = min_threads;
}
void setMaxThreadNum(int max_threads) {
max_thread_num = max_threads;
}
void setMaxIdleTime(int ms) {
max_idle_time = ms;
}
int currentThreadNum() {
return cur_thread_num;
}
int idleThreadNum() {
return idle_thread_num;
}
size_t taskNum() {
std::lock_guard<std::mutex> locker(task_mutex);
return tasks.size();
}
bool isStarted() {
return status != STOP;
}
bool isStopped() {
return status == STOP;
}
int start(int start_threads = 0) {
if (status != STOP) return -1;
status = RUNNING;
if (start_threads < min_thread_num) start_threads = min_thread_num;
if (start_threads > max_thread_num) start_threads = max_thread_num;
for (int i = 0; i < start_threads; ++i) {
createThread();
}
return 0;
}
int stop() {
if (status == STOP) return -1;
status = STOP;
task_cond.notify_all();
for (auto& i : threads) {
if (i.thread->joinable()) {
i.thread->join();
}
}
threads.clear();
cur_thread_num = 0;
idle_thread_num = 0;
return 0;
}
int pause() {
if (status == RUNNING) {
status = PAUSE;
}
return 0;
}
int resume() {
if (status == PAUSE) {
status = RUNNING;
}
return 0;
}
int wait() {
while (status != STOP) {
if (tasks.empty() && idle_thread_num == cur_thread_num) {
break;
}
std::this_thread::yield();
}
return 0;
}
/*
* return a future, calling future.get() will wait task done and return RetType.
* commit(fn, args...)
* commit(std::bind(&Class::mem_fn, &obj))
* commit(std::mem_fn(&Class::mem_fn, &obj))
*
*/
template<class Fn, class... Args>
auto commit(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))> {
if (status == STOP) start();
if (idle_thread_num <= tasks.size() && cur_thread_num < max_thread_num) {
createThread();
}
using RetType = decltype(fn(args...));
auto task = std::make_shared<std::packaged_task<RetType()> >(
std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...));
std::future<RetType> future = task->get_future();
{
std::lock_guard<std::mutex> locker(task_mutex);
tasks.emplace([task]{
(*task)();
});
}
task_cond.notify_one();
return future;
}
protected:
bool createThread() {
if (cur_thread_num >= max_thread_num) return false;
std::thread* thread = new std::thread([this] {
while (status != STOP) {
while (status == PAUSE) {
std::this_thread::yield();
}
Task task;
{
std::unique_lock<std::mutex> locker(task_mutex);
task_cond.wait_for(locker, std::chrono::milliseconds(max_idle_time), [this]() {
return status == STOP || !tasks.empty();
});
if (status == STOP) return;
if (tasks.empty()) {
if (cur_thread_num > min_thread_num) {
delThread(std::this_thread::get_id());
return;
}
continue;
}
--idle_thread_num;
task = std::move(tasks.front());
tasks.pop();
}
if (task) {
task();
++idle_thread_num;
}
}
});
addThread(thread);
return true;
}
void addThread(std::thread* thread) {
thread_mutex.lock();
++cur_thread_num;
++idle_thread_num;
ThreadData data;
data.thread = std::shared_ptr<std::thread>(thread);
data.id = thread->get_id();
data.status = RUNNING;
data.start_time = time(NULL);
data.stop_time = 0;
threads.emplace_back(data);
thread_mutex.unlock();
}
void delThread(std::thread::id id) {
time_t now = time(NULL);
thread_mutex.lock();
--cur_thread_num;
--idle_thread_num;
auto iter = threads.begin();
while (iter != threads.end()) {
if (iter->status == STOP && now > iter->stop_time) {
if (iter->thread->joinable()) {
iter->thread->join();
iter = threads.erase(iter);
continue;
}
} else if (iter->id == id) {
iter->status = STOP;
iter->stop_time = time(NULL);
}
++iter;
}
thread_mutex.unlock();
}
public:
int min_thread_num;
int max_thread_num;
int max_idle_time;
protected:
enum Status {
STOP,
RUNNING,
PAUSE,
};
struct ThreadData {
std::shared_ptr<std::thread> thread;
std::thread::id id;
Status status;
time_t start_time;
time_t stop_time;
};
std::atomic<Status> status;
std::atomic<int> cur_thread_num;
std::atomic<int> idle_thread_num;
std::list<ThreadData> threads;
std::mutex thread_mutex;
std::queue<Task> tasks;
std::mutex task_mutex;
std::condition_variable task_cond;
};
#endif // HV_THREAD_POOL_H_

View File

@ -1,114 +0,0 @@
#ifndef HV_TIME_H_
#define HV_TIME_H_
#include "hexport.h"
#include "hplatform.h"
BEGIN_EXTERN_C
#define SECONDS_PER_MINUTE 60
#define SECONDS_PER_HOUR 3600
#define SECONDS_PER_DAY 86400 // 24*3600
#define SECONDS_PER_WEEK 604800 // 7*24*3600
#define IS_LEAP_YEAR(year) (((year)%4 == 0 && (year)%100 != 0) || (year)%400 == 0)
typedef struct datetime_s {
int year;
int month;
int day;
int hour;
int min;
int sec;
int ms;
} datetime_t;
#ifdef _MSC_VER
/* @see winsock2.h
// Structure used in select() call, taken from the BSD file sys/time.h
struct timeval {
long tv_sec;
long tv_usec;
};
*/
struct timezone {
int tz_minuteswest; /* of Greenwich */
int tz_dsttime; /* type of dst correction to apply */
};
#include <sys/timeb.h>
HV_INLINE int gettimeofday(struct timeval *tv, struct timezone *tz) {
struct _timeb tb;
_ftime(&tb);
if (tv) {
tv->tv_sec = (long)tb.time;
tv->tv_usec = tb.millitm * 1000;
}
if (tz) {
tz->tz_minuteswest = tb.timezone;
tz->tz_dsttime = tb.dstflag;
}
return 0;
}
#endif
HV_EXPORT unsigned int gettick_ms();
HV_INLINE unsigned long long gettimeofday_ms() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * (unsigned long long)1000 + tv.tv_usec/1000;
}
HV_INLINE unsigned long long gettimeofday_us() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * (unsigned long long)1000000 + tv.tv_usec;
}
HV_EXPORT unsigned long long gethrtime_us();
HV_EXPORT datetime_t datetime_now();
HV_EXPORT datetime_t datetime_localtime(time_t seconds);
HV_EXPORT time_t datetime_mktime(datetime_t* dt);
HV_EXPORT datetime_t* datetime_past(datetime_t* dt, int days DEFAULT(1));
HV_EXPORT datetime_t* datetime_future(datetime_t* dt, int days DEFAULT(1));
#define TIME_FMT "%02d:%02d:%02d"
#define TIME_FMT_BUFLEN 12
HV_EXPORT char* duration_fmt(int sec, char* buf);
#define DATETIME_FMT "%04d-%02d-%02d %02d:%02d:%02d"
#define DATETIME_FMT_ISO "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ"
#define DATETIME_FMT_BUFLEN 30
HV_EXPORT char* datetime_fmt(datetime_t* dt, char* buf);
HV_EXPORT char* datetime_fmt_iso(datetime_t* dt, char* buf);
#define GMTIME_FMT "%.3s, %02d %.3s %04d %02d:%02d:%02d GMT"
#define GMTIME_FMT_BUFLEN 30
HV_EXPORT char* gmtime_fmt(time_t time, char* buf);
HV_EXPORT int days_of_month(int month, int year);
HV_EXPORT int month_atoi(const char* month);
HV_EXPORT const char* month_itoa(int month);
HV_EXPORT int weekday_atoi(const char* weekday);
HV_EXPORT const char* weekday_itoa(int weekday);
HV_EXPORT datetime_t hv_compile_datetime();
/*
* minute hour day week month action
* 0~59 0~23 1~31 0~6 1~12
* -1 -1 -1 -1 -1 cron.minutely
* 30 -1 -1 -1 -1 cron.hourly
* 30 1 -1 -1 -1 cron.daily
* 30 1 15 -1 -1 cron.monthly
* 30 1 -1 0 -1 cron.weekly
* 30 1 1 -1 10 cron.yearly
*/
HV_EXPORT time_t cron_next_timeout(int minute, int hour, int day, int week, int month);
END_EXTERN_C
#endif // HV_TIME_H_

View File

@ -1,148 +0,0 @@
#ifndef HV_HTTP_CLIENT_H_
#define HV_HTTP_CLIENT_H_
#include "hexport.h"
#include "hssl.h"
#include "HttpMessage.h"
/*
#include <stdio.h>
#include "http_client.h"
int main(int argc, char* argv[]) {
HttpRequest req;
req.method = HTTP_GET;
req.url = "http://www.example.com";
HttpResponse res;
int ret = http_client_send(&req, &res);
printf("%s\n", req.Dump(true,true).c_str());
if (ret != 0) {
printf("* Failed:%s:%d\n", http_client_strerror(ret), ret);
}
else {
printf("%s\n", res.Dump(true,true).c_str());
}
return ret;
}
*/
typedef struct http_client_s http_client_t;
HV_EXPORT http_client_t* http_client_new(const char* host = NULL, int port = DEFAULT_HTTP_PORT, int https = 0);
HV_EXPORT int http_client_close(http_client_t* cli);
HV_EXPORT int http_client_del(http_client_t* cli);
HV_EXPORT const char* http_client_strerror(int errcode);
// timeout: s
HV_EXPORT int http_client_set_timeout(http_client_t* cli, int timeout);
// SSL/TLS
HV_EXPORT int http_client_set_ssl_ctx(http_client_t* cli, hssl_ctx_t ssl_ctx);
// hssl_ctx_new(opt) -> http_client_set_ssl_ctx
HV_EXPORT int http_client_new_ssl_ctx(http_client_t* cli, hssl_ctx_opt_t* opt);
// common headers
HV_EXPORT int http_client_clear_headers(http_client_t* cli);
HV_EXPORT int http_client_set_header(http_client_t* cli, const char* key, const char* value);
HV_EXPORT int http_client_del_header(http_client_t* cli, const char* key);
HV_EXPORT const char* http_client_get_header(http_client_t* cli, const char* key);
// http_proxy
HV_EXPORT int http_client_set_http_proxy(http_client_t* cli, const char* host, int port);
// https_proxy
HV_EXPORT int http_client_set_https_proxy(http_client_t* cli, const char* host, int port);
// no_proxy
HV_EXPORT int http_client_add_no_proxy(http_client_t* cli, const char* host);
// sync
HV_EXPORT int http_client_send(http_client_t* cli, HttpRequest* req, HttpResponse* resp);
// async
// Intern will start an EventLoopThread when http_client_send_async first called,
// http_client_del will destroy the thread.
HV_EXPORT int http_client_send_async(http_client_t* cli, HttpRequestPtr req, HttpResponseCallback resp_cb = NULL);
// top-level api
// http_client_new -> http_client_send -> http_client_del
HV_EXPORT int http_client_send(HttpRequest* req, HttpResponse* resp);
// http_client_send_async(&default_async_client, ...)
HV_EXPORT int http_client_send_async(HttpRequestPtr req, HttpResponseCallback resp_cb = NULL);
namespace hv {
class HttpClient {
public:
HttpClient(const char* host = NULL, int port = DEFAULT_HTTP_PORT, int https = 0) {
_client = http_client_new(host, port, https);
}
~HttpClient() {
if (_client) {
http_client_del(_client);
_client = NULL;
}
}
// timeout: s
int setTimeout(int timeout) {
return http_client_set_timeout(_client, timeout);
}
// SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx) {
return http_client_set_ssl_ctx(_client, ssl_ctx);
}
int newSslCtx(hssl_ctx_opt_t* opt) {
return http_client_new_ssl_ctx(_client, opt);
}
// headers
int clearHeaders() {
return http_client_clear_headers(_client);
}
int setHeader(const char* key, const char* value) {
return http_client_set_header(_client, key, value);
}
int delHeader(const char* key) {
return http_client_del_header(_client, key);
}
const char* getHeader(const char* key) {
return http_client_get_header(_client, key);
}
// http_proxy
int setHttpProxy(const char* host, int port) {
return http_client_set_http_proxy(_client, host, port);
}
// https_proxy
int setHttpsProxy(const char* host, int port) {
return http_client_set_https_proxy(_client, host, port);
}
// no_proxy
int addNoProxy(const char* host) {
return http_client_add_no_proxy(_client, host);
}
// sync
int send(HttpRequest* req, HttpResponse* resp) {
return http_client_send(_client, req, resp);
}
// async
int sendAsync(HttpRequestPtr req, HttpResponseCallback resp_cb = NULL) {
return http_client_send_async(_client, req, std::move(resp_cb));
}
// close
int close() {
return http_client_close(_client);
}
private:
http_client_t* _client;
};
}
#endif // HV_HTTP_CLIENT_H_

View File

@ -1,78 +0,0 @@
#ifndef HV_HTTP_CONTENT_H_
#define HV_HTTP_CONTENT_H_
#include "hexport.h"
#include "hstring.h"
// NOTE: WITHOUT_HTTP_CONTENT
// ndk-r10e no std::to_string and can't compile modern json.hpp
#ifndef WITHOUT_HTTP_CONTENT
#include "json.hpp" // https://github.com/nlohmann/json
#endif
BEGIN_NAMESPACE_HV
// QueryParams
using QueryParams = hv::KeyValue;
HV_EXPORT std::string dump_query_params(const QueryParams& query_params);
HV_EXPORT int parse_query_params(const char* query_string, QueryParams& query_params);
#ifndef WITHOUT_HTTP_CONTENT
/**************multipart/form-data*************************************
--boundary
Content-Disposition: form-data; name="user"
content
--boundary
Content-Disposition: form-data; name="avatar"; filename="user.jpg"
Content-Type: image/jpeg
content
--boundary--
***********************************************************************/
// FormData
struct FormData {
std::string filename;
std::string content;
FormData(const char* content = NULL, const char* filename = NULL) {
if (content) {
this->content = content;
}
if (filename) {
this->filename = filename;
}
}
template<typename T>
FormData(T num) {
content = hv::to_string(num);
}
};
// FormFile
struct FormFile : public FormData {
FormFile(const char* filename = NULL) {
if (filename) {
this->filename = filename;
}
}
};
// MultiPart
// name => FormData
typedef HV_MAP<std::string, FormData> MultiPart;
#define DEFAULT_MULTIPART_BOUNDARY "----WebKitFormBoundary7MA4YWxkTrZu0gW"
HV_EXPORT std::string dump_multipart(MultiPart& mp, const char* boundary = DEFAULT_MULTIPART_BOUNDARY);
HV_EXPORT int parse_multipart(const std::string& str, MultiPart& mp, const char* boundary);
// Json
using Json = nlohmann::json;
// using Json = nlohmann::ordered_json;
HV_EXPORT std::string dump_json(const hv::Json& json, int indent = -1);
HV_EXPORT int parse_json(const char* str, hv::Json& json, std::string& errmsg);
#endif
END_NAMESPACE_HV
#endif // HV_HTTP_CONTENT_H_

View File

@ -1,295 +0,0 @@
#ifndef HV_HTTP_DEF_H_
#define HV_HTTP_DEF_H_
#include "hexport.h"
#define DEFAULT_HTTP_PORT 80
#define DEFAULT_HTTPS_PORT 443
enum http_version { HTTP_V1 = 1, HTTP_V2 = 2 };
enum http_session_type { HTTP_CLIENT, HTTP_SERVER };
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
enum http_parser_state {
HP_START_REQ_OR_RES,
HP_MESSAGE_BEGIN,
HP_URL,
HP_STATUS,
HP_HEADER_FIELD,
HP_HEADER_VALUE,
HP_HEADERS_COMPLETE,
HP_CHUNK_HEADER,
HP_BODY,
HP_CHUNK_COMPLETE,
HP_MESSAGE_COMPLETE,
HP_ERROR
};
// http_status
// XX(num, name, string)
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
// HTTP_STATUS_##name
enum http_status {
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
HTTP_CUSTOM_STATUS
};
#define HTTP_STATUS_IS_REDIRECT(status) \
( \
(status) == HTTP_STATUS_MOVED_PERMANENTLY || \
(status) == HTTP_STATUS_FOUND || \
(status) == HTTP_STATUS_SEE_OTHER || \
(status) == HTTP_STATUS_TEMPORARY_REDIRECT || \
(status) == HTTP_STATUS_PERMANENT_REDIRECT \
)
// http_mehtod
// XX(num, name, string)
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \
// HTTP_##name
enum http_method {
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
HTTP_CUSTOM_METHOD
};
// MIME: https://www.iana.org/assignments/media-types/media-types.xhtml
// XX(name, mime, suffix)
#define MIME_TYPE_TEXT_MAP(XX) \
XX(TEXT_PLAIN, text/plain, txt) \
XX(TEXT_HTML, text/html, html) \
XX(TEXT_CSS, text/css, css) \
XX(TEXT_CSV, text/csv, csv) \
XX(TEXT_MARKDOWN, text/markdown, md) \
XX(TEXT_EVENT_STREAM, text/event-stream, sse) \
#define MIME_TYPE_APPLICATION_MAP(XX) \
XX(APPLICATION_JAVASCRIPT, application/javascript, js) \
XX(APPLICATION_JSON, application/json, json) \
XX(APPLICATION_XML, application/xml, xml) \
XX(APPLICATION_URLENCODED, application/x-www-form-urlencoded, kv) \
XX(APPLICATION_OCTET_STREAM,application/octet-stream, bin) \
XX(APPLICATION_ZIP, application/zip, zip) \
XX(APPLICATION_GZIP, application/gzip, gzip) \
XX(APPLICATION_7Z, application/x-7z-compressed, 7z) \
XX(APPLICATION_RAR, application/x-rar-compressed, rar) \
XX(APPLICATION_PDF, application/pdf, pdf) \
XX(APPLICATION_RTF, application/rtf, rtf) \
XX(APPLICATION_GRPC, application/grpc, grpc) \
XX(APPLICATION_WASM, application/wasm, wasm) \
XX(APPLICATION_JAR, application/java-archive, jar) \
XX(APPLICATION_XHTML, application/xhtml+xml, xhtml) \
XX(APPLICATION_ATOM, application/atom+xml, atom) \
XX(APPLICATION_RSS, application/rss+xml, rss) \
XX(APPLICATION_WORD, application/msword, doc) \
XX(APPLICATION_EXCEL, application/vnd.ms-excel, xls) \
XX(APPLICATION_PPT, application/vnd.ms-powerpoint, ppt) \
XX(APPLICATION_EOT, application/vnd.ms-fontobject, eot) \
XX(APPLICATION_M3U8, application/vnd.apple.mpegurl, m3u8) \
XX(APPLICATION_DOCX, application/vnd.openxmlformats-officedocument.wordprocessingml.document, docx) \
XX(APPLICATION_XLSX, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, xlsx) \
XX(APPLICATION_PPTX, application/vnd.openxmlformats-officedocument.presentationml.presentation, pptx) \
#define MIME_TYPE_MULTIPART_MAP(XX) \
XX(MULTIPART_FORM_DATA, multipart/form-data, mp) \
#define MIME_TYPE_IMAGE_MAP(XX) \
XX(IMAGE_JPEG, image/jpeg, jpg) \
XX(IMAGE_PNG, image/png, png) \
XX(IMAGE_GIF, image/gif, gif) \
XX(IMAGE_ICO, image/x-icon, ico) \
XX(IMAGE_BMP, image/x-ms-bmp, bmp) \
XX(IMAGE_SVG, image/svg+xml, svg) \
XX(IMAGE_TIFF, image/tiff, tiff) \
XX(IMAGE_WEBP, image/webp, webp) \
#define MIME_TYPE_VIDEO_MAP(XX) \
XX(VIDEO_MP4, video/mp4, mp4) \
XX(VIDEO_FLV, video/x-flv, flv) \
XX(VIDEO_M4V, video/x-m4v, m4v) \
XX(VIDEO_MNG, video/x-mng, mng) \
XX(VIDEO_TS, video/mp2t, ts) \
XX(VIDEO_MPEG, video/mpeg, mpeg) \
XX(VIDEO_WEBM, video/webm, webm) \
XX(VIDEO_MOV, video/quicktime, mov) \
XX(VIDEO_3GPP, video/3gpp, 3gpp) \
XX(VIDEO_AVI, video/x-msvideo, avi) \
XX(VIDEO_WMV, video/x-ms-wmv, wmv) \
XX(VIDEO_ASF, video/x-ms-asf, asf) \
#define MIME_TYPE_AUDIO_MAP(XX) \
XX(AUDIO_MP3, audio/mpeg, mp3) \
XX(AUDIO_OGG, audio/ogg, ogg) \
XX(AUDIO_M4A, audio/x-m4a, m4a) \
XX(AUDIO_AAC, audio/aac, aac) \
XX(AUDIO_PCMA, audio/PCMA, pcma) \
XX(AUDIO_OPUS, audio/opus, opus) \
#define MIME_TYPE_FONT_MAP(XX) \
XX(FONT_TTF, font/ttf, ttf) \
XX(FONT_OTF, font/otf, otf) \
XX(FONT_WOFF, font/woff, woff) \
XX(FONT_WOFF2, font/woff2, woff2) \
#define HTTP_CONTENT_TYPE_MAP(XX) \
MIME_TYPE_TEXT_MAP(XX) \
MIME_TYPE_APPLICATION_MAP(XX) \
MIME_TYPE_MULTIPART_MAP(XX) \
MIME_TYPE_IMAGE_MAP(XX) \
MIME_TYPE_VIDEO_MAP(XX) \
MIME_TYPE_AUDIO_MAP(XX) \
MIME_TYPE_FONT_MAP(XX) \
#define X_WWW_FORM_URLENCODED APPLICATION_URLENCODED // for compatibility
enum http_content_type {
#define XX(name, string, suffix) name,
CONTENT_TYPE_NONE = 0,
CONTENT_TYPE_TEXT = 100,
MIME_TYPE_TEXT_MAP(XX)
CONTENT_TYPE_APPLICATION = 200,
MIME_TYPE_APPLICATION_MAP(XX)
CONTENT_TYPE_MULTIPART = 300,
MIME_TYPE_MULTIPART_MAP(XX)
CONTENT_TYPE_IMAGE = 400,
MIME_TYPE_IMAGE_MAP(XX)
CONTENT_TYPE_VIDEO = 500,
MIME_TYPE_VIDEO_MAP(XX)
CONTENT_TYPE_AUDIO = 600,
MIME_TYPE_AUDIO_MAP(XX)
CONTENT_TYPE_FONT = 700,
MIME_TYPE_FONT_MAP(XX)
CONTENT_TYPE_UNDEFINED = 1000
#undef XX
};
BEGIN_EXTERN_C
HV_EXPORT const char* http_status_str(enum http_status status);
HV_EXPORT const char* http_method_str(enum http_method method);
HV_EXPORT const char* http_content_type_str(enum http_content_type type);
HV_EXPORT enum http_status http_status_enum(const char* str);
HV_EXPORT enum http_method http_method_enum(const char* str);
HV_EXPORT enum http_content_type http_content_type_enum(const char* str);
HV_EXPORT const char* http_content_type_suffix(enum http_content_type type);
HV_EXPORT const char* http_content_type_str_by_suffix(const char* suffix);
HV_EXPORT enum http_content_type http_content_type_enum_by_suffix(const char* suffix);
END_EXTERN_C
#endif // HV_HTTP_DEF_H_

View File

@ -1,41 +0,0 @@
#ifndef HV_URL_H_
#define HV_URL_H_
#include <string> // import std::string
#include "hexport.h"
class HV_EXPORT HUrl {
public:
static std::string escape(const std::string& str, const char* unescaped_chars = "");
static std::string unescape(const std::string& str);
HUrl() : port(0) {}
~HUrl() {}
void reset();
bool parse(const std::string& url);
const std::string& dump();
std::string url;
std::string scheme;
std::string username;
std::string password;
std::string host;
int port;
std::string path;
std::string query;
std::string fragment;
};
namespace hv {
HV_INLINE std::string escapeURL(const std::string& url) {
return HUrl::escape(url, ":/@?=&#+");
}
HV_EXPORT std::string escapeHTML(const std::string& str);
} // end namespace hv
#endif // HV_URL_H_

View File

@ -1,41 +0,0 @@
#ifndef HV_H_
#define HV_H_
/**
* @copyright 2018 HeWei, all rights reserved.
*/
// platform
#include "hconfig.h"
#include "hexport.h"
#include "hplatform.h"
// c
#include "hdef.h" // <stddef.h>
#include "hatomic.h"// <stdatomic.h>
#include "herr.h" // <errno.h>
#include "htime.h" // <time.h>
#include "hmath.h" // <math.h>
#include "hbase.h"
#include "hversion.h"
#include "hsysinfo.h"
#include "hproc.h"
#include "hthread.h"
#include "hmutex.h"
#include "hsocket.h"
#include "hlog.h"
#include "hbuf.h"
// cpp
#ifdef __cplusplus
#include "hmap.h" // <map>
#include "hstring.h" // <string>
#include "hfile.h"
#include "hpath.h"
#include "hdir.h"
#include "hurl.h"
#endif
#endif // HV_H_

View File

@ -1,34 +0,0 @@
#ifndef HV_VERSION_H_
#define HV_VERSION_H_
#include "hexport.h"
#include "hdef.h"
BEGIN_EXTERN_C
#define HV_VERSION_MAJOR 1
#define HV_VERSION_MINOR 3
#define HV_VERSION_PATCH 2
#define HV_VERSION_STRING STRINGIFY(HV_VERSION_MAJOR) "." \
STRINGIFY(HV_VERSION_MINOR) "." \
STRINGIFY(HV_VERSION_PATCH)
#define HV_VERSION_NUMBER ((HV_VERSION_MAJOR << 16) | (HV_VERSION_MINOR << 8) | HV_VERSION_PATCH)
HV_INLINE const char* hv_version() {
return HV_VERSION_STRING;
}
HV_EXPORT const char* hv_compile_version();
// 1.2.3.4 => 0x01020304
HV_EXPORT int version_atoi(const char* str);
// 0x01020304 => 1.2.3.4
HV_EXPORT void version_itoa(int hex, char* str);
END_EXTERN_C
#endif // HV_VERSION_H_

View File

@ -1,15 +0,0 @@
#ifndef HV_ICMP_H_
#define HV_ICMP_H_
#include "hexport.h"
BEGIN_EXTERN_C
// @param cnt: ping count
// @return: ok count
// @note: printd $CC -DPRINT_DEBUG
HV_EXPORT int ping(const char* host, int cnt DEFAULT(4));
END_EXTERN_C
#endif // HV_ICMP_H_

View File

@ -1,36 +0,0 @@
#ifndef HV_IFCONFIG_H_
#define HV_IFCONFIG_H_
#include <vector>
#include "hexport.h"
#ifdef _MSC_VER
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#endif
typedef struct ifconfig_s {
char name[128];
char ip[16];
char mask[16];
char broadcast[16];
char mac[20];
} ifconfig_t;
/*
* @test
std::vector<ifconfig_t> ifcs;
ifconfig(ifcs);
for (auto& item : ifcs) {
printf("%s\nip: %s\nmask: %s\nbroadcast: %s\nmac: %s\n\n",
item.name,
item.ip,
item.mask,
item.broadcast,
item.mac);
}
*/
HV_EXPORT int ifconfig(std::vector<ifconfig_t>& ifcs);
#endif // HV_IFCONFIG_H_

View File

@ -1,53 +0,0 @@
#ifndef HV_INI_PARSER_H_
#define HV_INI_PARSER_H_
#include <string>
#include <list>
#include "hexport.h"
#define DEFAULT_INI_COMMENT "#"
#define DEFAULT_INI_DELIM "="
// fwd
class IniNode;
class HV_EXPORT IniParser {
public:
IniParser();
~IniParser();
int LoadFromFile(const char* filepath);
int LoadFromMem(const char* data);
int Unload();
int Reload();
std::string DumpString();
int Save();
int SaveAs(const char* filepath);
std::list<std::string> GetSections();
std::list<std::string> GetKeys(const std::string& section = "");
std::string GetValue(const std::string& key, const std::string& section = "");
void SetValue(const std::string& key, const std::string& value, const std::string& section = "");
// T = [bool, int, float]
template<typename T>
T Get(const std::string& key, const std::string& section = "", T defvalue = 0);
// T = [bool, int, float]
template<typename T>
void Set(const std::string& key, const T& value, const std::string& section = "");
protected:
void DumpString(IniNode* pNode, std::string& str);
public:
std::string _comment;
std::string _delim;
std::string _filepath;
private:
IniNode* root_;
};
#endif // HV_INI_PARSER_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
#ifndef HV_MD5_H_
#define HV_MD5_H_
#include "hexport.h"
typedef struct {
unsigned int count[2];
unsigned int state[4];
unsigned char buffer[64];
} HV_MD5_CTX;
BEGIN_EXTERN_C
HV_EXPORT void HV_MD5Init(HV_MD5_CTX *ctx);
HV_EXPORT void HV_MD5Update(HV_MD5_CTX *ctx, unsigned char *input, unsigned int inputlen);
HV_EXPORT void HV_MD5Final(HV_MD5_CTX *ctx, unsigned char digest[16]);
HV_EXPORT void hv_md5(unsigned char* input, unsigned int inputlen, unsigned char digest[16]);
// NOTE: if outputlen > 32: output[32] = '\0'
HV_EXPORT void hv_md5_hex(unsigned char* input, unsigned int inputlen, char* output, unsigned int outputlen);
END_EXTERN_C
#endif // HV_MD5_H_

View File

@ -1,335 +0,0 @@
#ifndef HV_MQTT_CLIENT_H_
#define HV_MQTT_CLIENT_H_
#include "mqtt_protocol.h"
#include "hloop.h"
#include "hssl.h"
#include "hmutex.h"
#define DEFAULT_MQTT_KEEPALIVE 60 // s
typedef struct mqtt_client_s mqtt_client_t;
// @type mqtt_type_e
// @example examples/mqtt
typedef void (*mqtt_client_cb)(mqtt_client_t* cli, int type);
struct mqtt_client_s {
// connect: host:port
char host[256];
int port;
int connect_timeout; // ms
// reconnect
reconn_setting_t* reconn_setting;
// login: flags + keepalive + client_id + will + username + password
// flags
unsigned char protocol_version; // Default MQTT_PROTOCOL_V311
unsigned char clean_session: 1;
unsigned char ssl: 1; // Read Only
unsigned char alloced_ssl_ctx: 1; // intern
unsigned char connected : 1;
unsigned short keepalive;
int ping_cnt;
char client_id[64];
// will
mqtt_message_t* will;
// auth
char username[64];
char password[64];
// message
mqtt_head_t head;
int error; // for MQTT_TYPE_CONNACK
int mid; // for MQTT_TYPE_SUBACK, MQTT_TYPE_PUBACK
mqtt_message_t message; // for MQTT_TYPE_PUBLISH
// callback
mqtt_client_cb cb;
// userdata
void* userdata;
// privdata
hloop_t* loop;
hio_t* io;
htimer_t* reconn_timer;
// SSL/TLS
hssl_ctx_t ssl_ctx;
// thread-safe
hmutex_t mutex_;
};
BEGIN_EXTERN_C
// hloop_new -> malloc(mqtt_client_t)
HV_EXPORT mqtt_client_t* mqtt_client_new(hloop_t* loop DEFAULT(NULL));
// @see hloop_run
HV_EXPORT void mqtt_client_run (mqtt_client_t* cli);
// @see hloop_stop
HV_EXPORT void mqtt_client_stop(mqtt_client_t* cli);
// hloop_free -> free(mqtt_client_t)
HV_EXPORT void mqtt_client_free(mqtt_client_t* cli);
// id
HV_EXPORT void mqtt_client_set_id(mqtt_client_t* cli, const char* id);
// will
HV_EXPORT void mqtt_client_set_will(mqtt_client_t* cli,
mqtt_message_t* will);
// auth
HV_EXPORT void mqtt_client_set_auth(mqtt_client_t* cli,
const char* username, const char* password);
// callback
HV_EXPORT void mqtt_client_set_callback(mqtt_client_t* cli, mqtt_client_cb cb);
// userdata
HV_EXPORT void mqtt_client_set_userdata(mqtt_client_t* cli, void* userdata);
HV_EXPORT void* mqtt_client_get_userdata(mqtt_client_t* cli);
// error
HV_EXPORT int mqtt_client_get_last_error(mqtt_client_t* cli);
// SSL/TLS
HV_EXPORT int mqtt_client_set_ssl_ctx(mqtt_client_t* cli, hssl_ctx_t ssl_ctx);
// hssl_ctx_new(opt) -> mqtt_client_set_ssl_ctx
HV_EXPORT int mqtt_client_new_ssl_ctx(mqtt_client_t* cli, hssl_ctx_opt_t* opt);
// reconnect
HV_EXPORT int mqtt_client_set_reconnect(mqtt_client_t* cli,
reconn_setting_t* reconn);
HV_EXPORT int mqtt_client_reconnect(mqtt_client_t* cli);
// connect
// hio_create_socket -> hio_connect ->
// on_connect -> mqtt_client_login ->
// on_connack
HV_EXPORT void mqtt_client_set_connect_timeout(mqtt_client_t* cli, int ms);
HV_EXPORT int mqtt_client_connect(mqtt_client_t* cli,
const char* host,
int port DEFAULT(DEFAULT_MQTT_PORT),
int ssl DEFAULT(0));
HV_EXPORT bool mqtt_client_is_connected(mqtt_client_t* cli);
// disconnect
// @see hio_close
HV_EXPORT int mqtt_client_disconnect(mqtt_client_t* cli);
// publish
HV_EXPORT int mqtt_client_publish(mqtt_client_t* cli,
mqtt_message_t* msg);
// subscribe
HV_EXPORT int mqtt_client_subscribe(mqtt_client_t* cli,
const char* topic, int qos DEFAULT(0));
// unsubscribe
HV_EXPORT int mqtt_client_unsubscribe(mqtt_client_t* cli,
const char* topic);
END_EXTERN_C
#ifdef __cplusplus
#include <functional>
#include <map>
#include <mutex>
#include <string>
namespace hv {
// @usage examples/mqtt/mqtt_client_test.cpp
class MqttClient {
public:
mqtt_client_t* client;
// callbacks
typedef std::function<void(MqttClient*)> MqttCallback;
typedef std::function<void(MqttClient*, mqtt_message_t*)> MqttMessageCallback;
MqttCallback onConnect;
MqttCallback onClose;
MqttMessageCallback onMessage;
MqttClient(hloop_t* loop = NULL) {
client = mqtt_client_new(loop);
}
~MqttClient() {
if (client) {
mqtt_client_free(client);
client = NULL;
}
}
void run() {
mqtt_client_set_callback(client, on_mqtt);
mqtt_client_set_userdata(client, this);
mqtt_client_run(client);
}
void stop() {
mqtt_client_stop(client);
}
void setID(const char* id) {
mqtt_client_set_id(client, id);
}
void setWill(mqtt_message_t* will) {
mqtt_client_set_will(client, will);
}
void setAuth(const char* username, const char* password) {
mqtt_client_set_auth(client, username, password);
}
void setPingInterval(int sec) {
client->keepalive = sec;
}
int lastError() {
return mqtt_client_get_last_error(client);
}
// SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx) {
return mqtt_client_set_ssl_ctx(client, ssl_ctx);
}
int newSslCtx(hssl_ctx_opt_t* opt) {
return mqtt_client_new_ssl_ctx(client, opt);
}
void setReconnect(reconn_setting_t* reconn) {
mqtt_client_set_reconnect(client, reconn);
}
void setConnectTimeout(int ms) {
mqtt_client_set_connect_timeout(client, ms);
}
int connect(const char* host, int port = DEFAULT_MQTT_PORT, int ssl = 0) {
return mqtt_client_connect(client, host, port, ssl);
}
int reconnect() {
return mqtt_client_reconnect(client);
}
int disconnect() {
return mqtt_client_disconnect(client);
}
bool isConnected() {
return mqtt_client_is_connected(client);
}
int publish(mqtt_message_t* msg, MqttCallback ack_cb = NULL) {
int mid = mqtt_client_publish(client, msg);
if (msg->qos > 0 && mid >= 0 && ack_cb) {
setAckCallback(mid, ack_cb);
}
return mid;
}
int publish(const std::string& topic, const std::string& payload, int qos = 0, int retain = 0, MqttCallback ack_cb = NULL) {
mqtt_message_t msg;
memset(&msg, 0, sizeof(msg));
msg.topic_len = topic.size();
msg.topic = topic.c_str();
msg.payload_len = payload.size();
msg.payload = payload.c_str();
msg.qos = qos;
msg.retain = retain;
return publish(&msg, ack_cb);
}
int subscribe(const char* topic, int qos = 0, MqttCallback ack_cb = NULL) {
int mid = mqtt_client_subscribe(client, topic, qos);
if (qos > 0 && mid >= 0 && ack_cb) {
setAckCallback(mid, ack_cb);
}
return mid;
}
int unsubscribe(const char* topic, MqttCallback ack_cb = NULL) {
int mid = mqtt_client_unsubscribe(client, topic);
if (mid >= 0 && ack_cb) {
setAckCallback(mid, ack_cb);
}
return mid;
}
protected:
void setAckCallback(int mid, MqttCallback cb) {
ack_cbs_mutex.lock();
ack_cbs[mid] = std::move(cb);
ack_cbs_mutex.unlock();
}
void invokeAckCallback(int mid) {
MqttCallback ack_cb = NULL;
ack_cbs_mutex.lock();
auto iter = ack_cbs.find(mid);
if (iter != ack_cbs.end()) {
ack_cb = std::move(iter->second);
ack_cbs.erase(iter);
}
ack_cbs_mutex.unlock();
if (ack_cb) ack_cb(this);
}
static void on_mqtt(mqtt_client_t* cli, int type) {
MqttClient* client = (MqttClient*)mqtt_client_get_userdata(cli);
// printf("on_mqtt type=%d\n", type);
switch(type) {
case MQTT_TYPE_CONNECT:
// printf("mqtt connected!\n");
break;
case MQTT_TYPE_DISCONNECT:
// printf("mqtt disconnected!\n");
if (client->onClose) {
client->onClose(client);
}
break;
case MQTT_TYPE_CONNACK:
// printf("mqtt connack!\n");
if (client->onConnect) {
client->onConnect(client);
}
break;
case MQTT_TYPE_PUBLISH:
if (client->onMessage) {
client->onMessage(client, &cli->message);
}
break;
case MQTT_TYPE_PUBACK: /* qos = 1 */
// printf("mqtt puback mid=%d\n", cli->mid);
client->invokeAckCallback(cli->mid);
break;
case MQTT_TYPE_PUBREC: /* qos = 2 */
// printf("mqtt pubrec mid=%d\n", cli->mid);
// wait MQTT_TYPE_PUBCOMP
break;
case MQTT_TYPE_PUBCOMP: /* qos = 2 */
// printf("mqtt pubcomp mid=%d\n", cli->mid);
client->invokeAckCallback(cli->mid);
break;
case MQTT_TYPE_SUBACK:
// printf("mqtt suback mid=%d\n", cli->mid);
client->invokeAckCallback(cli->mid);
break;
case MQTT_TYPE_UNSUBACK:
// printf("mqtt unsuback mid=%d\n", cli->mid);
client->invokeAckCallback(cli->mid);
break;
default:
break;
}
}
private:
// mid => ack callback
std::map<int, MqttCallback> ack_cbs;
std::mutex ack_cbs_mutex;
};
}
#endif
#endif // HV_MQTT_CLIENT_H_

View File

@ -1,82 +0,0 @@
#ifndef HV_MQTT_PROTOCOL_H_
#define HV_MQTT_PROTOCOL_H_
#include "hexport.h"
#define DEFAULT_MQTT_PORT 1883
#define MQTT_PROTOCOL_V31 3
#define MQTT_PROTOCOL_V311 4
#define MQTT_PROTOCOL_V5 5 // Not yet supproted
#define MQTT_PROTOCOL_NAME "MQTT"
#define MQTT_PROTOCOL_NAME_v31 "MQIsdp"
/*
* connect flags
* 0 1 2 3-4 5 6 7
* reserved clean_session has_will will_qos will_retain has_password has_username
*/
#define MQTT_CONN_CLEAN_SESSION 0x02
#define MQTT_CONN_HAS_WILL 0x04
#define MQTT_CONN_WILL_RETAIN 0x20
#define MQTT_CONN_HAS_PASSWORD 0x40
#define MQTT_CONN_HAS_USERNAME 0x80
typedef enum {
MQTT_TYPE_CONNECT = 1,
MQTT_TYPE_CONNACK = 2,
MQTT_TYPE_PUBLISH = 3,
MQTT_TYPE_PUBACK = 4,
MQTT_TYPE_PUBREC = 5,
MQTT_TYPE_PUBREL = 6,
MQTT_TYPE_PUBCOMP = 7,
MQTT_TYPE_SUBSCRIBE = 8,
MQTT_TYPE_SUBACK = 9,
MQTT_TYPE_UNSUBSCRIBE = 10,
MQTT_TYPE_UNSUBACK = 11,
MQTT_TYPE_PINGREQ = 12,
MQTT_TYPE_PINGRESP = 13,
MQTT_TYPE_DISCONNECT = 14,
} mqtt_type_e;
typedef enum {
MQTT_CONNACK_ACCEPTED = 0,
MQTT_CONNACK_REFUSED_PROTOCOL_VERSION = 1,
MQTT_CONNACK_REFUSED_IDENTIFIER_REJECTED = 2,
MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE = 3,
MQTT_CONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4,
MQTT_CONNACK_REFUSED_NOT_AUTHORIZED = 5,
} mqtt_connack_e;
typedef struct mqtt_head_s {
unsigned char type: 4;
unsigned char dup: 1;
unsigned char qos: 2;
unsigned char retain: 1;
unsigned int length;
} mqtt_head_t;
typedef struct mqtt_message_s {
unsigned int topic_len;
const char* topic;
unsigned int payload_len;
const char* payload;
unsigned char qos;
unsigned char retain;
} mqtt_message_t;
BEGIN_EXTERN_C
#define DEFAULT_MQTT_PACKAGE_MAX_LENGTH (1 << 28) // 256M
HV_INLINE int mqtt_estimate_length(mqtt_head_t* head) {
// 28 bits => 4*7 bits varint
return 1 + 4 + head->length;
}
HV_EXPORT int mqtt_head_pack(mqtt_head_t* head, unsigned char buf[]);
HV_EXPORT int mqtt_head_unpack(mqtt_head_t* head, const unsigned char* buf, int len);
END_EXTERN_C
#endif // HV_MQTT_PROTOCOL_H_

View File

@ -1,44 +0,0 @@
#ifndef HV_NLOG_H_
#define HV_NLOG_H_
// nlog: extend hlog use hloop
/* you can recv log by:
* Windows: telnet ip port
* Linux: nc ip port
*/
/*
* @see examples/hloop_test.c
#include "hlog.h"
#include "nlog.h"
void timer_write_log(htimer_t* timer) {
static int cnt = 0;
hlogi("[%d] Do you recv me?", ++cnt);
}
int main() {
hloop_t* loop = hloop_new(0);
hlog_set_handler(network_logger);
nlog_listen(loop, DEFAULT_LOG_PORT);
htimer_add(loop, timer_write_log, 1000, INFINITE);
hloop_run(loop);
hloop_free(&loop);
}
*/
#include "hexport.h"
#include "hloop.h"
#define DEFAULT_LOG_PORT 10514
BEGIN_EXTERN_C
HV_EXPORT void network_logger(int loglevel, const char* buf, int len);
HV_EXPORT hio_t* nlog_listen(hloop_t* loop, int port);
END_EXTERN_C
#endif // HV_NLOG_H_

View File

@ -1,233 +0,0 @@
#ifndef HV_REQUESTS_H_
#define HV_REQUESTS_H_
/*
* Inspired by python requests
*
* @code
#include "requests.h"
int main() {
auto resp = requests::get("http://127.0.0.1:8080/ping");
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%d %s\r\n", resp->status_code, resp->status_message());
printf("%s\n", resp->body.c_str());
}
resp = requests::post("http://127.0.0.1:8080/echo", "hello,world!");
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%d %s\r\n", resp->status_code, resp->status_message());
printf("%s\n", resp->body.c_str());
}
return 0;
}
**/
#include <memory>
#include "HttpClient.h"
namespace requests {
typedef HttpRequestPtr Request;
typedef HttpResponsePtr Response;
typedef HttpResponseCallback ResponseCallback;
HV_INLINE Response request(Request req) {
auto resp = std::make_shared<HttpResponse>();
int ret = http_client_send(req.get(), resp.get());
return ret ? NULL : resp;
}
HV_INLINE Response request(http_method method, const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders) {
auto req = std::make_shared<HttpRequest>();
req->method = method;
req->url = url;
if (&body != &NoBody) {
req->body = body;
}
if (&headers != &DefaultHeaders) {
req->headers = headers;
}
return request(req);
}
HV_INLINE Response head(const char* url, const http_headers& headers = DefaultHeaders) {
return request(HTTP_HEAD, url, NoBody, headers);
}
HV_INLINE Response get(const char* url, const http_headers& headers = DefaultHeaders) {
return request(HTTP_GET, url, NoBody, headers);
}
HV_INLINE Response post(const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders) {
return request(HTTP_POST, url, body, headers);
}
HV_INLINE Response put(const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders) {
return request(HTTP_PUT, url, body, headers);
}
HV_INLINE Response patch(const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders) {
return request(HTTP_PATCH, url, body, headers);
}
// delete is c++ keyword, we have to replace delete with Delete.
HV_INLINE Response Delete(const char* url, const http_headers& headers = DefaultHeaders) {
return request(HTTP_DELETE, url, NoBody, headers);
}
HV_INLINE int async(Request req, ResponseCallback resp_cb) {
return http_client_send_async(req, std::move(resp_cb));
}
// Sample codes for uploading and downloading files
HV_INLINE Response uploadFile(const char* url, const char* filepath, http_method method = HTTP_POST, const http_headers& headers = DefaultHeaders) {
auto req = std::make_shared<HttpRequest>();
req->method = method;
req->url = url;
req->timeout = 600; // 10min
if (req->File(filepath) != 200) return NULL;
if (&headers != &DefaultHeaders) {
req->headers = headers;
}
return request(req);
}
#ifndef WITHOUT_HTTP_CONTENT
HV_INLINE Response uploadFormFile(const char* url, const char* name, const char* filepath, std::map<std::string, std::string>& params = hv::empty_map, http_method method = HTTP_POST, const http_headers& headers = DefaultHeaders) {
auto req = std::make_shared<HttpRequest>();
req->method = method;
req->url = url;
req->timeout = 600; // 10min
req->content_type = MULTIPART_FORM_DATA;
req->SetFormFile(name, filepath);
for (auto& param : params) {
req->SetFormData(param.first.c_str(), param.second);
}
if (&headers != &DefaultHeaders) {
req->headers = headers;
}
return request(req);
}
#endif
typedef std::function<void(size_t sended_bytes, size_t total_bytes)> upload_progress_cb;
HV_INLINE Response uploadLargeFile(const char* url, const char* filepath, upload_progress_cb progress_cb = NULL, http_method method = HTTP_POST, const http_headers& headers = DefaultHeaders) {
// open file
HFile file;
int ret = file.open(filepath, "rb");
if (ret != 0) {
return NULL;
}
hv::HttpClient cli;
auto req = std::make_shared<HttpRequest>();
req->method = method;
req->url = url;
req->timeout = 3600; // 1h
if (&headers != &DefaultHeaders) {
req->headers = headers;
}
// connect
req->ParseUrl();
int connfd = cli.connect(req->host.c_str(), req->port, req->IsHttps(), req->connect_timeout);
if (connfd < 0) {
return NULL;
}
// send header
size_t total_bytes = file.size(filepath);
req->SetHeader("Content-Length", hv::to_string(total_bytes));
ret = cli.sendHeader(req.get());
if (ret != 0) {
return NULL;
}
// send file
size_t sended_bytes = 0;
char filebuf[40960]; // 40K
int filebuflen = sizeof(filebuf);
int nread = 0, nsend = 0;
while (sended_bytes < total_bytes) {
nread = file.read(filebuf, filebuflen);
if (nread <= 0) {
return NULL;
}
nsend = cli.sendData(filebuf, nread);
if (nsend != nread) {
return NULL;
}
sended_bytes += nsend;
if (progress_cb) {
progress_cb(sended_bytes, total_bytes);
}
}
// recv response
auto resp = std::make_shared<HttpResponse>();
ret = cli.recvResponse(resp.get());
if (ret != 0) {
return NULL;
}
return resp;
}
// see examples/wget.cpp
typedef std::function<void(size_t received_bytes, size_t total_bytes)> download_progress_cb;
HV_INLINE size_t downloadFile(const char* url, const char* filepath, download_progress_cb progress_cb = NULL) {
// open file
std::string filepath_download(filepath);
filepath_download += ".download";
HFile file;
int ret = file.open(filepath_download.c_str(), "wb");
if (ret != 0) {
return 0;
}
// download
auto req = std::make_shared<HttpRequest>();
req->method = HTTP_GET;
req->url = url;
req->timeout = 3600; // 1h
size_t content_length = 0;
size_t received_bytes = 0;
req->http_cb = [&file, &content_length, &received_bytes, &progress_cb]
(HttpMessage* resp, http_parser_state state, const char* data, size_t size) {
if (!resp->headers["Location"].empty()) return;
if (state == HP_HEADERS_COMPLETE) {
content_length = hv::from_string<size_t>(resp->GetHeader("Content-Length"));
} else if (state == HP_BODY) {
if (data && size) {
// write file
file.write(data, size);
received_bytes += size;
if (progress_cb) {
progress_cb(received_bytes, content_length);
}
}
}
};
auto resp = request(req);
file.close();
if (resp == NULL || resp->status_code != 200) {
return 0;
}
// check filesize
if (content_length != 0 && hv_filesize(filepath_download.c_str()) != content_length) {
remove(filepath_download.c_str());
return 0;
}
rename(filepath_download.c_str(), filepath);
return hv_filesize(filepath);
}
}
#endif // HV_REQUESTS_H_

View File

@ -1,55 +0,0 @@
#ifndef HV_SHA1_H_
#define HV_SHA1_H_
/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
*/
/* for uint32_t */
#include <stdint.h>
#include "hexport.h"
typedef struct {
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} HV_SHA1_CTX;
BEGIN_EXTERN_C
HV_EXPORT void HV_SHA1Transform(
uint32_t state[5],
const unsigned char buffer[64]
);
HV_EXPORT void HV_SHA1Init(
HV_SHA1_CTX * context
);
HV_EXPORT void HV_SHA1Update(
HV_SHA1_CTX * context,
const unsigned char *data,
uint32_t len
);
HV_EXPORT void HV_SHA1Final(
unsigned char digest[20],
HV_SHA1_CTX * context
);
HV_EXPORT void HV_SHA1(
char *hash_out,
const char *str,
uint32_t len);
HV_EXPORT void hv_sha1(unsigned char* input, uint32_t inputlen, unsigned char digest[20]);
// NOTE: if outputlen > 40: output[40] = '\0'
HV_EXPORT void hv_sha1_hex(unsigned char* input, uint32_t inputlen, char* output, uint32_t outputlen);
END_EXTERN_C
#endif // HV_SHA1_H_

View File

@ -1,36 +0,0 @@
#ifndef HV_SINGLETON_H_
#define HV_SINGLETON_H_
#include <mutex>
#define DISABLE_COPY(Class) \
Class(const Class&) = delete; \
Class& operator=(const Class&) = delete;
#define SINGLETON_DECL(Class) \
public: \
static Class* instance(); \
static void exitInstance(); \
private: \
DISABLE_COPY(Class) \
static Class* s_pInstance; \
static std::once_flag s_initFlag; \
static std::mutex s_mutex;
#define SINGLETON_IMPL(Class) \
Class* Class::s_pInstance = NULL; \
std::once_flag Class::s_initFlag; \
std::mutex Class::s_mutex; \
Class* Class::instance() { \
std::call_once(s_initFlag, []() {s_pInstance = new Class;}); \
return s_pInstance; \
} \
void Class::exitInstance() { \
std::lock_guard<std::mutex> lock(s_mutex); \
if (s_pInstance) { \
delete s_pInstance; \
s_pInstance = nullptr; \
} \
}
#endif // HV_SINGLETON_H_

View File

@ -1,74 +0,0 @@
#ifndef HV_SMTP_H_
#define HV_SMTP_H_
#include "hexport.h"
#define SMTP_PORT 25
#define SMTPS_PORT 465
#define SMTP_EOB "\r\n.\r\n"
#define SMTP_EOB_LEN 5
// smtp_command
// XX(name, string)
#define SMTP_COMMAND_MAP(XX)\
XX(HELO, HELO) \
XX(EHLO, EHLO) \
XX(AUTH, AUTH) \
XX(MAIL, MAIL FROM:) \
XX(RCPT, RCPT TO:) \
XX(DATA, DATA) \
XX(QUIT, QUIT) \
enum smtp_command {
#define XX(name, string) SMTP_##name,
SMTP_COMMAND_MAP(XX)
#undef XX
};
// smtp_status
// XXX(code, name, string)
#define SMTP_STATUS_MAP(XXX) \
XXX(220, READY, Ready) \
XXX(221, BYE, Bye) \
XXX(235, AUTH_SUCCESS, Authentication success) \
XXX(250, OK, OK) \
XXX(334, AUTH, Auth input) \
XXX(354, DATA, End with <CR><LF>.<CR><LF>) \
XXX(500, BAD_SYNTAX, Bad syntax) \
XXX(502, NOT_IMPLEMENTED,Command not implemented) \
XXX(503, BAD_SEQUENCE, Bad sequence of commands) \
XXX(504, UNRECOGNIZED_AUTH_TYPE, Unrecognized authentication type) \
XXX(535, AUTH_FAILED, Authentication failed) \
XXX(553, ERR_MAIL, Mailbox name not allowed) \
XXX(554, ERR_DATA, Transaction failed) \
enum smtp_status {
#define XXX(code, name, string) SMTP_STATUS_##name = code,
SMTP_STATUS_MAP(XXX)
#undef XXX
};
typedef struct mail_s {
char* from;
char* to;
char* subject;
char* body;
} mail_t;
BEGIN_EXTERN_C
HV_EXPORT const char* smtp_command_str(enum smtp_command cmd);
HV_EXPORT const char* smtp_status_str(enum smtp_status status);
// cmd param\r\n
HV_EXPORT int smtp_build_command(enum smtp_command cmd, const char* param, char* buf, int buflen);
// status_code status_message\r\n
HV_EXPORT int sendmail(const char* smtp_server,
const char* username,
const char* password,
mail_t* mail);
END_EXTERN_C
#endif // HV_SMTP_H_

View File

@ -1,89 +0,0 @@
#ifndef HV_WS_DEF_H_
#define HV_WS_DEF_H_
#include "hexport.h"
#include <stdbool.h>
#include <stdlib.h> // import rand
#define SEC_WEBSOCKET_VERSION "Sec-WebSocket-Version"
#define SEC_WEBSOCKET_KEY "Sec-WebSocket-Key"
#define SEC_WEBSOCKET_ACCEPT "Sec-WebSocket-Accept"
#define SEC_WEBSOCKET_PROTOCOL "Sec-WebSocket-Protocol"
#define SEC_WEBSOCKET_EXTENSIONS "Sec-WebSocket-Extensions"
#define WS_SERVER_MIN_FRAME_SIZE 2
// 1000 1001 0000 0000
#define WS_SERVER_PING_FRAME "\211\0"
// 1000 1010 0000 0000
#define WS_SERVER_PONG_FRAME "\212\0"
#define WS_CLIENT_MIN_FRAME_SIZE 6
// 1000 1001 1000 0000
#define WS_CLIENT_PING_FRAME "\211\200WSWS"
// 1000 1010 1000 0000
#define WS_CLIENT_PONG_FRAME "\212\200WSWS"
enum ws_session_type {
WS_CLIENT,
WS_SERVER,
};
enum ws_opcode {
WS_OPCODE_CONTINUE = 0x0,
WS_OPCODE_TEXT = 0x1,
WS_OPCODE_BINARY = 0x2,
WS_OPCODE_CLOSE = 0x8,
WS_OPCODE_PING = 0x9,
WS_OPCODE_PONG = 0xA,
};
BEGIN_EXTERN_C
// Sec-WebSocket-Key => Sec-WebSocket-Accept
HV_EXPORT void ws_encode_key(const char* key, char accept[]);
// fix-header[2] + var-length[2/8] + mask[4] + data[data_len]
HV_EXPORT int ws_calc_frame_size(int data_len, bool has_mask DEFAULT(false));
HV_EXPORT int ws_build_frame(
char* out,
const char* data,
int data_len,
const char mask[4],
bool has_mask DEFAULT(false),
enum ws_opcode opcode DEFAULT(WS_OPCODE_TEXT),
bool fin DEFAULT(true));
HV_INLINE int ws_client_build_frame(
char* out,
const char* data,
int data_len,
/* const char mask[4] */
/* bool has_mask = true */
enum ws_opcode opcode DEFAULT(WS_OPCODE_TEXT),
bool fin DEFAULT(true)) {
char mask[4] = {0};
int i = 0;
int imask = rand();
for (i = 0; i < 4; i++) {
mask[i] = (imask >> (8 * i)) & 0xff;
}
return ws_build_frame(out, data, data_len, mask, true, opcode, fin);
}
HV_INLINE int ws_server_build_frame(
char* out,
const char* data,
int data_len,
/* const char mask[4] */
/* bool has_mask = false */
enum ws_opcode opcode DEFAULT(WS_OPCODE_TEXT),
bool fin DEFAULT(true)) {
char mask[4] = {0};
return ws_build_frame(out, data, data_len, mask, false, opcode, fin);
}
END_EXTERN_C
#endif // HV_WS_DEF_H_