Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
grpc_official_wrapper.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
20
21#if NETWORK_GRPC_OFFICIAL
22
23#include <grpcpp/grpcpp.h>
24#include <grpcpp/ext/proto_server_reflection_plugin.h>
25#include <grpcpp/security/credentials.h>
26#include <grpcpp/support/byte_buffer.h>
27
29{
30
31// ============================================================================
32// Error Code Mapping
33// ============================================================================
34
35namespace detail
36{
37
38auto map_grpc_code_to_error(int code) -> int
39{
40 switch (static_cast<::grpc::StatusCode>(code))
41 {
42 case ::grpc::StatusCode::OK:
43 return 0;
44 case ::grpc::StatusCode::CANCELLED:
45 return static_cast<int>(status_code::cancelled);
46 case ::grpc::StatusCode::UNKNOWN:
47 return static_cast<int>(status_code::unknown);
48 case ::grpc::StatusCode::INVALID_ARGUMENT:
49 return static_cast<int>(status_code::invalid_argument);
50 case ::grpc::StatusCode::DEADLINE_EXCEEDED:
51 return static_cast<int>(status_code::deadline_exceeded);
52 case ::grpc::StatusCode::NOT_FOUND:
53 return static_cast<int>(status_code::not_found);
54 case ::grpc::StatusCode::ALREADY_EXISTS:
55 return static_cast<int>(status_code::already_exists);
56 case ::grpc::StatusCode::PERMISSION_DENIED:
57 return static_cast<int>(status_code::permission_denied);
58 case ::grpc::StatusCode::RESOURCE_EXHAUSTED:
59 return static_cast<int>(status_code::resource_exhausted);
60 case ::grpc::StatusCode::FAILED_PRECONDITION:
61 return static_cast<int>(status_code::failed_precondition);
62 case ::grpc::StatusCode::ABORTED:
63 return static_cast<int>(status_code::aborted);
64 case ::grpc::StatusCode::OUT_OF_RANGE:
65 return static_cast<int>(status_code::out_of_range);
66 case ::grpc::StatusCode::UNIMPLEMENTED:
67 return static_cast<int>(status_code::unimplemented);
68 case ::grpc::StatusCode::INTERNAL:
69 return static_cast<int>(status_code::internal);
70 case ::grpc::StatusCode::UNAVAILABLE:
71 return static_cast<int>(status_code::unavailable);
72 case ::grpc::StatusCode::DATA_LOSS:
73 return static_cast<int>(status_code::data_loss);
74 case ::grpc::StatusCode::UNAUTHENTICATED:
75 return static_cast<int>(status_code::unauthenticated);
76 default:
77 return static_cast<int>(status_code::unknown);
78 }
79}
80
81auto map_status_to_grpc_code(status_code code) -> int
82{
83 switch (code)
84 {
85 case status_code::ok:
86 return static_cast<int>(::grpc::StatusCode::OK);
87 case status_code::cancelled:
88 return static_cast<int>(::grpc::StatusCode::CANCELLED);
89 case status_code::unknown:
90 return static_cast<int>(::grpc::StatusCode::UNKNOWN);
91 case status_code::invalid_argument:
92 return static_cast<int>(::grpc::StatusCode::INVALID_ARGUMENT);
93 case status_code::deadline_exceeded:
94 return static_cast<int>(::grpc::StatusCode::DEADLINE_EXCEEDED);
95 case status_code::not_found:
96 return static_cast<int>(::grpc::StatusCode::NOT_FOUND);
97 case status_code::already_exists:
98 return static_cast<int>(::grpc::StatusCode::ALREADY_EXISTS);
99 case status_code::permission_denied:
100 return static_cast<int>(::grpc::StatusCode::PERMISSION_DENIED);
101 case status_code::resource_exhausted:
102 return static_cast<int>(::grpc::StatusCode::RESOURCE_EXHAUSTED);
103 case status_code::failed_precondition:
104 return static_cast<int>(::grpc::StatusCode::FAILED_PRECONDITION);
105 case status_code::aborted:
106 return static_cast<int>(::grpc::StatusCode::ABORTED);
107 case status_code::out_of_range:
108 return static_cast<int>(::grpc::StatusCode::OUT_OF_RANGE);
109 case status_code::unimplemented:
110 return static_cast<int>(::grpc::StatusCode::UNIMPLEMENTED);
111 case status_code::internal:
112 return static_cast<int>(::grpc::StatusCode::INTERNAL);
113 case status_code::unavailable:
114 return static_cast<int>(::grpc::StatusCode::UNAVAILABLE);
115 case status_code::data_loss:
116 return static_cast<int>(::grpc::StatusCode::DATA_LOSS);
117 case status_code::unauthenticated:
118 return static_cast<int>(::grpc::StatusCode::UNAUTHENTICATED);
119 default:
120 return static_cast<int>(::grpc::StatusCode::UNKNOWN);
121 }
122}
123
124} // namespace detail
125
126// ============================================================================
127// Status Conversion
128// ============================================================================
129
130auto from_grpc_status(const ::grpc::Status& status) -> grpc_status
131{
132 if (status.ok())
133 {
134 return grpc_status::ok_status();
135 }
136
137 auto code = static_cast<status_code>(detail::map_grpc_code_to_error(
138 static_cast<int>(status.error_code())));
139 return grpc_status::error_status(code, std::string(status.error_message()));
140}
141
142auto to_grpc_status(const grpc_status& status) -> ::grpc::Status
143{
144 if (status.is_ok())
145 {
146 return ::grpc::Status::OK;
147 }
148
149 auto grpc_code = static_cast<::grpc::StatusCode>(
150 detail::map_status_to_grpc_code(status.code));
151 return ::grpc::Status(grpc_code, status.message);
152}
153
154template<typename T>
155auto result_to_grpc_status(const Result<T>& result) -> ::grpc::Status
156{
157 if (result.is_ok())
158 {
159 return ::grpc::Status::OK;
160 }
161
162 const auto& err = result.error();
163
164 // Map error code to appropriate gRPC status code
165 ::grpc::StatusCode grpc_code = ::grpc::StatusCode::INTERNAL;
166
167 // Check for specific error codes
169 {
170 grpc_code = ::grpc::StatusCode::INVALID_ARGUMENT;
171 }
172 else if (err.code == error_codes::common_errors::not_found)
173 {
174 grpc_code = ::grpc::StatusCode::NOT_FOUND;
175 }
177 {
178 grpc_code = ::grpc::StatusCode::ALREADY_EXISTS;
179 }
181 {
182 grpc_code = ::grpc::StatusCode::PERMISSION_DENIED;
183 }
185 {
186 grpc_code = ::grpc::StatusCode::UNAVAILABLE;
187 }
188 else if (err.code == static_cast<int>(status_code::deadline_exceeded))
189 {
190 grpc_code = ::grpc::StatusCode::DEADLINE_EXCEEDED;
191 }
192 else if (err.code == static_cast<int>(status_code::cancelled))
193 {
194 grpc_code = ::grpc::StatusCode::CANCELLED;
195 }
196 else if (err.code == static_cast<int>(status_code::unauthenticated))
197 {
198 grpc_code = ::grpc::StatusCode::UNAUTHENTICATED;
199 }
200 else if (err.code == static_cast<int>(status_code::resource_exhausted))
201 {
202 grpc_code = ::grpc::StatusCode::RESOURCE_EXHAUSTED;
203 }
204 else if (err.code == static_cast<int>(status_code::unimplemented))
205 {
206 grpc_code = ::grpc::StatusCode::UNIMPLEMENTED;
207 }
208
209 return ::grpc::Status(grpc_code, err.message);
210}
211
212auto void_result_to_grpc_status(const VoidResult& result) -> ::grpc::Status
213{
214 if (result.is_ok())
215 {
216 return ::grpc::Status::OK;
217 }
218
219 const auto& err = result.error();
220
221 // Map error code to appropriate gRPC status code
222 ::grpc::StatusCode grpc_code = ::grpc::StatusCode::INTERNAL;
223
225 {
226 grpc_code = ::grpc::StatusCode::INVALID_ARGUMENT;
227 }
228 else if (err.code == error_codes::common_errors::not_found)
229 {
230 grpc_code = ::grpc::StatusCode::NOT_FOUND;
231 }
233 {
234 grpc_code = ::grpc::StatusCode::ALREADY_EXISTS;
235 }
237 {
238 grpc_code = ::grpc::StatusCode::PERMISSION_DENIED;
239 }
241 {
242 grpc_code = ::grpc::StatusCode::UNAVAILABLE;
243 }
244
245 return ::grpc::Status(grpc_code, err.message);
246}
247
248auto grpc_status_to_void_result(const ::grpc::Status& status) -> VoidResult
249{
250 if (status.ok())
251 {
252 return ok();
253 }
254
255 return error_void(
256 detail::map_grpc_code_to_error(static_cast<int>(status.error_code())),
257 std::string(status.error_message()),
258 "grpc");
259}
260
261// Explicit template instantiations for common types
262template auto result_to_grpc_status(const Result<std::vector<uint8_t>>&) -> ::grpc::Status;
263template auto result_to_grpc_status(const Result<grpc_message>&) -> ::grpc::Status;
264template auto result_to_grpc_status(const Result<std::string>&) -> ::grpc::Status;
265
266// ============================================================================
267// Deadline Utilities
268// ============================================================================
269
270auto set_deadline(::grpc::ClientContext* ctx,
271 std::chrono::system_clock::time_point deadline) -> void
272{
273 if (ctx != nullptr)
274 {
275 ctx->set_deadline(deadline);
276 }
277}
278
279auto get_remaining_time(const ::grpc::ServerContext* ctx)
280 -> std::optional<std::chrono::milliseconds>
281{
282 if (ctx == nullptr)
283 {
284 return std::nullopt;
285 }
286
287 auto deadline = ctx->deadline();
288
289 // Check for infinite deadline
290 if (deadline == std::chrono::system_clock::time_point::max())
291 {
292 return std::nullopt;
293 }
294
295 auto now = std::chrono::system_clock::now();
296 if (deadline <= now)
297 {
298 return std::chrono::milliseconds(0);
299 }
300
301 return std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now);
302}
303
304// ============================================================================
305// Channel Management
306// ============================================================================
307
308auto create_channel(const std::string& target,
309 const channel_credentials_config& config)
310 -> std::shared_ptr<::grpc::Channel>
311{
312 if (config.insecure)
313 {
314 return create_insecure_channel(target);
315 }
316
317 // Build SSL credentials options
318 ::grpc::SslCredentialsOptions ssl_opts;
319
320 if (!config.root_certificates.empty())
321 {
322 ssl_opts.pem_root_certs = config.root_certificates;
323 }
324
325 if (config.client_certificate.has_value() && config.client_key.has_value())
326 {
327 ssl_opts.pem_cert_chain = config.client_certificate.value();
328 ssl_opts.pem_private_key = config.client_key.value();
329 }
330
331 auto creds = ::grpc::SslCredentials(ssl_opts);
332
333 // Create channel arguments
334 ::grpc::ChannelArguments args;
335
336 return ::grpc::CreateCustomChannel(target, creds, args);
337}
338
339auto create_insecure_channel(const std::string& target)
340 -> std::shared_ptr<::grpc::Channel>
341{
342 return ::grpc::CreateChannel(target, ::grpc::InsecureChannelCredentials());
343}
344
345auto wait_for_channel_ready(const std::shared_ptr<::grpc::Channel>& channel,
346 std::chrono::milliseconds timeout) -> bool
347{
348 if (!channel)
349 {
350 return false;
351 }
352
353 auto deadline = std::chrono::system_clock::now() + timeout;
354 return channel->WaitForConnected(deadline);
355}
356
357// ============================================================================
358// ByteBuffer Utilities
359// ============================================================================
360
361namespace detail
362{
363
369auto vector_to_byte_buffer(const std::vector<uint8_t>& data) -> ::grpc::ByteBuffer
370{
371 ::grpc::Slice slice(data.data(), data.size());
372 return ::grpc::ByteBuffer(&slice, 1);
373}
374
380auto byte_buffer_to_vector(const ::grpc::ByteBuffer& buffer) -> std::vector<uint8_t>
381{
382 std::vector<::grpc::Slice> slices;
383 buffer.Dump(&slices);
384
385 std::vector<uint8_t> result;
386 result.reserve(buffer.Length());
387
388 for (const auto& slice : slices)
389 {
390 const auto* begin = reinterpret_cast<const uint8_t*>(slice.begin());
391 result.insert(result.end(), begin, begin + slice.size());
392 }
393
394 return result;
395}
396
402auto message_to_byte_buffer(const grpc_message& msg) -> ::grpc::ByteBuffer
403{
404 auto serialized = msg.serialize();
405 return vector_to_byte_buffer(serialized);
406}
407
413auto byte_buffer_to_message(const ::grpc::ByteBuffer& buffer) -> Result<grpc_message>
414{
415 auto data = byte_buffer_to_vector(buffer);
416 return grpc_message::parse(data);
417}
418
419} // namespace detail
420
421// ============================================================================
422// Server Streaming Adapters
423// ============================================================================
424
429template<typename W>
430class official_server_writer_impl : public server_writer
431{
432public:
433 explicit official_server_writer_impl(W* writer)
434 : writer_(writer)
435 {
436 }
437
438 auto write(const std::vector<uint8_t>& message) -> VoidResult override
439 {
440 if (writer_ == nullptr)
441 {
442 return error_void(
444 "Writer is null",
445 "grpc::server_writer");
446 }
447
448 ::grpc::ByteBuffer buffer = detail::vector_to_byte_buffer(message);
449 bool success = writer_->Write(buffer);
450
451 if (!success)
452 {
453 return error_void(
455 "Failed to write message",
456 "grpc::server_writer");
457 }
458
459 return ok();
460 }
461
462private:
463 W* writer_;
464};
465
470template<typename R>
471class official_server_reader_impl : public server_reader
472{
473public:
474 explicit official_server_reader_impl(R* reader)
475 : reader_(reader)
476 , has_more_(true)
477 {
478 }
479
480 auto read() -> Result<std::vector<uint8_t>> override
481 {
482 if (reader_ == nullptr)
483 {
486 "Reader is null",
487 "grpc::server_reader");
488 }
489
490 ::grpc::ByteBuffer buffer;
491 bool success = reader_->Read(&buffer);
492
493 if (!success)
494 {
495 has_more_ = false;
497 static_cast<int>(status_code::ok),
498 "End of stream",
499 "grpc::server_reader");
500 }
501
502 return ok(detail::byte_buffer_to_vector(buffer));
503 }
504
505 auto has_more() const -> bool override
506 {
507 return has_more_;
508 }
509
510private:
511 R* reader_;
512 bool has_more_;
513};
514
519template<typename RW>
520class official_server_reader_writer_impl : public server_reader_writer
521{
522public:
523 explicit official_server_reader_writer_impl(RW* stream)
524 : stream_(stream)
525 , has_more_(true)
526 {
527 }
528
529 auto read() -> Result<std::vector<uint8_t>> override
530 {
531 if (stream_ == nullptr)
532 {
533 return error<std::vector<uint8_t>>(
534 error_codes::common_errors::invalid_argument,
535 "Stream is null",
536 "grpc::server_reader_writer");
537 }
538
539 ::grpc::ByteBuffer buffer;
540 bool success = stream_->Read(&buffer);
541
542 if (!success)
543 {
544 has_more_ = false;
545 return error<std::vector<uint8_t>>(
546 static_cast<int>(status_code::ok),
547 "End of stream",
548 "grpc::server_reader_writer");
549 }
550
551 return ok(detail::byte_buffer_to_vector(buffer));
552 }
553
554 auto write(const std::vector<uint8_t>& message) -> VoidResult override
555 {
556 if (stream_ == nullptr)
557 {
558 return error_void(
559 error_codes::common_errors::invalid_argument,
560 "Stream is null",
561 "grpc::server_reader_writer");
562 }
563
564 ::grpc::ByteBuffer buffer = detail::vector_to_byte_buffer(message);
565 bool success = stream_->Write(buffer);
566
567 if (!success)
568 {
569 return error_void(
570 error_codes::network_system::connection_failed,
571 "Failed to write message",
572 "grpc::server_reader_writer");
573 }
574
575 return ok();
576 }
577
578 auto has_more() const -> bool override
579 {
580 return has_more_;
581 }
582
583private:
584 RW* stream_;
585 bool has_more_;
586};
587
588} // namespace kcenon::network::protocols::grpc
589
590#endif // NETWORK_GRPC_OFFICIAL
tracing_config config
Definition exporters.cpp:29
Official gRPC library wrapper interfaces.
gRPC message framing and serialization.
gRPC protocol implementation
Definition client.h:34
status_code
gRPC status codes (as defined in grpc/status.h)
Definition status.h:36
@ cancelled
The operation was cancelled.
@ deadline_exceeded
Deadline expired before operation completed.
@ unimplemented
Operation not implemented.
@ ok
Not an error; returned on success.
@ unauthenticated
Request lacks valid authentication.
Result< std::monostate > VoidResult
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
Network-specific error and result type definitions.
gRPC status codes and error representation.
static auto parse(std::span< const uint8_t > input) -> Result< grpc_message >
Parse gRPC message from raw bytes.
Definition frame.cpp:14
static auto ok_status() -> grpc_status
Create OK status.
Definition status.h:154
static auto error_status(status_code c, std::string msg) -> grpc_status
Create error status.
Definition status.h:165