Files
LNXRNT/Sources/httprequest.h
2026-02-20 23:40:15 -08:00

235 lines
8.6 KiB
C++

#pragma once
#ifdef WITH_NETWORKING
#include <v8.h>
#include <string>
#include <memory>
#include <map>
#include <functional>
#include <mutex>
#include <queue>
#include <atomic>
#include <thread>
#include <vector>
#ifdef WITH_SSL
#ifndef _WIN32
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/crypto.h>
#endif
#endif
namespace HttpRequestWrapper {
enum ReadyState {
UNSENT = 0,
OPENED = 1,
HEADERS_RECEIVED = 2,
LOADING = 3,
DONE = 4
};
enum ResponseType {
DEFAULT = 0,
ARRAY_BUFFER = 1,
BLOB = 2,
DOCUMENT = 3,
JSON = 4,
TEXT = 5
};
struct HttpResponse {
int status;
std::string statusText;
std::map<std::string, std::string> headers;
std::string responseText;
std::string error;
HttpResponse() : status(0), statusText("") {}
};
class HttpRequestClient {
public:
HttpRequestClient(v8::Isolate* isolate, v8::Global<v8::Context>* global_context);
~HttpRequestClient();
void open(const std::string& method, const std::string& url, bool async = true);
void setRequestHeader(const std::string& header, const std::string& value);
void send(const std::string& data = "");
void abort();
std::string getAllResponseHeaders();
std::string getResponseHeader(const std::string& header);
ReadyState getReadyState() const { return ready_state_; }
int getStatus() const { return response_.status; }
std::string getStatusText() const { return response_.statusText; }
std::string getResponseText() const { return response_.responseText; }
std::string getResponseURL() const { return url_; }
ResponseType getResponseType() const { return response_type_; }
int getTimeout() const { return timeout_; }
bool getWithCredentials() const { return with_credentials_; }
void setTimeout(int timeout) { timeout_ = timeout; }
void setResponseType(ResponseType type) { response_type_ = type; }
void setWithCredentials(bool withCredentials) { with_credentials_ = withCredentials; }
void setOnReadyStateChange(v8::Local<v8::Function> callback);
void setOnLoad(v8::Local<v8::Function> callback);
void setOnError(v8::Local<v8::Function> callback);
void setOnTimeout(v8::Local<v8::Function> callback);
void setOnAbort(v8::Local<v8::Function> callback);
void setOnProgress(v8::Local<v8::Function> callback);
void setOnLoadStart(v8::Local<v8::Function> callback);
void setOnLoadEnd(v8::Local<v8::Function> callback);
// from main thread
void processEvents();
private:
v8::Isolate* isolate_;
v8::Global<v8::Context>* global_context_;
std::string method_;
std::string url_;
std::map<std::string, std::string> request_headers_;
std::string request_data_;
HttpResponse response_;
ReadyState ready_state_;
ResponseType response_type_;
int timeout_;
bool with_credentials_;
bool async_;
std::thread request_thread_;
std::atomic<bool> should_abort_;
v8::Global<v8::Function> on_ready_state_change_;
v8::Global<v8::Function> on_load_;
v8::Global<v8::Function> on_error_;
v8::Global<v8::Function> on_timeout_;
v8::Global<v8::Function> on_abort_;
v8::Global<v8::Function> on_progress_;
v8::Global<v8::Function> on_load_start_;
v8::Global<v8::Function> on_load_end_;
std::mutex state_mutex_;
public:
std::atomic<bool> active_operation_{false};
private:
// TODO: find real solution for duplicate done
bool done_event_fired_{false};
#ifdef WITH_SSL
#ifdef _WIN32
void* ssl_cred_handle_;
void* ssl_context_handle_;
bool ssl_context_initialized_;
int ssl_socket_;
std::vector<char> ssl_buffer_;
#else
SSL_CTX* ssl_ctx_;
SSL* ssl_;
bool ssl_initialized_;
#endif
bool initSSL();
void cleanupSSL();
bool performSSLHandshake(int socket, const std::string& host);
int sslRead(char* buffer, int length);
int sslWrite(const char* buffer, int length);
#endif
enum EventType {
HTTP_READY_STATE_CHANGE = 100,
HTTP_LOAD = 101,
HTTP_ERROR = 102,
HTTP_TIMEOUT = 103,
HTTP_ABORT = 104,
HTTP_PROGRESS = 105,
HTTP_LOAD_START = 106,
HTTP_LOAD_END = 107
};
struct Event {
EventType type;
std::string data;
int code;
Event(EventType t, const std::string& d = "", int c = 0)
: type(t), data(d), code(c) {}
};
std::queue<Event> event_queue_;
std::mutex event_queue_mutex_;
// handle* called from background thread
void handleReadyStateChange();
void handleLoad();
void handleError(const std::string& error);
void handleTimeout();
void handleAbort();
void handleProgress();
void handleLoadStart();
void handleLoadEnd();
// process* called from main thread
void processReadyStateChangeEvent();
void processLoadEvent();
void processErrorEvent(const std::string& error);
void processTimeoutEvent();
void processAbortEvent();
void processProgressEvent();
void processLoadStartEvent();
void processLoadEndEvent();
void performRequest();
void loadLocalFile(const std::string& path);
void setReadyState(ReadyState state);
void callReadyStateChangeCallback();
void callCallback(v8::Global<v8::Function>& callback, int argc, v8::Local<v8::Value> argv[]);
bool parseUrl(const std::string& url, std::string& host, int& port, std::string& path, bool& is_https);
std::string formatHttpRequest(const std::string& method, const std::string& path, const std::string& host, const std::string& data);
void parseHttpResponse(const std::string& response);
};
void initialize();
void cleanup();
// process events from main thread
void processEvents();
int createHttpRequest(v8::Isolate* isolate);
v8::Global<v8::Context>* getGlobalContext();
}
// V8 binding functions called from main.cpp
void runt_httprequest_create(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_open(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_send(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_abort(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_request_header(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_get_all_response_headers(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_get_response_header(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_get_ready_state(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_get_status(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_get_status_text(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_get_response_text(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_get_response_url(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_timeout(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_response_type(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_with_credentials(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_onreadystatechange(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_onload(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_onerror(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_ontimeout(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_onabort(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_onprogress(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_onloadstart(const v8::FunctionCallbackInfo<v8::Value>& args);
void runt_httprequest_set_onloadend(const v8::FunctionCallbackInfo<v8::Value>& args);
void createXMLHttpRequestClass(v8::Isolate* isolate, v8::Local<v8::ObjectTemplate>& global);
#endif