ui布局自适应
This commit is contained in:
1 parent
98f44890f8
commit
3587129b90
40 files changed
+929
-142
No files matched your search
@@ -3,11 +3,18 @@
|
||||
#include "encoding.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <winsock2.h>
|
||||
@@ -19,6 +26,11 @@ constexpr Socket kInvalidSocket = INVALID_SOCKET;
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/SecImportExport.h>
|
||||
#include <Security/SecureTransport.h>
|
||||
#endif
|
||||
using Socket = int;
|
||||
constexpr Socket kInvalidSocket = -1;
|
||||
#endif
|
||||
@@ -37,6 +49,119 @@ void close_socket(Socket socket) {
|
||||
#endif
|
||||
}
|
||||
|
||||
struct ConnectionStream {
|
||||
Socket socket = kInvalidSocket;
|
||||
#ifdef __APPLE__
|
||||
SSLContextRef tls = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __APPLE__
|
||||
OSStatus socket_read(SSLConnectionRef reference, void* data, std::size_t* length) {
|
||||
const Socket socket = *static_cast<const Socket*>(reference);
|
||||
const long count = recv(socket, static_cast<char*>(data), static_cast<int>(*length), 0);
|
||||
if (count > 0) {
|
||||
*length = static_cast<std::size_t>(count);
|
||||
return noErr;
|
||||
}
|
||||
*length = 0;
|
||||
if (count == 0) return errSSLClosedGraceful;
|
||||
return errno == EAGAIN || errno == EWOULDBLOCK ? errSSLWouldBlock : errSSLInternal;
|
||||
}
|
||||
|
||||
OSStatus socket_write(SSLConnectionRef reference, const void* data, std::size_t* length) {
|
||||
const Socket socket = *static_cast<const Socket*>(reference);
|
||||
const long count = send(socket, static_cast<const char*>(data), static_cast<int>(*length), 0);
|
||||
if (count > 0) {
|
||||
*length = static_cast<std::size_t>(count);
|
||||
return noErr;
|
||||
}
|
||||
*length = 0;
|
||||
return errno == EAGAIN || errno == EWOULDBLOCK ? errSSLWouldBlock : errSSLInternal;
|
||||
}
|
||||
|
||||
void* load_identity(const std::filesystem::path& path, std::string& error) {
|
||||
std::ifstream input(path, std::ios::binary);
|
||||
if (!input) {
|
||||
error = "无法读取 HTTPS 身份证书";
|
||||
return nullptr;
|
||||
}
|
||||
const std::string bytes((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
|
||||
CFDataRef data = CFDataCreate(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(bytes.data()),
|
||||
static_cast<CFIndex>(bytes.size()));
|
||||
const void* keys[] = {kSecImportExportPassphrase};
|
||||
const void* values[] = {CFSTR("spine-connector-local")};
|
||||
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1,
|
||||
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFArrayRef items = nullptr;
|
||||
const OSStatus status = SecPKCS12Import(data, options, &items);
|
||||
CFRelease(options);
|
||||
CFRelease(data);
|
||||
if (status != errSecSuccess || !items || CFArrayGetCount(items) == 0) {
|
||||
if (items) CFRelease(items);
|
||||
error = "无法读取 HTTPS 身份证书";
|
||||
return nullptr;
|
||||
}
|
||||
CFDictionaryRef item = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(items, 0));
|
||||
CFTypeRef identity = CFDictionaryGetValue(item, kSecImportItemIdentity);
|
||||
if (identity) CFRetain(identity);
|
||||
CFRelease(items);
|
||||
if (!identity) error = "HTTPS 身份证书缺少私钥";
|
||||
return const_cast<void*>(identity);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool open_stream(ConnectionStream& stream, void* identity, std::string& error) {
|
||||
if (!identity) return true;
|
||||
#ifdef __APPLE__
|
||||
SSLContextRef context = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType);
|
||||
if (!context) {
|
||||
error = "无法创建 HTTPS 服务";
|
||||
return false;
|
||||
}
|
||||
SSLSetIOFuncs(context, socket_read, socket_write);
|
||||
SSLSetConnection(context, &stream.socket);
|
||||
SSLSetProtocolVersionMin(context, kTLSProtocol12);
|
||||
const void* certificates[] = {identity};
|
||||
CFArrayRef certificate_array = CFArrayCreate(kCFAllocatorDefault, certificates, 1, &kCFTypeArrayCallBacks);
|
||||
const OSStatus certificate_status = SSLSetCertificate(context, certificate_array);
|
||||
CFRelease(certificate_array);
|
||||
if (certificate_status != noErr) {
|
||||
CFRelease(context);
|
||||
error = "无法配置 HTTPS 身份证书";
|
||||
return false;
|
||||
}
|
||||
OSStatus status;
|
||||
do {
|
||||
status = SSLHandshake(context);
|
||||
} while (status == errSSLWouldBlock);
|
||||
if (status != noErr) {
|
||||
CFRelease(context);
|
||||
return false;
|
||||
}
|
||||
stream.tls = context;
|
||||
return true;
|
||||
#else
|
||||
(void)stream;
|
||||
error = "当前平台不支持 HTTPS 连接器";
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void close_stream(ConnectionStream& stream) {
|
||||
#ifdef __APPLE__
|
||||
if (stream.tls) {
|
||||
SSLClose(stream.tls);
|
||||
CFRelease(stream.tls);
|
||||
stream.tls = nullptr;
|
||||
}
|
||||
#endif
|
||||
if (stream.socket != kInvalidSocket) {
|
||||
close_socket(stream.socket);
|
||||
stream.socket = kInvalidSocket;
|
||||
}
|
||||
}
|
||||
|
||||
std::string lowercase(std::string value) {
|
||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
return value;
|
||||
@@ -56,21 +181,49 @@ std::string status_text(int status) {
|
||||
}
|
||||
}
|
||||
|
||||
void send_all(Socket socket, const std::string& data) {
|
||||
int read_stream(ConnectionStream& stream, char* buffer, int length) {
|
||||
#ifdef __APPLE__
|
||||
if (stream.tls) {
|
||||
std::size_t received = 0;
|
||||
const OSStatus status = SSLRead(stream.tls, buffer, static_cast<std::size_t>(length), &received);
|
||||
if (status == errSSLClosedGraceful || status == errSSLClosedNoNotify) return static_cast<int>(received);
|
||||
if (status != noErr && status != errSSLWouldBlock) return -1;
|
||||
return static_cast<int>(received);
|
||||
}
|
||||
#endif
|
||||
return recv(stream.socket, buffer, length, 0);
|
||||
}
|
||||
|
||||
void send_all(ConnectionStream& stream, const std::string& data) {
|
||||
std::size_t sent = 0;
|
||||
while (sent < data.size()) {
|
||||
const int count = send(socket, data.data() + sent, static_cast<int>((std::min)(data.size() - sent, static_cast<std::size_t>(1 << 20))), 0);
|
||||
int count;
|
||||
#ifdef __APPLE__
|
||||
if (stream.tls) {
|
||||
std::size_t written = 0;
|
||||
const OSStatus status = SSLWrite(stream.tls, data.data() + sent,
|
||||
(std::min)(data.size() - sent, static_cast<std::size_t>(1 << 20)), &written);
|
||||
if (status != noErr && status != errSSLWouldBlock) return;
|
||||
count = static_cast<int>(written);
|
||||
} else {
|
||||
count = send(stream.socket, data.data() + sent,
|
||||
static_cast<int>((std::min)(data.size() - sent, static_cast<std::size_t>(1 << 20))), 0);
|
||||
}
|
||||
#else
|
||||
count = send(stream.socket, data.data() + sent,
|
||||
static_cast<int>((std::min)(data.size() - sent, static_cast<std::size_t>(1 << 20))), 0);
|
||||
#endif
|
||||
if (count <= 0) return;
|
||||
sent += static_cast<std::size_t>(count);
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_request(Socket socket, HttpRequest& request, int& error_status) {
|
||||
bool parse_request(ConnectionStream& stream, HttpRequest& request, int& error_status) {
|
||||
std::string buffer;
|
||||
char chunk[8192];
|
||||
std::size_t header_end = std::string::npos;
|
||||
while ((header_end = buffer.find("\r\n\r\n")) == std::string::npos) {
|
||||
const int count = recv(socket, chunk, sizeof(chunk), 0);
|
||||
const int count = read_stream(stream, chunk, sizeof(chunk));
|
||||
if (count <= 0) return false;
|
||||
buffer.append(chunk, count);
|
||||
if (buffer.size() > kMaxHeaders) { error_status = 413; return false; }
|
||||
@@ -109,7 +262,7 @@ bool parse_request(Socket socket, HttpRequest& request, int& error_status) {
|
||||
if (content_length > kMaxBody) { error_status = 413; return false; }
|
||||
request.body = buffer.substr(header_end + 4);
|
||||
while (request.body.size() < content_length) {
|
||||
const int count = recv(socket, chunk, sizeof(chunk), 0);
|
||||
const int count = read_stream(stream, chunk, sizeof(chunk));
|
||||
if (count <= 0) return false;
|
||||
request.body.append(chunk, count);
|
||||
}
|
||||
@@ -117,11 +270,20 @@ bool parse_request(Socket socket, HttpRequest& request, int& error_status) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void serve_client(Socket client, const RequestHandler& handler) {
|
||||
void serve_client(Socket client, void* identity, const RequestHandler& handler) {
|
||||
ConnectionStream stream{client};
|
||||
std::string tls_error;
|
||||
if (!open_stream(stream, identity, tls_error)) {
|
||||
close_stream(stream);
|
||||
return;
|
||||
}
|
||||
HttpRequest request;
|
||||
int error_status = 400;
|
||||
HttpResponse response;
|
||||
if (parse_request(client, request, error_status)) {
|
||||
if (parse_request(stream, request, error_status)) {
|
||||
#ifdef __APPLE__
|
||||
request.secure = stream.tls != nullptr;
|
||||
#endif
|
||||
try { response = handler(request); }
|
||||
catch (const std::exception& error) {
|
||||
response.status = 500;
|
||||
@@ -138,9 +300,9 @@ void serve_client(Socket client, const RequestHandler& handler) {
|
||||
output << "HTTP/1.1 " << response.status << ' ' << status_text(response.status) << "\r\n";
|
||||
for (const auto& [name, value] : response.headers) output << name << ": " << value << "\r\n";
|
||||
output << "\r\n";
|
||||
send_all(client, output.str());
|
||||
send_all(client, response.body);
|
||||
close_socket(client);
|
||||
send_all(stream, output.str());
|
||||
send_all(stream, response.body);
|
||||
close_stream(stream);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -148,8 +310,21 @@ void serve_client(Socket client, const RequestHandler& handler) {
|
||||
HttpServer::HttpServer() = default;
|
||||
HttpServer::~HttpServer() { stop(); }
|
||||
|
||||
bool HttpServer::start(unsigned short port, RequestHandler handler, std::string& error) {
|
||||
bool HttpServer::start(unsigned short port, RequestHandler handler, std::string& error,
|
||||
const std::filesystem::path& identity) {
|
||||
if (running_) return true;
|
||||
#ifdef __APPLE__
|
||||
if (!identity.empty()) {
|
||||
void* loaded = load_identity(identity, error);
|
||||
if (loaded) tls_identity_ = std::shared_ptr<void>(loaded, [](void* value) { CFRelease(value); });
|
||||
if (!tls_identity_) return false;
|
||||
}
|
||||
#else
|
||||
if (!identity.empty()) {
|
||||
error = "当前平台不支持 HTTPS 连接器";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
WSADATA data{};
|
||||
if (WSAStartup(MAKEWORD(2, 2), &data) != 0) { error = "无法初始化网络服务"; return false; }
|
||||
@@ -182,7 +357,9 @@ void HttpServer::accept_loop() {
|
||||
if (running_) continue;
|
||||
break;
|
||||
}
|
||||
std::thread(serve_client, client, handler_).detach();
|
||||
std::thread([client, identity = tls_identity_, handler = handler_] {
|
||||
serve_client(client, identity.get(), handler);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,3 +383,7 @@ void HttpServer::stop() {
|
||||
}
|
||||
|
||||
} // namespace connector
|
||||
|
||||
#ifdef __APPLE__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
Reference in new issue
Block a user