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 // Session Handler of Mozc server.
31 // Migrated from ipc/interpreter and session/session_manager
32
33 #include "session/session_handler.h"
34
35 #include <algorithm>
36 #include <string>
37 #include <utility>
38 #include <vector>
39
40 #include "base/clock.h"
41 #include "base/flags.h"
42 #include "base/logging.h"
43 #include "base/port.h"
44 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
45 #include "base/process.h"
46 #endif // MOZC_DISABLE_SESSION_WATCHDOG
47 #include "base/singleton.h"
48 #include "base/stopwatch.h"
49 #include "base/util.h"
50 #include "composer/table.h"
51 #include "config/character_form_manager.h"
52 #include "config/config_handler.h"
53 #include "dictionary/user_dictionary_session_handler.h"
54 #include "engine/engine_interface.h"
55 #include "engine/user_data_manager_interface.h"
56 #include "protocol/commands.pb.h"
57 #include "protocol/config.pb.h"
58 #include "protocol/user_dictionary_storage.pb.h"
59 #include "session/generic_storage_manager.h"
60 #include "session/session.h"
61 #include "session/session_observer_handler.h"
62 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
63 #include "session/session_watch_dog.h"
64 #else // MOZC_DISABLE_SESSION_WATCHDOG
65 // Session watch dog is not aviable from android mozc and nacl mozc for now.
66 // TODO(kkojima): Remove this guard after
67 // enabling session watch dog for android.
68 #endif // MOZC_DISABLE_SESSION_WATCHDOG
69 #include "usage_stats/usage_stats.h"
70
71 using mozc::usage_stats::UsageStats;
72
73 DEFINE_int32(timeout, -1,
74 "server timeout. "
75 "if sessions get empty for \"timeout\", "
76 "shutdown message is automatically emitted");
77
78 DEFINE_int32(max_session_size, 64,
79 "maximum sessions size. "
80 "if size of sessions reaches to \"max_session_size\", "
81 "oldest session is removed");
82
83 DEFINE_int32(create_session_min_interval, 0,
84 "minimum interval (sec) for create session");
85
86 DEFINE_int32(watch_dog_interval, 180,
87 "watch dog timer intaval (sec)");
88
89 DEFINE_int32(last_command_timeout, 3600,
90 "remove session if it is not accessed for "
91 "\"last_command_timeout\" sec");
92
93 DEFINE_int32(last_create_session_timeout, 300,
94 "remove session if it is not accessed for "
95 "\"last_create_session_timeout\" sec "
96 "after create session command");
97
98 DEFINE_bool(restricted, false,
99 "Launch server with restricted setting");
100
101 namespace mozc {
102
103 namespace {
IsApplicationAlive(const session::SessionInterface * session)104 bool IsApplicationAlive(const session::SessionInterface *session) {
105 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
106 const commands::ApplicationInfo &info = session->application_info();
107 // When the thread/process's current status is unknown, i.e.,
108 // if IsThreadAlive/IsProcessAlive functions failed to know the
109 // status of the thread/process, return true just in case.
110 // Here, we want to kill the session only when the target thread/process
111 // are terminated with 100% probability. Otherwise, it's better to do
112 // nothing to prevent any side effects.
113 #ifdef OS_WIN
114 if (info.has_thread_id()) {
115 return Process::IsThreadAlive(
116 static_cast<size_t>(info.thread_id()), true);
117 }
118 #else // OS_WIN
119 if (info.has_process_id()) {
120 return Process::IsProcessAlive(
121 static_cast<size_t>(info.process_id()), true);
122 }
123 #endif // OS_WIN
124 #else // MOZC_DISABLE_SESSION_WATCHDOG
125 // Currently the process is not available through android mozc and nacl mozc.
126 // TODO(kkojima): remove this guard after
127 // android version supports base/process.cc
128 #endif // MOZC_DISABLE_SESSION_WATCHDOG
129 return true;
130 }
131
IsCarrierEmoji(const string & utf8_str)132 bool IsCarrierEmoji(const string &utf8_str) {
133 if (Util::CharsLen(utf8_str) != 1) {
134 return false;
135 }
136 const char *utf8_begin = utf8_str.c_str();
137 size_t mblen = 0;
138 const uint32 ucs4_val = static_cast<uint32>(
139 Util::UTF8ToUCS4(utf8_begin, utf8_begin + utf8_str.size(), &mblen));
140 const uint32 kMinEmojiPuaCodePoint = 0xFE000;
141 const uint32 kMaxEmojiPuaCodePoint = 0xFEEA0;
142 return kMinEmojiPuaCodePoint <= ucs4_val && ucs4_val <= kMaxEmojiPuaCodePoint;
143 }
144 } // namespace
145
SessionHandler(std::unique_ptr<EngineInterface> engine)146 SessionHandler::SessionHandler(std::unique_ptr<EngineInterface> engine) {
147 Init(std::move(engine), std::unique_ptr<EngineBuilderInterface>());
148 }
149
SessionHandler(std::unique_ptr<EngineInterface> engine,std::unique_ptr<EngineBuilderInterface> engine_builder)150 SessionHandler::SessionHandler(
151 std::unique_ptr<EngineInterface> engine,
152 std::unique_ptr<EngineBuilderInterface> engine_builder) {
153 Init(std::move(engine), std::move(engine_builder));
154 }
155
Init(std::unique_ptr<EngineInterface> engine,std::unique_ptr<EngineBuilderInterface> engine_builder)156 void SessionHandler::Init(
157 std::unique_ptr<EngineInterface> engine,
158 std::unique_ptr<EngineBuilderInterface> engine_builder) {
159 is_available_ = false;
160 max_session_size_ = 0;
161 last_session_empty_time_ = Clock::GetTime();
162 last_cleanup_time_ = 0;
163 last_create_session_time_ = 0;
164 engine_ = std::move(engine);
165 engine_builder_ = std::move(engine_builder);
166 observer_handler_.reset(new session::SessionObserverHandler());
167 stopwatch_.reset(new Stopwatch);
168 user_dictionary_session_handler_.reset(
169 new user_dictionary::UserDictionarySessionHandler);
170 table_manager_.reset(new composer::TableManager);
171 request_.reset(new commands::Request);
172 config_.reset(new config::Config);
173
174 if (FLAGS_restricted) {
175 VLOG(1) << "Server starts with restricted mode";
176 // --restricted is almost always specified when mozc_client is inside Job.
177 // The typical case is Startup processes on Vista.
178 // On Vista, StartUp processes are in Job for 60 seconds. In order
179 // to launch new mozc_server inside sandbox, we set the timeout
180 // to be 60sec. Client application hopefully re-launch mozc_server.
181 FLAGS_timeout = 60;
182 FLAGS_max_session_size = 8;
183 FLAGS_watch_dog_interval = 15;
184 FLAGS_last_create_session_timeout = 60;
185 FLAGS_last_command_timeout = 60;
186 }
187
188 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
189 session_watch_dog_.reset(new SessionWatchDog(FLAGS_watch_dog_interval));
190 #else // MOZC_DISABLE_SESSION_WATCHDOG
191 // Session watch dog is not aviable from android mozc and nacl mozc for now.
192 // TODO(kkojima): Remove this guard after
193 // enabling session watch dog for android.
194 #endif // MOZC_DISABLE_SESSION_WATCHDOG
195
196 config::ConfigHandler::GetConfig(config_.get());
197
198 // allow [2..128] sessions
199 max_session_size_ = std::max(2, std::min(FLAGS_max_session_size, 128));
200 session_map_.reset(new SessionMap(max_session_size_));
201
202 if (!engine_) {
203 return;
204 }
205
206 // everything is OK
207 is_available_ = true;
208 }
209
~SessionHandler()210 SessionHandler::~SessionHandler() {
211 for (SessionElement *element =
212 const_cast<SessionElement *>(session_map_->Head());
213 element != nullptr; element = element->next) {
214 delete element->value;
215 element->value = nullptr;
216 }
217 session_map_->Clear();
218 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
219 if (session_watch_dog_->IsRunning()) {
220 session_watch_dog_->Terminate();
221 }
222 #else // MOZC_DISABLE_SESSION_WATCHDOG
223 // Session watch dog is not aviable from android mozc and nacl mozc for now.
224 // TODO(kkojima): Remove this guard after
225 // enabling session watch dog for android.
226 #endif // MOZC_DISABLE_SESSION_WATCHDOG
227 }
228
IsAvailable() const229 bool SessionHandler::IsAvailable() const {
230 return is_available_;
231 }
232
StartWatchDog()233 bool SessionHandler::StartWatchDog() {
234 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
235 if (!session_watch_dog_->IsRunning()) {
236 session_watch_dog_->Start("WatchDog");
237 }
238 return session_watch_dog_->IsRunning();
239 #else // MOZC_DISABLE_SESSION_WATCHDOG
240 // Session watch dog is not aviable from android mozc and nacl mozc for now.
241 // TODO(kkojima): Remove this guard after
242 // enabling session watch dog for android.
243 return false;
244 #endif // MOZC_DISABLE_SESSION_WATCHDOG
245 }
246
SetConfig(const config::Config & config)247 void SessionHandler::SetConfig(const config::Config &config) {
248 *config_ = config;
249 const composer::Table *table = table_manager_->GetTable(
250 *request_, *config_, *engine_->GetDataManager());
251 for (SessionElement *element =
252 const_cast<SessionElement *>(session_map_->Head());
253 element != NULL; element = element->next) {
254 if (element->value != NULL) {
255 element->value->SetConfig(config_.get());
256 element->value->SetRequest(request_.get());
257 element->value->SetTable(table);
258 }
259 }
260 config::CharacterFormManager::GetCharacterFormManager()->ReloadConfig(config);
261 }
262
SyncData(commands::Command * command)263 bool SessionHandler::SyncData(commands::Command *command) {
264 VLOG(1) << "Syncing user data";
265 engine_->GetUserDataManager()->Sync();
266 return true;
267 }
268
Shutdown(commands::Command * command)269 bool SessionHandler::Shutdown(commands::Command *command) {
270 VLOG(1) << "Shutdown server";
271 SyncData(command);
272 is_available_ = false;
273 UsageStats::IncrementCount("ShutDown");
274 return true;
275 }
276
Reload(commands::Command * command)277 bool SessionHandler::Reload(commands::Command *command) {
278 VLOG(1) << "Reloading server";
279 {
280 config::Config config;
281 config::ConfigHandler::GetConfig(&config);
282 SetConfig(config);
283 }
284 engine_->Reload();
285 return true;
286 }
287
ClearUserHistory(commands::Command * command)288 bool SessionHandler::ClearUserHistory(commands::Command *command) {
289 VLOG(1) << "Clearing user history";
290 engine_->GetUserDataManager()->ClearUserHistory();
291 UsageStats::IncrementCount("ClearUserHistory");
292 return true;
293 }
294
ClearUserPrediction(commands::Command * command)295 bool SessionHandler::ClearUserPrediction(commands::Command *command) {
296 VLOG(1) << "Clearing user prediction";
297 engine_->GetUserDataManager()->ClearUserPrediction();
298 UsageStats::IncrementCount("ClearUserPrediction");
299 return true;
300 }
301
ClearUnusedUserPrediction(commands::Command * command)302 bool SessionHandler::ClearUnusedUserPrediction(commands::Command *command) {
303 VLOG(1) << "Clearing unused user prediction";
304 engine_->GetUserDataManager()->ClearUnusedUserPrediction();
305 UsageStats::IncrementCount("ClearUnusedUserPrediction");
306 return true;
307 }
308
GetStoredConfig(commands::Command * command)309 bool SessionHandler::GetStoredConfig(commands::Command *command) {
310 VLOG(1) << "Getting stored config";
311 // Use GetStoredConfig instead of GetConfig because GET_CONFIG
312 // command should return raw stored config, which is not
313 // affected by imposed config.
314 if (!config::ConfigHandler::GetStoredConfig(
315 command->mutable_output()->mutable_config())) {
316 LOG(WARNING) << "cannot get config";
317 return false;
318 }
319
320 // Ensure the onmemory config is same as the locally stored one
321 // because the local data could be changed by sync.
322 SetConfig(command->output().config());
323
324 return true;
325 }
326
SetStoredConfig(commands::Command * command)327 bool SessionHandler::SetStoredConfig(commands::Command *command) {
328 VLOG(1) << "Setting user config";
329 if (!command->input().has_config()) {
330 LOG(WARNING) << "config is empty";
331 return false;
332 }
333
334 *command->mutable_output()->mutable_config() = command->input().config();
335 MaybeUpdateStoredConfig(command);
336
337 UsageStats::IncrementCount("SetConfig");
338 return true;
339 }
340
SetImposedConfig(commands::Command * command)341 bool SessionHandler::SetImposedConfig(commands::Command *command) {
342 VLOG(1) << "Setting imposed config";
343 if (!command->input().has_config()) {
344 LOG(WARNING) << "config is empty";
345 return false;
346 }
347
348 const mozc::config::Config &config = command->input().config();
349 config::ConfigHandler::SetImposedConfig(config);
350
351 Reload(command);
352
353 return true;
354 }
355
SetRequest(commands::Command * command)356 bool SessionHandler::SetRequest(commands::Command *command) {
357 VLOG(1) << "Setting client's request";
358 if (!command->input().has_request()) {
359 LOG(WARNING) << "request is empty";
360 return false;
361 }
362
363 request_->CopyFrom(command->input().request());
364
365 Reload(command);
366
367 return true;
368 }
369
InsertToStorage(commands::Command * command)370 bool SessionHandler::InsertToStorage(commands::Command *command) {
371 VLOG(1) << "Insert to generic storage";
372 if (!command->input().has_storage_entry()) {
373 LOG(WARNING) << "No storage_entry";
374 return false;
375 }
376 const commands::GenericStorageEntry &storage_entry =
377 command->input().storage_entry();
378 if (!storage_entry.has_type() ||
379 !storage_entry.has_key() ||
380 storage_entry.value().size() == 0) {
381 LOG(WARNING) << "storage_entry lacks some fields.";
382 return false;
383 }
384
385 GenericStorageInterface *storage =
386 GenericStorageManagerFactory::GetStorage(storage_entry.type());
387 if (!storage) {
388 LOG(WARNING) << "No storage found";
389 return false;
390 }
391
392 for (int i = 0; i < storage_entry.value_size(); ++i) {
393 const string &value = storage_entry.value(i);
394 storage->Insert(value, value.c_str());
395 }
396
397 if (storage_entry.type() == commands::GenericStorageEntry::EMOJI_HISTORY) {
398 for (int i = 0; i < storage_entry.value_size(); ++i) {
399 if (IsCarrierEmoji(storage_entry.value(i))) {
400 UsageStats::IncrementCount("CommitCarrierEmoji");
401 } else {
402 UsageStats::IncrementCount("CommitUnicodeEmoji");
403 }
404 }
405 }
406
407 return true;
408 }
409
ReadAllFromStorage(commands::Command * command)410 bool SessionHandler::ReadAllFromStorage(commands::Command *command) {
411 VLOG(1) << "Read all from storage";
412 commands::Output *output = command->mutable_output();
413 if (!command->input().has_storage_entry()) {
414 LOG(WARNING) << "No storage_entry";
415 return false;
416 }
417 if (!command->input().storage_entry().has_type()) {
418 LOG(WARNING) << "storage_entry lacks type fields.";
419 return false;
420 }
421
422 commands::GenericStorageEntry::StorageType storage_type =
423 command->input().storage_entry().type();
424 GenericStorageInterface *storage =
425 GenericStorageManagerFactory::GetStorage(storage_type);
426 if (!storage) {
427 LOG(WARNING) << "No storage found";
428 return false;
429 }
430
431 std::vector<string> result;
432 storage->GetAllValues(&result);
433 output->mutable_storage_entry()->set_type(storage_type);
434 for (size_t i = 0; i < result.size(); ++i) {
435 output->mutable_storage_entry()->add_value(result[i]);
436 }
437 return true;
438 }
439
ClearStorage(commands::Command * command)440 bool SessionHandler::ClearStorage(commands::Command *command) {
441 VLOG(1) << "Clear storage";
442 commands::Output *output = command->mutable_output();
443 if (!command->input().has_storage_entry()) {
444 LOG(WARNING) << "No storage_entry";
445 return false;
446 }
447 if (!command->input().storage_entry().has_type()) {
448 LOG(WARNING) << "storage_entry lacks type fields.";
449 return false;
450 }
451
452 commands::GenericStorageEntry::StorageType storage_type =
453 command->input().storage_entry().type();
454 GenericStorageInterface *storage =
455 GenericStorageManagerFactory::GetStorage(storage_type);
456 if (!storage) {
457 LOG(WARNING) << "No storage found";
458 return false;
459 }
460 output->mutable_storage_entry()->set_type(storage_type);
461 return storage->Clear();
462 }
463
EvalCommand(commands::Command * command)464 bool SessionHandler::EvalCommand(commands::Command *command) {
465 if (!is_available_) {
466 LOG(ERROR) << "SessionHandler is not available.";
467 return false;
468 }
469
470 bool eval_succeeded = false;
471 stopwatch_->Reset();
472 stopwatch_->Start();
473
474 switch (command->input().type()) {
475 case commands::Input::CREATE_SESSION:
476 eval_succeeded = CreateSession(command);
477 break;
478 case commands::Input::DELETE_SESSION:
479 eval_succeeded = DeleteSession(command);
480 break;
481 case commands::Input::SEND_KEY:
482 eval_succeeded = SendKey(command);
483 break;
484 case commands::Input::TEST_SEND_KEY:
485 eval_succeeded = TestSendKey(command);
486 break;
487 case commands::Input::SEND_COMMAND:
488 eval_succeeded = SendCommand(command);
489 break;
490 case commands::Input::SYNC_DATA:
491 eval_succeeded = SyncData(command);
492 break;
493 case commands::Input::CLEAR_USER_HISTORY:
494 eval_succeeded = ClearUserHistory(command);
495 break;
496 case commands::Input::CLEAR_USER_PREDICTION:
497 eval_succeeded = ClearUserPrediction(command);
498 break;
499 case commands::Input::CLEAR_UNUSED_USER_PREDICTION:
500 eval_succeeded = ClearUnusedUserPrediction(command);
501 break;
502 case commands::Input::GET_CONFIG:
503 eval_succeeded = GetStoredConfig(command);
504 break;
505 case commands::Input::SET_CONFIG:
506 eval_succeeded = SetStoredConfig(command);
507 break;
508 case commands::Input::SET_IMPOSED_CONFIG:
509 eval_succeeded = SetImposedConfig(command);
510 break;
511 case commands::Input::SET_REQUEST:
512 eval_succeeded = SetRequest(command);
513 break;
514 case commands::Input::SHUTDOWN:
515 eval_succeeded = Shutdown(command);
516 break;
517 case commands::Input::RELOAD:
518 eval_succeeded = Reload(command);
519 break;
520 case commands::Input::CLEANUP:
521 eval_succeeded = Cleanup(command);
522 break;
523 case commands::Input::INSERT_TO_STORAGE:
524 eval_succeeded = InsertToStorage(command);
525 break;
526 case commands::Input::READ_ALL_FROM_STORAGE:
527 eval_succeeded = ReadAllFromStorage(command);
528 break;
529 case commands::Input::CLEAR_STORAGE:
530 eval_succeeded = ClearStorage(command);
531 break;
532 case commands::Input::SEND_USER_DICTIONARY_COMMAND:
533 eval_succeeded = SendUserDictionaryCommand(command);
534 break;
535 case commands::Input::SEND_ENGINE_RELOAD_REQUEST:
536 eval_succeeded = SendEngineReloadRequest(command);
537 break;
538 case commands::Input::NO_OPERATION:
539 eval_succeeded = NoOperation(command);
540 break;
541 default:
542 eval_succeeded = false;
543 }
544
545 if (eval_succeeded) {
546 UsageStats::IncrementCount("SessionAllEvent");
547 if (command->input().type() != commands::Input::CREATE_SESSION) {
548 // Fill a session ID even if command->input() doesn't have a id to ensure
549 // that response size should not be 0, which causes disconnection of IPC.
550 command->mutable_output()->set_id(command->input().id());
551 }
552 } else {
553 command->mutable_output()->set_id(0);
554 command->mutable_output()->set_error_code(
555 commands::Output::SESSION_FAILURE);
556 }
557
558 if (eval_succeeded) {
559 // TODO(komatsu): Make sre if checking eval_succeeded is necessary or not.
560 observer_handler_->EvalCommandHandler(*command);
561 }
562
563 stopwatch_->Stop();
564 UsageStats::UpdateTiming("ElapsedTimeUSec",
565 stopwatch_->GetElapsedMicroseconds());
566
567 return is_available_;
568 }
569
NewSession()570 session::SessionInterface *SessionHandler::NewSession() {
571 // Session doesn't take the ownership of engine.
572 return new session::Session(engine_.get());
573 }
574
AddObserver(session::SessionObserverInterface * observer)575 void SessionHandler::AddObserver(session::SessionObserverInterface *observer) {
576 observer_handler_->AddObserver(observer);
577 }
578
MaybeUpdateStoredConfig(commands::Command * command)579 void SessionHandler::MaybeUpdateStoredConfig(commands::Command *command) {
580 if (!command->output().has_config()) {
581 return;
582 }
583
584 *config_ = command->output().config();
585 config::ConfigHandler::SetConfig(*config_);
586 Reload(command);
587 }
588
SendKey(commands::Command * command)589 bool SessionHandler::SendKey(commands::Command *command) {
590 const SessionID id = command->input().id();
591 session::SessionInterface **session = session_map_->MutableLookup(id);
592 if (session == NULL || *session == NULL) {
593 LOG(WARNING) << "SessionID " << id << " is not available";
594 return false;
595 }
596 (*session)->SendKey(command);
597 MaybeUpdateStoredConfig(command);
598 return true;
599 }
600
TestSendKey(commands::Command * command)601 bool SessionHandler::TestSendKey(commands::Command *command) {
602 const SessionID id = command->input().id();
603 session::SessionInterface **session = session_map_->MutableLookup(id);
604 if (session == NULL || *session == NULL) {
605 LOG(WARNING) << "SessionID " << id << " is not available";
606 return false;
607 }
608 (*session)->TestSendKey(command);
609 return true;
610 }
611
SendCommand(commands::Command * command)612 bool SessionHandler::SendCommand(commands::Command *command) {
613 const SessionID id = command->input().id();
614 session::SessionInterface **session =
615 const_cast<session::SessionInterface **>(session_map_->Lookup(id));
616 if (session == NULL || *session == NULL) {
617 LOG(WARNING) << "SessionID " << id << " is not available";
618 return false;
619 }
620 (*session)->SendCommand(command);
621 MaybeUpdateStoredConfig(command);
622 return true;
623 }
624
CreateSession(commands::Command * command)625 bool SessionHandler::CreateSession(commands::Command *command) {
626 // prevent DOS attack
627 // don't allow CreateSession in very short period.
628 const int create_session_minimum_interval =
629 std::max(0, std::min(FLAGS_create_session_min_interval, 10));
630
631 uint64 current_time = Clock::GetTime();
632 if (last_create_session_time_ != 0 &&
633 (current_time - last_create_session_time_) <
634 create_session_minimum_interval) {
635 return false;
636 }
637
638 last_create_session_time_ = current_time;
639
640 // if session map is FULL, remove the oldest item from the LRU
641 SessionElement *oldest_element = NULL;
642 if (session_map_->Size() >= max_session_size_) {
643 oldest_element = const_cast<SessionElement *>(session_map_->Tail());
644 if (oldest_element == NULL) {
645 LOG(ERROR) << "oldest SessionElement is NULL";
646 return false;
647 }
648 delete oldest_element->value;
649 oldest_element->value = NULL;
650 session_map_->Erase(oldest_element->key);
651 VLOG(1) << "Session is FULL, oldest SessionID "
652 << oldest_element->key << " is removed";
653 }
654
655 if (engine_builder_ &&
656 session_map_->Size() == 0 &&
657 engine_builder_->HasResponse()) {
658 auto *response =
659 command->mutable_output()->mutable_engine_reload_response();
660 engine_builder_->GetResponse(response);
661 if (response->status() == EngineReloadResponse::RELOAD_READY) {
662 if (engine_->GetUserDataManager()) {
663 engine_->GetUserDataManager()->Wait();
664 }
665 engine_.reset();
666 engine_ = engine_builder_->BuildFromPreparedData();
667 LOG_IF(FATAL, !engine_) << "Critical failure in engine replace";
668 table_manager_->ClearCaches();
669 response->set_status(EngineReloadResponse::RELOADED);
670 }
671 engine_builder_->Clear();
672 }
673
674 session::SessionInterface *session = NewSession();
675 if (session == NULL) {
676 LOG(ERROR) << "Cannot allocate new Session";
677 return false;
678 }
679
680 const SessionID new_id = CreateNewSessionID();
681 SessionElement *element = session_map_->Insert(new_id);
682 element->value = session;
683 command->mutable_output()->set_id(new_id);
684
685 // The oldes item should be reused
686 DCHECK(oldest_element == NULL || oldest_element == element);
687
688 if (command->input().has_capability()) {
689 session->set_client_capability(command->input().capability());
690 }
691
692 if (command->input().has_application_info()) {
693 session->set_application_info(command->input().application_info());
694 #ifdef OS_NACL
695 if (command->input().application_info().has_timezone_offset()) {
696 Clock::SetTimezoneOffset(
697 command->input().application_info().timezone_offset());
698 }
699 #endif // OS_NACL
700 }
701
702 // Ensure the onmemory config is same as the locally stored one
703 // because the local data could be changed by sync.
704 {
705 config::Config config;
706 config::ConfigHandler::GetConfig(&config);
707 SetConfig(config);
708 }
709
710 // session is not empty.
711 last_session_empty_time_ = 0;
712
713 UsageStats::IncrementCount("SessionCreated");
714
715 return true;
716 }
717
DeleteSession(commands::Command * command)718 bool SessionHandler::DeleteSession(commands::Command *command) {
719 DeleteSessionID(command->input().id());
720 if (engine_->GetUserDataManager()) {
721 engine_->GetUserDataManager()->Sync();
722 }
723 return true;
724 }
725
726 // Scan all sessions and find and delete session which is either
727 // (a) The session is not activated for 60min
728 // (b) The session is created but not accessed for 5min
729 // (c) application is already terminated.
730 // Also, if timeout is enabled, shutdown server if there is
731 // no active session and client doesn't send any conversion
732 // request to the server for FLAGS_timeout sec.
Cleanup(commands::Command * command)733 bool SessionHandler::Cleanup(commands::Command *command) {
734 const uint64 current_time = Clock::GetTime();
735
736 // suspend/hibernation may happen
737 uint64 suspend_time = 0;
738 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
739 if (last_cleanup_time_ != 0 &&
740 session_watch_dog_->IsRunning() &&
741 (current_time - last_cleanup_time_) >
742 2 * session_watch_dog_->interval()) {
743 suspend_time = current_time - last_cleanup_time_ -
744 session_watch_dog_->interval();
745 LOG(WARNING) << "server went to suspend mode for "
746 << suspend_time << " sec";
747 }
748 #else // MOZC_DISABLE_SESSION_WATCHDOG
749 // Session watch dog is not aviable from android mozc and nacl mozc for now.
750 // TODO(kkojima): Remove this guard after
751 // enabling session watch dog for android.
752 #endif // MOZC_DISABLE_SESSION_WATCHDOG
753
754 // allow [1..600] sec. default: 300
755 const uint64 create_session_timeout =
756 suspend_time +
757 std::max(1, std::min(FLAGS_last_create_session_timeout, 600));
758
759 // allow [10..7200] sec. default 3600
760 const uint64 last_command_timeout =
761 suspend_time + std::max(10, std::min(FLAGS_last_command_timeout, 7200));
762
763 std::vector<SessionID> remove_ids;
764 for (SessionElement *element =
765 const_cast<SessionElement *>(session_map_->Head());
766 element != NULL; element = element->next) {
767 session::SessionInterface *session = element->value;
768 if (!IsApplicationAlive(session)) {
769 VLOG(2) << "Application is not alive. Removing: " << element->key;
770 remove_ids.push_back(element->key);
771 } else if (session->last_command_time() == 0) {
772 // no command is exectuted
773 if ((current_time - session->create_session_time()) >=
774 create_session_timeout) {
775 remove_ids.push_back(element->key);
776 }
777 } else { // some commands are executed already
778 if ((current_time - session->last_command_time()) >=
779 last_command_timeout) {
780 remove_ids.push_back(element->key);
781 }
782 }
783 }
784
785 for (size_t i = 0; i < remove_ids.size(); ++i) {
786 DeleteSessionID(remove_ids[i]);
787 VLOG(1) << "Session ID " << remove_ids[i] << " is removed by server";
788 }
789
790 // Sync all data. This is a regression bug fix http://b/3033708
791 engine_->GetUserDataManager()->Sync();
792
793 // timeout is enabled.
794 if (FLAGS_timeout > 0 &&
795 last_session_empty_time_ != 0 &&
796 (current_time - last_session_empty_time_)
797 >= suspend_time + FLAGS_timeout) {
798 Shutdown(command);
799 }
800
801 last_cleanup_time_ = current_time;
802
803 return true;
804 }
805
SendUserDictionaryCommand(commands::Command * command)806 bool SessionHandler::SendUserDictionaryCommand(commands::Command *command) {
807 if (!command->input().has_user_dictionary_command()) {
808 return false;
809 }
810 user_dictionary::UserDictionaryCommandStatus status;
811 const bool result = user_dictionary_session_handler_->Evaluate(
812 command->input().user_dictionary_command(), &status);
813 if (result) {
814 status.Swap(
815 command->mutable_output()->mutable_user_dictionary_command_status());
816 }
817 return result;
818 }
819
SendEngineReloadRequest(commands::Command * command)820 bool SessionHandler::SendEngineReloadRequest(commands::Command *command) {
821 if (!engine_builder_ || !command->input().has_engine_reload_request()) {
822 return false;
823 }
824 engine_builder_->PrepareAsync(
825 command->input().engine_reload_request(),
826 command->mutable_output()->mutable_engine_reload_response());
827 return true;
828 }
829
NoOperation(commands::Command * command)830 bool SessionHandler::NoOperation(commands::Command *command) {
831 return true;
832 }
833
834 // Create Random Session ID in order to make the session id unpredicable
CreateNewSessionID()835 SessionID SessionHandler::CreateNewSessionID() {
836 SessionID id = 0;
837 while (true) {
838 Util::GetRandomSequence(reinterpret_cast<char *>(&id), sizeof(id));
839 // don't allow id == 0, as it is reserved for
840 // "invalid id"
841 if (id != 0 && !session_map_->HasKey(id)) {
842 break;
843 }
844
845 LOG(WARNING) << "Session ID " << id << " is already used. retry";
846 }
847
848 return id;
849 }
850
DeleteSessionID(SessionID id)851 bool SessionHandler::DeleteSessionID(SessionID id) {
852 session::SessionInterface **session = session_map_->MutableLookup(id);
853 if (session == NULL || *session == NULL) {
854 LOG_IF(WARNING, id != 0) << "cannot find SessionID " << id;
855 return false;
856 }
857 delete *session;
858
859 session_map_->Erase(id); // remove from LRU
860
861 // if session gets empty, save the timestamp
862 if (last_session_empty_time_ == 0 &&
863 session_map_->Size() == 0) {
864 last_session_empty_time_ = Clock::GetTime();
865 }
866
867 return true;
868 }
869 } // namespace mozc
870