comparison 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
comparison
equal deleted inserted replaced
389:1e5d922fe82b 390:2d3e10319112
63 } 63 }
64 64
65 return base; 65 return base;
66 } 66 }
67 67
68 static void SetCurlOpts(CURL *curl, const std::string &url, const std::vector<std::string> &headers, const std::string &data, Type type)
69 {
70 struct curl_slist *list;
71
72 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
73
74 list = NULL;
75 for (const std::string &h : headers)
76 list = curl_slist_append(list, h.c_str());
77 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
78
79 switch (type) {
80 case Type::Patch:
81 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
82 [[fallthrough]];
83 case Type::Post:
84 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
85 break;
86 case Type::Get:
87 break;
88 }
89
90 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
91 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // threading
92 }
93
68 static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userdata) 94 static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userdata)
69 { 95 {
70 reinterpret_cast<QByteArray *>(userdata)->append(reinterpret_cast<char *>(contents), size * nmemb); 96 reinterpret_cast<QByteArray *>(userdata)->append(reinterpret_cast<char *>(contents), size * nmemb);
71 return size * nmemb; 97 return size * nmemb;
72 } 98 }
73 99
74 QByteArray Request(const std::string &url, const std::vector<std::string> &headers, const std::string &data, Type type) 100 QByteArray Request(const std::string &url, const std::vector<std::string> &headers, const std::string &data, Type type)
75 { 101 {
76 struct curl_slist *list = NULL;
77 QByteArray userdata; 102 QByteArray userdata;
78 103
79 CURL *curl = curl_easy_init(); 104 CURL *curl = curl_easy_init();
80 if (curl) { 105 if (curl) {
81 for (const std::string &h : headers) 106 SetCurlOpts(curl, url, headers, data, type);
82 list = curl_slist_append(list, h.c_str()); 107
83 108 /* Use our specific userdata & write callback
84 curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); 109 * TODO can this just be a lambda instead */
85 if (type == Type::Post || type == Type::Patch)
86 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
87 if (type == Type::Patch)
88 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
89 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
90 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata); 110 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata);
91 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback); 111 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback);
92 /* Use system certs... useful on Windows. */ 112
93 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
94 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // threading
95 CURLcode res = curl_easy_perform(curl); 113 CURLcode res = curl_easy_perform(curl);
96 session.IncrementRequests(); 114 session.IncrementRequests();
97 curl_easy_cleanup(curl); 115 curl_easy_cleanup(curl);
98 if (res != CURLE_OK) 116 if (res != CURLE_OK)
99 session.SetStatusBar(std::string("curl_easy_perform(curl) failed!: ") + curl_easy_strerror(res)); 117 session.SetStatusBar(std::string("curl_easy_perform(curl) failed!: ") + curl_easy_strerror(res));
104 /* this function is static */ 122 /* this function is static */
105 size_t RequestThread::WriteCallback(void *contents, size_t size, size_t nmemb, void *userdata) 123 size_t RequestThread::WriteCallback(void *contents, size_t size, size_t nmemb, void *userdata)
106 { 124 {
107 RequestThread *thread = reinterpret_cast<RequestThread *>(userdata); 125 RequestThread *thread = reinterpret_cast<RequestThread *>(userdata);
108 126
109 const std::lock_guard<std::mutex> lock(thread->callback_data_mutex_);
110
111 /* stop writing, then! */
112 if (thread->cancelled_) 127 if (thread->cancelled_)
113 return CURL_WRITEFUNC_ERROR; 128 return CURL_WRITEFUNC_ERROR;
114 129
115 /* else, continue on as normal */ 130 /* else, continue on as normal */
116 thread->array_.append(reinterpret_cast<char *>(contents), size * nmemb); 131 thread->array_.append(reinterpret_cast<char *>(contents), size * nmemb);
135 RequestThread::~RequestThread() 150 RequestThread::~RequestThread()
136 { 151 {
137 /* block until the function can safely exit */ 152 /* block until the function can safely exit */
138 Stop(); 153 Stop();
139 wait(); 154 wait();
155
156 /* Kill off any curl thing we made */
157 if (curl_)
158 curl_easy_cleanup(reinterpret_cast<CURL *>(curl_));
140 } 159 }
141 160
142 void RequestThread::SetUrl(const std::string &url) 161 void RequestThread::SetUrl(const std::string &url)
143 { 162 {
144 url_ = url; 163 url_ = url;
159 type_ = type; 178 type_ = type;
160 } 179 }
161 180
162 void RequestThread::run() 181 void RequestThread::run()
163 { 182 {
164 struct curl_slist *list = NULL; 183 /* If we don't have a curl object, create one */
165 184 if (!curl_) {
166 CURL *curl = curl_easy_init(); 185 curl_ = reinterpret_cast<void *>(curl_easy_init());
186 }
187
188 CURL *curl = reinterpret_cast<CURL *>(curl_);
189
167 if (curl) { 190 if (curl) {
168 curl_easy_setopt(curl, CURLOPT_URL, url_.c_str()); 191 SetCurlOpts(curl, url_, headers_, data_, type_);
169
170 if (type_ == Type::Post || type_ == Type::Patch)
171 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data_.c_str());
172
173 for (const std::string &h : headers_)
174 list = curl_slist_append(list, h.c_str());
175 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
176 192
177 curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); 193 curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
178 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RequestThread::WriteCallback); 194 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RequestThread::WriteCallback);
179 195
180 /* Use system certs... useful on Windows. */
181 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
182
183 /* does something with threading, don't remember what though */
184 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
185
186 CURLcode res = curl_easy_perform(curl); 196 CURLcode res = curl_easy_perform(curl);
187 session.IncrementRequests(); 197 session.IncrementRequests();
188 curl_easy_cleanup(curl); 198 curl_easy_cleanup(curl);
189 199
190 callback_data_mutex_.lock();
191 if (res != CURLE_OK && !(res == CURLE_WRITE_ERROR && cancelled_)) 200 if (res != CURLE_OK && !(res == CURLE_WRITE_ERROR && cancelled_))
192 session.SetStatusBar(std::string("curl_easy_perform(curl) failed!: ") + curl_easy_strerror(res)); 201 session.SetStatusBar(std::string("curl_easy_perform(curl) failed!: ") + curl_easy_strerror(res));
193 callback_data_mutex_.unlock();
194 } 202 }
195 203
196 emit ReceivedData(array_); 204 emit ReceivedData(array_);
205 /* Clear it out for any subsequent runs */
197 array_.clear(); 206 array_.clear();
198 } 207 }
199 208
200 void RequestThread::Stop() 209 void RequestThread::Stop()
201 { 210 {
202 const std::lock_guard<std::mutex> lock(callback_data_mutex_);
203 cancelled_ = true; 211 cancelled_ = true;
204 } 212 }
205 213
206 } // namespace HTTP 214 } // namespace HTTP