Skip to content

Commit ecac074

Browse files
Add a HAR file output (draft)
Relates-To: OCMAM-418 Signed-off-by: Mykhailo Kuchma <[email protected]>
1 parent b64dd79 commit ecac074

File tree

2 files changed

+485
-33
lines changed

2 files changed

+485
-33
lines changed

olp-cpp-sdk-core/src/http/curl/NetworkCurl.cpp

Lines changed: 160 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -203,37 +203,33 @@ int ConvertErrorCode(CURLcode curl_code) {
203203
}
204204
}
205205

206-
/**
207-
* @brief CURL get upload/download data.
208-
* @param[in] handle CURL easy handle.
209-
* @param[out] upload_bytes uploaded bytes(headers+data).
210-
* @param[out] download_bytes downloaded bytes(headers+data).
211-
*/
212206
void GetTrafficData(CURL* handle, uint64_t& upload_bytes,
213-
uint64_t& download_bytes) {
207+
uint64_t& download_headers_size,
208+
uint64_t& download_body_bytes) {
214209
upload_bytes = 0;
215-
download_bytes = 0;
210+
download_headers_size = 0;
211+
download_body_bytes = 0;
216212

217213
long headers_size;
218214
if (curl_easy_getinfo(handle, CURLINFO_HEADER_SIZE, &headers_size) ==
219215
CURLE_OK &&
220216
headers_size > 0) {
221-
download_bytes += headers_size;
217+
download_headers_size = headers_size;
222218
}
223219

224220
#if CURL_AT_LEAST_VERSION(7, 55, 0)
225221
off_t length_downloaded = 0;
226222
if (curl_easy_getinfo(handle, CURLINFO_SIZE_DOWNLOAD_T, &length_downloaded) ==
227223
CURLE_OK &&
228224
length_downloaded > 0) {
229-
download_bytes += length_downloaded;
225+
download_body_bytes = length_downloaded;
230226
}
231227
#else
232228
double length_downloaded;
233229
if (curl_easy_getinfo(handle, CURLINFO_SIZE_DOWNLOAD, &length_downloaded) ==
234230
CURLE_OK &&
235231
length_downloaded > 0.0) {
236-
download_bytes += length_downloaded;
232+
download_body_bytes += length_downloaded;
237233
}
238234
#endif
239235

@@ -357,6 +353,85 @@ const char* MethodName(NetworkRequest::HttpVerb verb) {
357353
return "UNKNOWN";
358354
}
359355

356+
SessionRecording::Timings GetTimings(CURL* handle) {
357+
using Microseconds = SessionRecording::MicroSeconds;
358+
359+
SessionRecording::Timings timings{};
360+
361+
long queue_time_us = 0;
362+
#if CURL_AT_LEAST_VERSION(8, 6, 0)
363+
if (curl_easy_getinfo(handle, CURLINFO_QUEUE_TIME_T, &queue_time_us) ==
364+
CURLE_OK &&
365+
queue_time_us > 0) {
366+
timings.queue_time = Microseconds(queue_time_us);
367+
}
368+
#else
369+
timings.queue_time = Microseconds(queue_time_us);
370+
#endif
371+
372+
// 7.61.0
373+
long name_lookup_us = 0;
374+
if (curl_easy_getinfo(handle, CURLINFO_NAMELOOKUP_TIME_T, &name_lookup_us) ==
375+
CURLE_OK &&
376+
name_lookup_us > 0) {
377+
timings.name_lookup_time = Microseconds(name_lookup_us);
378+
}
379+
380+
// 7.61.0
381+
long connect_time_us = 0;
382+
if (curl_easy_getinfo(handle, CURLINFO_CONNECT_TIME_T, &connect_time_us) ==
383+
CURLE_OK &&
384+
connect_time_us > 0) {
385+
timings.connect_time = Microseconds(connect_time_us);
386+
}
387+
388+
// 7.61.0
389+
long app_connect_time_us = 0;
390+
if (curl_easy_getinfo(handle, CURLINFO_APPCONNECT_TIME_T,
391+
&app_connect_time_us) == CURLE_OK &&
392+
app_connect_time_us > 0) {
393+
timings.app_connect_time = Microseconds(app_connect_time_us);
394+
}
395+
396+
// 7.61.0
397+
long pre_transfer_time_us = 0;
398+
if (curl_easy_getinfo(handle, CURLINFO_PRETRANSFER_TIME_T,
399+
&pre_transfer_time_us) == CURLE_OK &&
400+
pre_transfer_time_us > 0) {
401+
timings.pre_transfer_time = Microseconds(pre_transfer_time_us);
402+
}
403+
404+
// 7.61.0
405+
long start_transfer_time_us = 0;
406+
if (curl_easy_getinfo(handle, CURLINFO_STARTTRANSFER_TIME_T,
407+
&start_transfer_time_us) == CURLE_OK &&
408+
start_transfer_time_us > 0) {
409+
timings.start_transfer_time = Microseconds(start_transfer_time_us);
410+
}
411+
412+
// 8.10.0
413+
#if CURL_AT_LEAST_VERSION(8, 10, 0)
414+
long post_transfer_time_us = 0;
415+
if (curl_easy_getinfo(handle, CURLINFO_POSTTRANSFER_TIME_T,
416+
&post_transfer_time_us) == CURLE_OK &&
417+
post_transfer_time_us > 0) {
418+
timings.post_transfer_time = Microseconds(post_transfer_time_us);
419+
}
420+
#else
421+
timings.post_transfer_time = timings.start_transfer_time;
422+
#endif
423+
424+
// 7.61.0
425+
long total_time_us = 0;
426+
if (curl_easy_getinfo(handle, CURLINFO_TOTAL_TIME_T, &total_time_us) ==
427+
CURLE_OK &&
428+
total_time_us > 0) {
429+
timings.total_time = Microseconds(total_time_us);
430+
}
431+
432+
return timings;
433+
}
434+
360435
} // anonymous namespace
361436

