PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/runtime/base/server/rpc_request_handler.cpp

https://github.com/kevlund/hiphop-php
C++ | 292 lines | 235 code | 28 blank | 29 comment | 52 complexity | 1d7395b75e7af0a9866c5c36e6a46de0 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010- 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 <runtime/base/server/http_request_handler.h>
  17. #include <runtime/base/server/rpc_request_handler.h>
  18. #include <runtime/base/program_functions.h>
  19. #include <runtime/base/runtime_option.h>
  20. #include <runtime/base/server/server_stats.h>
  21. #include <runtime/base/server/http_protocol.h>
  22. #include <runtime/base/server/access_log.h>
  23. #include <runtime/base/server/source_root_info.h>
  24. #include <runtime/base/server/request_uri.h>
  25. #include <runtime/ext/ext_json.h>
  26. #include <util/process.h>
  27. using namespace std;
  28. namespace HPHP {
  29. ///////////////////////////////////////////////////////////////////////////////
  30. RPCRequestHandler::RPCRequestHandler() : m_count(0), m_reset(false),
  31. m_returnEncodeType(Json) {
  32. hphp_session_init();
  33. m_context = hphp_context_init();
  34. m_created = time(0);
  35. Logger::ResetRequestCount();
  36. Logger::Info("creating new RPC request handler");
  37. }
  38. RPCRequestHandler::~RPCRequestHandler() {
  39. hphp_context_exit(m_context, false);
  40. hphp_session_exit();
  41. }
  42. bool RPCRequestHandler::needReset() const {
  43. if (m_reset || m_serverInfo->alwaysReset()) return true;
  44. return (time(0) - m_created) > m_serverInfo->getMaxDuration();
  45. }
  46. void RPCRequestHandler::handleRequest(Transport *transport) {
  47. ExecutionProfiler ep(ThreadInfo::RuntimeFunctions);
  48. Logger::OnNewRequest();
  49. HttpRequestHandler::GetAccessLog().onNewRequest();
  50. m_context->setTransport(transport);
  51. transport->enableCompression();
  52. ServerStatsHelper ssh("all", true);
  53. Logger::Verbose("receiving %s", transport->getCommand().c_str());
  54. // will clear all extra logging when this function goes out of scope
  55. StackTraceNoHeap::ExtraLoggingClearer clearer;
  56. StackTraceNoHeap::AddExtraLogging("RPC-URL", transport->getUrl());
  57. // authentication
  58. const set<string> &passwords = m_serverInfo->getPasswords();
  59. if (!passwords.empty()) {
  60. set<string>::const_iterator iter =
  61. passwords.find(transport->getParam("auth"));
  62. if (iter == passwords.end()) {
  63. transport->sendString("Unauthorized", 401);
  64. transport->onSendEnd();
  65. HttpRequestHandler::GetAccessLog().log(transport, NULL);
  66. return;
  67. }
  68. } else {
  69. const string &password = m_serverInfo->getPassword();
  70. if (!password.empty() && password != transport->getParam("auth")) {
  71. transport->sendString("Unauthorized", 401);
  72. transport->onSendEnd();
  73. HttpRequestHandler::GetAccessLog().log(transport, NULL);
  74. return;
  75. }
  76. }
  77. // resolve virtual host
  78. const VirtualHost *vhost = HttpProtocol::GetVirtualHost(transport);
  79. ASSERT(vhost);
  80. if (vhost->disabled()) {
  81. transport->sendString("Virtual host disabled.", 404);
  82. transport->onSendEnd();
  83. HttpRequestHandler::GetAccessLog().log(transport, vhost);
  84. return;
  85. }
  86. vhost->setRequestTimeoutSeconds();
  87. // resolve source root
  88. string host = transport->getHeader("Host");
  89. SourceRootInfo sourceRootInfo(host.c_str());
  90. // set thread type
  91. switch (m_serverInfo->getType()) {
  92. case SatelliteServer::KindOfRPCServer:
  93. transport->setThreadType(Transport::RpcThread);
  94. break;
  95. case SatelliteServer::KindOfXboxServer:
  96. transport->setThreadType(Transport::XboxThread);
  97. break;
  98. default:
  99. break;
  100. }
  101. // record request for debugging purpose
  102. std::string tmpfile = HttpProtocol::RecordRequest(transport);
  103. bool ret = executePHPFunction(transport, sourceRootInfo);
  104. HttpRequestHandler::GetAccessLog().log(transport, vhost);
  105. HttpProtocol::ClearRecord(ret, tmpfile);
  106. }
  107. bool RPCRequestHandler::executePHPFunction(Transport *transport,
  108. SourceRootInfo &sourceRootInfo) {
  109. // reset timeout counter
  110. ThreadInfo::s_threadInfo->m_reqInjectionData.started = time(0);
  111. string rpcFunc = transport->getCommand();
  112. {
  113. ServerStatsHelper ssh("input");
  114. RequestURI reqURI(rpcFunc);
  115. HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo);
  116. }
  117. bool isFile = rpcFunc.rfind('.') != string::npos;
  118. string rpcFile;
  119. bool error = false;
  120. Array params;
  121. string sparams = transport->getParam("params");
  122. if (!sparams.empty()) {
  123. Variant jparams = f_json_decode(String(sparams), true);
  124. if (jparams.isArray()) {
  125. params = jparams.toArray();
  126. } else {
  127. error = true;
  128. }
  129. } else {
  130. vector<string> sparams;
  131. transport->getArrayParam("p", sparams);
  132. if (!sparams.empty()) {
  133. for (unsigned int i = 0; i < sparams.size(); i++) {
  134. Variant jparams = f_json_decode(String(sparams[i]), true);
  135. if (same(jparams, false)) {
  136. error = true;
  137. break;
  138. }
  139. params.append(jparams);
  140. }
  141. } else {
  142. // single string parameter, used by xbox to avoid any en/decoding
  143. int size;
  144. const void *data = transport->getPostData(size);
  145. if (data && size) {
  146. params.append(String((char*)data, size, AttachLiteral));
  147. }
  148. }
  149. }
  150. if (transport->getIntParam("reset") == 1) {
  151. m_reset = true;
  152. }
  153. int output = transport->getIntParam("output");
  154. int code;
  155. if (!error) {
  156. Variant funcRet;
  157. string errorMsg = "Internal Server Error";
  158. string warmupDoc, reqInitFunc, reqInitDoc;
  159. if (m_serverInfo) {
  160. warmupDoc = m_serverInfo->getWarmupDoc();
  161. reqInitFunc = m_serverInfo->getReqInitFunc();
  162. reqInitDoc = m_serverInfo->getReqInitDoc();
  163. }
  164. if (!warmupDoc.empty()) warmupDoc = canonicalize_path(warmupDoc, "", 0);
  165. if (!warmupDoc.empty()) {
  166. warmupDoc = getSourceFilename(warmupDoc, sourceRootInfo);
  167. }
  168. if (!reqInitDoc.empty()) reqInitDoc = canonicalize_path(reqInitDoc, "", 0);
  169. if (!reqInitDoc.empty()) {
  170. reqInitDoc = getSourceFilename(reqInitDoc, sourceRootInfo);
  171. }
  172. bool runOnce = false;
  173. bool ret = true;
  174. if (isFile) {
  175. rpcFile = rpcFunc;
  176. rpcFunc.clear();
  177. } else {
  178. rpcFile = transport->getParam("include");
  179. if (rpcFile.empty()) {
  180. rpcFile = transport->getParam("include_once");
  181. runOnce = true;
  182. }
  183. }
  184. if (!rpcFile.empty()) {
  185. // invoking a file through rpc
  186. bool forbidden = false;
  187. if (!RuntimeOption::ForbiddenFileExtensions.empty()) {
  188. const char *ext = rpcFile.c_str() + rpcFile.rfind('.') + 1;
  189. if (RuntimeOption::ForbiddenFileExtensions.find(ext) !=
  190. RuntimeOption::ForbiddenFileExtensions.end()) {
  191. forbidden = true;
  192. }
  193. }
  194. if (!forbidden) {
  195. rpcFile = canonicalize_path(rpcFile, "", 0);
  196. rpcFile = getSourceFilename(rpcFile, sourceRootInfo);
  197. ret = hphp_invoke(m_context, rpcFile, false, Array(), null,
  198. warmupDoc, reqInitFunc, reqInitDoc,
  199. error, errorMsg, runOnce);
  200. }
  201. // no need to do the initialization for a second time
  202. warmupDoc.clear();
  203. reqInitFunc.clear();
  204. reqInitDoc.clear();
  205. }
  206. if (ret && !rpcFunc.empty()) {
  207. ret = hphp_invoke(m_context, rpcFunc, true, params, ref(funcRet),
  208. warmupDoc, reqInitFunc, reqInitDoc,
  209. error, errorMsg);
  210. }
  211. if (ret) {
  212. String response;
  213. switch (output) {
  214. case 0: {
  215. ASSERT(m_returnEncodeType == Json ||
  216. m_returnEncodeType == Serialize);
  217. response = (m_returnEncodeType == Json) ? f_json_encode(funcRet)
  218. : f_serialize(funcRet);
  219. break;
  220. }
  221. case 1: response = m_context->obDetachContents(); break;
  222. case 2:
  223. response =
  224. f_json_encode(CREATE_MAP2("output", m_context->obDetachContents(),
  225. "return", f_json_encode(funcRet)));
  226. break;
  227. case 3: response = f_serialize(funcRet); break;
  228. }
  229. transport->sendRaw((void*)response.data(), response.size());
  230. code = transport->getResponseCode();
  231. } else if (error) {
  232. code = 500;
  233. transport->sendString(errorMsg, 500);
  234. m_reset = true;
  235. } else {
  236. code = 404;
  237. transport->sendString("Not Found", 404);
  238. }
  239. } else {
  240. code = 400;
  241. transport->sendString("Bad Request", 400);
  242. }
  243. params.reset();
  244. sourceRootInfo.clear();
  245. transport->onSendEnd();
  246. ServerStats::LogPage(isFile ? rpcFile : rpcFunc, code);
  247. m_context->onShutdownPostSend();
  248. m_context->obClean(); // in case postsend/cleanup output something
  249. m_context->restoreSession();
  250. return !error;
  251. }
  252. string RPCRequestHandler::getSourceFilename(const string &path,
  253. SourceRootInfo &sourceRootInfo) {
  254. if (path.empty() || path[0] == '/') return path;
  255. // If it is not a sandbox, sourceRoot will be the same as
  256. // RuntimeOption::SourceRoot.
  257. string sourceRoot = sourceRootInfo.path();
  258. if (sourceRoot.empty()) {
  259. return Process::GetCurrentDirectory() + "/" + path;
  260. }
  261. return sourceRoot + path;
  262. }
  263. ///////////////////////////////////////////////////////////////////////////////
  264. }