Skip to content

Commit fe21927

Browse files
Use boost::json in HarCaptureAdapter
Migrating from RapidJSON Relates-To: OCMAM-446 Signed-off-by: Rustam Gamidov <ext-rustam.gamidov@here.com>
1 parent 2d3e76a commit fe21927

File tree

1 file changed

+191
-149
lines changed

1 file changed

+191
-149
lines changed

olp-cpp-sdk-core/src/http/adapters/HarCaptureAdapter.cpp

Lines changed: 191 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2025 HERE Europe B.V.
2+
* Copyright (C) 2025-2026 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,8 +19,10 @@
1919

2020
#include <olp/core/http/adapters/HarCaptureAdapter.h>
2121

22-
#include <rapidjson/ostreamwrapper.h>
23-
#include <rapidjson/prettywriter.h>
22+
#include <boost/json/serialize.hpp>
23+
#include <boost/json/value.hpp>
24+
25+
#include <generated/serializer/SerializerWrapper.h>
2426

2527
#include <deque>
2628
#include <fstream>
@@ -68,56 +70,96 @@ std::string FormatTime(const std::chrono::system_clock::time_point timestamp) {
6870
return ss.str();
6971
}
7072

71-
class JsonFileSerializer {
72-
public:
73-
explicit JsonFileSerializer(std::ofstream& file)
74-
: out_stream_(file), writer_(out_stream_) {}
73+
// In addition to making result easier to read formats floating point numbers
74+
// from `1.00281E2` to `100.281` and from `0` to `0.000`
75+
void PrettyPrint(std::ostream& os, boost::json::value const& jv,
76+
std::string* indent = nullptr) {
77+
const auto initial_precision{os.precision()};
78+
const auto initial_floatfield{os.floatfield};
79+
os.precision(3);
80+
os.setf(std::ios_base::fixed, std::ios_base::floatfield);
81+
82+
std::string indent_;
83+
if (!indent)
84+
indent = &indent_;
85+
switch (jv.kind()) {
86+
case boost::json::kind::object: {
87+
os << "{\n";
88+
indent->append(4, ' ');
89+
auto const& obj = jv.get_object();
90+
if (!obj.empty()) {
91+
auto it = obj.begin();
92+
for (;;) {
93+
os << *indent << boost::json::serialize(it->key()) << ": ";
94+
PrettyPrint(os, it->value(), indent);
95+
if (++it == obj.end())
96+
break;
97+
os << ",\n";
98+
}
99+
}
100+
os << "\n";
101+
indent->resize(indent->size() - 4);
102+
os << *indent << "}";
103+
break;
104+
}
75105

76-
void Object(const std::function<void()>& body) {
77-
writer_.StartObject();
78-
body();
79-
writer_.EndObject();
80-
}
106+
case boost::json::kind::array: {
107+
auto const& arr = jv.get_array();
108+
if (arr.empty()) {
109+
os << "[]";
110+
} else {
111+
os << "[\n";
112+
indent->append(4, ' ');
113+
auto it = arr.begin();
114+
for (;;) {
115+
os << *indent;
116+
PrettyPrint(os, *it, indent);
117+
if (++it == arr.end())
118+
break;
119+
os << ",\n";
120+
}
121+
os << "\n";
122+
indent->resize(indent->size() - 4);
123+
os << *indent << "]";
124+
}
125+
break;
126+
}
81127

82-
void Object(const char* key, const std::function<void()>& body) {
83-
writer_.Key(key);
84-
writer_.StartObject();
85-
body();
86-
writer_.EndObject();
87-
}
128+
case boost::json::kind::string: {
129+
os << boost::json::serialize(jv.get_string());
130+
break;
131+
}
88132

89-
void Array(const char* key, const std::function<void()>& body) {
90-
writer_.Key(key);
91-
writer_.StartArray();
92-
body();
93-
writer_.EndArray();
94-
}
133+
case boost::json::kind::uint64:
134+
os << jv.get_uint64();
135+
break;
95136

96-
void String(const char* key, const std::string& value) {
97-
writer_.Key(key);
98-
writer_.String(value.c_str(), value.size());
99-
}
137+
case boost::json::kind::int64:
138+
os << jv.get_int64();
139+
break;
100140

101-
void Int(const char* key, const int value) {
102-
writer_.Key(key);
103-
writer_.Int(value);
104-
}
141+
case boost::json::kind::double_:
142+
os << jv.get_double();
143+
break;
105144

106-
void Double(const char* key, const double value) {
107-
writer_.Key(key);
108-
writer_.Double(value);
109-
}
145+
case boost::json::kind::bool_:
146+
if (jv.get_bool())
147+
os << "true";
148+
else
149+
os << "false";
150+
break;
110151

111-
void EmptyArray(const char* key) {
112-
writer_.Key(key);
113-
writer_.StartArray();
114-
writer_.EndArray();
152+
case boost::json::kind::null:
153+
os << "null";
154+
break;
115155
}
116156

117-
private:
118-
rapidjson::OStreamWrapper out_stream_;
119-
rapidjson::PrettyWriter<rapidjson::OStreamWrapper> writer_{};
120-
};
157+
if (indent->empty())
158+
os << "\n";
159+
160+
os.precision(initial_precision);
161+
os.setf(std::ios_base::fixed, initial_floatfield);
162+
}
121163

122164
} // namespace
123165