362437
NetworkCurl::NetworkCurl(NetworkInitializationSettings settings)
@@ -386,6 +461,8 @@ NetworkCurl::NetworkCurl(NetworkInitializationSettings settings)
386461
}
387462
}
388463

464+
session_recording_.emplace(SessionRecording());
465+
389466
#ifdef OLP_SDK_CURL_HAS_SUPPORT_SSL_BLOBS
390467
SetupCertificateBlobs();
391468
#else
@@ -437,6 +514,11 @@ NetworkCurl::~NetworkCurl() {
437514
if (stderr_) {
438515
fclose(stderr_);
439516
}
517+
if (session_recording_) {
518+
session_recording_->locked([](const SessionRecording& recording) {
519+
recording.ArchiveToFile("/tmp/test.har");
520+
});
521+
}
440522
}
441523

442524
bool NetworkCurl::Initialize() {
@@ -672,6 +754,14 @@ ErrorCode NetworkCurl::SendImplementation(
672754
handle->request_body = request.GetBody();
673755
handle->request_headers = SetupHeaders(request.GetHeaders());
674756

757+
if (session_recording_) {
758+
session_recording_->locked([&](SessionRecording& recording) {
759+
recording.SetRequestParameters(
760+
id, handle->request_url, request.GetVerb(), request.GetHeaders(),
761+
handle->request_body ? handle->request_body->size() : 0);
762+
});
763+
}
764+
675765
OLP_SDK_LOG_DEBUG(kLogTag,
676766
"Send request with url="
677767
<< utils::CensorCredentialsInUrl(request.GetUrl())
@@ -876,13 +966,21 @@ NetworkCurl::RequestHandle* NetworkCurl::InitRequestHandle() {
876966

877967
void NetworkCurl::ReleaseHandleUnlocked(RequestHandle* handle,
878968
bool cleanup_easy_handle) {
969+
// Reset the RequestHandle to defaults, but keep the curl_handle and
970+
// response_headers allocated buffer.
879971
std::shared_ptr<CURL> curl_handle;
972+
std::vector<std::pair<std::string, std::string>> response_headers;
973+
880974
std::swap(curl_handle, handle->curl_handle);
975+
std::swap(response_headers, handle->response_headers);
881976

977+
response_headers.clear();
882978
curl_easy_reset(curl_handle.get());
979+
883980
*handle = RequestHandle{};
884981

885982
std::swap(curl_handle, handle->curl_handle);
983+
std::swap(response_headers, handle->response_headers);
886984

887985
// When using C-Ares on Android, DNS parameters are calculated in
888986
// curl_easy_init(). Those parameters are not reset in curl_easy_reset(...),
@@ -895,7 +993,7 @@ void NetworkCurl::ReleaseHandleUnlocked(RequestHandle* handle,
895993

896994
#if defined(ANDROID)
897995
if (cleanup_easy_handle) {
898-
handle->handle = nullptr;
996+
handle->curl_handle = nullptr;
899997
}
900998
#endif
901999
OLP_SDK_CORE_UNUSED(cleanup_easy_handle);
@@ -994,6 +1092,7 @@ size_t NetworkCurl::HeaderFunction(char* ptr, size_t size, size_t nitems,
9941092

9951093
// Callback with header key+value
9961094
handle->out_header_callback(key, value);
1095+
handle->response_headers.emplace_back(std::move(key), std::move(value));
9971096

9981097
return len;
9991098
}
@@ -1025,13 +1124,16 @@ void NetworkCurl::CompleteMessage(CURL* curl_handle, CURLcode result) {
10251124
}
10261125

10271126
uint64_t upload_bytes = 0u;
1028-
uint64_t download_bytes = 0u;
1029-
GetTrafficData(curl_handle, upload_bytes, download_bytes);
1127+
uint64_t download_headers_bytes = 0u;
1128+
uint64_t download_body_bytes = 0u;
1129+
GetTrafficData(curl_handle, upload_bytes, download_headers_bytes,
1130+
download_body_bytes);
10301131

1031-
auto response = NetworkResponse()
1032-
.WithRequestId(request_handle->id)
1033-
.WithBytesDownloaded(download_bytes)
1034-
.WithBytesUploaded(upload_bytes);
1132+
auto response =
1133+
NetworkResponse()
1134+
.WithRequestId(request_handle->id)
1135+
.WithBytesDownloaded(download_headers_bytes + download_body_bytes)
1136+
.WithBytesUploaded(upload_bytes);
10351137

10361138
if (request_handle->cancelled) {
10371139
response.WithStatus(static_cast<int>(ErrorCode::CANCELLED_ERROR))
@@ -1074,19 +1176,44 @@ void NetworkCurl::CompleteMessage(CURL* curl_handle, CURLcode result) {
10741176
const char* url;
10751177
curl_easy_getinfo(curl_handle, CURLINFO_EFFECTIVE_URL, &url);
10761178

1077-
OLP_SDK_LOG_DEBUG(
1078-
kLogTag, "Message completed, id="
1079-
<< request_handle->id << ", url='"
1080-
<< utils::CensorCredentialsInUrl(url) << "', status=("
1081-
<< status << ") " << error
1082-
<< ", time=" << GetElapsedTime(request_handle->send_time)
1083-
<< "ms, bytes=" << download_bytes + upload_bytes);
1179+
if (session_recording_) {
1180+
long http_version = 0;
1181+
curl_easy_getinfo(curl_handle, CURLINFO_HTTP_VERSION, &http_version);
1182+
1183+
if (http_version == CURL_HTTP_VERSION_1_0 ||
1184+
http_version == CURL_HTTP_VERSION_1_1) {
1185+
http_version = 1;
1186+
} else if (http_version == CURL_HTTP_VERSION_2_0 ||
1187+
http_version == CURL_HTTP_VERSION_2TLS ||
1188+
http_version == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
1189+
http_version = 2;
1190+
} else {
1191+
http_version = 0;
1192+
}
10841193

1085-
response.WithStatus(status).WithError(error);
1194+
const char* content_type;
1195+
curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &content_type);
10861196

1087-
if (harfile_ != nullptr) {
1197+
session_recording_->locked([&](SessionRecording& recording) {
1198+
recording.SetResponseParameters(
1199+
request_handle->id, static_cast<int>(http_version), status,
1200+
request_handle->response_headers, content_type,
1201+
download_headers_bytes, download_body_bytes);
1202+
recording.SetRequestTimings(request_handle->id, GetTimings(curl_handle));
1203+
});
10881204
}
10891205

1206+
OLP_SDK_LOG_DEBUG(
1207+
kLogTag,
1208+
"Message completed, id="
1209+
<< request_handle->id << ", url='"
1210+
<< utils::CensorCredentialsInUrl(url) << "', status=(" << status
1211+
<< ") " << error << ", time="
1212+
<< GetElapsedTime(request_handle->send_time) << "ms, bytes="
1213+
<< download_headers_bytes + download_body_bytes + upload_bytes);
1214+
1215+
response.WithStatus(status).WithError(error);
1216+
10901217
ReleaseHandleUnlocked(request_handle, cleanup_easy_handle);
10911218

10921219
lock.unlock();
@@ -1223,15 +1350,18 @@ void NetworkCurl::Run() {
12231350
lock.unlock();
12241351

12251352
uint64_t upload_bytes = 0u;
1226-
uint64_t download_bytes = 0u;
1227-
GetTrafficData(curl_handle, upload_bytes, download_bytes);
1353+
uint64_t download_headers_bytes = 0u;
1354+
uint64_t download_body_bytes = 0u;
1355+
GetTrafficData(curl_handle, upload_bytes, download_headers_bytes,
1356+
download_body_bytes);
12281357

12291358
auto response =
12301359
NetworkResponse()
12311360
.WithRequestId(request_handle->id)
12321361
.WithStatus(static_cast<int>(ErrorCode::IO_ERROR))
12331362
.WithError("CURL error")
1234-
.WithBytesDownloaded(download_bytes)
1363+
.WithBytesDownloaded(download_headers_bytes +
1364+
download_body_bytes)
12351365
.WithBytesUploaded(upload_bytes);
12361366
callback(response);
12371367
lock.lock();

0 commit comments

Comments
 (0)