-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathNetworkCurl.h
More file actions
387 lines (319 loc) · 10.8 KB
/
NetworkCurl.h
File metadata and controls
387 lines (319 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
/*
* Copyright (C) 2019-2026 HERE Europe B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/
#pragma once
#include <curl/curl.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <deque>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <olp/core/porting/optional.h>
#if defined(OLP_SDK_ENABLE_ANDROID_CURL) && !defined(ANDROID_HOST)
#include <openssl/ossl_typ.h>
#ifdef OPENSSL_NO_MD5
#error cURL enabled network implementation for Android requires MD5 from OpenSSL
#endif
// Android uses MD5 to encode certificate name, but OpenSSL uses SHA1 for
// certificate lookup, so OpenSSL won't be able to get the appropriate
// certificate as it won't be able to locate one. We need to add our custom
// lookup method that uses MD5. Old SHA1 lookup will be left as it is.
#define OLP_SDK_USE_MD5_CERT_LOOKUP
#endif
// CURLOPT_CAINFO_BLOB has become available only in curl-7.77
// cf. https://curl.se/libcurl/c/CURLOPT_CAINFO_BLOB.html,
#if CURL_AT_LEAST_VERSION(7, 77, 0)
#define OLP_SDK_CURL_HAS_SUPPORT_SSL_BLOBS
#endif
#include "olp/core/http/CertificateSettings.h"
#include "olp/core/http/Network.h"
#include "olp/core/http/NetworkInitializationSettings.h"
#include "olp/core/http/NetworkRequest.h"
#include "olp/core/logging/LogContext.h"
namespace olp {
namespace http {
/**
* @brief The implementation of Network based on cURL.
*/
class NetworkCurl : public Network,
public std::enable_shared_from_this<NetworkCurl> {
public:
/**
* @brief NetworkCurl constructor.
*/
explicit NetworkCurl(NetworkInitializationSettings settings);
/**
* @brief ~NetworkCurl destructor.
*/
~NetworkCurl() override;
/**
* @brief Not copyable.
*/
NetworkCurl(const NetworkCurl& other) = delete;
/**
* @brief Not movable.
*/
NetworkCurl(NetworkCurl&& other) = delete;
/**
* @brief Not copy-assignable.
*/
NetworkCurl& operator=(const NetworkCurl& other) = delete;
/**
* @brief Not move-assignable.
*/
NetworkCurl& operator=(NetworkCurl&& other) = delete;
/**
* @brief Implementation of Send method from Network abstract class.
*/
SendOutcome Send(NetworkRequest request, Payload payload, Callback callback,
HeaderCallback header_callback,
DataCallback data_callback) override;
/**
* @brief Implementation of Cancel method from Network abstract class.
*/
void Cancel(RequestId id) override;
private:
/**
* @brief Context of each network request.
*/
struct RequestHandle {
NetworkRequest::RequestBodyType request_body;
std::shared_ptr<curl_slist> request_headers;
HeaderCallback out_header_callback;
DataCallback out_data_callback;
Payload out_data_stream;
Callback out_completion_callback;
std::uint64_t bytes_received{0};
std::chrono::steady_clock::time_point send_time{};
std::weak_ptr<NetworkCurl> self{};
std::shared_ptr<CURL> curl_handle;
RequestId id{};
bool in_use{false};
bool is_cancelled{false};
char error_text[CURL_ERROR_SIZE]{};
std::shared_ptr<const logging::LogContext> log_context;
};
/**
* @brief POD type represents worker thread notification event.
*/
struct EventInfo {
/**
* @brief Event type.
*/
enum class Type : char {
SEND_EVENT, ///< New request send.
CANCEL_EVENT, ///< New request cancellation.
};
/**
* @brief EventInfo constructor.
*/
EventInfo(Type type, RequestHandle* handle) : type(type), handle(handle) {}
/// Event type.
Type type{};
/// Associated request context.
RequestHandle* handle{};
};
#ifdef OLP_SDK_CURL_HAS_SUPPORT_SSL_BLOBS
/**
* @brief Blobs required for custom certificate validation.
*/
struct SslCertificateBlobs {
using OptionalBlob = porting::optional<struct curl_blob>;
/// Certificate blob.
OptionalBlob ssl_cert_blob;
/// Private key blob.
OptionalBlob ssl_key_blob;
/// Certificate authority blob.
OptionalBlob ca_info_blob;
};
#endif
/**
* @brief Actual routine that sends network request.
*
* @param[in] request Network request.
* @param[in] id Unique request id.
* @param[out] payload Stream to store response payload data.
* @param[in] header_callback Callback that is called for every header from
* response.
* @param[in] data_callback Callback to be called when a chunk of data is
* received. This callback can be triggered multiple times all prior to the
* final Callback call.
* @param[in] callback Callback to be called when request is fully processed
* or canceled. After this call, there will be no more callbacks triggered and
* users can consider the request as done.
* @return ErrorCode.
*/
ErrorCode SendImplementation(const NetworkRequest& request, RequestId id,
const std::shared_ptr<std::ostream>& payload,
HeaderCallback header_callback,
DataCallback data_callback, Callback callback);
/**
* @brief Initialize internal data structures, start worker thread.
* @return @c true if initialized successfully, @c false otherwise.
*/
bool Initialize();
/**
* @brief Release network resources, join worker thread.
* @return @c true if deinitialized successfully, @c false otherwise.
*/
void Deinitialize();
/**
* @brief Check whether the network is initialized.
* @return @c true if initialized, @c false otherwise.
*/
bool Initialized() const;
/**
* @brief. Check whether NetworkCurl has resources to handle more requests.
* @return @c true if curl network has free network connections,
* @c false otherwise.
*/
bool Ready();
/**
* @brief Return count of pending network requests.
* @return Number of pending network requests.
*/
size_t AmountPending();
/**
* @brief Find a handle in handles_ by curl handle.
* @param[in] handle CURL handle.
* @return Pointer to the RequestHandle.
*/
RequestHandle* FindRequestHandle(const CURL* handle);
/**
* @brief Allocate new handle RequestHandle.
* @note Must be protected by event_mutex_
*
* @return Pointer to the allocated RequestHandle.
*/
RequestHandle* InitRequestHandleUnsafe();
/**
* @brief Reset the handle after network request is done.
* @param[in] handle Request handle.
* @param[in] cleanup_handle If true then handle is completelly release.
* Otherwise, a handle is reset, which preserves DNS cache, Session ID cache,
* cookies, and so on.
*/
static void ReleaseHandleUnlocked(RequestHandle* handle, bool cleanup_handle);
/**
* @brief Routine that is called when the last bit of response is received.
*
* @param[in] curl_handle CURL handle associated with request.
* @param[in] result CURL return code.
*/
void CompleteMessage(CURL* curl_handle, CURLcode result);
/**
* @brief CURL read callback.
*/
static size_t RxFunction(void* ptr, size_t size, size_t nmemb,
RequestHandle* handle);
/**
* @brief CURL header callback.
*/
static size_t HeaderFunction(char* ptr, size_t size, size_t nmemb,
RequestHandle* handle);
/**
* @brief The worker thread's main method.
*/
void Run();
/**
* @brief Free resources after the thread terminates.
*/
void Teardown();
/**
* @brief Notify worker thread on some event.
* @param[in] type Event type.
* @param[in] handle Related RequestHandle.
*/
void AddEvent(EventInfo::Type type, RequestHandle* handle);
/**
* @brief Checks whether the worker thread is started.
* @return @c true if the thread is started, @c false otherwise.
*/
inline bool IsStarted() const;
#ifdef OLP_SDK_CURL_HAS_SUPPORT_SSL_BLOBS
/**
* @brief Setups all necessary blobs for custom certificate settings.
*/
void SetupCertificateBlobs();
#endif
#ifdef OLP_SDK_USE_MD5_CERT_LOOKUP
/**
* @brief Adds new lookup method for certificates search routine.
*
* @param[in] curl cURL instance.
* @param[in] ssl_ctx OpenSSL context.
* @param[in] handle Related RequestHandle.
* @return An error code for the operation.
*/
static CURLcode AddMd5LookupMethod(CURL* curl, SSL_CTX* ssl_ctx,
RequestHandle* handle);
#endif
/// Contexts for every network request.
std::vector<RequestHandle> handles_;
/// Number of CURL easy handles that are always opened.
const size_t static_handle_count_;
/// Condition variable used to notify worker thread on event.
std::condition_variable event_condition_;
/// Synchronization mutex used during event processing.
std::mutex event_mutex_;
/// Synchronization mutex prevents parallel initialization of network.
std::mutex init_mutex_;
/// Worker thread.
std::thread thread_;
/// Variable used to assign unique request id to each request.
RequestId request_id_counter_{
static_cast<RequestId>(RequestIdConstants::RequestIdMin)};
/**
* @brief @copydoc NetworkCurl::state_
*/
enum class WorkerState {
STOPPED, ///< The worker thread is not started.
STARTED, ///< The worker thread is running.
STOPPING, ///< The worker thread will be stopped soon.
};
/// The state of the worker thread.
std::atomic<WorkerState> state_{WorkerState::STOPPED};
/// Queue of events passed to worker thread.
std::deque<EventInfo> events_{};
/// CURL multi handle. Shared among all network requests.
CURLM* curl_{nullptr};
/// Turn on and off verbose mode for CURL.
bool verbose_{false};
/// Set custom stderr for CURL.
FILE* stderr_{nullptr};
/// UNIX Pipe used to notify sleeping worker thread during select() call.
int pipe_[2]{};
/// Stores value if `curl_global_init()` was successful on construction.
bool curl_initialized_;
/// Store original certificate setting in order to reference them in the SSL
/// blobs so cURL does not need to copy them.
CertificateSettings certificate_settings_;
/// Maximum transfer rate in bytes per second applied per connection (0 =
/// unlimited).
size_t max_transfer_bytes_per_second_{0u};
#ifdef OLP_SDK_CURL_HAS_SUPPORT_SSL_BLOBS
/// SSL certificate blobs.
porting::optional<SslCertificateBlobs> ssl_certificates_blobs_;
#endif
};
} // namespace http
} // namespace olp