@ -1,4 +1,5 @@
|
|||||||
add_library(Base
|
add_library(Base
|
||||||
|
Database.h Database.cpp
|
||||||
DataStructure.h DataStructure.cpp
|
DataStructure.h DataStructure.cpp
|
||||||
HttpSession.h HttpSession.cpp
|
HttpSession.h HttpSession.cpp
|
||||||
)
|
)
|
||||||
@ -12,4 +13,5 @@ target_link_libraries(Base
|
|||||||
PRIVATE OpenSSL::Crypto
|
PRIVATE OpenSSL::Crypto
|
||||||
PUBLIC Kylin::Router
|
PUBLIC Kylin::Router
|
||||||
PUBLIC Kylin::Core
|
PUBLIC Kylin::Core
|
||||||
|
PRIVATE sqlite3
|
||||||
)
|
)
|
@ -13,6 +13,15 @@ struct VisitorStats {
|
|||||||
int64_t lastViewTime = 0;
|
int64_t lastViewTime = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct VisitRecord {
|
||||||
|
int id;
|
||||||
|
std::string url;
|
||||||
|
std::string visitorUuid;
|
||||||
|
std::string lastUserAgent;
|
||||||
|
int lastViewTime;
|
||||||
|
int pageViewCount;
|
||||||
|
};
|
||||||
|
|
||||||
struct SiteStats {
|
struct SiteStats {
|
||||||
int totalViews = 0;
|
int totalViews = 0;
|
||||||
int totalVisitors = 0;
|
int totalVisitors = 0;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
namespace Older {
|
namespace Older {
|
||||||
bool Database::open(const std::string &path) {
|
bool Database::open(const std::string &path) {
|
||||||
if (sqlite3_open(path.c_str(), &m_sqlite)) {
|
if (sqlite3_open(path.c_str(), &m_sqlite) != SQLITE_OK) {
|
||||||
LOG(error) << "Can't open database: " << sqlite3_errmsg(m_sqlite);
|
LOG(error) << "Can't open database: " << sqlite3_errmsg(m_sqlite);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ std::list<VisitorStats> Database::mostViewedUrls(int n) {
|
|||||||
ORDER BY total_page_views DESC
|
ORDER BY total_page_views DESC
|
||||||
LIMIT ?;
|
LIMIT ?;
|
||||||
)";
|
)";
|
||||||
|
|
||||||
if (sqlite3_prepare_v2(m_sqlite, query, -1, &statement, nullptr) == SQLITE_OK) {
|
if (sqlite3_prepare_v2(m_sqlite, query, -1, &statement, nullptr) == SQLITE_OK) {
|
||||||
sqlite3_bind_int(statement, 1, n);
|
sqlite3_bind_int(statement, 1, n);
|
||||||
|
|
||||||
@ -228,6 +228,59 @@ Account Database::user(int64_t id) const {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::list<VisitRecord> Database::visitRecords() {
|
||||||
|
constexpr char *sql = "SELECT * FROM visit_analysis;";
|
||||||
|
sqlite3_stmt *statement = nullptr;
|
||||||
|
std::list<VisitRecord> ret;
|
||||||
|
if (sqlite3_prepare_v2(m_sqlite, sql, -1, &statement, nullptr) != SQLITE_OK) {
|
||||||
|
LOG(error) << "sqlite3_prepare_v2() failed: " << sqlite3_errmsg(m_sqlite);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sqlite3_step(statement) == SQLITE_ROW) {
|
||||||
|
VisitRecord record;
|
||||||
|
record.id = sqlite3_column_int(statement, 0);
|
||||||
|
record.url = reinterpret_cast<const char *>(sqlite3_column_text(statement, 1));
|
||||||
|
record.visitorUuid = reinterpret_cast<const char *>(sqlite3_column_text(statement, 2));
|
||||||
|
record.lastUserAgent = reinterpret_cast<const char *>(sqlite3_column_text(statement, 3));
|
||||||
|
record.lastViewTime = sqlite3_column_int(statement, 4);
|
||||||
|
record.pageViewCount = sqlite3_column_int(statement, 5);
|
||||||
|
ret.push_back(record);
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::removeVisitRecord(int id) {
|
||||||
|
constexpr char *sql = "DELETE FROM visit_analysis WHERE id = ?;";
|
||||||
|
sqlite3_stmt *statement = nullptr;
|
||||||
|
if (sqlite3_prepare_v2(m_sqlite, sql, -1, &statement, nullptr) != SQLITE_OK) {
|
||||||
|
LOG(error) << "sqlite3_prepare_v2() failed: " << sqlite3_errmsg(m_sqlite);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sqlite3_bind_int(statement, 1, id) != SQLITE_OK) {
|
||||||
|
LOG(error) << "sqlite3_bind_int() failed: " << sqlite3_errmsg(m_sqlite);
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
if (sqlite3_step(statement) == SQLITE_DONE) {
|
||||||
|
if (sqlite3_changes(m_sqlite) > 0) {
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
LOG(warning) << "cannot find item " << id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(error) << "sqlite3_step() failed: " << sqlite3_errmsg(m_sqlite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理资源
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
void Database::initialize() {
|
void Database::initialize() {
|
||||||
createVisitAnalysisTable();
|
createVisitAnalysisTable();
|
||||||
createUsersTable();
|
createUsersTable();
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef __DATABASE_H__
|
#ifndef __DATABASE_H__
|
||||||
#define __DATABASE_H__
|
#define __DATABASE_H__
|
||||||
|
|
||||||
#include "Base/DataStructure.h"
|
#include "DataStructure.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -18,9 +18,11 @@ public:
|
|||||||
std::list<VisitorStats> mostViewedUrls(int n);
|
std::list<VisitorStats> mostViewedUrls(int n);
|
||||||
std::list<VisitorStats> latestViewedUrls(int n);
|
std::list<VisitorStats> latestViewedUrls(int n);
|
||||||
SiteStats siteStats();
|
SiteStats siteStats();
|
||||||
|
std::list<VisitRecord> visitRecords();
|
||||||
|
bool removeVisitRecord(int id);
|
||||||
void createUser(const Account &account);
|
void createUser(const Account &account);
|
||||||
Account user(const std::string &identifier)const;
|
Account user(const std::string &identifier) const;
|
||||||
Account user(int64_t id)const;
|
Account user(int64_t id) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void createVisitAnalysisTable();
|
void createVisitAnalysisTable();
|
||||||
@ -29,6 +31,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
sqlite3 *m_sqlite = nullptr;
|
sqlite3 *m_sqlite = nullptr;
|
||||||
};}
|
};
|
||||||
|
} // namespace Older
|
||||||
|
|
||||||
#endif // __DATABASE_H__
|
#endif // __DATABASE_H__
|
@ -5,14 +5,21 @@ project(Older VERSION 0.1 LANGUAGES C CXX)
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
find_package(Boost REQUIRED COMPONENTS json)
|
find_package(Boost REQUIRED COMPONENTS json program_options)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND git rev-parse --short HEAD
|
||||||
|
OUTPUT_VARIABLE GIT_COMMIT_ID
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(3rdparty)
|
add_subdirectory(3rdparty)
|
||||||
add_subdirectory(Base)
|
add_subdirectory(Base)
|
||||||
add_subdirectory(Server)
|
add_subdirectory(Server)
|
||||||
add_subdirectory(WebRTC)
|
add_subdirectory(WebRTC)
|
||||||
add_subdirectory(WeChat)
|
add_subdirectory(WeChat)
|
||||||
|
add_subdirectory(Tools)
|
||||||
add_subdirectory(UnitTest)
|
add_subdirectory(UnitTest)
|
||||||
|
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
22
Readme.md
22
Readme.md
@ -30,6 +30,28 @@
|
|||||||
5. 将 url 的 last_view_time进行排序,统计出最新的 n 个记录。
|
5. 将 url 的 last_view_time进行排序,统计出最新的 n 个记录。
|
||||||
6. 将所有记录的不同 visitor_uuid 相加作为网站总访客数,将所有page_view_count相加作为网站总访问次数。
|
6. 将所有记录的不同 visitor_uuid 相加作为网站总访客数,将所有page_view_count相加作为网站总访问次数。
|
||||||
|
|
||||||
|
### 问题
|
||||||
|
|
||||||
|
sqlite3 如何将所有记录的 url 字段(TEXT类型) 中的 工作笔记 替换为 漫步闲谈?例如 /工作笔记/abc 替换为 /漫步闲谈/abc
|
||||||
|
|
||||||
|
```sqlite
|
||||||
|
sqlite3 ./database.sqlite
|
||||||
|
|
||||||
|
DELETE FROM visit_analysis WHERE rowid IN (
|
||||||
|
SELECT t1.rowid
|
||||||
|
FROM visit_analysis t1
|
||||||
|
JOIN visit_analysis t2
|
||||||
|
ON t1.visitor_uuid = t2.visitor_uuid
|
||||||
|
AND REPLACE(t1.url, '工作笔记', '漫步闲谈') = t2.url
|
||||||
|
AND t1.rowid != t2.rowid
|
||||||
|
WHERE t1.url LIKE '%工作笔记%'
|
||||||
|
);
|
||||||
|
|
||||||
|
UPDATE visit_analysis SET url = REPLACE(url, '工作笔记', '漫步闲谈') WHERE url LIKE '%工作笔记%';
|
||||||
|
|
||||||
|
.quit
|
||||||
|
```
|
||||||
|
|
||||||
## 注册/登录
|
## 注册/登录
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "Base/Database.h"
|
||||||
#include "Base/HttpSession.h"
|
#include "Base/HttpSession.h"
|
||||||
#include "Base/Messages.h"
|
#include "Base/Messages.h"
|
||||||
#include "Core/IoContext.h"
|
#include "Core/IoContext.h"
|
||||||
#include "Core/MessageManager.h"
|
#include "Core/MessageManager.h"
|
||||||
#include "Core/Singleton.h"
|
#include "Core/Singleton.h"
|
||||||
#include "Database.h"
|
|
||||||
#include "Nng/Asio.h"
|
#include "Nng/Asio.h"
|
||||||
#include "Nng/Message.h"
|
#include "Nng/Message.h"
|
||||||
#include "Nng/Socket.h"
|
#include "Nng/Socket.h"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
configure_file(Configuration.h.in Configuration.h)
|
||||||
|
|
||||||
add_executable(Older main.cpp
|
add_executable(Older main.cpp
|
||||||
Application.h Application.cpp
|
Application.h Application.cpp
|
||||||
Database.h Database.cpp
|
|
||||||
ResponseUtility.h ResponseUtility.cpp
|
ResponseUtility.h ResponseUtility.cpp
|
||||||
ServiceLogic.h ServiceLogic.inl ServiceLogic.cpp
|
ServiceLogic.h ServiceLogic.inl ServiceLogic.cpp
|
||||||
SessionStore.h SessionStore.cpp
|
SessionStore.h SessionStore.cpp
|
||||||
@ -9,6 +10,7 @@ add_executable(Older main.cpp
|
|||||||
|
|
||||||
target_include_directories(Older
|
target_include_directories(Older
|
||||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(Older
|
target_link_libraries(Older
|
||||||
@ -17,5 +19,5 @@ target_link_libraries(Older
|
|||||||
PRIVATE Kylin::Http
|
PRIVATE Kylin::Http
|
||||||
PRIVATE Kylin::Nng
|
PRIVATE Kylin::Nng
|
||||||
PRIVATE Boost::json
|
PRIVATE Boost::json
|
||||||
PRIVATE sqlite3
|
PRIVATE Boost::program_options
|
||||||
)
|
)
|
2
Server/Configuration.h.in
Normal file
2
Server/Configuration.h.in
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#define GIT_COMMIT_ID "@GIT_COMMIT_ID@"
|
||||||
|
#define APP_VERSION "@PROJECT_VERSION@"
|
@ -1,7 +1,7 @@
|
|||||||
#include "ServiceLogic.h"
|
#include "ServiceLogic.h"
|
||||||
|
#include "Base/Database.h"
|
||||||
#include "Base/HttpSession.h"
|
#include "Base/HttpSession.h"
|
||||||
#include "Core/Singleton.h"
|
#include "Core/Singleton.h"
|
||||||
#include "Database.h"
|
|
||||||
#include "SessionStore.h"
|
#include "SessionStore.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@ -1,19 +1,57 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "Configuration.h"
|
||||||
#include "Core/Logger.h"
|
#include "Core/Logger.h"
|
||||||
#include "Core/Singleton.h"
|
#include "Core/Singleton.h"
|
||||||
#include <boost/asio/signal_set.hpp>
|
#include <boost/asio/signal_set.hpp>
|
||||||
|
#include <boost/program_options/options_description.hpp>
|
||||||
|
#include <boost/program_options/parsers.hpp>
|
||||||
|
#include <boost/program_options/variables_map.hpp>
|
||||||
|
|
||||||
int main(int argc, char const *argv[]) {
|
int main(int argc, char const *argv[]) {
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Older;
|
using namespace Older;
|
||||||
|
|
||||||
|
boost::program_options::options_description description("Allowed options");
|
||||||
|
// clang-format off
|
||||||
|
description.add_options()
|
||||||
|
("help,h", "produce help message.")
|
||||||
|
("version,v", "print app version.")
|
||||||
|
("exit,e", "signal program to exit.")
|
||||||
|
("prefix", boost::program_options::value<std::string>(),"set prefix path (default: ${pwd})");
|
||||||
|
// clang-format on
|
||||||
|
boost::program_options::variables_map values;
|
||||||
|
|
||||||
boost::log::initialize("logs/Older");
|
boost::log::initialize("logs/Older");
|
||||||
auto application = Singleton<Application>::construct();
|
|
||||||
|
|
||||||
boost::asio::signal_set signals(application->ioContext(), SIGINT, SIGTERM, SIGHUP);
|
try {
|
||||||
signals.async_wait([&application](boost::system::error_code const &, int signal) {
|
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, description), values);
|
||||||
LOG(info) << "capture " << (signal == SIGINT ? "SIGINT" : "SIGTERM") << ",stop!";
|
boost::program_options::notify(values);
|
||||||
application->exit(5);
|
|
||||||
});
|
|
||||||
|
|
||||||
return application->exec();
|
if (values.count("help")) {
|
||||||
|
std::cout << description << std::endl;
|
||||||
|
std::exit(0);
|
||||||
|
} else if (values.count("version")) {
|
||||||
|
std::cout << "version: " << APP_VERSION << std::endl;
|
||||||
|
std::cout << "commit: " << GIT_COMMIT_ID << std::endl;
|
||||||
|
std::cout << "compiled on: " << __DATE__ << " " << __TIME__ << std::endl;
|
||||||
|
std::exit(0);
|
||||||
|
} else if (values.count("exit")) {
|
||||||
|
// Application::requetExit();
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
LOG(info) << "version: " << APP_VERSION << std::endl;
|
||||||
|
LOG(info) << "commit: " << GIT_COMMIT_ID << std::endl;
|
||||||
|
LOG(info) << "compiled on: " << __DATE__ << " " << __TIME__ << std::endl;
|
||||||
|
auto application = Singleton<Application>::construct();
|
||||||
|
|
||||||
|
boost::asio::signal_set signals(application->ioContext(), SIGINT, SIGTERM, SIGHUP);
|
||||||
|
signals.async_wait([&application](boost::system::error_code const &, int signal) {
|
||||||
|
LOG(info) << "capture " << (signal == SIGINT ? "SIGINT" : "SIGTERM") << ",stop!";
|
||||||
|
application->exit(5);
|
||||||
|
});
|
||||||
|
return application->exec();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LOG(error) << e.what();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
8
Tools/CMakeLists.txt
Normal file
8
Tools/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
add_executable(UrlCheck UrlCheck.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(UrlCheck
|
||||||
|
PRIVATE Base
|
||||||
|
PRIVATE Boost::program_options
|
||||||
|
PRIVATE OpenSSL::SSL
|
||||||
|
PRIVATE OpenSSL::Crypto
|
||||||
|
)
|
121
Tools/UrlCheck.cpp
Normal file
121
Tools/UrlCheck.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include "Base/Database.h"
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/ssl/context.hpp>
|
||||||
|
#include <boost/beast/core/flat_buffer.hpp>
|
||||||
|
#include <boost/beast/core/tcp_stream.hpp>
|
||||||
|
#include <boost/beast/http/message.hpp>
|
||||||
|
#include <boost/beast/http/read.hpp>
|
||||||
|
#include <boost/beast/http/string_body.hpp>
|
||||||
|
#include <boost/beast/http/write.hpp>
|
||||||
|
#include <boost/beast/ssl/ssl_stream.hpp>
|
||||||
|
#include <boost/beast/version.hpp>
|
||||||
|
#include <boost/program_options/options_description.hpp>
|
||||||
|
#include <boost/program_options/parsers.hpp>
|
||||||
|
#include <boost/program_options/variables_map.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
bool isUrlValid(boost::asio::io_context &ioContext, const std::string &host, const std::string &port, const std::string &target);
|
||||||
|
|
||||||
|
int main(int argc, char const *argv[]) {
|
||||||
|
boost::program_options::options_description description("Allowed options");
|
||||||
|
// clang-format off
|
||||||
|
description.add_options()
|
||||||
|
("help,h", "produce help message.")
|
||||||
|
("database,d", boost::program_options::value<std::string>(),"set database path")
|
||||||
|
("delete-invalid", boost::program_options::value<bool>()->default_value(false),"delete invalid url");
|
||||||
|
// clang-format on
|
||||||
|
boost::program_options::variables_map values;
|
||||||
|
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, description), values);
|
||||||
|
boost::program_options::notify(values);
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
if (values.count("help")) {
|
||||||
|
std::cout << description << std::endl;
|
||||||
|
std::exit(0);
|
||||||
|
} else if (values.count("database")) {
|
||||||
|
path = values.at("database").as<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.empty()) {
|
||||||
|
std::cerr << "please specify the database path." << std::endl;
|
||||||
|
std::cout << description << std::endl;
|
||||||
|
return 1;
|
||||||
|
} else if (!std::filesystem::exists(path)) {
|
||||||
|
std::cerr << "database file " << path << " not existed." << std::endl;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Older::Database database;
|
||||||
|
if (!database.open(path)) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::io_context ioContext;
|
||||||
|
auto items = database.visitRecords();
|
||||||
|
for (auto &item : items) {
|
||||||
|
bool valid = isUrlValid(ioContext, "amass.fun", "443", item.url);
|
||||||
|
std::cout << item.url << std::endl;
|
||||||
|
std::cout << "valid: " << valid << std::endl;
|
||||||
|
if (!valid && values.at("delete-invalid").as<bool>()) {
|
||||||
|
std::cout << "delete: " << database.removeVisitRecord(item.id) << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << "----------" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUrlValid(boost::asio::io_context &ioContext, const std::string &host, const std::string &port, const std::string &target) {
|
||||||
|
using namespace boost;
|
||||||
|
using namespace boost::asio;
|
||||||
|
using namespace boost::asio::ip;
|
||||||
|
using namespace boost::beast;
|
||||||
|
try {
|
||||||
|
// 1. 创建SSL上下文
|
||||||
|
ssl::context ssl_ctx(ssl::context::tlsv12_client);
|
||||||
|
ssl_ctx.set_default_verify_paths();
|
||||||
|
ssl_ctx.set_verify_mode(ssl::verify_peer);
|
||||||
|
|
||||||
|
// 2. 创建TCP解析器和SSL流
|
||||||
|
tcp::resolver resolver(ioContext);
|
||||||
|
beast::ssl_stream<beast::tcp_stream> stream(ioContext, ssl_ctx);
|
||||||
|
|
||||||
|
// 3. 设置SNI主机名(重要!)
|
||||||
|
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
|
||||||
|
throw boost::system::system_error(::ERR_get_error(), boost::asio ::error::get_ssl_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 解析主机名并建立连接
|
||||||
|
auto const results = resolver.resolve(host, port);
|
||||||
|
beast::get_lowest_layer(stream).connect(results);
|
||||||
|
stream.handshake(ssl::stream_base::client);
|
||||||
|
|
||||||
|
// 5. 构造并发送HEAD请求
|
||||||
|
http::request<http::string_body> req{http::verb::get, target, 11};
|
||||||
|
req.set(http::field::host, host);
|
||||||
|
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||||
|
http::write(stream, req);
|
||||||
|
|
||||||
|
// 6. 读取响应
|
||||||
|
beast::flat_buffer buffer;
|
||||||
|
http::response<http::string_body> res;
|
||||||
|
http::read(stream, buffer, res);
|
||||||
|
// 7. 检查HTTP状态码(2xx/3xx视为可访问)
|
||||||
|
const unsigned status = res.result_int();
|
||||||
|
const bool accessible = (status >= 200 && status < 400);
|
||||||
|
|
||||||
|
// 8. 优雅关闭连接
|
||||||
|
beast::error_code ec;
|
||||||
|
stream.shutdown(ec);
|
||||||
|
if (ec == net::error::eof || ec == boost::asio::ssl::error::stream_truncated) {
|
||||||
|
ec = {};
|
||||||
|
}
|
||||||
|
return accessible;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user