• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Frontend command line interface.
16 #include "frontend/frontend_client.h"
17 
18 #include <google/protobuf/util/json_util.h>
19 #include <grpcpp/support/status.h>
20 #include <stdlib.h>
21 
22 #include <chrono>
23 #include <cstdint>
24 #include <iomanip>
25 #include <iostream>
26 #include <iterator>
27 #include <memory>
28 #include <optional>
29 #include <sstream>
30 #include <string>
31 #include <string_view>
32 
33 #include "frontend-client-cxx/src/lib.rs.h"
34 #include "frontend.grpc.pb.h"
35 #include "frontend.pb.h"
36 #include "google/protobuf/empty.pb.h"
37 #include "grpcpp/create_channel.h"
38 #include "grpcpp/security/credentials.h"
39 #include "grpcpp/support/status_code_enum.h"
40 #include "model.pb.h"
41 #include "util/ini_file.h"
42 #include "util/os_utils.h"
43 #include "util/string_utils.h"
44 
45 namespace netsim {
46 namespace frontend {
47 namespace {
48 const std::chrono::duration kConnectionDeadline = std::chrono::seconds(1);
49 
NewFrontendStub()50 std::unique_ptr<frontend::FrontendService::Stub> NewFrontendStub() {
51   auto port = netsim::osutils::GetServerAddress();
52   if (!port.has_value()) {
53     return {};
54   }
55   auto server = "localhost:" + port.value();
56   std::shared_ptr<grpc::Channel> channel =
57       grpc::CreateChannel(server, grpc::InsecureChannelCredentials());
58 
59   auto deadline = std::chrono::system_clock::now() + kConnectionDeadline;
60   if (!channel->WaitForConnected(deadline)) {
61     return nullptr;
62   }
63 
64   return frontend::FrontendService::NewStub(channel);
65 }
66 
67 // A synchronous client for the netsim frontend service.
68 class FrontendClientImpl : public FrontendClient {
69  public:
FrontendClientImpl(std::unique_ptr<frontend::FrontendService::Stub> stub)70   FrontendClientImpl(std::unique_ptr<frontend::FrontendService::Stub> stub)
71       : stub_(std::move(stub)) {}
72 
make_result(const grpc::Status & status,const google::protobuf::Message & message) const73   std::unique_ptr<ClientResult> make_result(
74       const grpc::Status &status,
75       const google::protobuf::Message &message) const {
76     std::vector<unsigned char> message_vec(message.ByteSizeLong());
77     message.SerializeToArray(message_vec.data(), message_vec.size());
78     if (!status.ok()) {
79       return std::make_unique<ClientResult>(false, status.error_message(),
80                                             message_vec);
81     }
82     return std::make_unique<ClientResult>(true, "", message_vec);
83   }
84 
85   // Gets the version of the network simulator service.
GetVersion() const86   std::unique_ptr<ClientResult> GetVersion() const override {
87     frontend::VersionResponse response;
88     grpc::ClientContext context_;
89     auto status = stub_->GetVersion(&context_, {}, &response);
90     return make_result(status, response);
91   }
92 
93   // Gets the list of device information
GetDevices() const94   std::unique_ptr<ClientResult> GetDevices() const override {
95     frontend::GetDevicesResponse response;
96     grpc::ClientContext context_;
97     auto status = stub_->GetDevices(&context_, {}, &response);
98     return make_result(status, response);
99   }
100 
Reset() const101   std::unique_ptr<ClientResult> Reset() const override {
102     grpc::ClientContext context_;
103     google::protobuf::Empty response;
104     auto status = stub_->Reset(&context_, {}, &response);
105     return make_result(status, response);
106   }
107 
108   // Patchs the information of the device
PatchDevice(rust::Vec<::rust::u8> const & request_byte_vec) const109   std::unique_ptr<ClientResult> PatchDevice(
110       rust::Vec<::rust::u8> const &request_byte_vec) const override {
111     google::protobuf::Empty response;
112     grpc::ClientContext context_;
113     frontend::PatchDeviceRequest request;
114     if (!request.ParseFromArray(request_byte_vec.data(),
115                                 request_byte_vec.size())) {
116       return make_result(
117           grpc::Status(
118               grpc::StatusCode::INVALID_ARGUMENT,
119               "Error parsing PatchDevice request protobuf. request size:" +
120                   std::to_string(request_byte_vec.size())),
121           response);
122     };
123     auto status = stub_->PatchDevice(&context_, request, &response);
124     return make_result(status, response);
125   }
126 
127   // Get the list of Capture information
ListCapture() const128   std::unique_ptr<ClientResult> ListCapture() const override {
129     frontend::ListCaptureResponse response;
130     grpc::ClientContext context_;
131     auto status = stub_->ListCapture(&context_, {}, &response);
132     return make_result(status, response);
133   }
134 
135   // Patch the Capture
PatchCapture(rust::Vec<::rust::u8> const & request_byte_vec) const136   std::unique_ptr<ClientResult> PatchCapture(
137       rust::Vec<::rust::u8> const &request_byte_vec) const override {
138     google::protobuf::Empty response;
139     grpc::ClientContext context_;
140     frontend::PatchCaptureRequest request;
141     if (!request.ParseFromArray(request_byte_vec.data(),
142                                 request_byte_vec.size())) {
143       return make_result(
144           grpc::Status(
145               grpc::StatusCode::INVALID_ARGUMENT,
146               "Error parsing PatchCapture request protobuf. request size:" +
147                   std::to_string(request_byte_vec.size())),
148           response);
149     };
150     auto status = stub_->PatchCapture(&context_, request, &response);
151     return make_result(status, response);
152   }
153 
154   // Download capture file by using ClientResponseReader to handle streaming
155   // grpc
GetCapture(rust::Vec<::rust::u8> const & request_byte_vec,ClientResponseReader const & client_reader) const156   std::unique_ptr<ClientResult> GetCapture(
157       rust::Vec<::rust::u8> const &request_byte_vec,
158       ClientResponseReader const &client_reader) const override {
159     grpc::ClientContext context_;
160     frontend::GetCaptureRequest request;
161     if (!request.ParseFromArray(request_byte_vec.data(),
162                                 request_byte_vec.size())) {
163       return make_result(
164           grpc::Status(
165               grpc::StatusCode::INVALID_ARGUMENT,
166               "Error parsing GetCapture request protobuf. request size:" +
167                   std::to_string(request_byte_vec.size())),
168           google::protobuf::Empty());
169     };
170     auto reader = stub_->GetCapture(&context_, request);
171     frontend::GetCaptureResponse chunk;
172     // Read every available chunks from grpc reader
173     while (reader->Read(&chunk)) {
174       // Using a mutable protobuf here so the move iterator can move
175       // the capture stream without copying.
176       auto mut_stream = chunk.mutable_capture_stream();
177       auto bytes =
178           std::vector<uint8_t>(std::make_move_iterator(mut_stream->begin()),
179                                std::make_move_iterator(mut_stream->end()));
180       client_reader.handle_chunk(
181           rust::Slice<const uint8_t>{bytes.data(), bytes.size()});
182     }
183     auto status = reader->Finish();
184     return make_result(status, google::protobuf::Empty());
185   }
186 
187   // Helper function to redirect to the correct Grpc call
SendGrpc(frontend::GrpcMethod const & grpc_method,rust::Vec<::rust::u8> const & request_byte_vec) const188   std::unique_ptr<ClientResult> SendGrpc(
189       frontend::GrpcMethod const &grpc_method,
190       rust::Vec<::rust::u8> const &request_byte_vec) const override {
191     switch (grpc_method) {
192       case frontend::GrpcMethod::GetVersion:
193         return GetVersion();
194       case frontend::GrpcMethod::PatchDevice:
195         return PatchDevice(request_byte_vec);
196       case frontend::GrpcMethod::GetDevices:
197         return GetDevices();
198       case frontend::GrpcMethod::Reset:
199         return Reset();
200       case frontend::GrpcMethod::ListCapture:
201         return ListCapture();
202       case frontend::GrpcMethod::PatchCapture:
203         return PatchCapture(request_byte_vec);
204       default:
205         return make_result(grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
206                                         "Unknown GrpcMethod found."),
207                            google::protobuf::Empty());
208     }
209   }
210 
211  private:
212   std::unique_ptr<frontend::FrontendService::Stub> stub_;
213 
CheckStatus(const grpc::Status & status,const std::string & message)214   static bool CheckStatus(const grpc::Status &status,
215                           const std::string &message) {
216     if (status.ok()) return true;
217     if (status.error_code() == grpc::StatusCode::UNAVAILABLE)
218       std::cerr << "error: netsim frontend service is unavailable, "
219                    "please restart."
220                 << std::endl;
221     else
222       std::cerr << "error: request to service failed (" << status.error_code()
223                 << ") - " << status.error_message() << std::endl;
224     return false;
225   }
226 };
227 
228 }  // namespace
229 
NewFrontendClient()230 std::unique_ptr<FrontendClient> NewFrontendClient() {
231   auto stub = NewFrontendStub();
232   return (stub == nullptr
233               ? nullptr
234               : std::make_unique<FrontendClientImpl>(std::move(stub)));
235 }
236 
237 }  // namespace frontend
238 }  // namespace netsim
239