Static Webview - RTC - UDP - TCP - WSS
This commit is contained in:
@ -11,6 +11,7 @@
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -299,14 +300,52 @@ namespace HttpRequestWrapper {
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Destroying HTTP request client");
|
||||
#endif
|
||||
should_abort_ = true;
|
||||
int s = active_socket_.exchange(-1);
|
||||
if (s >= 0) {
|
||||
#ifdef _WIN32
|
||||
::shutdown(static_cast<SOCKET>(s), SD_BOTH);
|
||||
#else
|
||||
::shutdown(s, SHUT_RDWR);
|
||||
#endif
|
||||
}
|
||||
if (request_future_.valid()) {
|
||||
request_future_.wait();
|
||||
}
|
||||
#ifdef WITH_SSL
|
||||
cleanupSSL();
|
||||
#endif
|
||||
if (isolate_ != nullptr) {
|
||||
v8::Locker locker{isolate_};
|
||||
v8::Isolate::Scope isolate_scope(isolate_);
|
||||
v8::HandleScope handle_scope(isolate_);
|
||||
on_ready_state_change_.Reset();
|
||||
on_load_.Reset();
|
||||
on_error_.Reset();
|
||||
on_timeout_.Reset();
|
||||
on_abort_.Reset();
|
||||
on_progress_.Reset();
|
||||
on_load_start_.Reset();
|
||||
on_load_end_.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequestClient::open(const std::string& method, const std::string& url, bool async) {
|
||||
if (request_future_.valid()) {
|
||||
should_abort_ = true;
|
||||
int s = active_socket_.exchange(-1);
|
||||
if (s >= 0) {
|
||||
#ifdef _WIN32
|
||||
::shutdown(static_cast<SOCKET>(s), SD_BOTH);
|
||||
#else
|
||||
::shutdown(s, SHUT_RDWR);
|
||||
#endif
|
||||
}
|
||||
request_future_.wait();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||
|
||||
|
||||
method_ = method;
|
||||
url_ = url;
|
||||
async_ = async;
|
||||
@ -350,10 +389,11 @@ namespace HttpRequestWrapper {
|
||||
#endif
|
||||
|
||||
request_data_ = data;
|
||||
|
||||
should_abort_ = false;
|
||||
|
||||
if (async_) {
|
||||
// use thread pool
|
||||
GlobalThreadPool::getInstance().enqueue([this]() {
|
||||
request_future_ = GlobalThreadPool::getInstance().enqueue([this]() {
|
||||
this->performRequest();
|
||||
});
|
||||
} else {
|
||||
@ -364,11 +404,21 @@ namespace HttpRequestWrapper {
|
||||
|
||||
void HttpRequestClient::abort() {
|
||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||
|
||||
|
||||
if (ready_state_ == DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
should_abort_ = true;
|
||||
int s = active_socket_.exchange(-1);
|
||||
if (s >= 0) {
|
||||
#ifdef _WIN32
|
||||
::shutdown(static_cast<SOCKET>(s), SD_BOTH);
|
||||
#else
|
||||
::shutdown(s, SHUT_RDWR);
|
||||
#endif
|
||||
}
|
||||
|
||||
setReadyState(DONE);
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "HTTP request aborted");
|
||||
@ -437,6 +487,26 @@ namespace HttpRequestWrapper {
|
||||
on_load_end_.Reset(isolate_, callback);
|
||||
}
|
||||
|
||||
static size_t response_end(const std::string& response, const std::string& method) {
|
||||
size_t header_end = response.find("\r\n\r\n");
|
||||
if (header_end == std::string::npos) return std::string::npos;
|
||||
std::string lower(response, 0, header_end);
|
||||
for (char& c : lower) c = (char)std::tolower((unsigned char)c);
|
||||
int status = atoi(lower.c_str() + lower.find(' ') + 1);
|
||||
if (status == 204 || status == 304 || method == "HEAD" || method == "head") {
|
||||
return header_end + 4;
|
||||
}
|
||||
size_t pos = lower.find("content-length:");
|
||||
if (pos != std::string::npos) {
|
||||
return header_end + 4 + strtoull(lower.c_str() + pos + 15, nullptr, 10);
|
||||
}
|
||||
if (lower.find("transfer-encoding:") != std::string::npos) {
|
||||
size_t term = response.find("0\r\n\r\n", header_end + 4);
|
||||
return term == std::string::npos ? std::string::npos : term + 5;
|
||||
}
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
void HttpRequestClient::performRequest() {
|
||||
active_operation_ = true;
|
||||
|
||||
@ -497,7 +567,8 @@ namespace HttpRequestWrapper {
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Acquiring connection for HTTP request");
|
||||
#endif
|
||||
SOCKET sock = g_connection_pool.acquire_connection(host, port, is_https);
|
||||
SOCKET sock = g_connection_pool.acquire_connection(host, port, is_https, &should_abort_);
|
||||
active_socket_ = static_cast<int>(sock);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
response_.error = "Failed to acquire connection";
|
||||
#ifdef DEBUG_NETWORK
|
||||
@ -523,27 +594,36 @@ namespace HttpRequestWrapper {
|
||||
::setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Connected successfully to %s:%d", host.c_str(), port);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SSL
|
||||
if (is_https) {
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Performing SSL handshake");
|
||||
#endif
|
||||
ssl_socket_ = static_cast<int>(sock);
|
||||
if (!performSSLHandshake(static_cast<int>(sock), host)) {
|
||||
response_.error = "SSL handshake failed";
|
||||
active_socket_ = -1;
|
||||
closesocket(sock);
|
||||
setReadyState(DONE);
|
||||
handleError(response_.error);
|
||||
active_operation_ = false;
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "SSL handshake completed successfully");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string request_data = formatHttpRequest(method_, path, host, request_data_);
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Sending HTTP request (%zu bytes):", request_data.length());
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Request preview: %.200s", request_data.c_str());
|
||||
#endif
|
||||
|
||||
int bytes_sent = 0;
|
||||
#ifdef WITH_SSL
|
||||
@ -565,20 +645,34 @@ namespace HttpRequestWrapper {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "HTTP request sent successfully");
|
||||
#endif
|
||||
|
||||
char buffer[4096];
|
||||
int bytes_received;
|
||||
std::string response_data;
|
||||
int total_bytes = 0;
|
||||
bool body_complete = false;
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Starting to receive HTTP response");
|
||||
#endif
|
||||
while (true) {
|
||||
size_t end = response_end(response_data, method_);
|
||||
if (end != std::string::npos && response_data.size() >= end) {
|
||||
body_complete = true;
|
||||
break;
|
||||
}
|
||||
#ifdef WITH_SSL
|
||||
if (is_https) {
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Calling sslRead for %zu bytes", sizeof(buffer) - 1);
|
||||
#endif
|
||||
bytes_received = sslRead(buffer, sizeof(buffer) - 1);
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "sslRead returned %d bytes", bytes_received);
|
||||
#endif
|
||||
} else {
|
||||
bytes_received = ::recv(sock, buffer, sizeof(buffer) - 1, 0);
|
||||
}
|
||||
@ -587,16 +681,21 @@ namespace HttpRequestWrapper {
|
||||
#endif
|
||||
|
||||
if (bytes_received <= 0) {
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Breaking from response reading loop, bytes_received: %d", bytes_received);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[bytes_received] = '\0';
|
||||
response_data += buffer;
|
||||
response_data.append(buffer, bytes_received);
|
||||
total_bytes += bytes_received;
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Received %d bytes (total: %d)", bytes_received, total_bytes);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Finished receiving data, total bytes: %d", total_bytes);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SSL
|
||||
if (is_https) {
|
||||
@ -604,24 +703,40 @@ namespace HttpRequestWrapper {
|
||||
}
|
||||
#endif
|
||||
|
||||
// return connection to pool for reuse instead of closing
|
||||
g_connection_pool.release_connection(host, port, sock, is_https);
|
||||
|
||||
active_socket_ = -1;
|
||||
|
||||
if (bytes_received == SOCKET_ERROR) {
|
||||
response_.error = "Failed to receive response";
|
||||
closesocket(sock);
|
||||
setReadyState(DONE);
|
||||
handleError(response_.error);
|
||||
active_operation_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (should_abort_.load()) {
|
||||
closesocket(sock);
|
||||
active_operation_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (body_complete) {
|
||||
g_connection_pool.release_connection(host, port, sock, is_https);
|
||||
} else {
|
||||
closesocket(sock);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Raw HTTP response data (%zu bytes):", response_data.length());
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Response preview: %.200s", response_data.c_str());
|
||||
#endif
|
||||
|
||||
parseHttpResponse(response_data);
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Parsed response - status: %d, body length: %zu", response_.status, response_.responseText.length());
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Response body preview: %.200s", response_.responseText.c_str());
|
||||
#endif
|
||||
|
||||
setReadyState(HEADERS_RECEIVED);
|
||||
|
||||
@ -661,6 +776,7 @@ namespace HttpRequestWrapper {
|
||||
}
|
||||
|
||||
void HttpRequestClient::handleError(const std::string& error) {
|
||||
if (should_abort_.load()) return; // abort() already fired its event
|
||||
std::lock_guard<std::mutex> lock(event_queue_mutex_);
|
||||
event_queue_.push(Event(HTTP_ERROR, error));
|
||||
}
|
||||
@ -832,7 +948,9 @@ namespace HttpRequestWrapper {
|
||||
|
||||
bool HttpRequestClient::parseUrl(const std::string& url, std::string& host, int& port, std::string& path, bool& is_https) {
|
||||
if (url.find("://") == std::string::npos) {
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Detected local file path: %s", url.c_str());
|
||||
#endif
|
||||
host = "localhost";
|
||||
port = 0;
|
||||
path = url;
|
||||
@ -841,7 +959,9 @@ namespace HttpRequestWrapper {
|
||||
}
|
||||
|
||||
if (url.substr(0, 7) == "file://") {
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Detected file:// URL: %s", url.c_str());
|
||||
#endif
|
||||
host = "localhost";
|
||||
port = 0; // Special port to indicate local file
|
||||
path = url.substr(7); // Remove "file://" prefix
|
||||
@ -852,7 +972,9 @@ namespace HttpRequestWrapper {
|
||||
std::regex url_regex(R"(^(https?):\/\/([^\/\s:]+)(?::(\d+))?(\/[^\s]*)?(?:\?[^\s]*)?(?:#[^\s]*)?$)");
|
||||
std::smatch matches;
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Parsing HTTP URL: %s", url.c_str());
|
||||
#endif
|
||||
|
||||
if (!std::regex_match(url, matches, url_regex)) {
|
||||
// fallback parsing for simpler URLs
|
||||
@ -900,8 +1022,10 @@ namespace HttpRequestWrapper {
|
||||
port = is_https ? 443 : 80;
|
||||
}
|
||||
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "URL parsed successfully - scheme: %s, host: %s, port: %d, path: %s",
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "URL parsed successfully - scheme: %s, host: %s, port: %d, path: %s",
|
||||
scheme.c_str(), host.c_str(), port, path.c_str());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -911,7 +1035,7 @@ namespace HttpRequestWrapper {
|
||||
|
||||
request << method << " " << path << " HTTP/1.1\r\n";
|
||||
request << "Host: " << host << "\r\n";
|
||||
request << "Connection: close\r\n";
|
||||
request << "Connection: keep-alive\r\n";
|
||||
request << "User-Agent: LNXCORE/1.0\r\n";
|
||||
request << "Accept: */*\r\n";
|
||||
request << "Accept-Encoding: identity\r\n";
|
||||
@ -935,8 +1059,10 @@ namespace HttpRequestWrapper {
|
||||
}
|
||||
|
||||
std::string request_str = request.str();
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Formatted HTTP request (%zu bytes):", request_str.length());
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Request headers: %s", request_str.substr(0, request_str.find("\r\n\r\n")).c_str());
|
||||
#endif
|
||||
|
||||
return request_str;
|
||||
}
|
||||
@ -1011,11 +1137,15 @@ namespace HttpRequestWrapper {
|
||||
|
||||
response_.headers[header_name] = header_value;
|
||||
header_count++;
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Header: %s = %s", header_name.c_str(), header_value.c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Parsed %d headers", header_count);
|
||||
#endif
|
||||
|
||||
std::ostringstream body_stream;
|
||||
std::string body_line;
|
||||
@ -1030,12 +1160,16 @@ namespace HttpRequestWrapper {
|
||||
}
|
||||
|
||||
response_.responseText = body_stream.str();
|
||||
#ifdef DEBUG_NETWORK
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Parsed body: %zu lines, %zu bytes", body_lines, response_.responseText.length());
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
if (!response_.responseText.empty()) {
|
||||
std::string preview = response_.responseText.substr(0, 100);
|
||||
kinc_log(KINC_LOG_LEVEL_INFO, "Response body preview: %s", preview.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HttpRequestClient::loadLocalFile(const std::string& path) {
|
||||
@ -2394,4 +2528,4 @@ void bind_httprequest_class(Isolate* isolate, const Global<Context>& context) {
|
||||
global->Set(current_context, String::NewFromUtf8(isolate, "XMLHttpRequest").ToLocalChecked(), xhrFunc);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Reference in New Issue
Block a user