string RPCRequestHandler::getSourceFilename(const string &path, SourceRootInfo &sourceRootInfo) { if (path.empty() || path[0] == '/') return path; // If it is not a sandbox, sourceRoot will be the same as // RuntimeOption::SourceRoot. string sourceRoot = sourceRootInfo.path(); if (sourceRoot.empty()) { return Process::GetCurrentDirectory() + "/" + path; } return sourceRoot + path; }
void HttpProtocol::PrepareServerVariable(Array& server, Transport *transport, const RequestURI &r, const SourceRootInfo &sri, const VirtualHost *vhost) { // $_SERVER string contentType = transport->getHeader("Content-Type"); string contentLength = transport->getHeader("Content-Length"); // HTTP_ headers -- we don't exclude headers we handle elsewhere (e.g., // Content-Type, Authorization), since the CGI "spec" merely says the server // "may" exclude them; this is not what APE does, but it's harmless. HeaderMap headers; transport->getHeaders(headers); // Do this first so other methods can overwrite them CopyHeaderVariables(server, headers); CopyServerInfo(server, transport, vhost); // Do this last so it can overwrite all the previous settings CopyTransportParams(server, transport); CopyRemoteInfo(server, transport); CopyAuthInfo(server, transport); CopyPathInfo(server, transport, r, vhost); // APE sets CONTENT_TYPE and CONTENT_LENGTH without HTTP_ if (!contentType.empty()) { server.set(s_CONTENT_TYPE, String(contentType)); } if (!contentLength.empty()) { server.set(s_CONTENT_LENGTH, String(contentLength)); } for (auto& kv : RuntimeOption::ServerVariables) { server.set(String(kv.first), String(kv.second)); } for (auto& kv : vhost->getServerVars()) { server.set(String(kv.first), String(kv.second)); } server = sri.setServerVariables(std::move(server)); const char *threadType = transport->getThreadTypeName(); server.set(s_THREAD_TYPE, threadType); StackTraceNoHeap::AddExtraLogging("ThreadType", threadType); }
/** * PHP has "EGPCS" processing order of these global variables, and this * order is important in preparing $_REQUEST that needs to know which to * overwrite what when name happens to be the same. */ void HttpProtocol::PrepareSystemVariables(Transport *transport, const RequestURI &r, const SourceRootInfo &sri) { SystemGlobals *g = (SystemGlobals*)get_global_variables(); const VirtualHost *vhost = VirtualHost::GetCurrent(); // reset global symbols to nulls or empty arrays pm_php$globals$symbols_php(); Variant &server = g->GV(_SERVER); server.set("REQUEST_START_TIME", time(NULL)); // $_ENV process_env_variables(g->GV(_ENV)); g->GV(_ENV).set("HPHP", 1); g->GV(_ENV).set("HPHP_SERVER", 1); #ifdef HOTPROFILER g->GV(_ENV).set("HPHP_HOTPROFILER", 1); #endif Variant &request = g->GV(_REQUEST); // $_GET and $_REQUEST if (!r.queryString().empty()) { DecodeParameters(g->GV(_GET), r.queryString().data(), r.queryString().size()); CopyParams(request, g->GV(_GET)); } string contentType = transport->getHeader("Content-Type"); string contentLength = transport->getHeader("Content-Length"); // $_POST and $_REQUEST if (transport->getMethod() == Transport::POST) { bool needDelete = false; int size = 0; const void *data = transport->getPostData(size); if (data && size) { ASSERT(((char*)data)[size] == 0); // we need a NULL terminated string string boundary; int content_length = atoi(contentLength.c_str()); bool rfc1867Post = IsRfc1867(contentType, boundary); string files; if (rfc1867Post) { if (content_length > VirtualHost::GetMaxPostSize()) { // $_POST and $_FILES are empty Logger::Warning("POST Content-Length of %d bytes exceeds " "the limit of %lld bytes", content_length, VirtualHost::GetMaxPostSize()); needDelete = read_all_post_data(transport, data, size); } else { if (transport->hasMorePostData()) { needDelete = true; data = Util::buffer_duplicate(data, size); } DecodeRfc1867(transport, g->GV(_POST), g->GV(_FILES), content_length, data, size, boundary); } ASSERT(!transport->getFiles(files)); } else { needDelete = read_all_post_data(transport, data, size); bool decodeData = strncasecmp(contentType.c_str(), DEFAULT_POST_CONTENT_TYPE, sizeof(DEFAULT_POST_CONTENT_TYPE)-1) == 0; // Always decode data for now. (macvicar) decodeData = true; if (decodeData) { DecodeParameters(g->GV(_POST), (const char*)data, size, true); } bool ret = transport->getFiles(files); if (ret) { g->GV(_FILES) = f_unserialize(files); } } CopyParams(request, g->GV(_POST)); if (needDelete) { if (RuntimeOption::AlwaysPopulateRawPostData) { g->GV(HTTP_RAW_POST_DATA) = String((char*)data, size, AttachString); } else { free((void *)data); } } else { // For literal we disregard RuntimeOption::AlwaysPopulateRawPostData g->GV(HTTP_RAW_POST_DATA) = String((char*)data, size, AttachLiteral); } } } // $_COOKIE string cookie_data = transport->getHeader("Cookie"); if (!cookie_data.empty()) { StringBuffer sb; sb.append(cookie_data); DecodeCookies(g->GV(_COOKIE), (char*)sb.data()); CopyParams(request, g->GV(_COOKIE)); } // $_SERVER // HTTP_ headers -- we don't exclude headers we handle elsewhere (e.g., // Content-Type, Authorization), since the CGI "spec" merely says the server // "may" exclude them; this is not what APE does, but it's harmless. HeaderMap headers; transport->getHeaders(headers); for (HeaderMap::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) { const vector<string> &values = iter->second; for (unsigned int i = 0; i < values.size(); i++) { String key = "HTTP_"; key += StringUtil::ToUpper(iter->first).replace("-", "_"); server.set(key, String(values[i])); } } string host = transport->getHeader("Host"); String hostName(VirtualHost::GetCurrent()->serverName(host)); string hostHeader(host); if (hostHeader.empty()) { server.set("HTTP_HOST", hostName); StackTraceNoHeap::AddExtraLogging("Server", hostName.data()); } else { StackTraceNoHeap::AddExtraLogging("Server", hostHeader.c_str()); } if (hostName.empty() || RuntimeOption::ForceServerNameToHeader) { hostName = hostHeader; // _SERVER['SERVER_NAME'] shouldn't contain the port number int colonPos = hostName.find(':'); if (colonPos != String::npos) { hostName = hostName.substr(0, colonPos); } } // APE sets CONTENT_TYPE and CONTENT_LENGTH without HTTP_ if (!contentType.empty()) { server.set("CONTENT_TYPE", String(contentType)); } if (!contentLength.empty()) { server.set("CONTENT_LENGTH", String(contentLength)); } // APE processes Authorization: Basic into PHP_AUTH_USER and PHP_AUTH_PW string authorization = transport->getHeader("Authorization"); if (!authorization.empty()) { if (strncmp(authorization.c_str(), "Basic ", 6) == 0) { // it's safe to pass this as a string literal since authorization // outlives decodedAuth; this saves us a superfluous copy. String decodedAuth = StringUtil::Base64Decode(String(authorization.c_str() + 6)); int colonPos = decodedAuth.find(':'); if (colonPos != String::npos) { server.set("PHP_AUTH_USER", decodedAuth.substr(0, colonPos)); server.set("PHP_AUTH_PW", decodedAuth.substr(colonPos + 1)); } } } server.set("REQUEST_URI", String(transport->getUrl(), CopyString)); server.set("SCRIPT_URL", r.originalURL()); String prefix(transport->isSSL() ? "https://" : "http://"); String port_suffix(""); // Need to append port if (!transport->isSSL() && RuntimeOption::ServerPort != 80) { port_suffix = ":" + RuntimeOption::ServerPort; } server.set("SCRIPT_URI", String(prefix + (hostHeader.empty() ? hostName + port_suffix : hostHeader) + r.originalURL())); if (r.rewritten()) { // when URL is rewritten, PHP decided to put original URL as SCRIPT_NAME String name = r.originalURL(); if (!r.pathInfo().empty()) { int pos = name.find(r.pathInfo()); if (pos >= 0) { name = name.substr(0, pos); } } if (r.defaultDoc()) { if (!name.empty() && name[name.length() - 1] != '/') { name += "/"; } name += String(RuntimeOption::DefaultDocument); } server.set("SCRIPT_NAME", name); } else { server.set("SCRIPT_NAME", r.resolvedURL()); } if (!r.rewritten() && r.pathInfo().empty()) { server.set("PHP_SELF", r.resolvedURL()); } else { // when URL is rewritten, or pathinfo is not empty, use original URL server.set("PHP_SELF", r.originalURL()); } server.set("SCRIPT_FILENAME", r.absolutePath()); if (r.pathInfo().empty()) { server.set("PATH_TRANSLATED", r.absolutePath()); } else { server.set("PATH_TRANSLATED", String(vhost->getDocumentRoot() + r.pathInfo().data())); server.set("PATH_INFO", r.pathInfo()); } server.set("argv", r.queryString()); server.set("argc", 0); server.set("GATEWAY_INTERFACE", "CGI/1.1"); server.set("SERVER_ADDR", String(RuntimeOption::ServerPrimaryIP)); server.set("SERVER_NAME", hostName); server.set("SERVER_PORT", RuntimeOption::ServerPort); server.set("SERVER_SOFTWARE", "HPHP"); server.set("SERVER_PROTOCOL", "HTTP/" + transport->getHTTPVersion()); server.set("SERVER_ADMIN", ""); server.set("SERVER_SIGNATURE", ""); switch (transport->getMethod()) { case Transport::GET: server.set("REQUEST_METHOD", "GET"); break; case Transport::HEAD: server.set("REQUEST_METHOD", "HEAD"); break; case Transport::POST: if (transport->getExtendedMethod() == NULL) { server.set("REQUEST_METHOD", "POST"); } else { server.set("REQUEST_METHOD", transport->getExtendedMethod()); } break; default: server.set("REQUEST_METHOD", ""); break; } server.set("HTTPS", transport->isSSL() ? "1" : ""); server.set("REQUEST_TIME", time(NULL)); server.set("QUERY_STRING", r.queryString()); server.set("REMOTE_ADDR", String(transport->getRemoteHost(), CopyString)); server.set("REMOTE_HOST", ""); // I don't think we need to nslookup server.set("REMOTE_PORT", 0); // TODO: quite useless server.set("DOCUMENT_ROOT", String(vhost->getDocumentRoot())); for (map<string, string>::const_iterator iter = RuntimeOption::ServerVariables.begin(); iter != RuntimeOption::ServerVariables.end(); ++iter) { server.set(String(iter->first), String(iter->second)); } const map<string, string> &vServerVars = vhost->getServerVars(); for (map<string, string>::const_iterator iter = vServerVars.begin(); iter != vServerVars.end(); ++iter) { server.set(String(iter->first), String(iter->second)); } sri.setServerVariables(server); const char *threadType = transport->getThreadTypeName(); server.set("THREAD_TYPE", threadType); StackTraceNoHeap::AddExtraLogging("ThreadType", threadType); #ifdef TAINTED taint_array_variant(g->GV(_GET), "$_GET"); taint_array_variant(g->GV(_POST), "$_POST"); taint_array_variant(g->GV(_SERVER), "$_SERVER"); taint_array_variant(g->GV(_COOKIE), "$_COOKIE"); #endif }
bool RPCRequestHandler::executePHPFunction(Transport *transport, SourceRootInfo &sourceRootInfo) { // reset timeout counter ThreadInfo::s_threadInfo->m_reqInjectionData.started = time(0); string rpcFunc = transport->getCommand(); { ServerStatsHelper ssh("input"); RequestURI reqURI(rpcFunc); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); } bool isFile = rpcFunc.rfind('.') != string::npos; string rpcFile; bool error = false; Array params; string sparams = transport->getParam("params"); if (!sparams.empty()) { Variant jparams = f_json_decode(String(sparams), true); if (jparams.isArray()) { params = jparams.toArray(); } else { error = true; } } else { vector<string> sparams; transport->getArrayParam("p", sparams); if (!sparams.empty()) { for (unsigned int i = 0; i < sparams.size(); i++) { Variant jparams = f_json_decode(String(sparams[i]), true); if (same(jparams, false)) { error = true; break; } params.append(jparams); } } else { // single string parameter, used by xbox to avoid any en/decoding int size; const void *data = transport->getPostData(size); if (data && size) { params.append(String((char*)data, size, AttachLiteral)); } } } if (transport->getIntParam("reset") == 1) { m_reset = true; } int output = transport->getIntParam("output"); int code; if (!error) { Variant funcRet; string errorMsg = "Internal Server Error"; string warmupDoc, reqInitFunc, reqInitDoc; if (m_serverInfo) { warmupDoc = m_serverInfo->getWarmupDoc(); reqInitFunc = m_serverInfo->getReqInitFunc(); reqInitDoc = m_serverInfo->getReqInitDoc(); } if (!warmupDoc.empty()) warmupDoc = canonicalize_path(warmupDoc, "", 0); if (!warmupDoc.empty()) { warmupDoc = getSourceFilename(warmupDoc, sourceRootInfo); } if (!reqInitDoc.empty()) reqInitDoc = canonicalize_path(reqInitDoc, "", 0); if (!reqInitDoc.empty()) { reqInitDoc = getSourceFilename(reqInitDoc, sourceRootInfo); } bool runOnce = false; bool ret = true; if (isFile) { rpcFile = rpcFunc; rpcFunc.clear(); } else { rpcFile = transport->getParam("include"); if (rpcFile.empty()) { rpcFile = transport->getParam("include_once"); runOnce = true; } } if (!rpcFile.empty()) { // invoking a file through rpc bool forbidden = false; if (!RuntimeOption::ForbiddenFileExtensions.empty()) { const char *ext = rpcFile.c_str() + rpcFile.rfind('.') + 1; if (RuntimeOption::ForbiddenFileExtensions.find(ext) != RuntimeOption::ForbiddenFileExtensions.end()) { forbidden = true; } } if (!forbidden) { rpcFile = canonicalize_path(rpcFile, "", 0); rpcFile = getSourceFilename(rpcFile, sourceRootInfo); ret = hphp_invoke(m_context, rpcFile, false, Array(), null, warmupDoc, reqInitFunc, reqInitDoc, error, errorMsg, runOnce); } // no need to do the initialization for a second time warmupDoc.clear(); reqInitFunc.clear(); reqInitDoc.clear(); } if (ret && !rpcFunc.empty()) { ret = hphp_invoke(m_context, rpcFunc, true, params, ref(funcRet), warmupDoc, reqInitFunc, reqInitDoc, error, errorMsg); } if (ret) { String response; switch (output) { case 0: response = f_json_encode(funcRet); break; case 1: response = m_context->obDetachContents(); break; case 2: response = f_json_encode(CREATE_MAP2("output", m_context->obDetachContents(), "return", f_json_encode(funcRet))); break; } code = 200; transport->sendRaw((void*)response.data(), response.size()); } else if (error) { code = 500; transport->sendString(errorMsg, 500); m_reset = true; } else { code = 404; transport->sendString("Not Found", 404); } } else { code = 400; transport->sendString("Bad Request", 400); } params.reset(); sourceRootInfo.clear(); transport->onSendEnd(); ServerStats::LogPage(isFile ? rpcFile : rpcFunc, code); m_context->onShutdownPostSend(); m_context->obClean(); // in case postsend/cleanup output something m_context->restoreSession(); return !error; }
bool HttpRequestHandler::executePHPRequest(Transport *transport, RequestURI &reqURI, SourceRootInfo &sourceRootInfo, bool cachableDynamicContent) { ExecutionContext *context = hphp_context_init(); if (RuntimeOption::ImplicitFlush) { context->obSetImplicitFlush(true); } if (RuntimeOption::EnableOutputBuffering) { if (RuntimeOption::OutputHandler.empty()) { context->obStart(); } else { context->obStart(String(RuntimeOption::OutputHandler)); } } context->setTransport(transport); string file = reqURI.absolutePath().c_str(); { ServerStatsHelper ssh("input"); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); Extension::RequestInitModules(); if (RuntimeOption::EnableDebugger) { Eval::DSandboxInfo sInfo = sourceRootInfo.getSandboxInfo(); Eval::Debugger::RegisterSandbox(sInfo); context->setSandboxId(sInfo.id()); } reqURI.clear(); sourceRootInfo.clear(); } int code; bool ret = true; if (RuntimeOption::EnableDebugger) { Eval::Debugger::InterruptRequestStarted(transport->getUrl()); } bool error = false; std::string errorMsg = "Internal Server Error"; ret = hphp_invoke(context, file, false, Array(), uninit_null(), RuntimeOption::RequestInitFunction, RuntimeOption::RequestInitDocument, error, errorMsg); if (ret) { String content = context->obDetachContents(); if (cachableDynamicContent && !content.empty()) { assert(transport->getUrl()); string key = file + transport->getUrl(); DynamicContentCache::TheCache.store(key, content.data(), content.size()); } transport->sendRaw((void*)content.data(), content.size()); code = transport->getResponseCode(); } else if (error) { code = 500; string errorPage = context->getErrorPage().data(); if (errorPage.empty()) { errorPage = RuntimeOption::ErrorDocument500; } if (!errorPage.empty()) { context->obProtect(false); context->obEndAll(); context->obStart(); context->obProtect(true); ret = hphp_invoke(context, errorPage, false, Array(), uninit_null(), RuntimeOption::RequestInitFunction, RuntimeOption::RequestInitDocument, error, errorMsg); if (ret) { String content = context->obDetachContents(); transport->sendRaw((void*)content.data(), content.size()); code = transport->getResponseCode(); } else { Logger::Error("Unable to invoke error page %s", errorPage.c_str()); errorPage.clear(); // so we fall back to 500 return } } if (errorPage.empty()) { if (RuntimeOption::ServerErrorMessage) { transport->sendString(errorMsg, 500, false, false, "hphp_invoke"); } else { transport->sendString(RuntimeOption::FatalErrorMessage, 500, false, false, "hphp_invoke"); } } } else { code = 404; transport->sendString("Not Found", 404); } if (RuntimeOption::EnableDebugger) { Eval::Debugger::InterruptRequestEnded(transport->getUrl()); } transport->onSendEnd(); hphp_context_exit(context, true, true, transport->getUrl()); ServerStats::LogPage(file, code); return ret; }
bool RPCRequestHandler::executePHPFunction(Transport *transport, SourceRootInfo &sourceRootInfo, ReturnEncodeType returnEncodeType) { string rpcFunc = transport->getCommand(); { ServerStatsHelper ssh("input"); RequestURI reqURI(rpcFunc); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); GlobalVariables *g = get_global_variables(); g->getRef(s__ENV).set(s_HPHP_RPC, 1); } bool isFile = rpcFunc.rfind('.') != string::npos; string rpcFile; bool error = false; Array params; string sparams = transport->getParam("params"); if (!sparams.empty()) { Variant jparams = f_json_decode(String(sparams), true); if (jparams.isArray()) { params = jparams.toArray(); } else { error = true; } } else { vector<string> sparams; transport->getArrayParam("p", sparams); if (!sparams.empty()) { for (unsigned int i = 0; i < sparams.size(); i++) { Variant jparams = f_json_decode(String(sparams[i]), true); if (same(jparams, false)) { error = true; break; } params.append(jparams); } } else { // single string parameter, used by xbox to avoid any en/decoding int size; const void *data = transport->getPostData(size); if (data && size) { params.append(String((char*)data, size, CopyString)); } } } if (transport->getIntParam("reset") == 1) { m_reset = true; } int output = transport->getIntParam("output"); int code; if (!error) { Variant funcRet; string errorMsg = "Internal Server Error"; string reqInitFunc, reqInitDoc; reqInitDoc = transport->getHeader("ReqInitDoc"); if (reqInitDoc.empty() && m_serverInfo) { reqInitFunc = m_serverInfo->getReqInitFunc(); reqInitDoc = m_serverInfo->getReqInitDoc(); } if (!reqInitDoc.empty()) { reqInitDoc = (std::string)canonicalize_path(reqInitDoc, "", 0); } if (!reqInitDoc.empty()) { reqInitDoc = getSourceFilename(reqInitDoc, sourceRootInfo); } bool runOnce = false; bool ret = true; if (isFile) { rpcFile = rpcFunc; rpcFunc.clear(); } else { rpcFile = transport->getParam("include"); if (rpcFile.empty()) { rpcFile = transport->getParam("include_once"); runOnce = true; } } if (!rpcFile.empty()) { // invoking a file through rpc bool forbidden = false; if (!RuntimeOption::ForbiddenFileExtensions.empty()) { const char *ext = rpcFile.c_str() + rpcFile.rfind('.') + 1; if (RuntimeOption::ForbiddenFileExtensions.find(ext) != RuntimeOption::ForbiddenFileExtensions.end()) { forbidden = true; } } if (!forbidden) { rpcFile = (std::string) canonicalize_path(rpcFile, "", 0); rpcFile = getSourceFilename(rpcFile, sourceRootInfo); ret = hphp_invoke(m_context, rpcFile, false, Array(), uninit_null(), reqInitFunc, reqInitDoc, error, errorMsg, runOnce); } // no need to do the initialization for a second time reqInitFunc.clear(); reqInitDoc.clear(); } if (ret && !rpcFunc.empty()) { ret = hphp_invoke(m_context, rpcFunc, true, params, ref(funcRet), reqInitFunc, reqInitDoc, error, errorMsg); } if (ret) { bool serializeFailed = false; String response; switch (output) { case 0: { assert(returnEncodeType == ReturnEncodeType::Json || returnEncodeType == ReturnEncodeType::Serialize); try { response = (returnEncodeType == ReturnEncodeType::Json) ? f_json_encode(funcRet) : f_serialize(funcRet); } catch (...) { serializeFailed = true; } break; } case 1: response = m_context->obDetachContents(); break; case 2: response = f_json_encode( make_map_array(s_output, m_context->obDetachContents(), s_return, f_json_encode(funcRet))); break; case 3: response = f_serialize(funcRet); break; } if (serializeFailed) { code = 500; transport->sendString( "Serialization of the return value failed", 500); m_reset = true; } else { transport->sendRaw((void*)response.data(), response.size()); code = transport->getResponseCode(); } } else if (error) { code = 500; transport->sendString(errorMsg, 500); m_reset = true; } else { code = 404; transport->sendString("Not Found", 404); } } else { code = 400; transport->sendString("Bad Request", 400); } params.reset(); sourceRootInfo.clear(); transport->onSendEnd(); ServerStats::LogPage(isFile ? rpcFile : rpcFunc, code); m_context->onShutdownPostSend(); m_context->obClean(); // in case postsend/cleanup output something m_context->restoreSession(); return !error; }
bool RPCRequestHandler::executePHPFunction(Transport *transport, SourceRootInfo &sourceRootInfo) { // reset timeout counter ThreadInfo::s_threadInfo->m_reqInjectionData.started = time(0); std::string rpcFunc = transport->getCommand(); { ServerStatsHelper ssh("input"); RequestURI reqURI(rpcFunc); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); sourceRootInfo.clear(); } bool error = false; Array params; std::string sparams = transport->getParam("params"); if (!sparams.empty()) { Variant jparams = f_json_decode(String(sparams), true); if (jparams.isArray()) { params = jparams.toArray(); } else { error = true; } } else { vector<string> sparams; transport->getArrayParam("p", sparams); if (!sparams.empty()) { for (unsigned int i = 0; i < sparams.size(); i++) { Variant jparams = f_json_decode(String(sparams[i]), true); if (same(jparams, false)) { error = true; break; } params.append(jparams); } } else { // single string parameter, used by xbox to avoid any en/decoding int size; const void *data = transport->getPostData(size); if (data && size) { params.append(String((char*)data, size, AttachLiteral)); } } } if (transport->getIntParam("reset") == 1) { m_reset = true; } int output = transport->getIntParam("output"); int code; if (!error) { Variant funcRet; std::string errorMsg = "Internal Server Error"; string warmupDoc, reqInitFunc, reqInitDoc; if (m_serverInfo) { warmupDoc = m_serverInfo->getWarmupDoc(); reqInitFunc = m_serverInfo->getReqInitFunc(); reqInitDoc = m_serverInfo->getReqInitDoc(); } if (!warmupDoc.empty()) warmupDoc = canonicalize_path(warmupDoc, "", 0); if (!warmupDoc.empty()) warmupDoc = get_source_filename(warmupDoc.c_str()); bool ret = hphp_invoke(m_context, rpcFunc, true, params, ref(funcRet), warmupDoc, reqInitFunc, reqInitDoc, error, errorMsg); if (ret) { String response; switch (output) { case 0: response = f_json_encode(funcRet); break; case 1: response = m_context->obDetachContents(); break; case 2: response = f_json_encode(CREATE_MAP2("output", m_context->obDetachContents(), "return", f_json_encode(funcRet))); break; } code = 200; transport->sendRaw((void*)response.data(), response.size()); } else if (error) { code = 500; transport->sendString(errorMsg, 500); m_reset = true; } else { code = 404; transport->sendString("Not Found", 404); } } else { code = 400; transport->sendString("Bad Request", 400); } params.reset(); transport->onSendEnd(); ServerStats::LogPage(rpcFunc, code); m_context->onShutdownPostSend(); m_context->restoreSession(); return !error; }
bool HttpRequestHandler::executePHPRequest(Transport *transport, RequestURI &reqURI, SourceRootInfo &sourceRootInfo, bool cachableDynamicContent) { ExecutionContext *context = hphp_context_init(); context->setTransport(transport); string file = reqURI.absolutePath().c_str(); { ServerStatsHelper ssh("input"); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); reqURI.clear(); sourceRootInfo.clear(); } bool error = false; std::string errorMsg = "Internal Server Error"; bool ret = hphp_invoke(context, file, false, Array(), null, RuntimeOption::WarmupDocument, RuntimeOption::RequestInitFunction, error, errorMsg); int code; if (ret) { std::string content = context->getContents(); if (cachableDynamicContent && !content.empty()) { ASSERT(transport->getUrl()); string key = file + transport->getUrl(); DynamicContentCache::TheCache.store(key, content.data(), content.size()); } code = 200; transport->sendRaw((void*)content.data(), content.size()); } else if (error) { code = 500; string errorPage = context->getErrorPage(); if (errorPage.empty()) { errorPage = RuntimeOption::ErrorDocument500; } if (!errorPage.empty()) { context->obEndAll(); context->obStart(); context->obProtect(true); ret = hphp_invoke(context, errorPage, false, Array(), null, RuntimeOption::WarmupDocument, RuntimeOption::RequestInitFunction, error, errorMsg); if (ret) { std::string content = context->getContents(); transport->sendRaw((void*)content.data(), content.size()); } else { errorPage.clear(); // so we fall back to 500 return } } if (errorPage.empty()) { if (RuntimeOption::ServerErrorMessage) { transport->sendString(errorMsg, 500); } else { transport->sendString(RuntimeOption::FatalErrorMessage, 500); } } } else { code = 404; transport->sendString("Not Found", 404); } transport->onSendEnd(); ServerStats::LogPage(file, code); hphp_context_exit(context, true); return ret; }
bool HttpRequestHandler::executePHPRequest(Transport *transport, RequestURI &reqURI, SourceRootInfo &sourceRootInfo, bool cachableDynamicContent) { ExecutionContext *context = hphp_context_init(); if (RuntimeOption::ImplicitFlush) { context->obSetImplicitFlush(true); } if (RuntimeOption::EnableOutputBuffering) { if (RuntimeOption::OutputHandler.empty()) { context->obStart(); } else { context->obStart(String(RuntimeOption::OutputHandler)); } } context->setTransport(transport); string file = reqURI.absolutePath().c_str(); { ServerStatsHelper ssh("input"); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); reqURI.clear(); sourceRootInfo.clear(); } int code; bool ret = true; if (!RuntimeOption::ForbiddenFileExtensions.empty()) { size_t pos = file.rfind('.'); if (pos != string::npos) { const char *ext = file.c_str() + pos + 1; if (RuntimeOption::ForbiddenFileExtensions.find(ext) != RuntimeOption::ForbiddenFileExtensions.end()) { code = 403; transport->sendString("Forbidden", 403); ret = false; } } } if (ret) { if (RuntimeOption::EnableDebugger) { Eval::Debugger::InterruptRequestStarted(transport->getUrl()); } bool error = false; std::string errorMsg = "Internal Server Error"; ret = hphp_invoke(context, file, false, Array(), null, RuntimeOption::WarmupDocument, RuntimeOption::RequestInitFunction, RuntimeOption::RequestInitDocument, error, errorMsg); if (ret) { String content = context->obDetachContents(); if (cachableDynamicContent && !content.empty()) { ASSERT(transport->getUrl()); string key = file + transport->getUrl(); DynamicContentCache::TheCache.store(key, content.data(), content.size()); } code = 200; transport->sendRaw((void*)content.data(), content.size(), code); } else if (error) { code = 500; string errorPage = context->getErrorPage().data(); if (errorPage.empty()) { errorPage = RuntimeOption::ErrorDocument500; } if (!errorPage.empty()) { context->obProtect(false); context->obEndAll(); context->obStart(); context->obProtect(true); ret = hphp_invoke(context, errorPage, false, Array(), null, RuntimeOption::WarmupDocument, RuntimeOption::RequestInitFunction, RuntimeOption::RequestInitDocument, error, errorMsg); if (ret) { String content = context->obDetachContents(); transport->sendRaw((void*)content.data(), content.size()); } else { errorPage.clear(); // so we fall back to 500 return } } if (errorPage.empty()) { if (RuntimeOption::ServerErrorMessage) { transport->sendString(errorMsg, 500, false, false, "hphp_invoke"); } else { transport->sendString(RuntimeOption::FatalErrorMessage, 500, false, false, "hphp_invoke"); } } } else { code = 404; transport->sendString("Not Found", 404); } if (RuntimeOption::EnableDebugger) { Eval::Debugger::InterruptRequestEnded(transport->getUrl()); } } transport->onSendEnd(); ServerStats::LogPage(file, code); hphp_context_exit(context, true, true, transport->getUrl()); return ret; }
bool RPCRequestHandler::executePHPFunction(Transport *transport, SourceRootInfo &sourceRootInfo, ReturnEncodeType returnEncodeType) { std::string rpcFunc = transport->getCommand(); { ServerStatsHelper ssh("input"); RequestURI reqURI(rpcFunc); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); auto env = php_global(s__ENV); env.toArrRef().set(s_HPHP_RPC, 1); php_global_set(s__ENV, std::move(env)); InitFiniNode::GlobalsInit(); } bool isFile = rpcFunc.rfind('.') != std::string::npos; std::string rpcFile; bool error = false; Array params; Transport::Method requestMethod = m_serverInfo->getMethod(); std::string sparams = transport->getParam("params", requestMethod); if (!sparams.empty()) { Variant jparams = Variant::attach( HHVM_FN(json_decode)(String(sparams), true) ); if (jparams.isArray()) { params = jparams.toArray(); } else { error = true; } } else { std::vector<std::string> sparams; transport->getArrayParam("p", sparams, requestMethod); if (!sparams.empty()) { for (unsigned int i = 0; i < sparams.size(); i++) { Variant jparams = Variant::attach( HHVM_FN(json_decode)(String(sparams[i]), true) ); if (same(jparams, false)) { error = true; break; } params.append(jparams); } } else { // single string parameter, used by xbox to avoid any en/decoding size_t size; const void *data = transport->getPostData(size); if (data && size) { params.append(String((char*)data, size, CopyString)); } } } if (transport->getIntParam("reset", requestMethod) == 1) { m_reset = true; } int output = transport->getIntParam("output", requestMethod); // We don't debug RPC requests, so we need to detach XDebugHook if xdebug was // enabled. DEBUGGER_ATTACHED_ONLY(DebuggerHook::detach()); int code; if (!error) { Variant funcRet; std::string errorMsg = "Internal Server Error"; std::string reqInitFunc, reqInitDoc; reqInitDoc = transport->getHeader("ReqInitDoc"); if (reqInitDoc.empty() && m_serverInfo) { reqInitFunc = m_serverInfo->getReqInitFunc(); reqInitDoc = m_serverInfo->getReqInitDoc(); } if (!reqInitDoc.empty()) { reqInitDoc = (std::string)canonicalize_path(reqInitDoc, "", 0); } if (!reqInitDoc.empty()) { reqInitDoc = getSourceFilename(reqInitDoc, sourceRootInfo); } bool runOnce = false; bool ret = true; if (isFile) { rpcFile = rpcFunc; rpcFunc.clear(); } else { rpcFile = transport->getParam("include", requestMethod); if (rpcFile.empty()) { rpcFile = transport->getParam("include_once", requestMethod); runOnce = true; } } if (!rpcFile.empty()) { // invoking a file through rpc bool forbidden = false; if (!RuntimeOption::ForbiddenFileExtensions.empty()) { const char *ext = rpcFile.c_str() + rpcFile.rfind('.') + 1; if (RuntimeOption::ForbiddenFileExtensions.find(ext) != RuntimeOption::ForbiddenFileExtensions.end()) { forbidden = true; } } if (!forbidden) { rpcFile = (std::string) canonicalize_path(rpcFile, "", 0); rpcFile = getSourceFilename(rpcFile, sourceRootInfo); ret = hphp_invoke(m_context, rpcFile, false, Array(), uninit_null(), reqInitFunc, reqInitDoc, error, errorMsg, runOnce, false /* warmupOnly */, false /* richErrorMessage */); } // no need to do the initialization for a second time reqInitFunc.clear(); reqInitDoc.clear(); } if (ret && !rpcFunc.empty()) { ret = hphp_invoke(m_context, rpcFunc, true, params, ref(funcRet), reqInitFunc, reqInitDoc, error, errorMsg, true /* once */, false /* warmupOnly */, false /* richErrorMessage */); } if (ret) { bool serializeFailed = false; String response; switch (output) { case 0: { assert(returnEncodeType == ReturnEncodeType::Json || returnEncodeType == ReturnEncodeType::Serialize); try { response = (returnEncodeType == ReturnEncodeType::Json) ? Variant::attach(HHVM_FN(json_encode)(funcRet)).toString() : f_serialize(funcRet); } catch (...) { serializeFailed = true; } break; } case 1: response = m_context->obDetachContents(); break; case 2: response = Variant::attach(HHVM_FN(json_encode)( make_map_array( s_output, m_context->obDetachContents(), s_return, Variant::attach(HHVM_FN(json_encode)(funcRet)) ) )).toString(); break; case 3: response = f_serialize(funcRet); break; } if (serializeFailed) { code = 500; transport->sendString( "Serialization of the return value failed", 500); m_reset = true; } else { transport->sendRaw((void*)response.data(), response.size()); code = transport->getResponseCode(); } } else if (error) { code = 500; transport->sendString(errorMsg, 500); m_reset = true; } else { code = 404; transport->sendString("Not Found", 404); } } else { code = 400; transport->sendString("Bad Request", 400); } params.reset(); sourceRootInfo.clear(); transport->onSendEnd(); ServerStats::LogPage(isFile ? rpcFile : rpcFunc, code); m_context->onShutdownPostSend(); // in case postsend/cleanup output something // PHP5 always provides _START. m_context->obClean(k_PHP_OUTPUT_HANDLER_START | k_PHP_OUTPUT_HANDLER_CLEAN | k_PHP_OUTPUT_HANDLER_END); m_context->restoreSession(); return !error; }
bool HttpRequestHandler::executePHPRequest(Transport *transport, RequestURI &reqURI, SourceRootInfo &sourceRootInfo, bool cachableDynamicContent) { ExecutionContext *context = hphp_context_init(); if (RuntimeOption::ImplicitFlush) { context->obSetImplicitFlush(true); } if (RuntimeOption::EnableOutputBuffering) { if (RuntimeOption::OutputHandler.empty()) { context->obStart(); } else { context->obStart(String(RuntimeOption::OutputHandler)); } } context->setTransport(transport); string file = reqURI.absolutePath().c_str(); { ServerStatsHelper ssh("input"); HttpProtocol::PrepareSystemVariables(transport, reqURI, sourceRootInfo); Extension::RequestInitModules(); if (RuntimeOption::EnableDebugger) { Eval::DSandboxInfo sInfo = sourceRootInfo.getSandboxInfo(); Eval::Debugger::RegisterSandbox(sInfo); context->setSandboxId(sInfo.id()); } reqURI.clear(); sourceRootInfo.clear(); } int code; bool ret = true; // Let the debugger initialize. // FIXME: hphpd can be initialized this way as well DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestInitHook()); if (RuntimeOption::EnableDebugger) { Eval::Debugger::InterruptRequestStarted(transport->getUrl()); } bool error = false; std::string errorMsg = "Internal Server Error"; ret = hphp_invoke(context, file, false, Array(), uninit_null(), RuntimeOption::RequestInitFunction, RuntimeOption::RequestInitDocument, error, errorMsg, true /* once */, false /* warmupOnly */, false /* richErrorMessage */); if (ret) { String content = context->obDetachContents(); if (cachableDynamicContent && !content.empty()) { assert(transport->getUrl()); string key = file + transport->getUrl(); DynamicContentCache::TheCache.store(key, content.data(), content.size()); } transport->sendRaw((void*)content.data(), content.size()); code = transport->getResponseCode(); } else if (error) { code = 500; string errorPage = context->getErrorPage().data(); if (errorPage.empty()) { errorPage = RuntimeOption::ErrorDocument500; } if (!errorPage.empty()) { context->obProtect(false); context->obEndAll(); context->obStart(); context->obProtect(true); ret = hphp_invoke(context, errorPage, false, Array(), uninit_null(), RuntimeOption::RequestInitFunction, RuntimeOption::RequestInitDocument, error, errorMsg, true /* once */, false /* warmupOnly */, false /* richErrorMessage */); if (ret) { String content = context->obDetachContents(); transport->sendRaw((void*)content.data(), content.size()); code = transport->getResponseCode(); } else { Logger::Error("Unable to invoke error page %s", errorPage.c_str()); errorPage.clear(); // so we fall back to 500 return } } if (errorPage.empty()) { if (RuntimeOption::ServerErrorMessage) { transport->sendString(errorMsg, 500, false, false, "hphp_invoke"); } else { transport->sendString(RuntimeOption::FatalErrorMessage, 500, false, false, "hphp_invoke"); } } } else { code = 404; transport->sendString("Not Found", 404); } if (RuntimeOption::EnableDebugger) { Eval::Debugger::InterruptRequestEnded(transport->getUrl()); } // If we have registered post-send shutdown functions, end the request before // executing them. If we don't, be compatible with Zend by allowing usercode // in hphp_context_shutdown to run before we end the request. bool hasPostSend = context->hasShutdownFunctions(ExecutionContext::ShutdownType::PostSend); if (hasPostSend) { transport->onSendEnd(); } context->onShutdownPostSend(); Eval::Debugger::InterruptPSPEnded(transport->getUrl()); hphp_context_shutdown(); if (!hasPostSend) { transport->onSendEnd(); } hphp_context_exit(false); ServerStats::LogPage(file, code); return ret; }