#pragma once #ifdef WITH_NETWORKING #include #include #include #include #include #include #include #include #include #include #ifdef WITH_SSL #ifndef _WIN32 #include #include #include #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 headers; std::string responseText; std::string error; HttpResponse() : status(0), statusText("") {} }; class HttpRequestClient { public: HttpRequestClient(v8::Isolate* isolate, v8::Global* 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 callback); void setOnLoad(v8::Local callback); void setOnError(v8::Local callback); void setOnTimeout(v8::Local callback); void setOnAbort(v8::Local callback); void setOnProgress(v8::Local callback); void setOnLoadStart(v8::Local callback); void setOnLoadEnd(v8::Local callback); // from main thread void processEvents(); private: v8::Isolate* isolate_; v8::Global* global_context_; std::string method_; std::string url_; std::map 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 should_abort_; v8::Global on_ready_state_change_; v8::Global on_load_; v8::Global on_error_; v8::Global on_timeout_; v8::Global on_abort_; v8::Global on_progress_; v8::Global on_load_start_; v8::Global on_load_end_; std::mutex state_mutex_; public: std::atomic 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 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_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& callback, int argc, v8::Local 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* getGlobalContext(); } // V8 binding functions called from main.cpp void runt_httprequest_create(const v8::FunctionCallbackInfo& args); void runt_httprequest_open(const v8::FunctionCallbackInfo& args); void runt_httprequest_send(const v8::FunctionCallbackInfo& args); void runt_httprequest_abort(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_request_header(const v8::FunctionCallbackInfo& args); void runt_httprequest_get_all_response_headers(const v8::FunctionCallbackInfo& args); void runt_httprequest_get_response_header(const v8::FunctionCallbackInfo& args); void runt_httprequest_get_ready_state(const v8::FunctionCallbackInfo& args); void runt_httprequest_get_status(const v8::FunctionCallbackInfo& args); void runt_httprequest_get_status_text(const v8::FunctionCallbackInfo& args); void runt_httprequest_get_response_text(const v8::FunctionCallbackInfo& args); void runt_httprequest_get_response_url(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_timeout(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_response_type(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_with_credentials(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_onreadystatechange(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_onload(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_onerror(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_ontimeout(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_onabort(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_onprogress(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_onloadstart(const v8::FunctionCallbackInfo& args); void runt_httprequest_set_onloadend(const v8::FunctionCallbackInfo& args); void createXMLHttpRequestClass(v8::Isolate* isolate, v8::Local& global); #endif