|
| 1 | +// |
| 2 | +// Created by Johannes Zottele on 11.07.25. |
| 3 | +// |
| 4 | + |
| 5 | +#include <grpcpp_c.h> |
| 6 | + |
| 7 | +#include <memory> |
| 8 | +#include <grpcpp/grpcpp.h> |
| 9 | +#include <grpcpp/generic/generic_stub.h> |
| 10 | +#include <grpcpp/impl/client_unary_call.h> |
| 11 | +#include <google/protobuf/io/coded_stream.h> |
| 12 | +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> |
| 13 | + |
| 14 | +namespace pb = google::protobuf; |
| 15 | + |
| 16 | +struct grpc_client { |
| 17 | + std::shared_ptr<grpc::Channel> channel; |
| 18 | + std::unique_ptr<grpc::GenericStub> stub; |
| 19 | +}; |
| 20 | + |
| 21 | +struct grpc_method { |
| 22 | + std::string name_str; |
| 23 | + std::unique_ptr<grpc::internal::RpcMethod> method; |
| 24 | +}; |
| 25 | + |
| 26 | +struct grpc_context { |
| 27 | + std::unique_ptr<grpc::ClientContext> context; |
| 28 | +}; |
| 29 | + |
| 30 | +extern "C" { |
| 31 | + |
| 32 | + grpc_client_t *grpc_client_create_insecure(const char *target) { |
| 33 | + std::string target_str = target; |
| 34 | + auto client = new grpc_client; |
| 35 | + client->channel = grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()); |
| 36 | + client->stub = std::make_unique<grpc::GenericStub>(client->channel); |
| 37 | + return client; |
| 38 | + } |
| 39 | + |
| 40 | + void grpc_client_delete(const grpc_client_t *client) { |
| 41 | + delete client; |
| 42 | + } |
| 43 | + |
| 44 | + grpc_method_t *grpc_method_create(const char *method_name) { |
| 45 | + auto *method = new grpc_method; |
| 46 | + method->name_str = method_name; |
| 47 | + method->method = std::make_unique<grpc::internal::RpcMethod>(method->name_str.c_str(), grpc::internal::RpcMethod::NORMAL_RPC); |
| 48 | + return method; |
| 49 | + } |
| 50 | + |
| 51 | + void grpc_method_delete(const grpc_method_t *method) { |
| 52 | + delete method; |
| 53 | + } |
| 54 | + |
| 55 | + const char *grpc_method_name(const grpc_method_t *method) { |
| 56 | + return method->method->name(); |
| 57 | + } |
| 58 | + |
| 59 | + grpc_context_t *grpc_context_create() { |
| 60 | + auto *context = new grpc_context; |
| 61 | + context->context = std::make_unique<grpc::ClientContext>(); |
| 62 | + return context; |
| 63 | + } |
| 64 | + |
| 65 | + void grpc_context_delete(const grpc_context_t *context) { |
| 66 | + delete context; |
| 67 | + } |
| 68 | + |
| 69 | + static grpc_status_code_t status_to_c(grpc::StatusCode status); |
| 70 | + |
| 71 | + grpc_status_code_t grpc_client_call_unary_blocking(grpc_client_t *client, const char *method, |
| 72 | + grpc_slice req_slice, grpc_slice *resp_slice) { |
| 73 | + |
| 74 | + if (!client || !method) return GRPC_C_STATUS_INVALID_ARGUMENT; |
| 75 | + |
| 76 | + grpc::Slice cc_req_slice(req_slice, grpc::Slice::ADD_REF); |
| 77 | + grpc::ByteBuffer req_bb(&cc_req_slice, 1); |
| 78 | + |
| 79 | + grpc::ClientContext context; |
| 80 | + grpc::ByteBuffer resp_bb; |
| 81 | + |
| 82 | + const std::string method_path = "/Greeter/SayHello"; |
| 83 | + grpc::internal::RpcMethod rpc(method_path.c_str(), |
| 84 | + grpc::internal::RpcMethod::NORMAL_RPC); |
| 85 | + |
| 86 | + grpc::Status st = |
| 87 | + grpc::internal::BlockingUnaryCall<grpc::ByteBuffer, grpc::ByteBuffer>( |
| 88 | + client->channel.get(), rpc, &context, req_bb, &resp_bb); |
| 89 | + |
| 90 | + |
| 91 | + if (!st.ok()) { |
| 92 | + // if not ok, no resp_buf is left null |
| 93 | + return status_to_c(st.error_code()); |
| 94 | + } |
| 95 | + |
| 96 | + grpc::Slice cc_resp_slice; |
| 97 | + resp_bb.DumpToSingleSlice(&cc_resp_slice); |
| 98 | + *resp_slice = cc_resp_slice.c_slice(); |
| 99 | + |
| 100 | + grpc::Slice test_slice(*resp_slice, grpc::Slice::ADD_REF); |
| 101 | + pb::io::ArrayInputStream ais(test_slice.begin(), test_slice.size()); |
| 102 | + pb::io::CodedInputStream cis(&ais); |
| 103 | + |
| 104 | + |
| 105 | + cis.ReadTag(); |
| 106 | + uint32_t id = 0; |
| 107 | + if (!cis.ReadVarint32(&id)) { |
| 108 | + std::cerr << "Failed to read id field\n"; |
| 109 | + } |
| 110 | + |
| 111 | + return status_to_c(st.error_code()); |
| 112 | + } |
| 113 | + |
| 114 | + void grpc_client_call_unary_callback(grpc_client_t *client, grpc_method_t *method, grpc_context_t *context, |
| 115 | + grpc_byte_buffer **req_buf, grpc_byte_buffer **resp_buf, void* callback_context, void (*callback)(grpc_status_code_t,void*)) { |
| 116 | + // the grpc::ByteBuffer representation is identical to (* grpc_byte_buffer) so we can safely cast it. |
| 117 | + // so a **grpc_byte_buffer can be cast to *grpc::ByteBuffer. |
| 118 | + static_assert(sizeof(grpc::ByteBuffer) == sizeof(grpc_byte_buffer*), |
| 119 | + "ByteBuffer must have same representation as " |
| 120 | + "grpc_byte_buffer*"); |
| 121 | + const auto req_bb = reinterpret_cast<grpc::ByteBuffer *>(req_buf); |
| 122 | + const auto resp_bb = reinterpret_cast<grpc::ByteBuffer *>(resp_buf); |
| 123 | + grpc::internal::CallbackUnaryCall<grpc::ByteBuffer, grpc::ByteBuffer>(client->channel.get(), *method->method, context->context.get(), req_bb, resp_bb, [callback, callback_context](grpc::Status st) { |
| 124 | + const auto c_st = status_to_c(st.error_code()); |
| 125 | + callback(c_st, callback_context); |
| 126 | + }); |
| 127 | + } |
| 128 | + |
| 129 | + grpc_status_code_t status_to_c(grpc::StatusCode status) { |
| 130 | + switch (status) { |
| 131 | + case grpc::OK: |
| 132 | + return GRPC_C_STATUS_OK; |
| 133 | + case grpc::CANCELLED: |
| 134 | + return GRPC_C_STATUS_CANCELLED; |
| 135 | + case grpc::UNKNOWN: |
| 136 | + return GRPC_C_STATUS_UNKNOWN; |
| 137 | + case grpc::INVALID_ARGUMENT: |
| 138 | + return GRPC_C_STATUS_INVALID_ARGUMENT; |
| 139 | + case grpc::DEADLINE_EXCEEDED: |
| 140 | + return GRPC_C_STATUS_DEADLINE_EXCEEDED; |
| 141 | + case grpc::NOT_FOUND: |
| 142 | + return GRPC_C_STATUS_NOT_FOUND; |
| 143 | + case grpc::ALREADY_EXISTS: |
| 144 | + return GRPC_C_STATUS_ALREADY_EXISTS; |
| 145 | + case grpc::PERMISSION_DENIED: |
| 146 | + return GRPC_C_STATUS_PERMISSION_DENIED; |
| 147 | + case grpc::UNAUTHENTICATED: |
| 148 | + return GRPC_C_STATUS_UNAUTHENTICATED; |
| 149 | + case grpc::RESOURCE_EXHAUSTED: |
| 150 | + return GRPC_C_STATUS_RESOURCE_EXHAUSTED; |
| 151 | + case grpc::FAILED_PRECONDITION: |
| 152 | + return GRPC_C_STATUS_FAILED_PRECONDITION; |
| 153 | + case grpc::ABORTED: |
| 154 | + return GRPC_C_STATUS_ABORTED; |
| 155 | + case grpc::UNIMPLEMENTED: |
| 156 | + return GRPC_C_STATUS_UNIMPLEMENTED; |
| 157 | + case grpc::OUT_OF_RANGE: |
| 158 | + return GRPC_C_STATUS_OUT_OF_RANGE; |
| 159 | + case grpc::INTERNAL: |
| 160 | + return GRPC_C_STATUS_INTERNAL; |
| 161 | + case grpc::UNAVAILABLE: |
| 162 | + return GRPC_C_STATUS_UNAVAILABLE; |
| 163 | + case grpc::DATA_LOSS: |
| 164 | + return GRPC_C_STATUS_DATA_LOSS; |
| 165 | + case grpc::DO_NOT_USE: |
| 166 | + return GRPC_C_STATUS_DO_NOT_USE; |
| 167 | + } |
| 168 | + } |
| 169 | + |
| 170 | + |
| 171 | + uint32_t pb_decode_greeter_sayhello_response(grpc_slice response) { |
| 172 | + grpc::Slice cc_resp_slice(response, grpc::Slice::ADD_REF); |
| 173 | + pb::io::ArrayInputStream asi(cc_resp_slice.begin(), cc_resp_slice.size()); |
| 174 | + pb::io::CodedInputStream cis(&asi); |
| 175 | + |
| 176 | + const auto tag = cis.ReadTag(); |
| 177 | + if (tag != 8) { |
| 178 | + std::cerr << "Failed to read tag. Got: " << tag << std::endl; |
| 179 | + } |
| 180 | + |
| 181 | + uint32_t result; |
| 182 | + if (!cis.ReadVarint32(&result)) { |
| 183 | + std::cerr << "Failed to read result" << std::endl; |
| 184 | + } else { |
| 185 | + |
| 186 | + } |
| 187 | + return result; |
| 188 | + } |
| 189 | + |
| 190 | + |
| 191 | + grpc_status_code_t grpc_byte_buffer_dump_to_single_slice(grpc_byte_buffer *byte_buffer, grpc_slice *slice) { |
| 192 | + auto bb = reinterpret_cast<grpc::ByteBuffer*>(&byte_buffer); |
| 193 | + grpc::Slice cc_slice; |
| 194 | + bb->DumpToSingleSlice(&cc_slice); |
| 195 | + *slice = cc_slice.c_slice(); |
| 196 | + return GRPC_C_STATUS_OK; |
| 197 | + } |
| 198 | + |
| 199 | +} |
| 200 | + |
| 201 | + |
0 commit comments