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;
}
void HttpRequestHandler::handleRequest(Transport *transport) {
  ExecutionProfiler ep(ThreadInfo::RuntimeFunctions);

  Logger::OnNewRequest();
  GetAccessLog().onNewRequest();
  transport->enableCompression();

  ServerStatsHelper ssh("all", ServerStatsHelper::TRACK_MEMORY);
  Logger::Verbose("receiving %s", transport->getCommand().c_str());

  // will clear all extra logging when this function goes out of scope
  StackTraceNoHeap::ExtraLoggingClearer clearer;
  StackTraceNoHeap::AddExtraLogging("URL", transport->getUrl());

  // resolve virtual host
  const VirtualHost *vhost = HttpProtocol::GetVirtualHost(transport);
  assert(vhost);
  if (vhost->disabled() ||
      vhost->isBlocking(transport->getCommand(), transport->getRemoteHost())) {
    transport->sendString("Not Found", 404);
    return;
  }

  // don't serve the request if it's been sitting in queue for longer than our
  // allowed request timeout.
  int requestTimeoutSeconds =
    vhost->getRequestTimeoutSeconds(getDefaultTimeout());
  if (requestTimeoutSeconds > 0) {
    timespec now;
    Timer::GetMonotonicTime(now);
    const timespec& queueTime = transport->getQueueTime();

    if (gettime_diff_us(queueTime, now) > requestTimeoutSeconds * 1000000) {
      transport->sendString("Service Unavailable", 503);
      m_requestTimedOutOnQueue->addValue(1);
      return;
    }
  }

  ServerStats::StartRequest(transport->getCommand().c_str(),
                            transport->getRemoteHost(),
                            vhost->getName().c_str());

  // resolve source root
  string host = transport->getHeader("Host");
  SourceRootInfo sourceRootInfo(host.c_str());

  if (sourceRootInfo.error()) {
    sourceRootInfo.handleError(transport);
    return;
  }

  // request URI
  string pathTranslation = m_pathTranslation ?
    vhost->getPathTranslation().c_str() : "";
  RequestURI reqURI(vhost, transport, sourceRootInfo.path(), pathTranslation);
  if (reqURI.done()) {
    return; // already handled with redirection or 404
  }
  string path = reqURI.path().data();
  string absPath = reqURI.absolutePath().data();

  // determine whether we should compress response
  bool compressed = transport->decideCompression();

  const char *data; int len;
  const char *ext = reqURI.ext();

  if (reqURI.forbidden()) {
    transport->sendString("Forbidden", 403);
    return;
  }

  bool cachableDynamicContent =
    (!RuntimeOption::StaticFileGenerators.empty() &&
     RuntimeOption::StaticFileGenerators.find(path) !=
     RuntimeOption::StaticFileGenerators.end());

  // If this is not a php file, check the static and dynamic content caches
  if (ext && strcasecmp(ext, "php") != 0) {
    if (RuntimeOption::EnableStaticContentCache) {
      bool original = compressed;
      // check against static content cache
      if (StaticContentCache::TheCache.find(path, data, len, compressed)) {
        Util::ScopedMem decompressed_data;
        // (qigao) not calling stat at this point because the timestamp of
        // local cache file is not valuable, maybe misleading. This way
        // the Last-Modified header will not show in response.
        // stat(RuntimeOption::FileCache.c_str(), &st);
        if (!original && compressed) {
          data = gzdecode(data, len);
          if (data == nullptr) {
            throw FatalErrorException("cannot unzip compressed data");
          }
          decompressed_data = const_cast<char*>(data);
          compressed = false;
        }
        sendStaticContent(transport, data, len, 0, compressed, path, ext);
        StaticContentCache::TheFileCache->adviseOutMemory();
        ServerStats::LogPage(path, 200);
        GetAccessLog().log(transport, vhost);
        return;
      }
    }

    if (RuntimeOption::EnableStaticContentFromDisk) {
      String translated = File::TranslatePath(String(absPath));
      if (!translated.empty()) {
        CstrBuffer sb(translated.data());
        if (sb.valid()) {
          struct stat st;
          st.st_mtime = 0;
          stat(translated.data(), &st);
          sendStaticContent(transport, sb.data(), sb.size(), st.st_mtime,
                            false, path, ext);
          ServerStats::LogPage(path, 200);
          GetAccessLog().log(transport, vhost);
          return;
        }
      }
    }

    // check static contents that were generated by dynamic pages
    if (cachableDynamicContent) {
      // check against dynamic content cache
      assert(transport->getUrl());
      string key = path + transport->getUrl();
      if (DynamicContentCache::TheCache.find(key, data, len, compressed)) {
        sendStaticContent(transport, data, len, 0, compressed, path, ext);
        ServerStats::LogPage(path, 200);
        GetAccessLog().log(transport, vhost);
        return;
      }
    }
  }

  // proxy any URLs that not specified in ServeURLs
  if (!RuntimeOption::ProxyOrigin.empty() &&
      ((RuntimeOption::UseServeURLs &&
        RuntimeOption::ServeURLs.find(path) ==
        RuntimeOption::ServeURLs.end()) ||
       (RuntimeOption::UseProxyURLs &&
        (RuntimeOption::ProxyURLs.find(path) !=
         RuntimeOption::ProxyURLs.end() ||
         MatchAnyPattern(path, RuntimeOption::ProxyPatterns) ||
         (abs(rand()) % 100) < RuntimeOption::ProxyPercentage)))) {
    for (int i = 0; i < RuntimeOption::ProxyRetry; i++) {
      bool force = (i == RuntimeOption::ProxyRetry - 1); // last one
      if (handleProxyRequest(transport, force)) break;
    }
    return;
  }

  // record request for debugging purpose
  std::string tmpfile = HttpProtocol::RecordRequest(transport);

  // main body
  hphp_session_init();
  ThreadInfo::s_threadInfo->m_reqInjectionData.
    setTimeout(requestTimeoutSeconds);

  bool ret = false;
  try {
    ret = executePHPRequest(transport, reqURI, sourceRootInfo,
                            cachableDynamicContent);
  } catch (const Eval::DebuggerException &e) {
    transport->sendString(e.what(), 200);
    transport->onSendEnd();
    hphp_context_exit(g_context.getNoCheck(), true, true, transport->getUrl());
  } catch (...) {
    Logger::Error("Unhandled exception in HPHP server engine.");
  }
  GetAccessLog().log(transport, vhost);
  /*
   * HPHP logs may need to access data in ServerStats, so we have to
   * clear the hashtable after writing the log entry.
   */
  ServerStats::Reset();
  hphp_session_exit();

  HttpProtocol::ClearRecord(ret, tmpfile);
}
예제 #3
0
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;
}
예제 #4
0
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;
}
예제 #5
0
void HttpRequestHandler::handleRequest(Transport *transport) {
  Logger::OnNewRequest();
  GetAccessLog().onNewRequest();
  transport->enableCompression();

  Logger::Verbose("receiving %s", transport->getCommand().c_str());
  ServerStatsHelper ssh("all", true);

  // resolve virtual host
  const VirtualHost *vhost = HttpProtocol::GetVirtualHost(transport);
  ASSERT(vhost);
  if (vhost->disabled() ||
      vhost->isBlocking(transport->getCommand(), transport->getRemoteHost())) {
    transport->sendString("Not Found", 404);
    return;
  }
  ServerStats::StartRequest(transport->getCommand().c_str(),
                            transport->getRemoteHost(),
                            vhost->getName().c_str());

  // resolve source root
  string host = transport->getHeader("Host");
  SourceRootInfo sourceRootInfo(host.c_str());

  if (sourceRootInfo.error()) {
    sourceRootInfo.handleError(transport);
    return;
  }

  // request URI
  string pathTranslation = m_pathTranslation ?
    vhost->getPathTranslation().c_str() : "";
  RequestURI reqURI(vhost, transport, sourceRootInfo.path(), pathTranslation);
  if (reqURI.done()) {
    return; // already handled with redirection or 404
  }
  string path = reqURI.path().data();
  string absPath = reqURI.absolutePath().data();

  bool compressed = transport->acceptEncoding("gzip");
  const char *data; int len;
  size_t pos = path.rfind('.');
  const char *ext = (pos != string::npos) ? (path.c_str() + pos + 1) : NULL;
  bool cachableDynamicContent =
    (!RuntimeOption::StaticFileGenerators.empty() &&
     RuntimeOption::StaticFileGenerators.find(path) !=
     RuntimeOption::StaticFileGenerators.end());

  // If this is not a php file, check the static and dynamic content caches
  if (ext && strcasecmp(ext, "php") != 0) {
    if (RuntimeOption::EnableStaticContentCache) {
      // check against static content cache
      if (StaticContentCache::TheCache.find(path, data, len, compressed)) {
        struct stat st;
        st.st_mtime = 0;
        // (qigao) not calling stat at this point because the timestamp of
        // local cache file is not valuable, maybe misleading. This way
        // the Last-Modified header will not show in response.
        // stat(RuntimeOption::FileCache.c_str(), &st);
        sendStaticContent(transport, data, len, st.st_mtime, compressed, path);
        ServerStats::LogPage(path, 200);
        return;
      }
    }

    if (RuntimeOption::EnableStaticContentFromDisk &&
        RuntimeOption::StaticFileExtensions.find(ext) !=
        RuntimeOption::StaticFileExtensions.end()) {
      String translated = File::TranslatePath(String(absPath));
      if (!translated.empty()) {
        StringBuffer sb(translated.data());
        if (sb.valid()) {
          struct stat st;
          st.st_mtime = 0;
          stat(translated.data(), &st);
          sendStaticContent(transport, sb.data(), sb.size(), st.st_mtime,
                            false, path);
          ServerStats::LogPage(path, 200);
          return;
        }
      }
    }

    // check static contents that were generated by dynamic pages
    if (cachableDynamicContent) {
      // check against dynamic content cache
      ASSERT(transport->getUrl());
      string key = path + transport->getUrl();
      if (DynamicContentCache::TheCache.find(key, data, len, compressed)) {
        sendStaticContent(transport, data, len, 0, compressed, path);
        ServerStats::LogPage(path, 200);
        return;
      }
    }
  }

  // proxy any URLs that not specified in ServeURLs
  if (!RuntimeOption::ProxyOrigin.empty() &&
      ((RuntimeOption::UseServeURLs &&
        RuntimeOption::ServeURLs.find(path) ==
        RuntimeOption::ServeURLs.end()) ||
       (RuntimeOption::UseProxyURLs &&
        (RuntimeOption::ProxyURLs.find(path) !=
         RuntimeOption::ProxyURLs.end() ||
         MatchAnyPattern(path, RuntimeOption::ProxyPatterns) ||
         (abs(rand()) % 100) < RuntimeOption::ProxyPercentage)))) {
    for (int i = 0; i < RuntimeOption::ProxyRetry; i++) {
      bool force = (i == RuntimeOption::ProxyRetry - 1); // last one
      if (handleProxyRequest(transport, force)) break;
    }
    return;
  }

  // record request for debugging purpose
  std::string tmpfile = HttpProtocol::RecordRequest(transport);

  // main body
  hphp_session_init();

  bool ret = false;
  try {
    ret = executePHPRequest(transport, reqURI, sourceRootInfo,
                            cachableDynamicContent);
  } catch (...) {
    Logger::Error("Unhandled exception in HPHP server engine.");
  }
  GetAccessLog().log(transport);
  hphp_session_exit();

  HttpProtocol::ClearRecord(ret, tmpfile);
}
void HttpRequestHandler::handleRequest(Transport *transport) {
  ExecutionProfiler ep(ThreadInfo::RuntimeFunctions);

  Logger::OnNewRequest();
  GetAccessLog().onNewRequest();
  transport->enableCompression();

  ServerStatsHelper ssh("all", true);
  Logger::Verbose("receiving %s", transport->getCommand().c_str());

  // will clear all extra logging when this function goes out of scope
  StackTraceNoHeap::ExtraLoggingClearer clearer;
  StackTraceNoHeap::AddExtraLogging("URL", transport->getUrl());

  // resolve virtual host
  const VirtualHost *vhost = HttpProtocol::GetVirtualHost(transport);
  ASSERT(vhost);
  if (vhost->disabled() ||
      vhost->isBlocking(transport->getCommand(), transport->getRemoteHost())) {
    transport->sendString("Not Found", 404);
    return;
  }
  ServerStats::StartRequest(transport->getCommand().c_str(),
                            transport->getRemoteHost(),
                            vhost->getName().c_str());

  // resolve source root
  string host = transport->getHeader("Host");
  SourceRootInfo sourceRootInfo(host.c_str());

  if (sourceRootInfo.error()) {
    sourceRootInfo.handleError(transport);
    return;
  }

  // request URI
  string pathTranslation = m_pathTranslation ?
    vhost->getPathTranslation().c_str() : "";
  RequestURI reqURI(vhost, transport, sourceRootInfo.path(), pathTranslation);
  if (reqURI.done()) {
    return; // already handled with redirection or 404
  }
  string path = reqURI.path().data();
  string absPath = reqURI.absolutePath().data();

  // determine whether we should compress response
  bool compressed = transport->decideCompression();

  const char *data; int len;
  size_t pos = path.rfind('.');
  const char *ext =
    (pos != string::npos) &&
    path.find('/', pos) == string::npos // no extention in ./foo or ../bar
      ? (path.c_str() + pos + 1) : NULL;
  bool cachableDynamicContent =
    (!RuntimeOption::StaticFileGenerators.empty() &&
     RuntimeOption::StaticFileGenerators.find(path) !=
     RuntimeOption::StaticFileGenerators.end());

  // If this is not a php file, check the static and dynamic content caches
  if (ext && strcasecmp(ext, "php") != 0) {
    if (RuntimeOption::EnableStaticContentCache) {
      bool original = compressed;
      // check against static content cache
      if (StaticContentCache::TheCache.find(path, data, len, compressed)) {
        struct stat st;
        st.st_mtime = 0;
        String str;
        // (qigao) not calling stat at this point because the timestamp of
        // local cache file is not valuable, maybe misleading. This way
        // the Last-Modified header will not show in response.
        // stat(RuntimeOption::FileCache.c_str(), &st);
        if (!original && compressed) {
          data = gzdecode(data, len);
          if (data == NULL) {
            throw FatalErrorException("cannot unzip compressed data");
          }
          compressed = false;
          str = NEW(StringData)(data, len, AttachString);
        }
        sendStaticContent(transport, data, len, st.st_mtime, compressed, path);
        StaticContentCache::TheFileCache->adviseOutMemory();
        ServerStats::LogPage(path, 200);
        return;
      }
    }

    if (RuntimeOption::EnableStaticContentFromDisk) {
      String translated = File::TranslatePath(String(absPath));
      if (!translated.empty()) {
        StringBuffer sb(translated.data());
        if (sb.valid()) {
          struct stat st;
          st.st_mtime = 0;
          stat(translated.data(), &st);
          sendStaticContent(transport, sb.data(), sb.size(), st.st_mtime,
                            false, path);
          ServerStats::LogPage(path, 200);
          return;
        }
      }
    }

    // check static contents that were generated by dynamic pages
    if (cachableDynamicContent) {
      // check against dynamic content cache
      ASSERT(transport->getUrl());
      string key = path + transport->getUrl();
      if (DynamicContentCache::TheCache.find(key, data, len, compressed)) {
        sendStaticContent(transport, data, len, 0, compressed, path);
        ServerStats::LogPage(path, 200);
        return;
      }
    }
  }

  // proxy any URLs that not specified in ServeURLs
  if (!RuntimeOption::ProxyOrigin.empty() &&
      ((RuntimeOption::UseServeURLs &&
        RuntimeOption::ServeURLs.find(path) ==
        RuntimeOption::ServeURLs.end()) ||
       (RuntimeOption::UseProxyURLs &&
        (RuntimeOption::ProxyURLs.find(path) !=
         RuntimeOption::ProxyURLs.end() ||
         MatchAnyPattern(path, RuntimeOption::ProxyPatterns) ||
         (abs(rand()) % 100) < RuntimeOption::ProxyPercentage)))) {
    for (int i = 0; i < RuntimeOption::ProxyRetry; i++) {
      bool force = (i == RuntimeOption::ProxyRetry - 1); // last one
      if (handleProxyRequest(transport, force)) break;
    }
    return;
  }

  // record request for debugging purpose
  std::string tmpfile = HttpProtocol::RecordRequest(transport);

  // main body
  hphp_session_init();
  vhost->setRequestTimeoutSeconds();

  bool ret = false;
  try {
    ret = executePHPRequest(transport, reqURI, sourceRootInfo,
                            cachableDynamicContent);
  } catch (const Eval::DebuggerException &e) {
    transport->sendString(e.what(), 200);
    transport->onSendEnd();
    hphp_context_exit(g_context.getNoCheck(), true, true, transport->getUrl());
  } catch (...) {
    Logger::Error("Unhandled exception in HPHP server engine.");
  }
  GetAccessLog().log(transport, vhost);
  hphp_session_exit();

  HttpProtocol::ClearRecord(ret, tmpfile);
}