PageRenderTime 29ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/server/xbox-server.cpp

https://gitlab.com/iranjith4/hhvm
C++ | 430 lines | 336 code | 67 blank | 27 comment | 52 complexity | 93a15ffb0c824f4f3cc3b9129f5a3fd5 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/runtime/server/xbox-server.h"
  17. #include "hphp/runtime/base/builtin-functions.h"
  18. #include "hphp/runtime/base/comparisons.h"
  19. #include "hphp/runtime/base/runtime-option.h"
  20. #include "hphp/runtime/server/rpc-request-handler.h"
  21. #include "hphp/runtime/server/satellite-server.h"
  22. #include "hphp/runtime/base/libevent-http-client.h"
  23. #include "hphp/runtime/server/job-queue-vm-stack.h"
  24. #include "hphp/runtime/server/server-task-event.h"
  25. #include "hphp/util/job-queue.h"
  26. #include "hphp/util/lock.h"
  27. #include "hphp/util/logger.h"
  28. #include "hphp/util/timer.h"
  29. #include "hphp/system/systemlib.h"
  30. namespace HPHP {
  31. ///////////////////////////////////////////////////////////////////////////////
  32. using std::string;
  33. XboxTransport::XboxTransport(const String& message,
  34. const String& reqInitDoc /* = "" */)
  35. : m_refCount(0), m_done(false), m_code(0), m_event(nullptr) {
  36. Timer::GetMonotonicTime(m_queueTime);
  37. m_message.append(message.data(), message.size());
  38. m_reqInitDoc.append(reqInitDoc.data(), reqInitDoc.size());
  39. disableCompression(); // so we don't have to decompress during sendImpl()
  40. }
  41. const char *XboxTransport::getUrl() {
  42. if (!m_reqInitDoc.empty()) {
  43. return "xbox_process_call_message";
  44. }
  45. return RuntimeOption::XboxProcessMessageFunc.c_str();
  46. }
  47. std::string XboxTransport::getHeader(const char *name) {
  48. if (!strcasecmp(name, "Host")) return m_host;
  49. if (!strcasecmp(name, "ReqInitDoc")) return m_reqInitDoc;
  50. return "";
  51. }
  52. void XboxTransport::sendImpl(const void *data, int size, int code,
  53. bool chunked, bool eom) {
  54. m_response.append((const char*)data, size);
  55. if (code) {
  56. m_code = code;
  57. }
  58. if (eom) {
  59. onSendEndImpl();
  60. }
  61. }
  62. void XboxTransport::onSendEndImpl() {
  63. Lock lock(this);
  64. if (m_done) {
  65. return;
  66. }
  67. m_done = true;
  68. if (m_event) {
  69. m_event->finish();
  70. }
  71. notify();
  72. }
  73. String XboxTransport::getResults(int &code, int timeout_ms /* = 0 */) {
  74. {
  75. Lock lock(this);
  76. while (!m_done) {
  77. if (timeout_ms > 0) {
  78. long long seconds = timeout_ms / 1000;
  79. long long nanosecs = (timeout_ms - seconds * 1000) * 1000;
  80. if (!wait(seconds, nanosecs)) {
  81. code = -1;
  82. return empty_string();
  83. }
  84. } else {
  85. wait();
  86. }
  87. }
  88. }
  89. String response(m_response.c_str(), m_response.size(), CopyString);
  90. code = m_code;
  91. return response;
  92. }
  93. ///////////////////////////////////////////////////////////////////////////////
  94. static IMPLEMENT_THREAD_LOCAL(std::shared_ptr<XboxServerInfo>,
  95. s_xbox_server_info);
  96. static IMPLEMENT_THREAD_LOCAL(std::string, s_xbox_prev_req_init_doc);
  97. struct XboxRequestHandler : RPCRequestHandler {
  98. XboxRequestHandler() : RPCRequestHandler(
  99. (*s_xbox_server_info)->getTimeoutSeconds().count(), Info) {}
  100. static bool Info;
  101. };
  102. bool XboxRequestHandler::Info = false;
  103. static IMPLEMENT_THREAD_LOCAL(XboxRequestHandler, s_xbox_request_handler);
  104. ///////////////////////////////////////////////////////////////////////////////
  105. struct XboxWorker
  106. : JobQueueWorker<XboxTransport*,Server*,true,false,JobQueueDropVMStack>
  107. {
  108. virtual void doJob(XboxTransport *job) {
  109. try {
  110. // If this job or the previous job that ran on this thread have
  111. // a custom initial document, make sure we do a reset
  112. string reqInitDoc = job->getHeader("ReqInitDoc");
  113. *s_xbox_prev_req_init_doc = reqInitDoc;
  114. job->onRequestStart(job->getStartTimer());
  115. createRequestHandler()->run(job);
  116. destroyRequestHandler();
  117. job->decRefCount();
  118. } catch (...) {
  119. Logger::Error("RpcRequestHandler leaked exceptions");
  120. }
  121. }
  122. private:
  123. RequestHandler *createRequestHandler() {
  124. if (!*s_xbox_server_info) {
  125. *s_xbox_server_info = std::make_shared<XboxServerInfo>();
  126. }
  127. if (RuntimeOption::XboxServerLogInfo) XboxRequestHandler::Info = true;
  128. s_xbox_request_handler->setServerInfo(*s_xbox_server_info);
  129. s_xbox_request_handler->setReturnEncodeType(
  130. RPCRequestHandler::ReturnEncodeType::Serialize);
  131. return s_xbox_request_handler.get();
  132. }
  133. void destroyRequestHandler() {
  134. if (!s_xbox_request_handler.isNull()) {
  135. s_xbox_request_handler.destroy();
  136. }
  137. }
  138. virtual void onThreadExit() {
  139. if (!s_xbox_request_handler.isNull()) {
  140. s_xbox_request_handler.destroy();
  141. }
  142. }
  143. };
  144. ///////////////////////////////////////////////////////////////////////////////
  145. static JobQueueDispatcher<XboxWorker> *s_dispatcher;
  146. static Mutex s_dispatchMutex;
  147. void XboxServer::Restart() {
  148. Stop();
  149. if (RuntimeOption::XboxServerThreadCount > 0) {
  150. {
  151. Lock l(s_dispatchMutex);
  152. s_dispatcher = new JobQueueDispatcher<XboxWorker>
  153. (RuntimeOption::XboxServerThreadCount,
  154. RuntimeOption::ServerThreadRoundRobin,
  155. RuntimeOption::ServerThreadDropCacheTimeoutSeconds,
  156. RuntimeOption::ServerThreadDropStack,
  157. nullptr);
  158. }
  159. if (RuntimeOption::XboxServerLogInfo) {
  160. Logger::Info("xbox server started");
  161. }
  162. s_dispatcher->start();
  163. }
  164. }
  165. void XboxServer::Stop() {
  166. if (s_dispatcher) {
  167. s_dispatcher->stop();
  168. Lock l(s_dispatchMutex);
  169. delete s_dispatcher;
  170. s_dispatcher = nullptr;
  171. }
  172. }
  173. ///////////////////////////////////////////////////////////////////////////////
  174. const StaticString
  175. s_code("code"),
  176. s_response("response"),
  177. s_error("error"),
  178. s_localhost("localhost"),
  179. s_127_0_0_1("127.0.0.1");
  180. static bool isLocalHost(const String& host) {
  181. return host.empty() || host == s_localhost || host == s_127_0_0_1;
  182. }
  183. bool XboxServer::SendMessage(const String& message,
  184. Array& ret,
  185. int timeout_ms,
  186. const String& host /* = "localhost" */) {
  187. if (isLocalHost(host)) {
  188. XboxTransport *job;
  189. {
  190. Lock l(s_dispatchMutex);
  191. if (!s_dispatcher) {
  192. return false;
  193. }
  194. job = new XboxTransport(message);
  195. job->incRefCount(); // paired with worker's decRefCount()
  196. job->incRefCount(); // paired with decRefCount() at below
  197. assert(s_dispatcher);
  198. s_dispatcher->enqueue(job);
  199. }
  200. if (timeout_ms <= 0) {
  201. timeout_ms = RuntimeOption::XboxDefaultLocalTimeoutMilliSeconds;
  202. }
  203. int code = 0;
  204. String response = job->getResults(code, timeout_ms);
  205. job->decRefCount(); // i'm done with this job
  206. if (code > 0) {
  207. ret.set(s_code, code);
  208. if (code == 200) {
  209. ret.set(s_response, unserialize_from_string(response));
  210. } else {
  211. ret.set(s_error, response);
  212. }
  213. return true;
  214. }
  215. } else { // remote
  216. string url = "http://";
  217. url += host.data();
  218. url += '/';
  219. url += RuntimeOption::XboxProcessMessageFunc;
  220. int timeoutSeconds = timeout_ms / 1000;
  221. if (timeoutSeconds <= 0) {
  222. timeoutSeconds = RuntimeOption::XboxDefaultRemoteTimeoutSeconds;
  223. }
  224. string hostStr(host.data());
  225. std::vector<std::string> headers;
  226. LibEventHttpClientPtr http =
  227. LibEventHttpClient::Get(hostStr, RuntimeOption::XboxServerPort);
  228. if (http->send(url, headers, timeoutSeconds, false,
  229. message.data(), message.size())) {
  230. int code = http->getCode();
  231. if (code > 0) {
  232. int len = 0;
  233. char *response = http->recv(len);
  234. String sresponse(response, len, AttachString);
  235. ret.set(s_code, code);
  236. if (code == 200) {
  237. ret.set(s_response, unserialize_from_string(sresponse));
  238. } else {
  239. ret.set(s_error, sresponse);
  240. }
  241. return true;
  242. }
  243. // code wasn't correctly set by http client, treat it as not found
  244. ret.set(s_code, 404);
  245. ret.set(s_error, "http client failed");
  246. }
  247. }
  248. return false;
  249. }
  250. bool XboxServer::PostMessage(const String& message,
  251. const String& host /* = "localhost" */) {
  252. if (isLocalHost(host)) {
  253. Lock l(s_dispatchMutex);
  254. if (!s_dispatcher) {
  255. return false;
  256. }
  257. XboxTransport *job = new XboxTransport(message);
  258. job->incRefCount(); // paired with worker's decRefCount()
  259. assert(s_dispatcher);
  260. s_dispatcher->enqueue(job);
  261. return true;
  262. } else { // remote
  263. string url = "http://";
  264. url += host.data();
  265. url += "/xbox_post_message";
  266. std::vector<std::string> headers;
  267. std::string hostStr(host.data());
  268. LibEventHttpClientPtr http =
  269. LibEventHttpClient::Get(hostStr, RuntimeOption::XboxServerPort);
  270. if (http->send(url, headers, 0, false, message.data(), message.size())) {
  271. int code = http->getCode();
  272. if (code > 0) {
  273. int len = 0;
  274. char *response = http->recv(len);
  275. String sresponse(response, len, AttachString);
  276. if (code == 200 && same(unserialize_from_string(sresponse), true)) {
  277. return true;
  278. }
  279. }
  280. }
  281. }
  282. return false;
  283. }
  284. ///////////////////////////////////////////////////////////////////////////////
  285. struct XboxTask : SweepableResourceData {
  286. DECLARE_RESOURCE_ALLOCATION(XboxTask)
  287. XboxTask(const String& message, const String& reqInitDoc = "") {
  288. m_job = new XboxTransport(message, reqInitDoc);
  289. m_job->incRefCount();
  290. }
  291. ~XboxTask() {
  292. m_job->decRefCount();
  293. }
  294. XboxTransport *getJob() { return m_job;}
  295. CLASSNAME_IS("XboxTask");
  296. // overriding ResourceData
  297. const String& o_getClassNameHook() const override { return classnameof(); }
  298. private:
  299. XboxTransport *m_job;
  300. };
  301. IMPLEMENT_RESOURCE_ALLOCATION(XboxTask)
  302. ///////////////////////////////////////////////////////////////////////////////
  303. Resource XboxServer::TaskStart(const String& msg,
  304. const String& reqInitDoc /* = "" */,
  305. ServerTaskEvent<XboxServer, XboxTransport> *event /* = nullptr */) {
  306. {
  307. Lock l(s_dispatchMutex);
  308. if (s_dispatcher &&
  309. (s_dispatcher->getActiveWorker() <
  310. RuntimeOption::XboxServerThreadCount ||
  311. s_dispatcher->getQueuedJobs() <
  312. RuntimeOption::XboxServerMaxQueueLength)) {
  313. auto task = req::make<XboxTask>(msg, reqInitDoc);
  314. XboxTransport *job = task->getJob();
  315. job->incRefCount(); // paired with worker's decRefCount()
  316. Transport *transport = g_context->getTransport();
  317. if (transport) {
  318. job->setHost(transport->getHeader("Host"));
  319. }
  320. if (event) {
  321. job->setAsioEvent(event);
  322. event->setJob(job);
  323. }
  324. assert(s_dispatcher);
  325. s_dispatcher->enqueue(job);
  326. return Resource(std::move(task));
  327. }
  328. }
  329. const char* errMsg =
  330. (RuntimeOption::XboxServerThreadCount > 0 ?
  331. "Cannot create new Xbox task because the Xbox queue has "
  332. "reached maximum capacity" :
  333. "Cannot create new Xbox task because the Xbox is not enabled");
  334. throw_exception(SystemLib::AllocExceptionObject(errMsg));
  335. return Resource();
  336. }
  337. bool XboxServer::TaskStatus(const Resource& task) {
  338. return cast<XboxTask>(task)->getJob()->isDone();
  339. }
  340. int XboxServer::TaskResult(const Resource& task, int timeout_ms, Variant *ret) {
  341. return TaskResult(cast<XboxTask>(task)->getJob(), timeout_ms, ret);
  342. }
  343. int XboxServer::TaskResult(XboxTransport *job, int timeout_ms, Variant *ret) {
  344. int code = 0;
  345. String response = job->getResults(code, timeout_ms);
  346. if (ret) {
  347. if (code == 200) {
  348. *ret = unserialize_from_string(response);
  349. } else {
  350. *ret = response;
  351. }
  352. }
  353. return code;
  354. }
  355. std::shared_ptr<XboxServerInfo> XboxServer::GetServerInfo() {
  356. return *s_xbox_server_info;
  357. }
  358. RPCRequestHandler *XboxServer::GetRequestHandler() {
  359. if (s_xbox_request_handler.isNull()) return nullptr;
  360. return s_xbox_request_handler.get();
  361. }
  362. ///////////////////////////////////////////////////////////////////////////////
  363. }