@@ -229,118 +271,118 @@ class HarCaptureAdapter::HarCaptureAdapterImpl final : public Network {
229271
}
230272

231273
void SaveSessionToFile() const {
274+
boost::json::object log;
275+
log["version"] = "1.2";
276+
log["creator"] = boost::json::object(
277+
{{"name", "DataSDK"}, {"version", OLP_SDK_VERSION_STRING}});
278+
279+
log["entries"] = [&]() {
280+
boost::json::array entries;
281+
entries.reserve(requests_.size());
282+
for (auto request_index = 0u; request_index < requests_.size();
283+
++request_index) {
284+
const auto& request = requests_[request_index];
285+
const auto diagnostics = diagnostics_.size() > request_index
286+
? diagnostics_[request_index]
287+
: Diagnostics{};
288+
289+
// return duration in milliseconds as float
290+
auto duration = [&](const Diagnostics::Timings timing,
291+
const double default_value = -1.0) {
292+
return diagnostics.available_timings[timing]
293+
? diagnostics.timings[timing].count() / 1000.0
294+
: default_value;
295+
};
296+
297+
const double total_time =
298+
duration(Diagnostics::Total,
299+
static_cast<double>(
300+
std::chrono::duration_cast<std::chrono::microseconds>(
301+
request.end_time - request.start_time)
302+
.count()) /
303+
1000.0);
304+
305+
auto output_headers = [&](const uint16_t headers_offset,
306+
const uint16_t headers_count) {
307+
boost::json::array headers;
308+
headers.reserve(headers_count);
309+
for (auto i = 0u; i < headers_count; ++i) {
310+
const auto& header = headers_[headers_offset + i];
311+
headers.emplace_back(
312+
boost::json::object({{"name", cache_.at(header.first)},
313+
{"value", cache_.at(header.second)}}));
314+
}
315+
return headers;
316+
};
317+
318+
boost::json::object entry;
319+
entry["startedDateTime"] = FormatTime(request.start_time);
320+
entry["time"] = total_time;
321+
322+
entry.emplace("request", [&]() {
323+
boost::json::object value;
324+
325+
value["method"] = VerbToString(
326+
static_cast<NetworkRequest::HttpVerb>(request.method));
327+
value["url"] = cache_.at(request.url);
328+
value["httpVersion"] = "UNSPECIFIED";
329+
value["cookies"] = boost::json::array{};
330+
value["headers"] = output_headers(request.request_headers_offset,
331+
request.request_headers_count);
332+
value["queryString"] = boost::json::array{};
333+
value["headersSize"] = -1;
334+
value["bodySize"] = -1;
335+
336+
return value;
337+
}());
338+
339+
entry.emplace("response", [&]() {
340+
boost::json::object value;
341+
value["status"] = request.status_code;
342+
value["statusText"] = "";
343+
value["httpVersion"] = "UNSPECIFIED";
344+
value["cookies"] = boost::json::array{};
345+
value["headers"] = output_headers(request.response_headers_offset,
346+
request.response_headers_count);
347+
value["content"] =
348+
boost::json::object({{"size", 0}, {"mimeType", ""}});
349+
value["redirectURL"] = "";
350+
value["headersSize"] = -1;
351+
value["bodySize"] = -1;
352+
value["_transferSize"] = static_cast<int>(request.transfer_size);
353+
return value;
354+
}());
355+
356+
entry.emplace("timings", [&]() {
357+
using Timings = Diagnostics::Timings;
358+
boost::json::object value;
359+
value["blocked"] = duration(Timings::Queue);
360+
value["dns"] = duration(Timings::NameLookup);
361+
value["connect"] = duration(Timings::Connect);
362+
value["ssl"] = duration(Timings::SSL_Handshake);
363+
value["send"] = duration(Timings::Send, 0.0);
364+
value["wait"] = duration(Timings::Wait, 0.0);
365+
value["receive"] = duration(Timings::Receive, total_time);
366+
return value;
367+
}());
368+
369+
entries.emplace_back(std::move(entry));
370+
}
371+
372+
return entries;
373+
}();
374+
375+
boost::json::object doc;
376+
doc.emplace("log", std::move(log));
377+
232378
std::ofstream file(har_out_path_);
233379
if (!file.is_open()) {
234380
OLP_SDK_LOG_ERROR("HarCaptureAdapter::SaveSession",
235381
"Failed to save session.");
236382
return;
237383
}
238384

239-
JsonFileSerializer serializer{file};
240-
241-
serializer.Object([&] {
242-
serializer.Object("log", [&] {
243-
serializer.String("version", "1.2");
244-
245-
serializer.Object("creator", [&] {
246-
serializer.String("name", "DataSDK");
247-
serializer.String("version", OLP_SDK_VERSION_STRING);
248-
});
249-
250-
serializer.Array("entries", [&] {
251-
for (auto request_index = 0u; request_index < requests_.size();
252-
++request_index) {
253-
const auto& request = requests_[request_index];
254-
const auto diagnostics = diagnostics_.size() > request_index
255-
? diagnostics_[request_index]
256-
: Diagnostics{};
257-
258-
// return duration in milliseconds as float
259-
auto duration = [&](const Diagnostics::Timings timing,
260-
const double default_value = -1.0) {
261-
return diagnostics.available_timings[timing]
262-
? diagnostics.timings[timing].count() / 1000.0
263-
: default_value;
264-
};
265-
266-
const double total_time = duration(
267-
Diagnostics::Total,
268-
static_cast<double>(
269-
std::chrono::duration_cast<std::chrono::microseconds>(
270-
request.end_time - request.start_time)
271-
.count()) /
272-
1000.0);
273-
274-
auto output_headers = [&](const uint16_t headers_offset,
275-
const uint16_t headers_count) {
276-
serializer.Array("headers", [&] {
277-
for (auto i = 0u; i < headers_count; ++i) {
278-
serializer.Object([&] {
279-
auto header = headers_[headers_offset + i];
280-
serializer.String("name", cache_.at(header.first));
281-
serializer.String("value", cache_.at(header.second));
282-
});
283-
}
284-
});
285-
};
286-
287-
serializer.Object([&] {
288-
serializer.String("startedDateTime",
289-
FormatTime(request.start_time));
290-
serializer.Double("time", total_time);
291-
292-
serializer.Object("request", [&] {
293-
serializer.String(
294-
"method",
295-
VerbToString(
296-
static_cast<NetworkRequest::HttpVerb>(request.method)));
297-
serializer.String("url", cache_.at(request.url));
298-
serializer.String("httpVersion", "UNSPECIFIED");
299-
serializer.EmptyArray("cookies");
300-
output_headers(request.request_headers_offset,
301-
request.request_headers_count);
302-
serializer.EmptyArray("queryString");
303-
serializer.Int("headersSize", -1);
304-
serializer.Int("bodySize", -1);
305-
});
306-
307-
// response
308-
serializer.Object("response", [&] {
309-
serializer.Int("status", request.status_code);
310-
serializer.String("statusText", "");
311-
serializer.String("httpVersion", "UNSPECIFIED");
312-
serializer.EmptyArray("cookies");
313-
output_headers(request.response_headers_offset,
314-
request.response_headers_count);
315-
serializer.Object("content", [&] {
316-
serializer.Int("size", 0);
317-
serializer.String("mimeType", "");
318-
});
319-
serializer.String("redirectURL", "");
320-
serializer.Int("headersSize", -1);
321-
serializer.Int("bodySize", -1);
322-
serializer.Int("_transferSize",
323-
static_cast<int>(request.transfer_size));
324-
});
325-
326-
// timings
327-
serializer.Object("timings", [&] {
328-
using Timings = Diagnostics::Timings;
329-
serializer.Double("blocked", duration(Timings::Queue));
330-
serializer.Double("dns", duration(Timings::NameLookup));
331-
serializer.Double("connect", duration(Timings::Connect));
332-
serializer.Double("ssl", duration(Timings::SSL_Handshake));
333-
serializer.Double("send", duration(Timings::Send, 0.0));
334-
serializer.Double("wait", duration(Timings::Wait, 0.0));
335-
serializer.Double("receive",
336-
duration(Timings::Receive, total_time));
337-
});
338-
});
339-
}
340-
});
341-
});
342-
});
343-
385+
PrettyPrint(file, doc);
344386
file.close();
345387

346388
OLP_SDK_LOG_INFO("HarCaptureAdapter::SaveSession",

0 commit comments

Comments
 (0)