diff src/core/http.cc @ 390:2d3e10319112

http: optimize HTTP request thread we don't need a mutex at all, in fact all we need is an atomic boolean to signify whether the thread is cancelled. curl options are now for the most part handled by a separate function to keep them in sync between non-threaded and threaded implementations
author Paper <paper@tflc.us>
date Fri, 07 Nov 2025 07:08:57 -0500
parents 1e5d922fe82b
children 963047512d34
line wrap: on
line diff
--- a/src/core/http.cc	Thu Nov 06 12:21:35 2025 -0500
+++ b/src/core/http.cc	Fri Nov 07 07:08:57 2025 -0500
@@ -65,6 +65,32 @@
 	return base;
 }
 
+static void SetCurlOpts(CURL *curl, const std::string &url, const std::vector<std::string> &headers, const std::string &data, Type type)
+{
+	struct curl_slist *list;
+
+	curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+
+	list = NULL;
+	for (const std::string &h : headers)
+		list = curl_slist_append(list, h.c_str());
+	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
+
+	switch (type) {
+	case Type::Patch:
+		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
+		[[fallthrough]];
+	case Type::Post:
+		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
+		break;
+	case Type::Get:
+		break;
+	}
+
+	curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
+	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // threading
+}
+
 static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userdata)
 {
 	reinterpret_cast<QByteArray *>(userdata)->append(reinterpret_cast<char *>(contents), size * nmemb);
@@ -73,25 +99,17 @@
 
 QByteArray Request(const std::string &url, const std::vector<std::string> &headers, const std::string &data, Type type)
 {
-	struct curl_slist *list = NULL;
 	QByteArray userdata;
 
 	CURL *curl = curl_easy_init();
 	if (curl) {
-		for (const std::string &h : headers)
-			list = curl_slist_append(list, h.c_str());
+		SetCurlOpts(curl, url, headers, data, type);
 
-		curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
-		if (type == Type::Post || type == Type::Patch)
-			curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
-		if (type == Type::Patch)
-			curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
-		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
+		/* Use our specific userdata & write callback
+		 * TODO can this just be a lambda instead */
 		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata);
 		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback);
-		/* Use system certs... useful on Windows. */
-		curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
-		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // threading
+
 		CURLcode res = curl_easy_perform(curl);
 		session.IncrementRequests();
 		curl_easy_cleanup(curl);
@@ -106,9 +124,6 @@
 {
 	RequestThread *thread = reinterpret_cast<RequestThread *>(userdata);
 
-	const std::lock_guard<std::mutex> lock(thread->callback_data_mutex_);
-
-	/* stop writing, then! */
 	if (thread->cancelled_)
 		return CURL_WRITEFUNC_ERROR;
 
@@ -137,6 +152,10 @@
 	/* block until the function can safely exit */
 	Stop();
 	wait();
+
+	/* Kill off any curl thing we made */
+	if (curl_)
+		curl_easy_cleanup(reinterpret_cast<CURL *>(curl_));
 }
 
 void RequestThread::SetUrl(const std::string &url)
@@ -161,45 +180,34 @@
 
 void RequestThread::run()
 {
-	struct curl_slist *list = NULL;
-
-	CURL *curl = curl_easy_init();
-	if (curl) {
-		curl_easy_setopt(curl, CURLOPT_URL, url_.c_str());
+	/* If we don't have a curl object, create one */
+	if (!curl_) {
+		curl_ = reinterpret_cast<void *>(curl_easy_init());
+	}
 
-		if (type_ == Type::Post || type_ == Type::Patch)
-			curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data_.c_str());
+	CURL *curl = reinterpret_cast<CURL *>(curl_);
 
-		for (const std::string &h : headers_)
-			list = curl_slist_append(list, h.c_str());
-		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
+	if (curl) {
+		SetCurlOpts(curl, url_, headers_, data_, type_);
 
 		curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
 		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RequestThread::WriteCallback);
 
-		/* Use system certs... useful on Windows. */
-		curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
-
-		/* does something with threading, don't remember what though */
-		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
-
 		CURLcode res = curl_easy_perform(curl);
 		session.IncrementRequests();
 		curl_easy_cleanup(curl);
 
-		callback_data_mutex_.lock();
 		if (res != CURLE_OK && !(res == CURLE_WRITE_ERROR && cancelled_))
 			session.SetStatusBar(std::string("curl_easy_perform(curl) failed!: ") + curl_easy_strerror(res));
-		callback_data_mutex_.unlock();
 	}
 
 	emit ReceivedData(array_);
+	/* Clear it out for any subsequent runs */
 	array_.clear();
 }
 
 void RequestThread::Stop()
 {
-	const std::lock_guard<std::mutex> lock(callback_data_mutex_);
 	cancelled_ = true;
 }