1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 #ifdef OS_WIN
32 #include <windows.h>
33 #include <ws2tcpip.h>
34 #pragma comment(lib, "ws2_32.lib")
35 using ssize_t = SSIZE_T;
36 #else
37 #include <fcntl.h>
38 #include <netdb.h>
39 #include <netinet/in.h>
40 #include <sys/socket.h>
41 #include <unistd.h>
42 #endif // OS_WIN
43
44 #include <cstddef>
45 #include <cstring>
46 #include <memory>
47 #include <string>
48 #include <vector>
49
50 #include "base/flags.h"
51 #include "base/init_mozc.h"
52 #include "base/singleton.h"
53 #include "base/system_util.h"
54 #include "engine/engine_factory.h"
55 #include "protocol/commands.pb.h"
56 #include "session/random_keyevents_generator.h"
57 #include "session/session_handler.h"
58 #include "session/session_usage_observer.h"
59
60 DEFINE_string(host, "localhost", "server host name");
61 DEFINE_bool(server, true, "server mode");
62 DEFINE_bool(client, false, "client mode");
63 DEFINE_int32(client_test_size, 100, "client test size");
64 DEFINE_int32(port, 8000, "port of RPC server");
65 DEFINE_int32(rpc_timeout, 60000, "timeout");
66 DEFINE_string(user_profile_directory, "", "user profile directory");
67
68 namespace mozc {
69
70 namespace {
71
72 const size_t kMaxRequestSize = 32 * 32 * 8192;
73 const size_t kMaxOutputSize = 32 * 32 * 8192;
74 const int kInvalidSocket = -1;
75
76 // TODO(taku): timeout should be handled.
Recv(int socket,char * buf,size_t buf_size,int timeout)77 bool Recv(int socket, char *buf,
78 size_t buf_size, int timeout) {
79 ssize_t buf_left = buf_size;
80 while (buf_left > 0) {
81 const ssize_t read_size = ::recv(socket, buf, buf_left, 0);
82 if (read_size < 0) {
83 LOG(ERROR) << "an error occurred during recv()";
84 return false;
85 }
86 buf += read_size;
87 buf_left -= read_size;
88 }
89 return buf_left == 0;
90 }
91
92 // TODO(taku): timeout should be handled.
Send(int socket,const char * buf,size_t buf_size,int timeout)93 bool Send(int socket, const char *buf,
94 size_t buf_size, int timeout) {
95 ssize_t buf_left = buf_size;
96 while (buf_left > 0) {
97 #if defined(OS_WIN)
98 const int kFlag = 0;
99 #elif defined(OS_MACOSX)
100 const int kFlag = SO_NOSIGPIPE;
101 #else
102 const int kFlag = MSG_NOSIGNAL;
103 #endif
104 const ssize_t read_size = ::send(socket, buf, buf_left, kFlag);
105 if (read_size < 0) {
106 LOG(ERROR) << "an error occurred during sending";
107 return false;
108 }
109 buf += read_size;
110 buf_left -= read_size;
111 }
112 return buf_left == 0;
113 }
114
CloseSocket(int client_socket)115 void CloseSocket(int client_socket) {
116 #ifdef OS_WIN
117 ::closesocket(client_socket);
118 ::shutdown(client_socket, SD_BOTH);
119 #else
120 ::close(client_socket);
121 ::shutdown(client_socket, SHUT_RDWR);
122 #endif
123 }
124
125 // Standalone RPCServer.
126 // TODO(taku): Make a RPC class inherited from IPCInterface.
127 // This allows us to reuse client::Session library and SessionServer.
128 class RPCServer {
129 public:
RPCServer()130 RPCServer() : server_socket_(kInvalidSocket),
131 handler_(new SessionHandler(
132 std::unique_ptr<Engine>(EngineFactory::Create()))) {
133 struct sockaddr_in sin;
134
135 server_socket_ = ::socket(AF_INET, SOCK_STREAM, 0);
136
137 CHECK_NE(server_socket_, kInvalidSocket) << "socket failed";
138
139 #ifndef OS_WIN
140 int flags = ::fcntl(server_socket_, F_GETFD, 0);
141 CHECK_GE(flags, 0) << "fcntl(F_GETFD) failed";
142 flags |= FD_CLOEXEC;
143 CHECK_EQ(::fcntl(server_socket_, F_SETFD, flags), 0)
144 << "fctl(F_SETFD) failed";
145 #endif
146
147 ::memset(&sin, 0, sizeof(sin));
148 sin.sin_port = htons(FLAGS_port);
149 sin.sin_family = AF_INET;
150 sin.sin_addr.s_addr = htonl(INADDR_ANY);
151
152 int on = 1;
153 ::setsockopt(server_socket_,
154 SOL_SOCKET,
155 SO_REUSEADDR, reinterpret_cast<char * >(&on),
156 sizeof(on));
157
158 CHECK_GE(::bind(server_socket_,
159 reinterpret_cast<struct sockaddr *>(&sin),
160 sizeof(sin)), 0) << "bind failed";
161
162 CHECK_GE(::listen(server_socket_, SOMAXCONN), 0) << "listen failed";
163 CHECK_NE(server_socket_, 0);
164
165 handler_->AddObserver(Singleton<session::SessionUsageObserver>::get());
166 }
167
~RPCServer()168 ~RPCServer() {
169 CloseSocket(server_socket_);
170 server_socket_ = kInvalidSocket;
171 }
172
Loop()173 void Loop() {
174 LOG(INFO) << "Start Mozc RPCServer";
175
176 while (true) {
177 const int client_socket = ::accept(server_socket_, NULL, NULL);
178
179 if (client_socket == kInvalidSocket) {
180 LOG(ERROR) << "accept failed";
181 continue;
182 }
183
184 uint32 request_size = 0;
185 // Receive the size of data.
186 if (!Recv(client_socket, reinterpret_cast<char *>(&request_size),
187 sizeof(request_size), FLAGS_rpc_timeout)) {
188 LOG(ERROR) << "Cannot receive request_size header.";
189 CloseSocket(client_socket);
190 continue;
191 }
192 request_size = ntohl(request_size);
193 CHECK_GT(request_size, 0);
194 CHECK_LT(request_size, kMaxRequestSize);
195
196 // Receive the body of serialized protobuf.
197 std::unique_ptr<char[]> request_str(new char[request_size]);
198 if (!Recv(client_socket,
199 request_str.get(), request_size, FLAGS_rpc_timeout)) {
200 LOG(ERROR) << "cannot receive body of request.";
201 CloseSocket(client_socket);
202 continue;
203 }
204
205 commands::Command command;
206 if (!command.mutable_input()->ParseFromArray(request_str.get(),
207 request_size)) {
208 LOG(ERROR) << "ParseFromArray failed";
209 CloseSocket(client_socket);
210 continue;
211 }
212
213 CHECK(handler_->EvalCommand(&command));
214
215 string output_str;
216 // Return the result.
217 CHECK(command.output().SerializeToString(&output_str));
218
219 uint32 output_size = output_str.size();
220 CHECK_GT(output_size, 0);
221 CHECK_LT(output_size, kMaxOutputSize);
222 output_size = htonl(output_size);
223
224 if (!Send(client_socket, reinterpret_cast<char *>(&output_size),
225 sizeof(output_size), FLAGS_rpc_timeout) ||
226 !Send(client_socket, output_str.data(), output_str.size(),
227 FLAGS_rpc_timeout)) {
228 LOG(ERROR) << "Cannot send reply.";
229 }
230
231 CloseSocket(client_socket);
232 }
233 }
234
235 private:
236 int server_socket_;
237 std::unique_ptr<SessionHandler> handler_;
238 };
239
240 // Standalone RPCClient.
241 // TODO(taku): Make a RPC class inherited from IPCInterface.
242 // This allows us to reuse client::Session library and SessionServer.
243 class RPCClient {
244 public:
RPCClient()245 RPCClient() : id_(0) {}
246
CreateSession()247 bool CreateSession() {
248 id_ = 0;
249 commands::Input input;
250 commands::Output output;
251 input.set_type(commands::Input::CREATE_SESSION);
252 if (!Call(input, &output) ||
253 output.error_code() != commands::Output::SESSION_SUCCESS) {
254 return false;
255 }
256 id_ = output.id();
257 return true;
258 }
259
DeleteSession()260 bool DeleteSession() {
261 commands::Input input;
262 commands::Output output;
263 id_ = 0;
264 input.set_type(commands::Input::DELETE_SESSION);
265 return (Call(input, &output) &&
266 output.error_code() == commands::Output::SESSION_SUCCESS);
267 }
268
SendKey(const mozc::commands::KeyEvent & key,mozc::commands::Output * output) const269 bool SendKey(const mozc::commands::KeyEvent &key,
270 mozc::commands::Output *output) const {
271 if (id_ == 0) {
272 return false;
273 }
274 commands::Input input;
275 input.set_type(commands::Input::SEND_KEY);
276 input.set_id(id_);
277 input.mutable_key()->CopyFrom(key);
278 return (Call(input, output) &&
279 output->error_code() == commands::Output::SESSION_SUCCESS);
280 }
281
282 private:
Call(const commands::Input & input,commands::Output * output) const283 bool Call(const commands::Input &input,
284 commands::Output *output) const {
285 struct addrinfo hints, *res;
286 ::memset(&hints, 0, sizeof(hints));
287 hints.ai_socktype = SOCK_STREAM;
288 hints.ai_family = AF_INET;
289
290 const string port_str = std::to_string(FLAGS_port);
291 CHECK_EQ(::getaddrinfo(FLAGS_host.c_str(), port_str.c_str(),
292 &hints, &res), 0)
293 << "getaddrinfo failed";
294
295 const int client_socket = ::socket(res->ai_family,
296 res->ai_socktype,
297 res->ai_protocol);
298 CHECK_NE(client_socket, kInvalidSocket) << "socket failed";
299 CHECK_GE(::connect(client_socket,
300 res->ai_addr, res->ai_addrlen), 0)
301 << "connect failed";
302
303 string request_str;
304 CHECK(input.SerializeToString(&request_str));
305 uint32 request_size = request_str.size();
306 CHECK_GT(request_size, 0);
307 CHECK_LT(request_size, kMaxRequestSize);
308 request_size = htonl(request_size);
309
310 CHECK(Send(client_socket, reinterpret_cast<char *>(&request_size),
311 sizeof(request_size), FLAGS_rpc_timeout));
312 CHECK(Send(client_socket, request_str.data(), request_str.size(),
313 FLAGS_rpc_timeout));
314
315 uint32 output_size = 0;
316 CHECK(Recv(client_socket, reinterpret_cast<char *>(&output_size),
317 sizeof(output_size), FLAGS_rpc_timeout));
318 output_size = ntohl(output_size);
319 CHECK_GT(output_size, 0);
320 CHECK_LT(output_size, kMaxOutputSize);
321
322 std::unique_ptr<char[]> output_str(new char[output_size]);
323 CHECK(Recv(client_socket,
324 output_str.get(), output_size, FLAGS_rpc_timeout));
325
326 CHECK(output->ParseFromArray(output_str.get(), output_size));
327
328 ::freeaddrinfo(res);
329
330 CloseSocket(client_socket);
331
332 return true;
333 }
334
335 uint64 id_;
336 };
337
338 // Wrapper class for WSAStartup on Windows.
339 class ScopedWSAData {
340 public:
ScopedWSAData()341 ScopedWSAData() {
342 #ifdef OS_WIN
343 WSADATA wsaData;
344 CHECK_EQ(::WSAStartup(MAKEWORD(2, 1), &wsaData), 0)
345 << "WSAStartup failed";
346 #endif
347 }
~ScopedWSAData()348 ~ScopedWSAData() {
349 #ifdef OS_WIN
350 ::WSACleanup();
351 #endif
352 }
353 };
354 } // namespace
355
356 } // namespace mozc
357
main(int argc,char * argv[])358 int main(int argc, char *argv[]) {
359 mozc::InitMozc(argv[0], &argc, &argv, false);
360
361 mozc::ScopedWSAData wsadata;
362
363 if (!FLAGS_user_profile_directory.empty()) {
364 LOG(INFO) << "Setting user profile directory to "
365 << FLAGS_user_profile_directory;
366 mozc::SystemUtil::SetUserProfileDirectory(FLAGS_user_profile_directory);
367 }
368
369 if (FLAGS_client) {
370 mozc::RPCClient client;
371 CHECK(client.CreateSession());
372 for (int n = 0; n < FLAGS_client_test_size; ++n) {
373 std::vector<mozc::commands::KeyEvent> keys;
374 mozc::session::RandomKeyEventsGenerator::GenerateSequence(&keys);
375 for (size_t i = 0; i < keys.size(); ++i) {
376 LOG(INFO) << "Sending to Server: " << keys[i].Utf8DebugString();
377 mozc::commands::Output output;
378 CHECK(client.SendKey(keys[i], &output));
379 LOG(INFO) << "Output of SendKey: " << output.Utf8DebugString();
380 }
381 }
382 CHECK(client.DeleteSession());
383 return 0;
384 } else if (FLAGS_server) {
385 mozc::RPCServer server;
386 server.Loop();
387 } else {
388 LOG(ERROR) << "use --server or --client option";
389 return -1;
390 }
391
392 return 0;
393 }
394