Compare commits
	
		
			2 Commits 
		
	
	
		
			bac55f5902
			...
			7eebd994ec
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								 | 
						7eebd994ec | |
| 
							
							
								 | 
						3fc451546d | 
| 
						 | 
					@ -3,3 +3,5 @@
 | 
				
			||||||
/appRelease/test.release
 | 
					/appRelease/test.release
 | 
				
			||||||
/sdk/include/SQLiteCpp
 | 
					/sdk/include/SQLiteCpp
 | 
				
			||||||
/appRelease/mysql/mysql-arm.tar
 | 
					/appRelease/mysql/mysql-arm.tar
 | 
				
			||||||
 | 
					/applications/ems_datahubs/.vs
 | 
				
			||||||
 | 
					/applications/ems_datahubs/obj
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -223,3 +223,110 @@ bool OpDatabase::queryUser(const std::string& user_id, const std::string& passwd
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool OpDatabase::queryInnerDevice(int dev_id, std::string& jsonResult)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						try
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
							// 数据库连接配置
 | 
				
			||||||
 | 
							std::string server = "tcp://127.0.0.1:3306";
 | 
				
			||||||
 | 
							std::string dbuser = "root";
 | 
				
			||||||
 | 
							std::string password = "Hj57471000";
 | 
				
			||||||
 | 
							std::string database = "hjems";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 创建连接
 | 
				
			||||||
 | 
							sql::mysql::MySQL_Driver* driver;
 | 
				
			||||||
 | 
							driver = sql::mysql::get_mysql_driver_instance();
 | 
				
			||||||
 | 
							//m_pDbConnection.reset(driver->connect(server, dbuser, password));
 | 
				
			||||||
 | 
							sql::Connection* pDbConnection = driver->connect(server, dbuser, password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if( !pDbConnection )
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								hloge("Failed to connect to database.");
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 设置为使用指定数据库
 | 
				
			||||||
 | 
							pDbConnection->setSchema(database);
 | 
				
			||||||
 | 
							hloge("%s : %s", user_id.c_str(), passwd_md5.c_str());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							// 准备SQL查询语句
 | 
				
			||||||
 | 
							std::string sql = "SELECT uid,uname, upasswd,usalt,email,mobile1,mobile2,memo FROM tbl_user WHERE uid = ?";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 创建预编译的prepared statement
 | 
				
			||||||
 | 
							std::unique_ptr<sql::PreparedStatement> pstmt(m_pDbConnection->prepareStatement(sql));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 绑定参数
 | 
				
			||||||
 | 
							pstmt->setString(1, user_id); // 替换为你要查询的用户名
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 执行查询
 | 
				
			||||||
 | 
							std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool ret = false;
 | 
				
			||||||
 | 
							OpenJson json;
 | 
				
			||||||
 | 
							auto& nodeRoot = json["user"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 处理结果集
 | 
				
			||||||
 | 
							while( res->next() )
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								auto& node = nodeRoot[i++];
 | 
				
			||||||
 | 
								node["valid"] = 0;
 | 
				
			||||||
 | 
								node["uid"] = res->getString("uid");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 假设username和password都是字符串类型
 | 
				
			||||||
 | 
								std::string pass = res->getString("upasswd");
 | 
				
			||||||
 | 
								std::string salt = res->getString("usalt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								//计算passwd和salt之间的关系
 | 
				
			||||||
 | 
								//!passwd=md5(passwd_md5+salt+salt)
 | 
				
			||||||
 | 
								// passwd_md5 是大写的,需要确保一下
 | 
				
			||||||
 | 
								std::string tmp2(passwd_md5);
 | 
				
			||||||
 | 
								std::transform(tmp2.begin(), tmp2.end(), tmp2.begin(),
 | 
				
			||||||
 | 
									[](unsigned char c)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										return std::toupper(c);
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								std::string tmp = tmp2 + salt + salt;
 | 
				
			||||||
 | 
								std::string smd5 = CalculateMD5(tmp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if( pass == smd5 )
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									node["valid"] = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									node["uname"] = res->getString("uname");
 | 
				
			||||||
 | 
									node["email"] = res->getString("email");
 | 
				
			||||||
 | 
									node["mobile1"] = res->getString("mobile1");
 | 
				
			||||||
 | 
									node["mobile2"] = res->getString("mobile2");
 | 
				
			||||||
 | 
									node["memo"] = res->getString("memo");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ret = true;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									hloge("upass=[%s],calc=[%s],src=[%s]", pass.c_str(), smd5.c_str(), tmp.c_str());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							jsonResult = json.encode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//pDbConnection->close();
 | 
				
			||||||
 | 
							//delete pDbConnection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						catch( sql::SQLException& e )
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::ostringstream ss;
 | 
				
			||||||
 | 
							ss << "SQLException: " << e.what();
 | 
				
			||||||
 | 
							ss << " (MySQL error code: " << e.getErrorCode();
 | 
				
			||||||
 | 
							ss << ", SQLState: " << e.getSQLState() << " )";
 | 
				
			||||||
 | 
							hloge("Failed to connect to database: %s", ss.str().c_str());
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,9 @@ public:
 | 
				
			||||||
	void InsertMessage(const std::string& ts, const std::string& msg_type, const std::string& fsu, const std::string& content, int topic, int dev_id);
 | 
						void InsertMessage(const std::string& ts, const std::string& msg_type, const std::string& fsu, const std::string& content, int topic, int dev_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool queryUser(const std::string& user_id, const std::string& passwd_md5, std::string& jsonResult);
 | 
						bool queryUser(const std::string& user_id, const std::string& passwd_md5, std::string& jsonResult);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//查询设备,dev_id=0则返回全部
 | 
				
			||||||
 | 
						bool queryInnerDevice(int dev_id, std::string& jsonResult);
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
	//std::unique_ptr<sql::Connection> m_pDbConnection;
 | 
						//std::unique_ptr<sql::Connection> m_pDbConnection;
 | 
				
			||||||
	sql::Connection* m_pDbConnection;
 | 
						sql::Connection* m_pDbConnection;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,254 @@
 | 
				
			||||||
 | 
					INSTALLATION INSTRUCTIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These instructions refer to the package you are installing as
 | 
				
			||||||
 | 
					some-package.tar.gz or some-package.zip. The .zip file is intended for use
 | 
				
			||||||
 | 
					on Windows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The directory you choose for the installation will be referred to as
 | 
				
			||||||
 | 
					your-install-dir.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note to Qt Visual Studio Integration users: In the instructions below,
 | 
				
			||||||
 | 
					instead of building from command line with nmake, you can use the menu
 | 
				
			||||||
 | 
					command 'Qt->Open Solution from .pro file' on the .pro files in the
 | 
				
			||||||
 | 
					example and plugin directories, and then build from within Visual
 | 
				
			||||||
 | 
					Studio.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unpacking and installation
 | 
				
			||||||
 | 
					--------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Unpacking the archive (if you have not done so already).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    On Unix and Mac OS X (in a terminal window):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cd your-install-dir
 | 
				
			||||||
 | 
						gunzip some-package.tar.gz
 | 
				
			||||||
 | 
						tar xvf some-package.tar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This creates the subdirectory some-package containing the files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    On Windows:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Unpack the .zip archive by right-clicking it in explorer and
 | 
				
			||||||
 | 
					    choosing "Extract All...". If your version of Windows does not
 | 
				
			||||||
 | 
					    have zip support, you can use the infozip tools available
 | 
				
			||||||
 | 
					    from www.info-zip.org.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If you are using the infozip tools (in a command prompt window):
 | 
				
			||||||
 | 
						cd your-install-dir
 | 
				
			||||||
 | 
						unzip some-package.zip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.  Configuring the package.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The configure script is called "configure" on unix/mac and
 | 
				
			||||||
 | 
					    "configure.bat" on Windows. It should be run from a command line
 | 
				
			||||||
 | 
					    after cd'ing to the package directory. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    You can choose whether you want to use the component by including
 | 
				
			||||||
 | 
					    its source code directly into your project, or build the component
 | 
				
			||||||
 | 
					    as a dynamic shared library (DLL) that is loaded into the
 | 
				
			||||||
 | 
					    application at run-time. The latter may be preferable for
 | 
				
			||||||
 | 
					    technical or licensing (LGPL) reasons. If you want to build a DLL,
 | 
				
			||||||
 | 
					    run the configure script with the argument "-library". Also see
 | 
				
			||||||
 | 
					    the note about usage below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (Components that are Qt plugins, e.g. styles and image formats,
 | 
				
			||||||
 | 
					    are by default built as a plugin DLL.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The configure script will prompt you in some cases for further
 | 
				
			||||||
 | 
					    information. Answer these questions and carefully read the license text
 | 
				
			||||||
 | 
					    before accepting the license conditions. The package cannot be used if
 | 
				
			||||||
 | 
					    you do not accept the license conditions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3.  Building the component and examples (when required).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If a DLL is to be built, or if you would like to build the
 | 
				
			||||||
 | 
					    examples, next give the commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        qmake
 | 
				
			||||||
 | 
					        make          [or nmake if your are using Microsoft Visual C++]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The example program(s) can be found in the directory called
 | 
				
			||||||
 | 
					    "examples" or "example".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Components that are Qt plugins, e.g. styles and image formats, are
 | 
				
			||||||
 | 
					    ready to be used as soon as they are built, so the rest of this
 | 
				
			||||||
 | 
					    installation instruction can be skipped.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					4.  Building the Qt Designer plugin (optional).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Some of the widget components are provided with plugins for Qt
 | 
				
			||||||
 | 
					    Designer. To build and install the plugin, cd into the
 | 
				
			||||||
 | 
					    some-package/plugin directory and give the commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qmake
 | 
				
			||||||
 | 
						make           [or nmake if your are using Microsoft Visual C++]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Restart Qt Designer to make it load the new widget plugin.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Note: If you are using the built-in Qt Designer from the Qt Visual
 | 
				
			||||||
 | 
					    Studio Integration, you will need to manually copy the plugin DLL
 | 
				
			||||||
 | 
					    file, i.e. copy
 | 
				
			||||||
 | 
						%QTDIR%\plugins\designer\some-component.dll 
 | 
				
			||||||
 | 
					    to the Qt Visual Studio Integration plugin path, typically:
 | 
				
			||||||
 | 
						C:\Program Files\Trolltech\Qt VS Integration\plugins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Note: If you for some reason are using a Qt Designer that is built
 | 
				
			||||||
 | 
					    in debug mode, you will need to build the plugin in debug mode
 | 
				
			||||||
 | 
					    also. Edit the file plugin.pro in the plugin directory, changing
 | 
				
			||||||
 | 
					    'release' to 'debug' in the CONFIG line, before running qmake.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Solutions components are intended to be used directly from the package
 | 
				
			||||||
 | 
					directory during development, so there is no 'make install' procedure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Using a component in your project
 | 
				
			||||||
 | 
					---------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To use this component in your project, add the following line to the
 | 
				
			||||||
 | 
					project's .pro file (or do the equivalent in your IDE):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    include(your-install-dir/some-package/src/some-package.pri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This adds the package's sources and headers to the SOURCES and HEADERS
 | 
				
			||||||
 | 
					project variables respectively (or, if the component has been
 | 
				
			||||||
 | 
					configured as a DLL, it adds that library to the LIBS variable), and
 | 
				
			||||||
 | 
					updates INCLUDEPATH to contain the package's src
 | 
				
			||||||
 | 
					directory. Additionally, the .pri file may include some dependencies
 | 
				
			||||||
 | 
					needed by the package.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To include a header file from the package in your sources, you can now
 | 
				
			||||||
 | 
					simply use:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #include <SomeClass>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					or alternatively, in pre-Qt 4 style:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #include <some-class.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Refer to the documentation to see the classes and headers this
 | 
				
			||||||
 | 
					components provides.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Install documentation (optional)
 | 
				
			||||||
 | 
					--------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The HTML documentation for the package's classes is located in the
 | 
				
			||||||
 | 
					your-install-dir/some-package/doc/html/index.html. You can open this
 | 
				
			||||||
 | 
					file and read the documentation with any web browser.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To install the documentation into Qt Assistant (for Qt version 4.4 and
 | 
				
			||||||
 | 
					later):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  In Assistant, open the Edit->Preferences dialog and choose the
 | 
				
			||||||
 | 
					    Documentation tab. Click the Add... button and select the file
 | 
				
			||||||
 | 
					       your-install-dir/some-package/doc/html/some-package.qch
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					For Qt versions prior to 4.4, do instead the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  The directory your-install-dir/some-package/doc/html contains a
 | 
				
			||||||
 | 
					    file called some-package.dcf. Execute the following commands in a
 | 
				
			||||||
 | 
					    shell, command prompt or terminal window:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cd your-install-dir/some-package/doc/html/
 | 
				
			||||||
 | 
						assistant -addContentFile some-package.dcf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The next time you start Qt Assistant, you can access the package's
 | 
				
			||||||
 | 
					documentation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Removing the documentation from assistant
 | 
				
			||||||
 | 
					-----------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you have installed the documentation into Qt Assistant, and want to uninstall it, do as follows, for Qt version 4.4 and later:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  In Assistant, open the Edit->Preferences dialog and choose the
 | 
				
			||||||
 | 
					    Documentation tab. In the list of Registered Documentation, select
 | 
				
			||||||
 | 
					    the item com.nokia.qtsolutions.some-package_version, and click
 | 
				
			||||||
 | 
					    the Remove button.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For Qt versions prior to 4.4, do instead the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  The directory your-install-dir/some-package/doc/html contains a
 | 
				
			||||||
 | 
					    file called some-package.dcf. Execute the following commands in a
 | 
				
			||||||
 | 
					    shell, command prompt or terminal window:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cd your-install-dir/some-package/doc/html/
 | 
				
			||||||
 | 
						assistant -removeContentFile some-package.dcf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Using the component as a DLL
 | 
				
			||||||
 | 
					----------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Normal components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The shared library (DLL) is built and placed in the
 | 
				
			||||||
 | 
					    some-package/lib directory. It is intended to be used directly
 | 
				
			||||||
 | 
					    from there during development. When appropriate, both debug and
 | 
				
			||||||
 | 
					    release versions are built, since the run-time linker will in some
 | 
				
			||||||
 | 
					    cases refuse to load a debug-built DLL into a release-built
 | 
				
			||||||
 | 
					    application or vice versa.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The following steps are taken by default to help the dynamic
 | 
				
			||||||
 | 
					    linker to locate the DLL at run-time (during development):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Unix: The some-package.pri file will add linker instructions to
 | 
				
			||||||
 | 
					    add the some-package/lib directory to the rpath of the
 | 
				
			||||||
 | 
					    executable. (When distributing, or if your system does not support
 | 
				
			||||||
 | 
					    rpath, you can copy the shared library to another place that is
 | 
				
			||||||
 | 
					    searched by the dynamic linker, e.g. the "lib" directory of your
 | 
				
			||||||
 | 
					    Qt installation.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Mac: The full path to the library is hardcoded into the library
 | 
				
			||||||
 | 
					    itself, from where it is copied into the executable at link time,
 | 
				
			||||||
 | 
					    and ready by the dynamic linker at run-time. (When distributing,
 | 
				
			||||||
 | 
					    you will want to edit these hardcoded paths in the same way as for
 | 
				
			||||||
 | 
					    the Qt DLLs. Refer to the document "Deploying an Application on
 | 
				
			||||||
 | 
					    Mac OS X" in the Qt Reference Documentation.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Windows: the .dll file(s) are copied into the "bin" directory of
 | 
				
			||||||
 | 
					    your Qt installation. The Qt installation will already have set up
 | 
				
			||||||
 | 
					    that directory to be searched by the dynamic linker.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. Plugins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    For Qt Solutions plugins (e.g. image formats), both debug and
 | 
				
			||||||
 | 
					    release versions of the plugin are built by default when
 | 
				
			||||||
 | 
					    appropriate, since in some cases the release Qt library will not
 | 
				
			||||||
 | 
					    load a debug plugin, and vice versa. The plugins are automatically
 | 
				
			||||||
 | 
					    copied into the plugins directory of your Qt installation when
 | 
				
			||||||
 | 
					    built, so no further setup is required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Plugins may also be built statically, i.e. as a library that will be
 | 
				
			||||||
 | 
					    linked into your application executable, and so will not need to
 | 
				
			||||||
 | 
					    be redistributed as a separate plugin DLL to end users. Static
 | 
				
			||||||
 | 
					    building is required if Qt itself is built statically. To do it,
 | 
				
			||||||
 | 
					    just add "static" to the CONFIG variable in the plugin/plugin.pro
 | 
				
			||||||
 | 
					    file before building. Refer to the "Static Plugins" section in the
 | 
				
			||||||
 | 
					    chapter "How to Create Qt Plugins" for explanation of how to use a
 | 
				
			||||||
 | 
					    static plugin in your application. The source code of the example
 | 
				
			||||||
 | 
					    program(s) will also typically contain the relevant instructions
 | 
				
			||||||
 | 
					    as comments.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Uninstalling
 | 
				
			||||||
 | 
					------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The following command will remove any fils that have been
 | 
				
			||||||
 | 
					    automatically placed outside the package directory itself during
 | 
				
			||||||
 | 
					    installation and building
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						make distclean [or nmake if your are using Microsoft Visual C++]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If Qt Assistant documentation or Qt Designer plugins have been
 | 
				
			||||||
 | 
					    installed, they can be uninstalled manually, ref. above.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Enjoy! :)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- The Qt Solutions Team.
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					#include "qtlockedfile.h"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					Qt Solutions Component: Single Application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The QtSingleApplication component provides support for
 | 
				
			||||||
 | 
					applications that can be only started once per user.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Version history:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.0: - Version 1.3 ported to Qt 4.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.1: - Fix compilation problem on Mac.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.2: - Really fix the Mac compilation problem.
 | 
				
			||||||
 | 
					     - Mac: fix crash due to wrong object releasing.
 | 
				
			||||||
 | 
					     - Mac: Fix memory leak.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.3: - Windows: Force creation of internal widget to make it work
 | 
				
			||||||
 | 
					     with Qt 4.2.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.4: - Fix the system for automatic window raising on message
 | 
				
			||||||
 | 
					     reception. NOTE: minor API change.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.5: - Mac: Fix isRunning() to work and report correctly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.6: - - initialize() is now obsolete, no longer necessary to call
 | 
				
			||||||
 | 
					     it
 | 
				
			||||||
 | 
					     - - Fixed race condition where multiple instances migth be started
 | 
				
			||||||
 | 
					     - - QtSingleCoreApplication variant provided for non-GUI (console)
 | 
				
			||||||
 | 
					     usage
 | 
				
			||||||
 | 
					     - Complete reimplementation. Visible changes:
 | 
				
			||||||
 | 
					     - LGPL release.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,205 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qtlocalpeer.h"
 | 
				
			||||||
 | 
					#include <QCoreApplication>
 | 
				
			||||||
 | 
					#include <QTime>
 | 
				
			||||||
 | 
					#include <QDataStream>
 | 
				
			||||||
 | 
					#include <QRegularExpression>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(Q_OS_WIN)
 | 
				
			||||||
 | 
					#include <QLibrary>
 | 
				
			||||||
 | 
					#include <qt_windows.h>
 | 
				
			||||||
 | 
					typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
 | 
				
			||||||
 | 
					static PProcessIdToSessionId pProcessIdToSessionId = 0;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#if defined(Q_OS_UNIX)
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace QtLP_Private {
 | 
				
			||||||
 | 
					#include "qtlockedfile.cpp"
 | 
				
			||||||
 | 
					#if defined(Q_OS_WIN)
 | 
				
			||||||
 | 
					#include "qtlockedfile_win.cpp"
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#include "qtlockedfile_unix.cpp"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* QtLocalPeer::ack = "ack";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
 | 
				
			||||||
 | 
					    : QObject(parent), id(appId)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QString prefix = id;
 | 
				
			||||||
 | 
					    if (id.isEmpty()) {
 | 
				
			||||||
 | 
					        id = QCoreApplication::applicationFilePath();
 | 
				
			||||||
 | 
					#if defined(Q_OS_WIN)
 | 
				
			||||||
 | 
					        id = id.toLower();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        prefix = id.section(QLatin1Char('/'), -1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    prefix.remove(QRegularExpression ("[^a-zA-Z]"));
 | 
				
			||||||
 | 
					    prefix.truncate(6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QByteArray idc = id.toUtf8();
 | 
				
			||||||
 | 
					    quint16 idNum = qChecksum(idc.constData(), idc.size());
 | 
				
			||||||
 | 
					    socketName = QLatin1String("qtsingleapp-") + prefix
 | 
				
			||||||
 | 
					                 + QLatin1Char('-') + QString::number(idNum, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(Q_OS_WIN)
 | 
				
			||||||
 | 
					    if (!pProcessIdToSessionId) {
 | 
				
			||||||
 | 
					        QLibrary lib("kernel32");
 | 
				
			||||||
 | 
					        pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (pProcessIdToSessionId) {
 | 
				
			||||||
 | 
					        DWORD sessionId = 0;
 | 
				
			||||||
 | 
					        pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
 | 
				
			||||||
 | 
					        socketName += QLatin1Char('-') + QString::number(sessionId, 16);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    server = new QLocalServer(this);
 | 
				
			||||||
 | 
					    QString lockName = QDir(QDir::tempPath()).absolutePath()
 | 
				
			||||||
 | 
					                       + QLatin1Char('/') + socketName
 | 
				
			||||||
 | 
					                       + QLatin1String("-lockfile");
 | 
				
			||||||
 | 
					    lockFile.setFileName(lockName);
 | 
				
			||||||
 | 
					    lockFile.open(QIODevice::ReadWrite);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtLocalPeer::isClient()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (lockFile.isLocked())
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false))
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool res = server->listen(socketName);
 | 
				
			||||||
 | 
					#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0))
 | 
				
			||||||
 | 
					    // ### Workaround
 | 
				
			||||||
 | 
					    if (!res && server->serverError() == QAbstractSocket::AddressInUseError) {
 | 
				
			||||||
 | 
					        QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName);
 | 
				
			||||||
 | 
					        res = server->listen(socketName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    if (!res)
 | 
				
			||||||
 | 
					        qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
 | 
				
			||||||
 | 
					    QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtLocalPeer::sendMessage(const QString &message, int timeout)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!isClient())
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QLocalSocket socket;
 | 
				
			||||||
 | 
					    bool connOk = false;
 | 
				
			||||||
 | 
					    for(int i = 0; i < 2; i++) {
 | 
				
			||||||
 | 
					        // Try twice, in case the other instance is just starting up
 | 
				
			||||||
 | 
					        socket.connectToServer(socketName);
 | 
				
			||||||
 | 
					        connOk = socket.waitForConnected(timeout/2);
 | 
				
			||||||
 | 
					        if (connOk || i)
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        int ms = 250;
 | 
				
			||||||
 | 
					#if defined(Q_OS_WIN)
 | 
				
			||||||
 | 
					        Sleep(DWORD(ms));
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
 | 
				
			||||||
 | 
					        nanosleep(&ts, NULL);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!connOk)
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QByteArray uMsg(message.toUtf8());
 | 
				
			||||||
 | 
					    QDataStream ds(&socket);
 | 
				
			||||||
 | 
					    ds.writeBytes(uMsg.constData(), uMsg.size());
 | 
				
			||||||
 | 
					    bool res = socket.waitForBytesWritten(timeout);
 | 
				
			||||||
 | 
					    if (res) {
 | 
				
			||||||
 | 
					        res &= socket.waitForReadyRead(timeout);   // wait for ack
 | 
				
			||||||
 | 
					        if (res)
 | 
				
			||||||
 | 
					            res &= (socket.read(qstrlen(ack)) == ack);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void QtLocalPeer::receiveConnection()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    QLocalSocket* socket = server->nextPendingConnection();
 | 
				
			||||||
 | 
					    if (!socket)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (socket->bytesAvailable() < (int)sizeof(quint32))
 | 
				
			||||||
 | 
					        socket->waitForReadyRead();
 | 
				
			||||||
 | 
					    QDataStream ds(socket);
 | 
				
			||||||
 | 
					    QByteArray uMsg;
 | 
				
			||||||
 | 
					    quint32 remaining;
 | 
				
			||||||
 | 
					    ds >> remaining;
 | 
				
			||||||
 | 
					    uMsg.resize(remaining);
 | 
				
			||||||
 | 
					    int got = 0;
 | 
				
			||||||
 | 
					    char* uMsgBuf = uMsg.data();
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        got = ds.readRawData(uMsgBuf, remaining);
 | 
				
			||||||
 | 
					        remaining -= got;
 | 
				
			||||||
 | 
					        uMsgBuf += got;
 | 
				
			||||||
 | 
					    } while (remaining && got >= 0 && socket->waitForReadyRead(2000));
 | 
				
			||||||
 | 
					    if (got < 0) {
 | 
				
			||||||
 | 
					        qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData());
 | 
				
			||||||
 | 
					        delete socket;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    QString message(QString::fromUtf8(uMsg));
 | 
				
			||||||
 | 
					    socket->write(ack, qstrlen(ack));
 | 
				
			||||||
 | 
					    socket->waitForBytesWritten(1000);
 | 
				
			||||||
 | 
					    socket->waitForDisconnected(1000); // make sure client reads ack
 | 
				
			||||||
 | 
					    delete socket;
 | 
				
			||||||
 | 
					    emit messageReceived(message); //### (might take a long time to return)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,77 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef QTLOCALPEER_H
 | 
				
			||||||
 | 
					#define QTLOCALPEER_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QLocalServer>
 | 
				
			||||||
 | 
					#include <QLocalSocket>
 | 
				
			||||||
 | 
					#include <QDir>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qtlockedfile.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QtLocalPeer : public QObject
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
 | 
				
			||||||
 | 
					    bool isClient();
 | 
				
			||||||
 | 
					    bool sendMessage(const QString &message, int timeout);
 | 
				
			||||||
 | 
					    QString applicationId() const
 | 
				
			||||||
 | 
					        { return id; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_SIGNALS:
 | 
				
			||||||
 | 
					    void messageReceived(const QString &message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected Q_SLOTS:
 | 
				
			||||||
 | 
					    void receiveConnection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    QString id;
 | 
				
			||||||
 | 
					    QString socketName;
 | 
				
			||||||
 | 
					    QLocalServer* server;
 | 
				
			||||||
 | 
					    QtLP_Private::QtLockedFile lockFile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    static const char* ack;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // QTLOCALPEER_H
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,193 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qtlockedfile.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \class QtLockedFile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \brief The QtLockedFile class extends QFile with advisory locking
 | 
				
			||||||
 | 
					    functions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A file may be locked in read or write mode. Multiple instances of
 | 
				
			||||||
 | 
					    \e QtLockedFile, created in multiple processes running on the same
 | 
				
			||||||
 | 
					    machine, may have a file locked in read mode. Exactly one instance
 | 
				
			||||||
 | 
					    may have it locked in write mode. A read and a write lock cannot
 | 
				
			||||||
 | 
					    exist simultaneously on the same file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The file locks are advisory. This means that nothing prevents
 | 
				
			||||||
 | 
					    another process from manipulating a locked file using QFile or
 | 
				
			||||||
 | 
					    file system functions offered by the OS. Serialization is only
 | 
				
			||||||
 | 
					    guaranteed if all processes that access the file use
 | 
				
			||||||
 | 
					    QLockedFile. Also, while holding a lock on a file, a process
 | 
				
			||||||
 | 
					    must not open the same file again (through any API), or locks
 | 
				
			||||||
 | 
					    can be unexpectedly lost.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The lock provided by an instance of \e QtLockedFile is released
 | 
				
			||||||
 | 
					    whenever the program terminates. This is true even when the
 | 
				
			||||||
 | 
					    program crashes and no destructors are called.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! \enum QtLockedFile::LockMode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This enum describes the available lock modes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \value ReadLock A read lock.
 | 
				
			||||||
 | 
					    \value WriteLock A write lock.
 | 
				
			||||||
 | 
					    \value NoLock Neither a read lock nor a write lock.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Constructs an unlocked \e QtLockedFile object. This constructor
 | 
				
			||||||
 | 
					    behaves in the same way as \e QFile::QFile().
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa QFile::QFile()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QtLockedFile::QtLockedFile()
 | 
				
			||||||
 | 
					    : QFile()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef Q_OS_WIN
 | 
				
			||||||
 | 
					    wmutex = 0;
 | 
				
			||||||
 | 
					    rmutex = 0;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    m_lock_mode = NoLock;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Constructs an unlocked QtLockedFile object with file \a name. This
 | 
				
			||||||
 | 
					    constructor behaves in the same way as \e QFile::QFile(const
 | 
				
			||||||
 | 
					    QString&).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa QFile::QFile()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QtLockedFile::QtLockedFile(const QString &name)
 | 
				
			||||||
 | 
					    : QFile(name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef Q_OS_WIN
 | 
				
			||||||
 | 
					    wmutex = 0;
 | 
				
			||||||
 | 
					    rmutex = 0;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    m_lock_mode = NoLock;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					  Opens the file in OpenMode \a mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  This is identical to QFile::open(), with the one exception that the
 | 
				
			||||||
 | 
					  Truncate mode flag is disallowed. Truncation would conflict with the
 | 
				
			||||||
 | 
					  advisory file locking, since the file would be modified before the
 | 
				
			||||||
 | 
					  write lock is obtained. If truncation is required, use resize(0)
 | 
				
			||||||
 | 
					  after obtaining the write lock.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Returns true if successful; otherwise false.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  \sa QFile::open(), QFile::resize()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					bool QtLockedFile::open(OpenMode mode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (mode & QIODevice::Truncate) {
 | 
				
			||||||
 | 
					        qWarning("QtLockedFile::open(): Truncate mode not allowed.");
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return QFile::open(mode);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Returns \e true if this object has a in read or write lock;
 | 
				
			||||||
 | 
					    otherwise returns \e false.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa lockMode()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					bool QtLockedFile::isLocked() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return m_lock_mode != NoLock;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Returns the type of lock currently held by this object, or \e
 | 
				
			||||||
 | 
					    QtLockedFile::NoLock.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa isLocked()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QtLockedFile::LockMode QtLockedFile::lockMode() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return m_lock_mode;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \fn bool QtLockedFile::lock(LockMode mode, bool block = true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Obtains a lock of type \a mode. The file must be opened before it
 | 
				
			||||||
 | 
					    can be locked.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If \a block is true, this function will block until the lock is
 | 
				
			||||||
 | 
					    aquired. If \a block is false, this function returns \e false
 | 
				
			||||||
 | 
					    immediately if the lock cannot be aquired.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If this object already has a lock of type \a mode, this function
 | 
				
			||||||
 | 
					    returns \e true immediately. If this object has a lock of a
 | 
				
			||||||
 | 
					    different type than \a mode, the lock is first released and then a
 | 
				
			||||||
 | 
					    new lock is obtained.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This function returns \e true if, after it executes, the file is
 | 
				
			||||||
 | 
					    locked by this object, and \e false otherwise.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa unlock(), isLocked(), lockMode()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \fn bool QtLockedFile::unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Releases a lock.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If the object has no lock, this function returns immediately.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This function returns \e true if, after it executes, the file is
 | 
				
			||||||
 | 
					    not locked by this object, and \e false otherwise.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa lock(), isLocked(), lockMode()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \fn QtLockedFile::~QtLockedFile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Destroys the \e QtLockedFile object. If any locks were held, they
 | 
				
			||||||
 | 
					    are released.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,97 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef QTLOCKEDFILE_H
 | 
				
			||||||
 | 
					#define QTLOCKEDFILE_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QFile>
 | 
				
			||||||
 | 
					#ifdef Q_OS_WIN
 | 
				
			||||||
 | 
					#include <QVector>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(Q_OS_WIN)
 | 
				
			||||||
 | 
					#  if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
 | 
				
			||||||
 | 
					#    define QT_QTLOCKEDFILE_EXPORT
 | 
				
			||||||
 | 
					#  elif defined(QT_QTLOCKEDFILE_IMPORT)
 | 
				
			||||||
 | 
					#    if defined(QT_QTLOCKEDFILE_EXPORT)
 | 
				
			||||||
 | 
					#      undef QT_QTLOCKEDFILE_EXPORT
 | 
				
			||||||
 | 
					#    endif
 | 
				
			||||||
 | 
					#    define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
 | 
				
			||||||
 | 
					#  elif defined(QT_QTLOCKEDFILE_EXPORT)
 | 
				
			||||||
 | 
					#    undef QT_QTLOCKEDFILE_EXPORT
 | 
				
			||||||
 | 
					#    define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
 | 
				
			||||||
 | 
					#  endif
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#  define QT_QTLOCKEDFILE_EXPORT
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace QtLP_Private {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    enum LockMode { NoLock = 0, ReadLock, WriteLock };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QtLockedFile();
 | 
				
			||||||
 | 
					    QtLockedFile(const QString &name);
 | 
				
			||||||
 | 
					    ~QtLockedFile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool open(OpenMode mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool lock(LockMode mode, bool block = true);
 | 
				
			||||||
 | 
					    bool unlock();
 | 
				
			||||||
 | 
					    bool isLocked() const;
 | 
				
			||||||
 | 
					    LockMode lockMode() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					#ifdef Q_OS_WIN
 | 
				
			||||||
 | 
					    Qt::HANDLE wmutex;
 | 
				
			||||||
 | 
					    Qt::HANDLE rmutex;
 | 
				
			||||||
 | 
					    QVector<Qt::HANDLE> rmutexes;
 | 
				
			||||||
 | 
					    QString mutexname;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Qt::HANDLE getMutexHandle(int idx, bool doCreate);
 | 
				
			||||||
 | 
					    bool waitMutex(Qt::HANDLE mutex, bool doBlock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    LockMode m_lock_mode;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,115 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <fcntl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qtlockedfile.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtLockedFile::lock(LockMode mode, bool block)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!isOpen()) {
 | 
				
			||||||
 | 
					        qWarning("QtLockedFile::lock(): file is not opened");
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    if (mode == NoLock)
 | 
				
			||||||
 | 
					        return unlock();
 | 
				
			||||||
 | 
					           
 | 
				
			||||||
 | 
					    if (mode == m_lock_mode)
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m_lock_mode != NoLock)
 | 
				
			||||||
 | 
					        unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct flock fl;
 | 
				
			||||||
 | 
					    fl.l_whence = SEEK_SET;
 | 
				
			||||||
 | 
					    fl.l_start = 0;
 | 
				
			||||||
 | 
					    fl.l_len = 0;
 | 
				
			||||||
 | 
					    fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK;
 | 
				
			||||||
 | 
					    int cmd = block ? F_SETLKW : F_SETLK;
 | 
				
			||||||
 | 
					    int ret = fcntl(handle(), cmd, &fl);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (ret == -1) {
 | 
				
			||||||
 | 
					        if (errno != EINTR && errno != EAGAIN)
 | 
				
			||||||
 | 
					            qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    m_lock_mode = mode;
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtLockedFile::unlock()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!isOpen()) {
 | 
				
			||||||
 | 
					        qWarning("QtLockedFile::unlock(): file is not opened");
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!isLocked())
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct flock fl;
 | 
				
			||||||
 | 
					    fl.l_whence = SEEK_SET;
 | 
				
			||||||
 | 
					    fl.l_start = 0;
 | 
				
			||||||
 | 
					    fl.l_len = 0;
 | 
				
			||||||
 | 
					    fl.l_type = F_UNLCK;
 | 
				
			||||||
 | 
					    int ret = fcntl(handle(), F_SETLKW, &fl);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (ret == -1) {
 | 
				
			||||||
 | 
					        qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    m_lock_mode = NoLock;
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QtLockedFile::~QtLockedFile()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (isOpen())
 | 
				
			||||||
 | 
					        unlock();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,211 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qtlockedfile.h"
 | 
				
			||||||
 | 
					#include <qt_windows.h>
 | 
				
			||||||
 | 
					#include <QFileInfo>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MUTEX_PREFIX "QtLockedFile mutex "
 | 
				
			||||||
 | 
					// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS
 | 
				
			||||||
 | 
					#define MAX_READERS MAXIMUM_WAIT_OBJECTS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if QT_VERSION >= 0x050000
 | 
				
			||||||
 | 
					#define QT_WA(unicode, ansi) unicode
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (mutexname.isEmpty()) {
 | 
				
			||||||
 | 
					        QFileInfo fi(*this);
 | 
				
			||||||
 | 
					        mutexname = QString::fromLatin1(MUTEX_PREFIX)
 | 
				
			||||||
 | 
					                    + fi.absoluteFilePath().toLower();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    QString mname(mutexname);
 | 
				
			||||||
 | 
					    if (idx >= 0)
 | 
				
			||||||
 | 
					        mname += QString::number(idx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Qt::HANDLE mutex;
 | 
				
			||||||
 | 
					    if (doCreate) {
 | 
				
			||||||
 | 
					        QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); },
 | 
				
			||||||
 | 
					               { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } );
 | 
				
			||||||
 | 
					        if (!mutex) {
 | 
				
			||||||
 | 
					            qErrnoWarning("QtLockedFile::lock(): CreateMutex failed");
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					        QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); },
 | 
				
			||||||
 | 
					               { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } );
 | 
				
			||||||
 | 
					        if (!mutex) {
 | 
				
			||||||
 | 
					            if (GetLastError() != ERROR_FILE_NOT_FOUND)
 | 
				
			||||||
 | 
					                qErrnoWarning("QtLockedFile::lock(): OpenMutex failed");
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return mutex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Q_ASSERT(mutex);
 | 
				
			||||||
 | 
					    DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0);
 | 
				
			||||||
 | 
					    switch (res) {
 | 
				
			||||||
 | 
					    case WAIT_OBJECT_0:
 | 
				
			||||||
 | 
					    case WAIT_ABANDONED:
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case WAIT_TIMEOUT:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtLockedFile::lock(LockMode mode, bool block)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!isOpen()) {
 | 
				
			||||||
 | 
					        qWarning("QtLockedFile::lock(): file is not opened");
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (mode == NoLock)
 | 
				
			||||||
 | 
					        return unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (mode == m_lock_mode)
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m_lock_mode != NoLock)
 | 
				
			||||||
 | 
					        unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!wmutex && !(wmutex = getMutexHandle(-1, true)))
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!waitMutex(wmutex, block))
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (mode == ReadLock) {
 | 
				
			||||||
 | 
					        int idx = 0;
 | 
				
			||||||
 | 
					        for (; idx < MAX_READERS; idx++) {
 | 
				
			||||||
 | 
					            rmutex = getMutexHandle(idx, false);
 | 
				
			||||||
 | 
					            if (!rmutex || waitMutex(rmutex, false))
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            CloseHandle(rmutex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        bool ok = true;
 | 
				
			||||||
 | 
					        if (idx >= MAX_READERS) {
 | 
				
			||||||
 | 
					            qWarning("QtLockedFile::lock(): too many readers");
 | 
				
			||||||
 | 
					            rmutex = 0;
 | 
				
			||||||
 | 
					            ok = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (!rmutex) {
 | 
				
			||||||
 | 
					            rmutex = getMutexHandle(idx, true);
 | 
				
			||||||
 | 
					            if (!rmutex || !waitMutex(rmutex, false))
 | 
				
			||||||
 | 
					                ok = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!ok && rmutex) {
 | 
				
			||||||
 | 
					            CloseHandle(rmutex);
 | 
				
			||||||
 | 
					            rmutex = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ReleaseMutex(wmutex);
 | 
				
			||||||
 | 
					        if (!ok)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					        Q_ASSERT(rmutexes.isEmpty());
 | 
				
			||||||
 | 
					        for (int i = 0; i < MAX_READERS; i++) {
 | 
				
			||||||
 | 
					            Qt::HANDLE mutex = getMutexHandle(i, false);
 | 
				
			||||||
 | 
					            if (mutex)
 | 
				
			||||||
 | 
					                rmutexes.append(mutex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (rmutexes.size()) {
 | 
				
			||||||
 | 
					            DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(),
 | 
				
			||||||
 | 
					                                               TRUE, block ? INFINITE : 0);
 | 
				
			||||||
 | 
					            if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
 | 
				
			||||||
 | 
					                if (res != WAIT_TIMEOUT)
 | 
				
			||||||
 | 
					                    qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed");
 | 
				
			||||||
 | 
					                m_lock_mode = WriteLock;  // trick unlock() to clean up - semiyucky
 | 
				
			||||||
 | 
					                unlock();
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_lock_mode = mode;
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtLockedFile::unlock()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!isOpen()) {
 | 
				
			||||||
 | 
					        qWarning("QtLockedFile::unlock(): file is not opened");
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!isLocked())
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m_lock_mode == ReadLock) {
 | 
				
			||||||
 | 
					        ReleaseMutex(rmutex);
 | 
				
			||||||
 | 
					        CloseHandle(rmutex);
 | 
				
			||||||
 | 
					        rmutex = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					        foreach(Qt::HANDLE mutex, rmutexes) {
 | 
				
			||||||
 | 
					            ReleaseMutex(mutex);
 | 
				
			||||||
 | 
					            CloseHandle(mutex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        rmutexes.clear();
 | 
				
			||||||
 | 
					        ReleaseMutex(wmutex);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_lock_mode = QtLockedFile::NoLock;
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QtLockedFile::~QtLockedFile()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (isOpen())
 | 
				
			||||||
 | 
					        unlock();
 | 
				
			||||||
 | 
					    if (wmutex)
 | 
				
			||||||
 | 
					        CloseHandle(wmutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,356 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qtsingleapplication.h"
 | 
				
			||||||
 | 
					#include "qtlocalpeer.h"
 | 
				
			||||||
 | 
					#include <QWidget>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \class QtSingleApplication qtsingleapplication.h
 | 
				
			||||||
 | 
					    \brief The QtSingleApplication class provides an API to detect and
 | 
				
			||||||
 | 
					    communicate with running instances of an application.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This class allows you to create applications where only one
 | 
				
			||||||
 | 
					    instance should be running at a time. I.e., if the user tries to
 | 
				
			||||||
 | 
					    launch another instance, the already running instance will be
 | 
				
			||||||
 | 
					    activated instead. Another usecase is a client-server system,
 | 
				
			||||||
 | 
					    where the first started instance will assume the role of server,
 | 
				
			||||||
 | 
					    and the later instances will act as clients of that server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    By default, the full path of the executable file is used to
 | 
				
			||||||
 | 
					    determine whether two processes are instances of the same
 | 
				
			||||||
 | 
					    application. You can also provide an explicit identifier string
 | 
				
			||||||
 | 
					    that will be compared instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The application should create the QtSingleApplication object early
 | 
				
			||||||
 | 
					    in the startup phase, and call isRunning() to find out if another
 | 
				
			||||||
 | 
					    instance of this application is already running. If isRunning()
 | 
				
			||||||
 | 
					    returns false, it means that no other instance is running, and
 | 
				
			||||||
 | 
					    this instance has assumed the role as the running instance. In
 | 
				
			||||||
 | 
					    this case, the application should continue with the initialization
 | 
				
			||||||
 | 
					    of the application user interface before entering the event loop
 | 
				
			||||||
 | 
					    with exec(), as normal.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The messageReceived() signal will be emitted when the running
 | 
				
			||||||
 | 
					    application receives messages from another instance of the same
 | 
				
			||||||
 | 
					    application. When a message is received it might be helpful to the
 | 
				
			||||||
 | 
					    user to raise the application so that it becomes visible. To
 | 
				
			||||||
 | 
					    facilitate this, QtSingleApplication provides the
 | 
				
			||||||
 | 
					    setActivationWindow() function and the activateWindow() slot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If isRunning() returns true, another instance is already
 | 
				
			||||||
 | 
					    running. It may be alerted to the fact that another instance has
 | 
				
			||||||
 | 
					    started by using the sendMessage() function. Also data such as
 | 
				
			||||||
 | 
					    startup parameters (e.g. the name of the file the user wanted this
 | 
				
			||||||
 | 
					    new instance to open) can be passed to the running instance with
 | 
				
			||||||
 | 
					    this function. Then, the application should terminate (or enter
 | 
				
			||||||
 | 
					    client mode).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If isRunning() returns true, but sendMessage() fails, that is an
 | 
				
			||||||
 | 
					    indication that the running instance is frozen.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Here's an example that shows how to convert an existing
 | 
				
			||||||
 | 
					    application to use QtSingleApplication. It is very simple and does
 | 
				
			||||||
 | 
					    not make use of all QtSingleApplication's functionality (see the
 | 
				
			||||||
 | 
					    examples for that).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \code
 | 
				
			||||||
 | 
					    // Original
 | 
				
			||||||
 | 
					    int main(int argc, char **argv)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        QApplication app(argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MyMainWidget mmw;
 | 
				
			||||||
 | 
					        mmw.show();
 | 
				
			||||||
 | 
					        return app.exec();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Single instance
 | 
				
			||||||
 | 
					    int main(int argc, char **argv)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        QtSingleApplication app(argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (app.isRunning())
 | 
				
			||||||
 | 
					            return !app.sendMessage(someDataString);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MyMainWidget mmw;
 | 
				
			||||||
 | 
					        app.setActivationWindow(&mmw);
 | 
				
			||||||
 | 
					        mmw.show();
 | 
				
			||||||
 | 
					        return app.exec();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    \endcode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Once this QtSingleApplication instance is destroyed (normally when
 | 
				
			||||||
 | 
					    the process exits or crashes), when the user next attempts to run the
 | 
				
			||||||
 | 
					    application this instance will not, of course, be encountered. The
 | 
				
			||||||
 | 
					    next instance to call isRunning() or sendMessage() will assume the
 | 
				
			||||||
 | 
					    role as the new running instance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    For console (non-GUI) applications, QtSingleCoreApplication may be
 | 
				
			||||||
 | 
					    used instead of this class, to avoid the dependency on the QtGui
 | 
				
			||||||
 | 
					    library.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa QtSingleCoreApplication
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void QtSingleApplication::sysInit(const QString &appId)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    actWin = 0;
 | 
				
			||||||
 | 
					    peer = new QtLocalPeer(this, appId);
 | 
				
			||||||
 | 
					    connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Creates a QtSingleApplication object. The application identifier
 | 
				
			||||||
 | 
					    will be QCoreApplication::applicationFilePath(). \a argc, \a
 | 
				
			||||||
 | 
					    argv, and \a GUIenabled are passed on to the QAppliation constructor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If you are creating a console application (i.e. setting \a
 | 
				
			||||||
 | 
					    GUIenabled to false), you may consider using
 | 
				
			||||||
 | 
					    QtSingleCoreApplication instead.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled)
 | 
				
			||||||
 | 
					    : QApplication(argc, argv, GUIenabled)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    sysInit();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Creates a QtSingleApplication object with the application
 | 
				
			||||||
 | 
					    identifier \a appId. \a argc and \a argv are passed on to the
 | 
				
			||||||
 | 
					    QAppliation constructor.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
 | 
				
			||||||
 | 
					    : QApplication(argc, argv),
 | 
				
			||||||
 | 
					      peer(nullptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    sysInit(appId);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QtSingleApplication::~QtSingleApplication()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (peer)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        delete peer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if QT_VERSION < 0x050000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Creates a QtSingleApplication object. The application identifier
 | 
				
			||||||
 | 
					    will be QCoreApplication::applicationFilePath(). \a argc, \a
 | 
				
			||||||
 | 
					    argv, and \a type are passed on to the QAppliation constructor.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type)
 | 
				
			||||||
 | 
					    : QApplication(argc, argv, type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    sysInit();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#  if defined(Q_WS_X11)
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					  Special constructor for X11, ref. the documentation of
 | 
				
			||||||
 | 
					  QApplication's corresponding constructor. The application identifier
 | 
				
			||||||
 | 
					  will be QCoreApplication::applicationFilePath(). \a dpy, \a visual,
 | 
				
			||||||
 | 
					  and \a cmap are passed on to the QApplication constructor.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap)
 | 
				
			||||||
 | 
					    : QApplication(dpy, visual, cmap)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    sysInit();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					  Special constructor for X11, ref. the documentation of
 | 
				
			||||||
 | 
					  QApplication's corresponding constructor. The application identifier
 | 
				
			||||||
 | 
					  will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a
 | 
				
			||||||
 | 
					  argv, \a visual, and \a cmap are passed on to the QApplication
 | 
				
			||||||
 | 
					  constructor.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
 | 
				
			||||||
 | 
					    : QApplication(dpy, argc, argv, visual, cmap)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    sysInit();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					  Special constructor for X11, ref. the documentation of
 | 
				
			||||||
 | 
					  QApplication's corresponding constructor. The application identifier
 | 
				
			||||||
 | 
					  will be \a appId. \a dpy, \a argc, \a
 | 
				
			||||||
 | 
					  argv, \a visual, and \a cmap are passed on to the QApplication
 | 
				
			||||||
 | 
					  constructor.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
 | 
				
			||||||
 | 
					    : QApplication(dpy, argc, argv, visual, cmap)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    sysInit(appId);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#  endif // Q_WS_X11
 | 
				
			||||||
 | 
					#endif // QT_VERSION < 0x050000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Returns true if another instance of this application is running;
 | 
				
			||||||
 | 
					    otherwise false.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This function does not find instances of this application that are
 | 
				
			||||||
 | 
					    being run by a different user (on Windows: that are running in
 | 
				
			||||||
 | 
					    another session).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa sendMessage()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtSingleApplication::isRunning()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return peer->isClient();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Tries to send the text \a message to the currently running
 | 
				
			||||||
 | 
					    instance. The QtSingleApplication object in the running instance
 | 
				
			||||||
 | 
					    will emit the messageReceived() signal when it receives the
 | 
				
			||||||
 | 
					    message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This function returns true if the message has been sent to, and
 | 
				
			||||||
 | 
					    processed by, the current instance. If there is no instance
 | 
				
			||||||
 | 
					    currently running, or if the running instance fails to process the
 | 
				
			||||||
 | 
					    message within \a timeout milliseconds, this function return false.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa isRunning(), messageReceived()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					bool QtSingleApplication::sendMessage(const QString &message, int timeout)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return peer->sendMessage(message, timeout);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Returns the application identifier. Two processes with the same
 | 
				
			||||||
 | 
					    identifier will be regarded as instances of the same application.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QString QtSingleApplication::id() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return peer->applicationId();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					  Sets the activation window of this application to \a aw. The
 | 
				
			||||||
 | 
					  activation window is the widget that will be activated by
 | 
				
			||||||
 | 
					  activateWindow(). This is typically the application's main window.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  If \a activateOnMessage is true (the default), the window will be
 | 
				
			||||||
 | 
					  activated automatically every time a message is received, just prior
 | 
				
			||||||
 | 
					  to the messageReceived() signal being emitted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  \sa activateWindow(), messageReceived()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    actWin = aw;
 | 
				
			||||||
 | 
					    if (activateOnMessage)
 | 
				
			||||||
 | 
					        connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Returns the applications activation window if one has been set by
 | 
				
			||||||
 | 
					    calling setActivationWindow(), otherwise returns 0.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa setActivationWindow()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QWidget* QtSingleApplication::activationWindow() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return actWin;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					  De-minimizes, raises, and activates this application's activation window.
 | 
				
			||||||
 | 
					  This function does nothing if no activation window has been set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  This is a convenience function to show the user that this
 | 
				
			||||||
 | 
					  application instance has been activated when he has tried to start
 | 
				
			||||||
 | 
					  another instance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  This function should typically be called in response to the
 | 
				
			||||||
 | 
					  messageReceived() signal. By default, that will happen
 | 
				
			||||||
 | 
					  automatically, if an activation window has been set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  \sa setActivationWindow(), messageReceived(), initialize()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					void QtSingleApplication::activateWindow()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (actWin) {
 | 
				
			||||||
 | 
					        actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
 | 
				
			||||||
 | 
					        actWin->raise();
 | 
				
			||||||
 | 
					        actWin->activateWindow();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \fn void QtSingleApplication::messageReceived(const QString& message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This signal is emitted when the current instance receives a \a
 | 
				
			||||||
 | 
					    message from another instance of this application.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa sendMessage(), setActivationWindow(), activateWindow()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \fn void QtSingleApplication::initialize(bool dummy = true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \obsolete
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,107 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef QTSINGLEAPPLICATION_H
 | 
				
			||||||
 | 
					#define QTSINGLEAPPLICATION_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QApplication>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QtLocalPeer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(Q_OS_WIN)
 | 
				
			||||||
 | 
					#  if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT)
 | 
				
			||||||
 | 
					#    define QT_QTSINGLEAPPLICATION_EXPORT
 | 
				
			||||||
 | 
					#  elif defined(QT_QTSINGLEAPPLICATION_IMPORT)
 | 
				
			||||||
 | 
					#    if defined(QT_QTSINGLEAPPLICATION_EXPORT)
 | 
				
			||||||
 | 
					#      undef QT_QTSINGLEAPPLICATION_EXPORT
 | 
				
			||||||
 | 
					#    endif
 | 
				
			||||||
 | 
					#    define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport)
 | 
				
			||||||
 | 
					#  elif defined(QT_QTSINGLEAPPLICATION_EXPORT)
 | 
				
			||||||
 | 
					#    undef QT_QTSINGLEAPPLICATION_EXPORT
 | 
				
			||||||
 | 
					#    define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport)
 | 
				
			||||||
 | 
					#  endif
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#  define QT_QTSINGLEAPPLICATION_EXPORT
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
 | 
				
			||||||
 | 
					    QtSingleApplication(const QString &id, int &argc, char **argv);
 | 
				
			||||||
 | 
					    virtual ~QtSingleApplication();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if QT_VERSION < 0x050000
 | 
				
			||||||
 | 
					    QtSingleApplication(int &argc, char **argv, Type type);
 | 
				
			||||||
 | 
					#  if defined(Q_WS_X11)
 | 
				
			||||||
 | 
					    QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
 | 
				
			||||||
 | 
					    QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
 | 
				
			||||||
 | 
					    QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
 | 
				
			||||||
 | 
					#  endif // Q_WS_X11
 | 
				
			||||||
 | 
					#endif // QT_VERSION < 0x050000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool isRunning();
 | 
				
			||||||
 | 
					    QString id() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
 | 
				
			||||||
 | 
					    QWidget* activationWindow() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Obsolete:
 | 
				
			||||||
 | 
					    void initialize(bool dummy = true)
 | 
				
			||||||
 | 
					        { isRunning(); Q_UNUSED(dummy) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public Q_SLOTS:
 | 
				
			||||||
 | 
					    bool sendMessage(const QString &message, int timeout = 5000);
 | 
				
			||||||
 | 
					    void activateWindow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_SIGNALS:
 | 
				
			||||||
 | 
					    void messageReceived(const QString &message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    void sysInit(const QString &appId = QString());
 | 
				
			||||||
 | 
					    QtLocalPeer *peer;
 | 
				
			||||||
 | 
					    QWidget *actWin;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // QTSINGLEAPPLICATION_H
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					#include(../common.pri)
 | 
				
			||||||
 | 
					INCLUDEPATH += $$PWD
 | 
				
			||||||
 | 
					DEPENDPATH += $$PWD
 | 
				
			||||||
 | 
					QT *= network
 | 
				
			||||||
 | 
					greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qtsingleapplication-uselib:!qtsingleapplication-buildlib {
 | 
				
			||||||
 | 
					    LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					    SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp
 | 
				
			||||||
 | 
					    HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					win32 {
 | 
				
			||||||
 | 
					    contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT
 | 
				
			||||||
 | 
					    else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,149 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qtsinglecoreapplication.h"
 | 
				
			||||||
 | 
					#include "qtlocalpeer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \class QtSingleCoreApplication qtsinglecoreapplication.h
 | 
				
			||||||
 | 
					    \brief A variant of the QtSingleApplication class for non-GUI applications.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This class is a variant of QtSingleApplication suited for use in
 | 
				
			||||||
 | 
					    console (non-GUI) applications. It is an extension of
 | 
				
			||||||
 | 
					    QCoreApplication (instead of QApplication). It does not require
 | 
				
			||||||
 | 
					    the QtGui library.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The API and usage is identical to QtSingleApplication, except that
 | 
				
			||||||
 | 
					    functions relating to the "activation window" are not present, for
 | 
				
			||||||
 | 
					    obvious reasons. Please refer to the QtSingleApplication
 | 
				
			||||||
 | 
					    documentation for explanation of the usage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A QtSingleCoreApplication instance can communicate to a
 | 
				
			||||||
 | 
					    QtSingleApplication instance if they share the same application
 | 
				
			||||||
 | 
					    id. Hence, this class can be used to create a light-weight
 | 
				
			||||||
 | 
					    command-line tool that sends commands to a GUI application.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa QtSingleApplication
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Creates a QtSingleCoreApplication object. The application identifier
 | 
				
			||||||
 | 
					    will be QCoreApplication::applicationFilePath(). \a argc and \a
 | 
				
			||||||
 | 
					    argv are passed on to the QCoreAppliation constructor.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
 | 
				
			||||||
 | 
					    : QCoreApplication(argc, argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    peer = new QtLocalPeer(this);
 | 
				
			||||||
 | 
					    connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Creates a QtSingleCoreApplication object with the application
 | 
				
			||||||
 | 
					    identifier \a appId. \a argc and \a argv are passed on to the
 | 
				
			||||||
 | 
					    QCoreAppliation constructor.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv)
 | 
				
			||||||
 | 
					    : QCoreApplication(argc, argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    peer = new QtLocalPeer(this, appId);
 | 
				
			||||||
 | 
					    connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Returns true if another instance of this application is running;
 | 
				
			||||||
 | 
					    otherwise false.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This function does not find instances of this application that are
 | 
				
			||||||
 | 
					    being run by a different user (on Windows: that are running in
 | 
				
			||||||
 | 
					    another session).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa sendMessage()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtSingleCoreApplication::isRunning()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return peer->isClient();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Tries to send the text \a message to the currently running
 | 
				
			||||||
 | 
					    instance. The QtSingleCoreApplication object in the running instance
 | 
				
			||||||
 | 
					    will emit the messageReceived() signal when it receives the
 | 
				
			||||||
 | 
					    message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This function returns true if the message has been sent to, and
 | 
				
			||||||
 | 
					    processed by, the current instance. If there is no instance
 | 
				
			||||||
 | 
					    currently running, or if the running instance fails to process the
 | 
				
			||||||
 | 
					    message within \a timeout milliseconds, this function return false.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa isRunning(), messageReceived()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return peer->sendMessage(message, timeout);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    Returns the application identifier. Two processes with the same
 | 
				
			||||||
 | 
					    identifier will be regarded as instances of the same application.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QString QtSingleCoreApplication::id() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return peer->applicationId();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					    \fn void QtSingleCoreApplication::messageReceived(const QString& message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This signal is emitted when the current instance receives a \a
 | 
				
			||||||
 | 
					    message from another instance of this application.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    \sa sendMessage()
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,71 @@
 | 
				
			||||||
 | 
					/****************************************************************************
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 | 
				
			||||||
 | 
					** Contact: http://www.qt-project.org/legal
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of the Qt Solutions component.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_BEGIN_LICENSE:BSD$
 | 
				
			||||||
 | 
					** You may use this file under the terms of the BSD license as follows:
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** "Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					** modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					** met:
 | 
				
			||||||
 | 
					**   * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					**   * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					**     notice, this list of conditions and the following disclaimer in
 | 
				
			||||||
 | 
					**     the documentation and/or other materials provided with the
 | 
				
			||||||
 | 
					**     distribution.
 | 
				
			||||||
 | 
					**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 | 
				
			||||||
 | 
					**     of its contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					**     from this software without specific prior written permission.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** $QT_END_LICENSE$
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					****************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef QTSINGLECOREAPPLICATION_H
 | 
				
			||||||
 | 
					#define QTSINGLECOREAPPLICATION_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QCoreApplication>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QtLocalPeer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QtSingleCoreApplication : public QCoreApplication
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    QtSingleCoreApplication(int &argc, char **argv);
 | 
				
			||||||
 | 
					    QtSingleCoreApplication(const QString &id, int &argc, char **argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool isRunning();
 | 
				
			||||||
 | 
					    QString id() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public Q_SLOTS:
 | 
				
			||||||
 | 
					    bool sendMessage(const QString &message, int timeout = 5000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_SIGNALS:
 | 
				
			||||||
 | 
					    void messageReceived(const QString &message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    QtLocalPeer* peer;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // QTSINGLECOREAPPLICATION_H
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					INCLUDEPATH	+= $$PWD
 | 
				
			||||||
 | 
					DEPENDPATH      += $$PWD
 | 
				
			||||||
 | 
					HEADERS		+= $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h
 | 
				
			||||||
 | 
					SOURCES		+= $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QT *= network
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					win32:contains(TEMPLATE, lib):contains(CONFIG, shared) {
 | 
				
			||||||
 | 
					    DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ win32:LIBS += Ws2_32.lib
 | 
				
			||||||
win32:LIBS += $$PWD/..\..\SDK\lib\libdes.lib
 | 
					win32:LIBS += $$PWD/..\..\SDK\lib\libdes.lib
 | 
				
			||||||
win32:LIBS += $$PWD/..\..\SDK\lib\OpenSSL_VC\libcrypto64MD.lib
 | 
					win32:LIBS += $$PWD/..\..\SDK\lib\OpenSSL_VC\libcrypto64MD.lib
 | 
				
			||||||
win32:LIBS += $$PWD/..\..\SDK\lib\OpenSSL_VC\libssl64MD.lib
 | 
					win32:LIBS += $$PWD/..\..\SDK\lib\OpenSSL_VC\libssl64MD.lib
 | 
				
			||||||
win32:LIBS += $$PWD/..\..\SDK\lib\hv.lib
 | 
					win32:LIBS += $$PWD/..\..\SDK\lib\hvd.lib
 | 
				
			||||||
win32:LIBS += Ws2_32.lib
 | 
					win32:LIBS += Ws2_32.lib
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,17 +40,21 @@ win32:LIBS += Ws2_32.lib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SOURCES += \
 | 
					SOURCES += \
 | 
				
			||||||
    main.cpp \
 | 
					    main.cpp \
 | 
				
			||||||
    mainwindow.cpp \
 | 
					    maindialog.cpp \
 | 
				
			||||||
    cloggermaganer.cpp
 | 
					    mainwindow.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HEADERS += \
 | 
					HEADERS += \
 | 
				
			||||||
    mainwindow.h \
 | 
					    maindialog.h \
 | 
				
			||||||
    cloggermaganer.h
 | 
					    mainwindow.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FORMS += \
 | 
					FORMS += \
 | 
				
			||||||
 | 
					    maindialog.ui \
 | 
				
			||||||
    mainwindow.ui
 | 
					    mainwindow.ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Default rules for deployment.
 | 
					# Default rules for deployment.
 | 
				
			||||||
qnx: target.path = /tmp/$${TARGET}/bin
 | 
					qnx: target.path = /tmp/$${TARGET}/bin
 | 
				
			||||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
 | 
					else: unix:!android: target.path = /opt/$${TARGET}/bin
 | 
				
			||||||
!isEmpty(target.path): INSTALLS += target
 | 
					!isEmpty(target.path): INSTALLS += target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RESOURCES += \
 | 
				
			||||||
 | 
					    emscfgres.qrc
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					<RCC>
 | 
				
			||||||
 | 
					    <qresource prefix="/">
 | 
				
			||||||
 | 
					        <file>images/emscfg-main.ico</file>
 | 
				
			||||||
 | 
					        <file>images/hj-net.png</file>
 | 
				
			||||||
 | 
					        <file>images/icon.png</file>
 | 
				
			||||||
 | 
					    </qresource>
 | 
				
			||||||
 | 
					</RCC>
 | 
				
			||||||
											
												Binary file not shown.
											
										
									
								| 
		 After Width: | Height: | Size: 17 KiB  | 
											
												Binary file not shown.
											
										
									
								| 
		 After Width: | Height: | Size: 20 KiB  | 
											
												Binary file not shown.
											
										
									
								| 
		 After Width: | Height: | Size: 8.7 KiB  | 
| 
						 | 
					@ -17,7 +17,6 @@
 | 
				
			||||||
#include <hv/hsocket.h>
 | 
					#include <hv/hsocket.h>
 | 
				
			||||||
#include <hv/hssl.h>
 | 
					#include <hv/hssl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "cloggermaganer.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char *argv[])
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -36,11 +35,9 @@ int main(int argc, char *argv[])
 | 
				
			||||||
    std::string logFilePath = (appDir + QString::fromStdString("/emsConfigurer.log")).toStdString();
 | 
					    std::string logFilePath = (appDir + QString::fromStdString("/emsConfigurer.log")).toStdString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    hlog_set_file(logFilePath.c_str());
 | 
					    hlog_set_file(logFilePath.c_str());
 | 
				
			||||||
    hlogi("=========--- Welcome to the Earth ---=========\n");
 | 
					    hlogi("=========--- Welcome to the Earth ---=========");
 | 
				
			||||||
    hlogi("%s version: %s\n", argv[0], "1.1.0");
 | 
					    hlogi("%s version: %s", argv[0], "1.1.0");
 | 
				
			||||||
    hlog_fsync();
 | 
					    hlog_fsync();
 | 
				
			||||||
    hlogi("%s versionversionversionversionversionversion: %s\n", argv[0], "1.1.0");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    QTranslator translator;
 | 
					    QTranslator translator;
 | 
				
			||||||
    const QStringList uiLanguages = QLocale::system().uiLanguages();
 | 
					    const QStringList uiLanguages = QLocale::system().uiLanguages();
 | 
				
			||||||
| 
						 | 
					@ -55,6 +52,13 @@ int main(int argc, char *argv[])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    MainWindow w;
 | 
					    MainWindow w;
 | 
				
			||||||
 | 
					    //隐藏(不显示)最大化最小化按钮
 | 
				
			||||||
 | 
					    w.setWindowFlags(w.windowFlags()&~Qt::WindowMinMaxButtonsHint);
 | 
				
			||||||
 | 
					    //w.setWindowFlags(w.windowFlags() | Qt::WindowStaysOnTopHint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    w.setStyleSheet("{border-radius: 4px;}"); // 定制圆角
 | 
				
			||||||
 | 
					    //                            ".QLabel{background: gray;}.QTextEdit{background: white;}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //获取窗口尺寸并居中
 | 
					    //获取窗口尺寸并居中
 | 
				
			||||||
    QScreen *scr = app.primaryScreen();
 | 
					    QScreen *scr = app.primaryScreen();
 | 
				
			||||||
    int scr_w = scr->size().width();
 | 
					    int scr_w = scr->size().width();
 | 
				
			||||||
| 
						 | 
					@ -62,5 +66,7 @@ int main(int argc, char *argv[])
 | 
				
			||||||
    w.move((scr_w - w.width()) / 2, (scr_h - w.height()) / 2);
 | 
					    w.move((scr_w - w.width()) / 2, (scr_h - w.height()) / 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    w.show();
 | 
					    w.show();
 | 
				
			||||||
    return app.exec();
 | 
					    int ret = app.exec();
 | 
				
			||||||
 | 
					    hlogi("=========--- I'll be back! ---=========");
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					#include "maindialog.h"
 | 
				
			||||||
 | 
					#include "ui_maindialog.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MainDialog::MainDialog(QWidget *parent) :
 | 
				
			||||||
 | 
					    QDialog(parent),
 | 
				
			||||||
 | 
					    ui(new Ui::MainDialog)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ui->setupUi(this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MainDialog::~MainDialog()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    delete ui;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					#ifndef MAINDIALOG_H
 | 
				
			||||||
 | 
					#define MAINDIALOG_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <QDialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ui {
 | 
				
			||||||
 | 
					class MainDialog;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MainDialog : public QDialog
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit MainDialog(QWidget *parent = nullptr);
 | 
				
			||||||
 | 
					    ~MainDialog();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    Ui::MainDialog *ui;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // MAINDIALOG_H
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<ui version="4.0">
 | 
				
			||||||
 | 
					 <class>MainDialog</class>
 | 
				
			||||||
 | 
					 <widget class="QDialog" name="MainDialog">
 | 
				
			||||||
 | 
					  <property name="geometry">
 | 
				
			||||||
 | 
					   <rect>
 | 
				
			||||||
 | 
					    <x>0</x>
 | 
				
			||||||
 | 
					    <y>0</y>
 | 
				
			||||||
 | 
					    <width>867</width>
 | 
				
			||||||
 | 
					    <height>665</height>
 | 
				
			||||||
 | 
					   </rect>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
 | 
					  <property name="windowTitle">
 | 
				
			||||||
 | 
					   <string>Dialog</string>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
 | 
					 </widget>
 | 
				
			||||||
 | 
					 <resources/>
 | 
				
			||||||
 | 
					 <connections/>
 | 
				
			||||||
 | 
					</ui>
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,53 @@
 | 
				
			||||||
#include "mainwindow.h"
 | 
					#include "mainwindow.h"
 | 
				
			||||||
#include "ui_mainwindow.h"
 | 
					#include "ui_mainwindow.h"
 | 
				
			||||||
 | 
					#include <QPixmap>
 | 
				
			||||||
 | 
					#include <QMessageBox>
 | 
				
			||||||
 | 
					#include <QRegularExpression>
 | 
				
			||||||
 | 
					#include <QRegularExpressionValidator>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "MainDialog.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MainWindow::MainWindow(QWidget *parent)
 | 
					MainWindow::MainWindow(QWidget *parent)
 | 
				
			||||||
    : QMainWindow(parent)
 | 
					    : QMainWindow(parent)
 | 
				
			||||||
    , ui(new Ui::MainWindow)
 | 
					    , ui(new Ui::MainWindow)
 | 
				
			||||||
 | 
					    , m_pMainDialog(nullptr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    ui->setupUi(this);
 | 
					    ui->setupUi(this);
 | 
				
			||||||
 | 
					    this->setWindowIcon(QIcon(":/images/icon.png"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QPixmap pixmap(":/images/hj-net.png");
 | 
				
			||||||
 | 
					    pixmap = pixmap.scaled(250, 75, Qt::KeepAspectRatio, Qt::SmoothTransformation);  // 按比例缩放
 | 
				
			||||||
 | 
					    ui->label_logo->setPixmap(pixmap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QString qsLineEditStyle("QLineEdit { min-height: 20px; min-width: 120px; }");
 | 
				
			||||||
 | 
					    ui->userToken->setStyleSheet(qsLineEditStyle);
 | 
				
			||||||
 | 
					    ui->serverIp->setStyleSheet(qsLineEditStyle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QRegularExpression rx("^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$");
 | 
				
			||||||
 | 
					    QRegularExpressionValidator* ipValidator = new QRegularExpressionValidator(rx, this);
 | 
				
			||||||
 | 
					    ui->serverIp->setValidator(ipValidator);
 | 
				
			||||||
 | 
					    setIp("127.0.0.1");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MainWindow::~MainWindow()
 | 
					MainWindow::~MainWindow()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    delete ui;
 | 
					    delete ui;
 | 
				
			||||||
 | 
					    if (m_pMainDialog)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        delete m_pMainDialog;
 | 
				
			||||||
 | 
					        m_pMainDialog = nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::setIp(const QString &ip)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ui->serverIp->setText(ip);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWindow::on_pb_Logon_clicked()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    m_pMainDialog = new MainDialog();
 | 
				
			||||||
 | 
					    m_pMainDialog->show();
 | 
				
			||||||
 | 
					    this->close();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,10 @@
 | 
				
			||||||
#ifndef MAINWINDOW_H
 | 
					#ifndef MAINWINDOW_H
 | 
				
			||||||
#define MAINWINDOW_H
 | 
					#define MAINWINDOW_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <QMainWindow>
 | 
					#include <QMainWindow>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MainDialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QT_BEGIN_NAMESPACE
 | 
					QT_BEGIN_NAMESPACE
 | 
				
			||||||
namespace Ui { class MainWindow; }
 | 
					namespace Ui { class MainWindow; }
 | 
				
			||||||
QT_END_NAMESPACE
 | 
					QT_END_NAMESPACE
 | 
				
			||||||
| 
						 | 
					@ -15,7 +17,14 @@ public:
 | 
				
			||||||
    MainWindow(QWidget *parent = nullptr);
 | 
					    MainWindow(QWidget *parent = nullptr);
 | 
				
			||||||
    ~MainWindow();
 | 
					    ~MainWindow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    void setIp(const QString &ip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
					    void on_pb_Logon_clicked();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    Ui::MainWindow *ui;
 | 
					    Ui::MainWindow *ui;
 | 
				
			||||||
 | 
					    MainDialog* m_pMainDialog;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#endif // MAINWINDOW_H
 | 
					#endif // MAINWINDOW_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,20 +2,217 @@
 | 
				
			||||||
<ui version="4.0">
 | 
					<ui version="4.0">
 | 
				
			||||||
 <class>MainWindow</class>
 | 
					 <class>MainWindow</class>
 | 
				
			||||||
 <widget class="QMainWindow" name="MainWindow">
 | 
					 <widget class="QMainWindow" name="MainWindow">
 | 
				
			||||||
 | 
					  <property name="windowModality">
 | 
				
			||||||
 | 
					   <enum>Qt::WindowModal</enum>
 | 
				
			||||||
 | 
					  </property>
 | 
				
			||||||
  <property name="geometry">
 | 
					  <property name="geometry">
 | 
				
			||||||
   <rect>
 | 
					   <rect>
 | 
				
			||||||
    <x>0</x>
 | 
					    <x>0</x>
 | 
				
			||||||
    <y>0</y>
 | 
					    <y>0</y>
 | 
				
			||||||
    <width>800</width>
 | 
					    <width>551</width>
 | 
				
			||||||
    <height>600</height>
 | 
					    <height>352</height>
 | 
				
			||||||
   </rect>
 | 
					   </rect>
 | 
				
			||||||
  </property>
 | 
					  </property>
 | 
				
			||||||
  <property name="windowTitle">
 | 
					  <property name="contextMenuPolicy">
 | 
				
			||||||
   <string>MainWindow</string>
 | 
					   <enum>Qt::NoContextMenu</enum>
 | 
				
			||||||
  </property>
 | 
					  </property>
 | 
				
			||||||
  <widget class="QWidget" name="centralwidget"/>
 | 
					  <property name="windowTitle">
 | 
				
			||||||
  <widget class="QMenuBar" name="menubar"/>
 | 
					   <string>EMS Configurer</string>
 | 
				
			||||||
  <widget class="QStatusBar" name="statusbar"/>
 | 
					  </property>
 | 
				
			||||||
 | 
					  <widget class="QWidget" name="centralwidget">
 | 
				
			||||||
 | 
					   <widget class="QLabel" name="label_4">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>130</x>
 | 
				
			||||||
 | 
					      <y>176</y>
 | 
				
			||||||
 | 
					      <width>61</width>
 | 
				
			||||||
 | 
					      <height>21</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="text">
 | 
				
			||||||
 | 
					     <string>Host IP</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QLabel" name="label">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>140</x>
 | 
				
			||||||
 | 
					      <y>60</y>
 | 
				
			||||||
 | 
					      <width>231</width>
 | 
				
			||||||
 | 
					      <height>71</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="font">
 | 
				
			||||||
 | 
					     <font>
 | 
				
			||||||
 | 
					      <pointsize>26</pointsize>
 | 
				
			||||||
 | 
					      <bold>true</bold>
 | 
				
			||||||
 | 
					     </font>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="text">
 | 
				
			||||||
 | 
					     <string>Configurer</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="alignment">
 | 
				
			||||||
 | 
					     <set>Qt::AlignCenter</set>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QLineEdit" name="serverIp">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>220</x>
 | 
				
			||||||
 | 
					      <y>173</y>
 | 
				
			||||||
 | 
					      <width>124</width>
 | 
				
			||||||
 | 
					      <height>23</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="inputMask">
 | 
				
			||||||
 | 
					     <string/>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QLabel" name="label_6">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>370</x>
 | 
				
			||||||
 | 
					      <y>88</y>
 | 
				
			||||||
 | 
					      <width>161</width>
 | 
				
			||||||
 | 
					      <height>41</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="font">
 | 
				
			||||||
 | 
					     <font>
 | 
				
			||||||
 | 
					      <pointsize>16</pointsize>
 | 
				
			||||||
 | 
					     </font>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="text">
 | 
				
			||||||
 | 
					     <string>for EMU Host</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="alignment">
 | 
				
			||||||
 | 
					     <set>Qt::AlignCenter</set>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QPushButton" name="pb_Test">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>350</x>
 | 
				
			||||||
 | 
					      <y>173</y>
 | 
				
			||||||
 | 
					      <width>51</width>
 | 
				
			||||||
 | 
					      <height>21</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="text">
 | 
				
			||||||
 | 
					     <string>Test</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QLabel" name="label_logo">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>7</x>
 | 
				
			||||||
 | 
					      <y>7</y>
 | 
				
			||||||
 | 
					      <width>261</width>
 | 
				
			||||||
 | 
					      <height>61</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="text">
 | 
				
			||||||
 | 
					     <string>WWW.HJ-NET.COM</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QWidget" name="layoutWidget">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>140</x>
 | 
				
			||||||
 | 
					      <y>280</y>
 | 
				
			||||||
 | 
					      <width>251</width>
 | 
				
			||||||
 | 
					      <height>31</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <layout class="QHBoxLayout" name="horizontalLayout">
 | 
				
			||||||
 | 
					     <item>
 | 
				
			||||||
 | 
					      <widget class="QPushButton" name="pb_Logon">
 | 
				
			||||||
 | 
					       <property name="text">
 | 
				
			||||||
 | 
					        <string>Logon</string>
 | 
				
			||||||
 | 
					       </property>
 | 
				
			||||||
 | 
					      </widget>
 | 
				
			||||||
 | 
					     </item>
 | 
				
			||||||
 | 
					     <item>
 | 
				
			||||||
 | 
					      <spacer name="horizontalSpacer">
 | 
				
			||||||
 | 
					       <property name="orientation">
 | 
				
			||||||
 | 
					        <enum>Qt::Horizontal</enum>
 | 
				
			||||||
 | 
					       </property>
 | 
				
			||||||
 | 
					       <property name="sizeHint" stdset="0">
 | 
				
			||||||
 | 
					        <size>
 | 
				
			||||||
 | 
					         <width>40</width>
 | 
				
			||||||
 | 
					         <height>20</height>
 | 
				
			||||||
 | 
					        </size>
 | 
				
			||||||
 | 
					       </property>
 | 
				
			||||||
 | 
					      </spacer>
 | 
				
			||||||
 | 
					     </item>
 | 
				
			||||||
 | 
					     <item>
 | 
				
			||||||
 | 
					      <widget class="QPushButton" name="pb_Close">
 | 
				
			||||||
 | 
					       <property name="text">
 | 
				
			||||||
 | 
					        <string>Close</string>
 | 
				
			||||||
 | 
					       </property>
 | 
				
			||||||
 | 
					      </widget>
 | 
				
			||||||
 | 
					     </item>
 | 
				
			||||||
 | 
					    </layout>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QLineEdit" name="userToken">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>220</x>
 | 
				
			||||||
 | 
					      <y>233</y>
 | 
				
			||||||
 | 
					      <width>181</width>
 | 
				
			||||||
 | 
					      <height>23</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="contextMenuPolicy">
 | 
				
			||||||
 | 
					     <enum>Qt::NoContextMenu</enum>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="acceptDrops">
 | 
				
			||||||
 | 
					     <bool>false</bool>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="inputMethodHints">
 | 
				
			||||||
 | 
					     <set>Qt::ImhHiddenText|Qt::ImhNoAutoUppercase|Qt::ImhNoEditMenu|Qt::ImhNoPredictiveText|Qt::ImhSensitiveData</set>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="echoMode">
 | 
				
			||||||
 | 
					     <enum>QLineEdit::Password</enum>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QLabel" name="label_2">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>130</x>
 | 
				
			||||||
 | 
					      <y>200</y>
 | 
				
			||||||
 | 
					      <width>68</width>
 | 
				
			||||||
 | 
					      <height>30</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="text">
 | 
				
			||||||
 | 
					     <string>Login name</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QLabel" name="label_3">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>130</x>
 | 
				
			||||||
 | 
					      <y>233</y>
 | 
				
			||||||
 | 
					      <width>56</width>
 | 
				
			||||||
 | 
					      <height>21</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					    <property name="text">
 | 
				
			||||||
 | 
					     <string>Password</string>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					   <widget class="QLineEdit" name="userName">
 | 
				
			||||||
 | 
					    <property name="geometry">
 | 
				
			||||||
 | 
					     <rect>
 | 
				
			||||||
 | 
					      <x>220</x>
 | 
				
			||||||
 | 
					      <y>203</y>
 | 
				
			||||||
 | 
					      <width>181</width>
 | 
				
			||||||
 | 
					      <height>23</height>
 | 
				
			||||||
 | 
					     </rect>
 | 
				
			||||||
 | 
					    </property>
 | 
				
			||||||
 | 
					   </widget>
 | 
				
			||||||
 | 
					  </widget>
 | 
				
			||||||
 </widget>
 | 
					 </widget>
 | 
				
			||||||
 <resources/>
 | 
					 <resources/>
 | 
				
			||||||
 <connections/>
 | 
					 <connections/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,8 @@
 | 
				
			||||||
#include "openjson.h"
 | 
					#include "openjson.h"
 | 
				
			||||||
#include "iconv-utils.h"
 | 
					#include "iconv-utils.h"
 | 
				
			||||||
#include "kutilities.h"
 | 
					#include "kutilities.h"
 | 
				
			||||||
 | 
					#include "opmysql.h"
 | 
				
			||||||
 | 
					#include "mqtt_msg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EventHandler::EventHandler()
 | 
					EventHandler::EventHandler()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -58,6 +60,23 @@ void EventHandler::onRecvHandler(hio_t* io, void* buf, int readbytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if( Frame_DeviceData_Request == pReadFrame->frame_type )
 | 
					    if( Frame_DeviceData_Request == pReadFrame->frame_type )
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        handleGatherData(io, buf, readbytes);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EventHandler::handleGatherData(hio_t* io, void* buf, int readbytes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __USING_NAMESPACE_HJ__;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MessageFrame respFrame;
 | 
				
			||||||
 | 
					    MessageFrame* pReadFrame = (MessageFrame*)buf;
 | 
				
			||||||
    hlogi("<=== reveive device data request");
 | 
					    hlogi("<=== reveive device data request");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    OpenJson json;
 | 
					    OpenJson json;
 | 
				
			||||||
| 
						 | 
					@ -66,8 +85,18 @@ void EventHandler::onRecvHandler(hio_t* io, void* buf, int readbytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    hlogi("<=== recieve buffer coding is [%s]", buf_code == GBK ? "GBK" : (buf_code == UTF8 ? "UTF8" : "UNKNOWN CODING"));
 | 
					    hlogi("<=== recieve buffer coding is [%s]", buf_code == GBK ? "GBK" : (buf_code == UTF8 ? "UTF8" : "UNKNOWN CODING"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MessageData* pData = (MessageData*)pReadFrame->frame_content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _DEBUG
 | 
				
			||||||
 | 
					    hlogd("<=== MessageData structure [\n%s\n]", printHex(pData, pReadFrame->frame_len).c_str());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //这里将帧内帧的内容转换为字符串,符合json格式的字符串,详见mqtt_msg.h的MessageData结构定义
 | 
					    //这里将帧内帧的内容转换为字符串,符合json格式的字符串,详见mqtt_msg.h的MessageData结构定义
 | 
				
			||||||
        std::string msg((char*)pReadFrame->frame_content + 8, pReadFrame->frame_len - 8);
 | 
					    std::string msg((char*)pReadFrame->frame_content + MSG_HEADER_LENGTH, pReadFrame->frame_len - MSG_HEADER_LENGTH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _DEBUG
 | 
				
			||||||
 | 
					    hlogd("<=== json content [\n%s\n]", msg.c_str());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if( buf_code == CODING::GBK
 | 
					    if( buf_code == CODING::GBK
 | 
				
			||||||
        || buf_code == CODING::UNKOWN )
 | 
					        || buf_code == CODING::UNKOWN )
 | 
				
			||||||
| 
						 | 
					@ -190,7 +219,7 @@ void EventHandler::onRecvHandler(hio_t* io, void* buf, int readbytes)
 | 
				
			||||||
            auto& pNode = IdCodeContent[0]; //这是只解析第一个节点
 | 
					            auto& pNode = IdCodeContent[0]; //这是只解析第一个节点
 | 
				
			||||||
            std::string oid = pNode["OID"].s();
 | 
					            std::string oid = pNode["OID"].s();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                //OpDatabase::getInstance()->InsertMessage(timestamp, msg_type, fsucode, out_compress, (int)pData->mqtt_topic, (int)pData->device_id);
 | 
					            OpDatabase::getInstance()->InsertMessage(timestamp, msg_type, fsucode, out_compress, (int)pData->mqtt_topic, (int)pData->device_id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if( msg_type == "web-read" )
 | 
					        if( msg_type == "web-read" )
 | 
				
			||||||
| 
						 | 
					@ -206,7 +235,7 @@ void EventHandler::onRecvHandler(hio_t* io, void* buf, int readbytes)
 | 
				
			||||||
            auto& pNode = IdCodeContent[0]; //这是只解析第一个节点
 | 
					            auto& pNode = IdCodeContent[0]; //这是只解析第一个节点
 | 
				
			||||||
            std::string oid = pNode.s();
 | 
					            std::string oid = pNode.s();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                //OpDatabase::getInstance()->InsertMessage(timestamp, msg_type, fsucode, out_compress, (int)pData->mqtt_topic, (int)pData->device_id);
 | 
					            OpDatabase::getInstance()->InsertMessage(timestamp, msg_type, fsucode, out_compress, (int)pData->mqtt_topic, (int)pData->device_id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        //delete[] pTmp;
 | 
					        //delete[] pTmp;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -217,6 +246,4 @@ void EventHandler::onRecvHandler(hio_t* io, void* buf, int readbytes)
 | 
				
			||||||
        hio_write(io, (void*)&respFrame, sizeof respFrame);
 | 
					        hio_write(io, (void*)&respFrame, sizeof respFrame);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -10,5 +10,9 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	static void onRecvHandler(hio_t* io, void* buf, int readbytes);
 | 
						static void onRecvHandler(hio_t* io, void* buf, int readbytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						//处理采集程序上传的数据,以JSON数据上报
 | 
				
			||||||
 | 
						static void handleGatherData(hio_t* io, void* buf, int readbytes);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__NAMESPACE_BEGIN__(HJ)
 | 
					__NAMESPACE_BEGIN__(HJ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FRAME_HEADER_LENGTH (5)
 | 
				
			||||||
 | 
					#define FRAME_TAILE_LENGTH (4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum tagErrorCode : unsigned char
 | 
					typedef enum tagErrorCode : unsigned char
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ERR_OK = 0X00,
 | 
						ERR_OK = 0X00,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,14 +6,6 @@
 | 
				
			||||||
#include <fstream>
 | 
					#include <fstream>
 | 
				
			||||||
#include <iomanip>
 | 
					#include <iomanip>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "kdefine.h"
 | 
					 | 
				
			||||||
#include "frame_define.h"
 | 
					 | 
				
			||||||
#include "kutilities.h"
 | 
					 | 
				
			||||||
#include "mqtt_msg.h"
 | 
					 | 
				
			||||||
#include "opmysql.h"
 | 
					 | 
				
			||||||
#include "iconv-utils.h"
 | 
					 | 
				
			||||||
#include "openjson.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <hv/hv.h>
 | 
					#include <hv/hv.h>
 | 
				
			||||||
#include <hv/hmain.h>
 | 
					#include <hv/hmain.h>
 | 
				
			||||||
#include <hv/iniparser.h>
 | 
					#include <hv/iniparser.h>
 | 
				
			||||||
| 
						 | 
					@ -21,6 +13,8 @@
 | 
				
			||||||
#include <hv/hsocket.h>
 | 
					#include <hv/hsocket.h>
 | 
				
			||||||
#include <hv/hssl.h>
 | 
					#include <hv/hssl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "kdefine.h"
 | 
				
			||||||
 | 
					#include "opmysql.h"
 | 
				
			||||||
#include "eventhandler.h"
 | 
					#include "eventhandler.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef TEST_UNPACK
 | 
					#ifndef TEST_UNPACK
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,17 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include "kdefine.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma pack(1)
 | 
					#pragma pack(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__NAMESPACE_BEGIN__(HJ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MSG_HEADER_LENGTH (8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum tagTopic : char
 | 
					typedef enum tagTopic : char
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	GateWayPublicTopic_Server = 0,
 | 
						GateWayPublicTopic_Server = 0,
 | 
				
			||||||
	ServerPublicTopic_GateWay = 1,
 | 
						ServerPublicTopic_GateWay = 1,
 | 
				
			||||||
}FrameType, MQTT_Topic;
 | 
					} MQTT_Topic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum tagDataType : char
 | 
					typedef enum tagDataType : char
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -19,7 +25,7 @@ typedef enum tagDataType : char
 | 
				
			||||||
	DT_GATEWAY_WRITE = 7,
 | 
						DT_GATEWAY_WRITE = 7,
 | 
				
			||||||
	FRAME_REQUEST = 8,
 | 
						FRAME_REQUEST = 8,
 | 
				
			||||||
	FRAME_RESPONSE = 9,
 | 
						FRAME_RESPONSE = 9,
 | 
				
			||||||
}FrameDataType,MQTT_DataType;
 | 
					}MQTT_DataType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -50,6 +56,6 @@ typedef struct tagMsgData
 | 
				
			||||||
	char content_data[1];				//MQTT包字符串的内容
 | 
						char content_data[1];				//MQTT包字符串的内容
 | 
				
			||||||
}MessageData;
 | 
					}MessageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__NAMESPACE_END__(HJ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma pack()
 | 
					#pragma pack()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,164 @@
 | 
				
			||||||
 | 
					#ifndef HV_ASYNC_HTTP_CLIENT_H_
 | 
				
			||||||
 | 
					#define HV_ASYNC_HTTP_CLIENT_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "EventLoopThread.h"
 | 
				
			||||||
 | 
					#include "Channel.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "HttpMessage.h"
 | 
				
			||||||
 | 
					#include "HttpParser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace hv {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename Conn>
 | 
				
			||||||
 | 
					class ConnPool {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    int size() {
 | 
				
			||||||
 | 
					        return conns_.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool get(Conn& conn) {
 | 
				
			||||||
 | 
					        if (conns_.empty()) return false;
 | 
				
			||||||
 | 
					        conn = conns_.front();
 | 
				
			||||||
 | 
					        conns_.pop_front();
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool add(const Conn& conn) {
 | 
				
			||||||
 | 
					        conns_.push_back(conn);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool remove(const Conn& conn) {
 | 
				
			||||||
 | 
					        auto iter = conns_.begin();
 | 
				
			||||||
 | 
					        while (iter != conns_.end()) {
 | 
				
			||||||
 | 
					            if (*iter == conn) {
 | 
				
			||||||
 | 
					                iter = conns_.erase(iter);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                ++iter;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    std::list<Conn>  conns_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct HttpClientTask {
 | 
				
			||||||
 | 
					    HttpRequestPtr          req;
 | 
				
			||||||
 | 
					    HttpResponseCallback    cb;
 | 
				
			||||||
 | 
					    uint64_t                start_time;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					typedef std::shared_ptr<HttpClientTask> HttpClientTaskPtr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct HttpClientContext {
 | 
				
			||||||
 | 
					    HttpClientTaskPtr   task;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpResponsePtr     resp;
 | 
				
			||||||
 | 
					    HttpParserPtr       parser;
 | 
				
			||||||
 | 
					    TimerID             timerID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpClientContext() {
 | 
				
			||||||
 | 
					        timerID = INVALID_TIMER_ID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ~HttpClientContext() {
 | 
				
			||||||
 | 
					        cancelTimer();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void cancelTimer() {
 | 
				
			||||||
 | 
					        if (timerID != INVALID_TIMER_ID) {
 | 
				
			||||||
 | 
					            killTimer(timerID);
 | 
				
			||||||
 | 
					            timerID = INVALID_TIMER_ID;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void cancelTask() {
 | 
				
			||||||
 | 
					        cancelTimer();
 | 
				
			||||||
 | 
					        task = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void callback() {
 | 
				
			||||||
 | 
					        cancelTimer();
 | 
				
			||||||
 | 
					        if (task && task->cb) {
 | 
				
			||||||
 | 
					            task->cb(resp);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // NOTE: task done
 | 
				
			||||||
 | 
					        task = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void successCallback() {
 | 
				
			||||||
 | 
					        callback();
 | 
				
			||||||
 | 
					        resp = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void errorCallback() {
 | 
				
			||||||
 | 
					        resp = NULL;
 | 
				
			||||||
 | 
					        callback();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HV_EXPORT AsyncHttpClient : private EventLoopThread {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    AsyncHttpClient(EventLoopPtr loop = NULL) : EventLoopThread(loop) {
 | 
				
			||||||
 | 
					        if (loop == NULL) {
 | 
				
			||||||
 | 
					            EventLoopThread::start(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ~AsyncHttpClient() {
 | 
				
			||||||
 | 
					        EventLoopThread::stop(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // thread-safe
 | 
				
			||||||
 | 
					    int send(const HttpRequestPtr& req, HttpResponseCallback resp_cb);
 | 
				
			||||||
 | 
					    int send(const HttpClientTaskPtr& task) {
 | 
				
			||||||
 | 
					        EventLoopThread::loop()->queueInLoop(std::bind(&AsyncHttpClient::sendInLoop, this, task));
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    void sendInLoop(HttpClientTaskPtr task) {
 | 
				
			||||||
 | 
					        int err = doTask(task);
 | 
				
			||||||
 | 
					        if (err != 0 && task->cb) {
 | 
				
			||||||
 | 
					            task->cb(NULL);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int doTask(const HttpClientTaskPtr& task);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static int sendRequest(const SocketChannelPtr& channel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // channel
 | 
				
			||||||
 | 
					    const SocketChannelPtr& getChannel(int fd) {
 | 
				
			||||||
 | 
					        return channels[fd];
 | 
				
			||||||
 | 
					        // return fd < channels.capacity() ? channels[fd] : NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const SocketChannelPtr& addChannel(hio_t* io) {
 | 
				
			||||||
 | 
					        auto channel = std::make_shared<SocketChannel>(io);
 | 
				
			||||||
 | 
					        channel->newContext<HttpClientContext>();
 | 
				
			||||||
 | 
					        int fd = channel->fd();
 | 
				
			||||||
 | 
					        channels[fd] = channel;
 | 
				
			||||||
 | 
					        return channels[fd];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void removeChannel(const SocketChannelPtr& channel) {
 | 
				
			||||||
 | 
					        channel->deleteContext<HttpClientContext>();
 | 
				
			||||||
 | 
					        int fd = channel->fd();
 | 
				
			||||||
 | 
					        channels.erase(fd);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    // NOTE: just one loop thread, no need mutex.
 | 
				
			||||||
 | 
					    // fd => SocketChannelPtr
 | 
				
			||||||
 | 
					    std::map<int, SocketChannelPtr>         channels;
 | 
				
			||||||
 | 
					    // peeraddr => ConnPool
 | 
				
			||||||
 | 
					    std::map<std::string, ConnPool<int>>    conn_pools;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // HV_ASYNC_HTTP_CLIENT_H_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,377 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,47 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,277 @@
 | 
				
			||||||
 | 
					#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 ::hv::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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,116 @@
 | 
				
			||||||
 | 
					#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;
 | 
				
			||||||
 | 
					        if (isRunning()) 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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,138 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,171 @@
 | 
				
			||||||
 | 
					#ifndef HV_HTTP_CLIENT_H_
 | 
				
			||||||
 | 
					#define HV_HTTP_CLIENT_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hexport.h"
 | 
				
			||||||
 | 
					#include "hssl.h"
 | 
				
			||||||
 | 
					#include "HttpMessage.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "HttpClient.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_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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// low-level api
 | 
				
			||||||
 | 
					// @retval >=0 connfd, <0 error
 | 
				
			||||||
 | 
					HV_EXPORT int http_client_connect(http_client_t* cli, const char* host, int port, int https, int timeout);
 | 
				
			||||||
 | 
					HV_EXPORT int http_client_send_header(http_client_t* cli, HttpRequest* req);
 | 
				
			||||||
 | 
					HV_EXPORT int http_client_send_data(http_client_t* cli, const char* data, int size);
 | 
				
			||||||
 | 
					HV_EXPORT int http_client_recv_data(http_client_t* cli, char* data, int size);
 | 
				
			||||||
 | 
					HV_EXPORT int http_client_recv_response(http_client_t* cli, HttpResponse* resp);
 | 
				
			||||||
 | 
					HV_EXPORT int http_client_close(http_client_t* cli);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // low-level api
 | 
				
			||||||
 | 
					    int connect(const char* host, int port = DEFAULT_HTTP_PORT, int https = 0, int timeout = DEFAULT_HTTP_CONNECT_TIMEOUT) {
 | 
				
			||||||
 | 
					        return http_client_connect(_client, host, port, https, timeout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int sendHeader(HttpRequest* req) {
 | 
				
			||||||
 | 
					        return http_client_send_header(_client, req);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int sendData(const char* data, int size) {
 | 
				
			||||||
 | 
					        return http_client_send_data(_client, data, size);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int recvData(char* data, int size) {
 | 
				
			||||||
 | 
					        return http_client_recv_data(_client, data, size);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int recvResponse(HttpResponse* resp) {
 | 
				
			||||||
 | 
					        return http_client_recv_response(_client, resp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int close() {
 | 
				
			||||||
 | 
					        return http_client_close(_client);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    http_client_t* _client;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // HV_HTTP_CLIENT_H_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,213 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,501 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,100 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,163 @@
 | 
				
			||||||
 | 
					#ifndef HV_HTTP_SERVER_H_
 | 
				
			||||||
 | 
					#define HV_HTTP_SERVER_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hexport.h"
 | 
				
			||||||
 | 
					#include "hssl.h"
 | 
				
			||||||
 | 
					// #include "EventLoop.h"
 | 
				
			||||||
 | 
					#include "HttpService.h"
 | 
				
			||||||
 | 
					// #include "WebSocketServer.h"
 | 
				
			||||||
 | 
					namespace hv {
 | 
				
			||||||
 | 
					class EventLoop;
 | 
				
			||||||
 | 
					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 HV_EXPORT 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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_ptr<hv::EventLoop> loop(int idx = -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setMaxWorkerConnectionNum(uint32_t num) {
 | 
				
			||||||
 | 
					        this->worker_connections = num;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    size_t connectionNum();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,281 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,308 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,322 @@
 | 
				
			||||||
 | 
					#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) {
 | 
				
			||||||
 | 
					        EventLoopPtr worker_loop = worker_threads.loop(idx);
 | 
				
			||||||
 | 
					        if (worker_loop == NULL) {
 | 
				
			||||||
 | 
					            worker_loop = acceptor_loop;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return worker_loop;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //@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);
 | 
				
			||||||
 | 
					        if (!isRunning()) {
 | 
				
			||||||
 | 
					            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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,200 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,174 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,71 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,193 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,50 @@
 | 
				
			||||||
 | 
					#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());
 | 
				
			||||||
 | 
					    if (decoded_size > 0) {
 | 
				
			||||||
 | 
					        decoded_buf.resize(decoded_size);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        decoded_buf.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return decoded_buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // HV_BASE64_H_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,49 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,130 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,145 @@
 | 
				
			||||||
 | 
					#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);
 | 
				
			||||||
 | 
					HV_EXPORT char* hv_strnrchr(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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,257 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,102 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,271 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,244 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,122 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,131 @@
 | 
				
			||||||
 | 
					#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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,134 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,176 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,758 @@
 | 
				
			||||||
 | 
					#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 hio_s        hio_t;
 | 
				
			||||||
 | 
					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 hevent_s     hsignal_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef void (*hevent_cb)   (hevent_t* ev);
 | 
				
			||||||
 | 
					typedef void (*hio_cb)      (hio_t* io);
 | 
				
			||||||
 | 
					typedef void (*hidle_cb)    (hidle_t* idle);
 | 
				
			||||||
 | 
					typedef void (*htimer_cb)   (htimer_t* timer);
 | 
				
			||||||
 | 
					typedef void (*hsignal_cb)  (hsignal_t* sig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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_SIGNAL  = 0x00000200,
 | 
				
			||||||
 | 
					    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_PIPE       = 0x00000020,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// signal
 | 
				
			||||||
 | 
					HV_EXPORT hsignal_t* hsignal_add(hloop_t* loop, hsignal_cb cb, int signo);
 | 
				
			||||||
 | 
					HV_EXPORT void       hsignal_del(hsignal_t* sig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//-----------------pipe---------------------------------------------
 | 
				
			||||||
 | 
					// @see examples/pipe_test.c
 | 
				
			||||||
 | 
					HV_EXPORT int hio_create_pipe(hloop_t* loop, hio_t* pipeio[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//-----------------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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,119 @@
 | 
				
			||||||
 | 
					#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;
 | 
				
			||||||
 | 
					    const char* description;
 | 
				
			||||||
 | 
					} 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 opt_size);
 | 
				
			||||||
 | 
					HV_EXPORT int dump_opt_long(const option_t* long_options, int opt_size, char* out_str, int out_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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,268 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,183 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,337 @@
 | 
				
			||||||
 | 
					#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
 | 
				
			||||||
 | 
					    #undef  OS_WIN
 | 
				
			||||||
 | 
					    #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_WINNT
 | 
				
			||||||
 | 
					    #define _WIN32_WINNT 0x0600
 | 
				
			||||||
 | 
					    #elif _WIN32_WINNT < 0x0600
 | 
				
			||||||
 | 
					    #undef _WIN32_WINNT
 | 
				
			||||||
 | 
					    #define _WIN32_WINNT 0x0600
 | 
				
			||||||
 | 
					    #endif
 | 
				
			||||||
 | 
					    #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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,79 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,287 @@
 | 
				
			||||||
 | 
					#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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef closesocket
 | 
				
			||||||
 | 
					HV_INLINE int closesocket(int sockfd) {
 | 
				
			||||||
 | 
					    return close(sockfd);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,91 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,92 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,217 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,249 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,114 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,78 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,295 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					#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    3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					#ifndef HV_INI_PARSER_H_
 | 
				
			||||||
 | 
					#define HV_INI_PARSER_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hexport.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DEFAULT_INI_COMMENT "#"
 | 
				
			||||||
 | 
					#define DEFAULT_INI_DELIM   "="
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// fwd
 | 
				
			||||||
 | 
					class IniNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HV_EXPORT IniParser {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    IniParser();
 | 
				
			||||||
 | 
					    ~IniParser();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int LoadFromFile(const char* filepath);
 | 
				
			||||||
 | 
					    int LoadFromMem(const char* data);
 | 
				
			||||||
 | 
					    int Unload();
 | 
				
			||||||
 | 
					    int Reload();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string DumpString();
 | 
				
			||||||
 | 
					    int Save();
 | 
				
			||||||
 | 
					    int SaveAs(const char* filepath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::list<std::string> GetSections();
 | 
				
			||||||
 | 
					    std::list<std::string> GetKeys(const std::string& section = "");
 | 
				
			||||||
 | 
					    std::string GetValue(const std::string& key, const std::string& section = "");
 | 
				
			||||||
 | 
					    void        SetValue(const std::string& key, const std::string& value, const std::string& section = "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // T = [bool, int, float]
 | 
				
			||||||
 | 
					    template<typename T>
 | 
				
			||||||
 | 
					    T Get(const std::string& key, const std::string& section = "", T defvalue = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // T = [bool, int, float]
 | 
				
			||||||
 | 
					    template<typename T>
 | 
				
			||||||
 | 
					    void Set(const std::string& key, const T& value, const std::string& section = "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    void DumpString(IniNode* pNode, std::string& str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    std::string _comment;
 | 
				
			||||||
 | 
					    std::string _delim;
 | 
				
			||||||
 | 
					    std::string _filepath;
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    IniNode* root_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // HV_INI_PARSER_H_
 | 
				
			||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					#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_
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,340 @@
 | 
				
			||||||
 | 
					#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*   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 void mqtt_client_set_host(mqtt_client_t* cli, const char* host, int port, int ssl);
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setHost(const char* host, int port = DEFAULT_MQTT_PORT, int ssl = 0) {
 | 
				
			||||||
 | 
					        mqtt_client_set_host(client, host, port, ssl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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_
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue