Mercurial > minori
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 |
