remove hv
parent
14c7af02bf
commit
bac55f5902
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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>
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
25447
sdk/include/hv/json.hpp
25447
sdk/include/hv/json.hpp
File diff suppressed because it is too large
Load Diff
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
Loading…
Reference in New Issue