コード例 #1
0
void HttpRequestHandler::setupRequest(Transport* transport) {
  MemoryManager::requestInit();

  g_context.getCheck();
  GetAccessLog().onNewRequest();

  // Set current virtual host.
  HttpProtocol::GetVirtualHost(transport);
}
コード例 #2
0
void AdminRequestHandler::setupRequest(Transport* transport) {
  auto const cmd = transport->getCommand();

  if (strncmp(cmd.c_str(), "dump-apc", 8) == 0) {
    hphp_session_init();
  } else {
    g_context.getCheck();
  }
  GetAccessLog().onNewRequest();
}
コード例 #3
0
void HttpRequestHandler::logToAccessLog(Transport* transport) {
  GetAccessLog().onNewRequest();
  GetAccessLog().log(transport, VirtualHost::GetCurrent());
}
コード例 #4
0
ファイル: admin-request-handler.cpp プロジェクト: skynet/hhvm
void AdminRequestHandler::setupRequest(Transport* transport) {
  g_context.getCheck();
  GetAccessLog().onNewRequest();
}
コード例 #5
0
ファイル: admin-request-handler.cpp プロジェクト: skynet/hhvm
void AdminRequestHandler::logToAccessLog(Transport* transport) {
  GetAccessLog().onNewRequest();
  GetAccessLog().log(transport, nullptr);
}
コード例 #6
0
void AdminRequestHandler::handleRequest(Transport *transport) {
  GetAccessLog().onNewRequest();
  string cmd = transport->getCommand();

  do {
    if (cmd == "" || cmd == "help") {
      string usage =
        "/stop:            stop the web server\n"
        "/translate:       translate hex encoded stacktrace in 'stack' param\n"
        "    stack         required, stack trace to translate\n"
        "    build-id      optional, if specified, build ID has to match\n"
        "    bare          optional, whether to display frame ordinates\n"
        "/build-id:        returns build id that's passed in from command line"
        "\n"
#ifdef COMPILER_ID
        "/compiler-id:     returns the compiler id that built this app\n"
#endif

        "/check-load:      how many threads are actively handling requests\n"
        "/check-queued:    how many http requests are queued waiting to be\n"
        "                  handled\n"
        "/check-mem:       report memory quick statistics in log file\n"
        "/check-apc:       report APC quick statistics\n"
        "/check-sql:       report SQL table statistics\n"

        "/status.xml:      show server status in XML\n"
        "/status.json:     show server status in JSON\n"
        "/status.html:     show server status in HTML\n"

        "/stats-on:        main switch: enable server stats\n"
        "/stats-off:       main switch: disable server stats\n"
        "/stats-clear:     clear all server stats\n"

        "/stats-web:       turn on/off server page stats (CPU and gen time)\n"
        "/stats-mem:       turn on/off memory statistics\n"
        "/stats-apc:       turn on/off APC statistics\n"
        "/stats-apc-key:   turn on/off APC key statistics\n"
        "/stats-mcc:       turn on/off memcache statistics\n"
        "/stats-sql:       turn on/off SQL statistics\n"
        "/stats-mutex:     turn on/off mutex statistics\n"
        "    sampling      optional, default 1000\n"

        "/stats.keys:      list all available keys\n"
        "    from          optional, <timestamp>, or <-n> second ago\n"
        "    to            optional, <timestamp>, or <-n> second ago\n"
        "/stats.xml:       show server stats in XML\n"
        "    from          optional, <timestamp>, or <-n> second ago\n"
        "    to            optional, <timestamp>, or <-n> second ago\n"
        "    agg           optional, aggragation: *, url, code\n"
        "    keys          optional, <key>,<key/hit>,<key/sec>,<:regex:>\n"
        "    url           optional, only stats of this page or URL\n"
        "    code          optional, only stats of pages returning this code\n"
        "/stats.json:      show server stats in JSON\n"
        "    (same as /stats.xml)\n"
        "/stats.kvp:       show server stats in key-value pairs\n"
        "    (same as /stats.xml)\n"
        "/stats.html:      show server stats in HTML\n"
        "    (same as /stats.xml)\n"

        "/apc-ss:          get apc size stats\n"
        "/apc-ss-flat:     get apc size stats in flat format\n"
        "/apc-ss-keys:     get apc size break-down on keys\n"
        "/apc-ss-dump:     dump the size info on each key to /tmp/APC_details\n"
        "                  only valid when EnableAPCSizeDetail is true\n"
        "    keysample     optional, only dump keys that belongs to the same\n"
        "                  group as <keysample>\n"

#ifdef GOOGLE_CPU_PROFILER
        "/prof-cpu-on:     turn on CPU profiler\n"
        "/prof-cpu-off:    turn off CPU profiler\n"
#endif
#ifdef GOOGLE_HEAP_PROFILER
        "/prof-heap-on:    turn on heap profiler\n"
        "/prof-heap-dump:  take one snapshot of the heap\n"
        "/prof-heap-off:   turn off heap profiler\n"
        "/stats-malloc:    turn on/off malloc statistics\n"
        "/leak-on:         start leak detection\n"
        "    sampling      required, frequency\n"
        "/leak-off:        end leak detection and report leaking\n"
        "    cutoff        optional, default 20 seconds, ignore newer allocs\n"
#endif
      ;
#ifndef NO_TCMALLOC
        if (MallocExtensionInstance) {
          usage.append(
              "/free-mem:        ask tcmalloc to release memory to system\n"
              "/tcmalloc-stats:  get internal tcmalloc stats\n"
              );
        }
#endif

#ifndef NO_JEMALLOC
        if (mallctl) {
          usage.append(
              "/jemalloc-stats:  get internal jemalloc stats\n"
              "/jemalloc-stats-print:\n"
              "                  get comprehensive jemalloc stats in\n"
              "                  human-readable form\n"
              "/jemalloc-prof-activate:\n"
              "                  activate heap profiling\n"
              "/jemalloc-prof-deactivate:\n"
              "                  deactivate heap profiling\n"
              "/jemalloc-prof-dump:\n"
              "                  dump heap profile\n"
              "    file          optional, filesystem path\n"
              );
        }
      transport->sendString(usage);
      break;
    }
#endif

    if (!RuntimeOption::AdminPassword.empty() &&
        RuntimeOption::AdminPassword != transport->getParam("auth")) {
      transport->sendString("Unauthorized", 401);
      break;
    }

    if (cmd == "stop") {
      transport->sendString("OK\n");
      HttpServer::Server->stop();
      break;
    }
    if (cmd == "build-id") {
      transport->sendString(RuntimeOption::BuildId, 200);
      break;
    }
#ifdef COMPILER_ID
    if (cmd == "compiler-id") {
      transport->sendString(COMPILER_ID, 200);
      break;
    }
#endif
    if (cmd == "translate") {
      string buildId = transport->getParam("build-id");
      if (!buildId.empty() && buildId != RuntimeOption::BuildId) {
        transport->sendString("Build ID doesn't match.", 500);
        break;
      }

      string translated = translate_stack(transport->getParam("stack").c_str(),
                                          transport->getParam("bare").empty());
      transport->sendString(translated);
      break;
    }
    if (strncmp(cmd.c_str(), "check", 5) == 0 &&
        handleCheckRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "status", 6) == 0 &&
        handleStatusRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "stats", 5) == 0 &&
        handleStatsRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "prof", 4) == 0 &&
        handleProfileRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "leak", 4) == 0 &&
        handleLeakRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "apc-ss", 6) == 0 &&
        handleAPCSizeRequest(cmd, transport)) {
      break;
    }

#ifndef NO_TCMALLOC
    if (MallocExtensionInstance) {
      if (cmd == "free-mem") {
        MallocExtensionInstance()->ReleaseFreeMemory();
        transport->sendString("OK\n");
        break;
      }
      if (cmd == "tcmalloc-stats") {
        ostringstream stats;
        size_t user_allocated, heap_size, slack_bytes;
        MallocExtensionInstance()->
          GetNumericProperty("generic.current_allocated_bytes",
              &user_allocated);
        MallocExtensionInstance()->
          GetNumericProperty("generic.heap_size", &heap_size);
        MallocExtensionInstance()->
          GetNumericProperty("tcmalloc.slack_bytes", &slack_bytes);
        stats << "<tcmalloc-stats>" << endl;
        stats << "  <user_allocated>" << user_allocated << "</user_allocated>"
          << endl;
        stats << "  <heap_size>" << heap_size << "</heap_size>" << endl;
        stats << "  <slack_bytes>" << slack_bytes << "</slack_bytes>" << endl;
        stats << "</tcmalloc-stats>" << endl;
        transport->sendString(stats.str());
        break;
      }
    }
#endif

#ifndef NO_JEMALLOC
    if (mallctl) {
      if (cmd == "jemalloc-stats") {
        // Force jemalloc to update stats cached for use by mallctl().
        uint64_t epoch = 1;
        mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));

        size_t allocated = 0; // Initialize in case stats aren't enabled.
        size_t sz = sizeof(size_t);
        mallctl("stats.allocated", &allocated, &sz, NULL, 0);

        size_t active = 0;
        mallctl("stats.active", &active, &sz, NULL, 0);

        size_t mapped = 0;
        mallctl("stats.mapped", &mapped, &sz, NULL, 0);

        ostringstream stats;
        stats << "<jemalloc-stats>" << endl;
        stats << "  <allocated>" << allocated << "</allocated>" << endl;
        stats << "  <active>" << active << "</active>" << endl;
        stats << "  <mapped>" << mapped << "</mapped>" << endl;
        stats << "</jemalloc-stats>" << endl;
        transport->sendString(stats.str());
        break;
      }
      if (cmd == "jemalloc-stats-print") {
        char *buf = (char *)malloc(MALLOC_WRITE_CB_BUFLEN);
        if (buf == NULL) {
          transport->sendString("OOM\n");
          break;
        }

        buf[0] = '\0';
        malloc_stats_print(malloc_write_cb, (void *)buf, "");
        transport->sendString(buf);
        free(buf);
        break;
      }
      if (cmd == "jemalloc-prof-activate") {
        bool active = true;
        int err = mallctl("prof.active", NULL, NULL, &active, sizeof(bool));
        if (err) {
          ostringstream estr;
          estr << "Error " << err << " in mallctl(\"prof.active\", ...)"
            << endl;
          transport->sendString(estr.str());
        } else {
          transport->sendString("OK\n");
        }
        break;
      }
      if (cmd == "jemalloc-prof-deactivate") {
        bool active = false;
        int err = mallctl("prof.active", NULL, NULL, &active, sizeof(bool));
        if (err) {
          ostringstream estr;
          estr << "Error " << err << " in mallctl(\"prof.active\", ...)"
            << endl;
          transport->sendString(estr.str());
        } else {
          transport->sendString("OK\n");
        }
        break;
      }
      if (cmd == "jemalloc-prof-dump") {
        string f = transport->getParam("file");
        if (f != "") {
          const char *s = f.c_str();
          int err = mallctl("prof.dump", NULL, NULL, (void *)&s,
              sizeof(char *));
          if (err) {
            ostringstream estr;
            estr << "Error " << err << " in mallctl(\"prof.dump\", ..., \"" << f
              << "\", ...)" << endl;
            transport->sendString(estr.str());
            break;
          }
        } else {
          int err = mallctl("prof.dump", NULL, NULL, NULL, 0);
          if (err) {
            ostringstream estr;
            estr << "Error " << err << " in mallctl(\"prof.dump\", ...)"
              << endl;
            transport->sendString(estr.str());
            break;
          }
        }
        transport->sendString("OK\n");
        break;
      }
#endif
    }

    transport->sendString("Unknown command: " + cmd + "\n", 404);
  } while (0);
  GetAccessLog().log(transport);
}
コード例 #7
0
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);
}
コード例 #8
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);
}
コード例 #9
0
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);
}
コード例 #10
0
void AdminRequestHandler::handleRequest(Transport *transport) {
  GetAccessLog().onNewRequest();
  transport->addHeader("Content-Type", "text/plain");
  string cmd = transport->getCommand();

  do {
    if (cmd == "" || cmd == "help") {
      string usage =
        "/stop:            stop the web server\n"
        "/translate:       translate hex encoded stacktrace in 'stack' param\n"
        "    stack         required, stack trace to translate\n"
        "    build-id      optional, if specified, build ID has to match\n"
        "    bare          optional, whether to display frame ordinates\n"
        "/build-id:        returns build id that's passed in from command line"
        "\n"
        "/compiler-id:     returns the compiler id that built this app\n"
        "/repo-schema:     return the repo schema id used by this app\n"
        "/check-load:      how many threads are actively handling requests\n"
        "/check-queued:    how many http requests are queued waiting to be\n"
        "                  handled\n"
        "/check-health:    return json containing basic load/usage stats\n"
        "/check-ev:        how many http requests are active by libevent\n"
        "/check-pl-load:   how many pagelet threads are actively handling\n"
        "                  requests\n"
        "/check-pl-queued: how many pagelet requests are queued waiting to\n"
        "                  be handled\n"
        "/check-mem:       report memory quick statistics in log file\n"
        "/check-sql:       report SQL table statistics\n"

        "/status.xml:      show server status in XML\n"
        "/status.json:     show server status in JSON\n"
        "/status.html:     show server status in HTML\n"

        "/stats-on:        main switch: enable server stats\n"
        "/stats-off:       main switch: disable server stats\n"
        "/stats-clear:     clear all server stats\n"

        "/stats-web:       turn on/off server page stats (CPU and gen time)\n"
        "/stats-mem:       turn on/off memory statistics\n"
        "/stats-apc:       turn on/off APC statistics\n"
        "/stats-apc-key:   turn on/off APC key statistics\n"
        "/stats-mcc:       turn on/off memcache statistics\n"
        "/stats-sql:       turn on/off SQL statistics\n"
        "/stats-mutex:     turn on/off mutex statistics\n"
        "    sampling      optional, default 1000\n"

        "/stats.keys:      list all available keys\n"
        "    from          optional, <timestamp>, or <-n> second ago\n"
        "    to            optional, <timestamp>, or <-n> second ago\n"
        "/stats.xml:       show server stats in XML\n"
        "    from          optional, <timestamp>, or <-n> second ago\n"
        "    to            optional, <timestamp>, or <-n> second ago\n"
        "    agg           optional, aggragation: *, url, code\n"
        "    keys          optional, <key>,<key/hit>,<key/sec>,<:regex:>\n"
        "    url           optional, only stats of this page or URL\n"
        "    code          optional, only stats of pages returning this code\n"
        "/stats.json:      show server stats in JSON\n"
        "    (same as /stats.xml)\n"
        "/stats.kvp:       show server stats in key-value pairs\n"
        "    (same as /stats.xml)\n"
        "/stats.html:      show server stats in HTML\n"
        "    (same as /stats.xml)\n"

        "/apc-ss:          get apc size stats\n"
        "/apc-ss-flat:     get apc size stats in flat format\n"
        "/apc-ss-keys:     get apc size break-down on keys\n"
        "/apc-ss-dump:     dump the size info on each key to /tmp/APC_details\n"
        "                  only valid when EnableAPCSizeDetail is true\n"
        "    keysample     optional, only dump keys that belongs to the same\n"
        "                  group as <keysample>\n"
        "/const-ss:        get const_map_size\n"
        "/static-strings:  get number of static strings\n"
        "/dump-apc:        dump all current value in APC to /tmp/apc_dump\n"
        "/dump-const:      dump all constant value in constant map to\n"
        "                  /tmp/const_map_dump\n"
        "/dump-file-repo:  dump file repository to /tmp/file_repo_dump\n"

        "/pcre-cache-size: get pcre cache map size\n"

#ifdef GOOGLE_CPU_PROFILER
        "/prof-cpu-on:     turn on CPU profiler\n"
        "/prof-cpu-off:    turn off CPU profiler\n"
#endif
#ifdef GOOGLE_HEAP_PROFILER
        "/prof-heap-on:    turn on heap profiler\n"
        "/prof-heap-dump:  take one snapshot of the heap\n"
        "/prof-heap-off:   turn off heap profiler\n"
        "/stats-malloc:    turn on/off malloc statistics\n"
        "/leak-on:         start leak detection\n"
        "    sampling      required, frequency\n"
        "/leak-off:        end leak detection and report leaking\n"
        "    cutoff        optional, default 20 seconds, ignore newer allocs\n"
#endif
#ifdef EXECUTION_PROFILER
        "/prof-exe:        returns sampled execution profile\n"
#endif
        "/vm-tcspace:      show space used by translator caches\n"
        "/vm-dump-tc:      dump translation cache to /tmp/tc_dump_a and\n"
        "                  /tmp/tc_dump_astub\n"
        "/vm-tcreset:      throw away translations and start over\n"
        "/vm-namedentities:show size of the NamedEntityTable\n"
        ;
#ifdef USE_TCMALLOC
        if (MallocExtensionInstance) {
          usage.append(
              "/free-mem:        ask tcmalloc to release memory to system\n"
              "/tcmalloc-stats:  get internal tcmalloc stats\n"
              "/tcmalloc-set-tc: set max mem tcmalloc thread-cache can use\n"
              );
        }
#endif

#ifdef USE_JEMALLOC
        if (mallctl) {
          usage.append(
              "/jemalloc-stats:  get internal jemalloc stats\n"
              "/jemalloc-stats-print:\n"
              "                  get comprehensive jemalloc stats in\n"
              "                  human-readable form\n"
              "/jemalloc-prof-activate:\n"
              "                  activate heap profiling\n"
              "/jemalloc-prof-deactivate:\n"
              "                  deactivate heap profiling\n"
              "/jemalloc-prof-dump:\n"
              "                  dump heap profile\n"
              "    file          optional, filesystem path\n"
              );
        }
#endif

      transport->sendString(usage);
      break;
    }

    if (!RuntimeOption::AdminPasswords.empty()) {
      std::set<std::string>::const_iterator iter =
        RuntimeOption::AdminPasswords.find(transport->getParam("auth"));
      if (iter == RuntimeOption::AdminPasswords.end()) {
        transport->sendString("Unauthorized", 401);
        break;
      }
    } else {
      if (!RuntimeOption::AdminPassword.empty() &&
          RuntimeOption::AdminPassword != transport->getParam("auth")) {
        transport->sendString("Unauthorized", 401);
        break;
      }
    }

    if (cmd == "stop") {
      transport->sendString("OK\n");
      Logger::Info("Got admin port stop request from %s",
                   transport->getRemoteHost());
      HttpServer::Server->stop();
      break;
    }
    if (cmd == "build-id") {
      transport->sendString(RuntimeOption::BuildId, 200);
      break;
    }
    if (cmd == "compiler-id") {
      transport->sendString(kCompilerId, 200);
      break;
    }
    if (cmd == "repo-schema") {
      transport->sendString(kRepoSchemaId, 200);
      break;
    }
    if (cmd == "translate") {
      string buildId = transport->getParam("build-id");
      if (!buildId.empty() && buildId != RuntimeOption::BuildId) {
        transport->sendString("Build ID doesn't match.", 500);
        break;
      }

      string translated = translate_stack(transport->getParam("stack").c_str(),
                                          transport->getParam("bare").empty());
      transport->sendString(translated);
      break;
    }
    if (strncmp(cmd.c_str(), "check", 5) == 0 &&
        handleCheckRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "status", 6) == 0 &&
        handleStatusRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "stats", 5) == 0 &&
        handleStatsRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "prof", 4) == 0 &&
        handleProfileRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "leak", 4) == 0 &&
        handleLeakRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "apc-ss", 6) == 0 &&
        handleAPCSizeRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "dump", 4) == 0 &&
        handleDumpCacheRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "const-ss", 8) == 0 &&
        handleConstSizeRequest(cmd, transport)) {
      break;
    }
    if (strcmp(cmd.c_str(), "static-strings") == 0 &&
        handleStaticStringsRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "vm-", 3) == 0 &&
        handleVMRequest(cmd, transport)) {
      break;
    }

    if (cmd == "pcre-cache-size") {
      std::ostringstream size;
      size << preg_pcre_cache_size() << endl;
      transport->sendString(size.str());
      break;
    }

#ifdef USE_TCMALLOC
    if (MallocExtensionInstance) {
      if (cmd == "free-mem") {
        MallocExtensionInstance()->ReleaseFreeMemory();
        transport->sendString("OK\n");
        break;
      }
      if (cmd == "tcmalloc-stats") {
        std::ostringstream stats;
        size_t user_allocated, heap_size, slack_bytes;
        size_t pageheap_free, pageheap_unmapped;
        size_t tc_max, tc_allocated;

        MallocExtensionInstance()->
          GetNumericProperty("generic.current_allocated_bytes",
              &user_allocated);
        MallocExtensionInstance()->
          GetNumericProperty("generic.heap_size", &heap_size);
        MallocExtensionInstance()->
          GetNumericProperty("tcmalloc.slack_bytes", &slack_bytes);
        MallocExtensionInstance()->
          GetNumericProperty("tcmalloc.pageheap_free_bytes", &pageheap_free);
        MallocExtensionInstance()->
          GetNumericProperty("tcmalloc.pageheap_unmapped_bytes",
              &pageheap_unmapped);
        MallocExtensionInstance()->
          GetNumericProperty("tcmalloc.max_total_thread_cache_bytes",
              &tc_max);
        MallocExtensionInstance()->
          GetNumericProperty("tcmalloc.current_total_thread_cache_bytes",
              &tc_allocated);
        stats << "<tcmalloc-stats>" << endl;
        stats << "  <user_allocated>" << user_allocated << "</user_allocated>"
          << endl;
        stats << "  <heap_size>" << heap_size << "</heap_size>" << endl;
        stats << "  <slack_bytes>" << slack_bytes << "</slack_bytes>" << endl;
        stats << "  <pageheap_free>" << pageheap_free
          << "</pageheap_free>" << endl;
        stats << "  <pageheap_unmapped>" << pageheap_unmapped
          << "</pageheap_unmapped>" << endl;
        stats << "  <thread_cache_max>" << tc_max
          << "</thread_cache_max>" << endl;
        stats << "  <thread_cache_allocated>" << tc_allocated
          << "</thread_cache_allocated>" << endl;
        stats << "</tcmalloc-stats>" << endl;
        transport->sendString(stats.str());
        break;
      }
      if (cmd == "tcmalloc-set-tc") {
        size_t tc_max;

        MallocExtensionInstance()->
          GetNumericProperty("tcmalloc.max_total_thread_cache_bytes", &tc_max);

        size_t tcache = transport->getInt64Param("s");
        bool retval = MallocExtensionInstance()->
          SetNumericProperty("tcmalloc.max_total_thread_cache_bytes", tcache);
        transport->sendString(retval == true ? "OK\n" : "FAILED\n");
        break;
      }
    }
#endif

#ifdef USE_JEMALLOC
    if (mallctl) {
      if (cmd == "free-mem") {
        // Purge all dirty unused pages.
        int err = mallctl("arenas.purge", nullptr, nullptr, nullptr, 0);
        if (err) {
          std::ostringstream estr;
          estr << "Error " << err << " in mallctl(\"arenas.purge\", ...)"
            << endl;
          transport->sendString(estr.str());
        } else {
          transport->sendString("OK\n");
        }
        break;
      }
      if (cmd == "jemalloc-stats") {
        // Force jemalloc to update stats cached for use by mallctl().
        uint64_t epoch = 1;
        mallctl("epoch", nullptr, nullptr, &epoch, sizeof(epoch));

        size_t allocated = 0; // Initialize in case stats aren't enabled.
        size_t sz = sizeof(size_t);
        mallctl("stats.allocated", &allocated, &sz, nullptr, 0);

        size_t active = 0;
        mallctl("stats.active", &active, &sz, nullptr, 0);

        size_t mapped = 0;
        mallctl("stats.mapped", &mapped, &sz, nullptr, 0);

        std::ostringstream stats;
        stats << "<jemalloc-stats>" << endl;
        stats << "  <allocated>" << allocated << "</allocated>" << endl;
        stats << "  <active>" << active << "</active>" << endl;
        stats << "  <mapped>" << mapped << "</mapped>" << endl;
        stats << "</jemalloc-stats>" << endl;
        transport->sendString(stats.str());
        break;
      }
      if (cmd == "jemalloc-stats-print") {
        malloc_write mwo;

        malloc_write_init(&mwo);
        malloc_stats_print(malloc_write_cb, (void *)&mwo, "");
        if (mwo.oom) {
          malloc_write_fini(&mwo);
          transport->sendString("OOM\n");
          break;
        }

        transport->sendString(mwo.s);
        malloc_write_fini(&mwo);
        break;
      }
      if (cmd == "jemalloc-prof-activate") {
        bool active = true;
        int err = mallctl("prof.active", nullptr, nullptr, &active, sizeof(bool));
        if (err) {
          std::ostringstream estr;
          estr << "Error " << err << " in mallctl(\"prof.active\", ...)"
            << endl;
          transport->sendString(estr.str());
        } else {
          transport->sendString("OK\n");
        }
        break;
      }
      if (cmd == "jemalloc-prof-deactivate") {
        bool active = false;
        int err = mallctl("prof.active", nullptr, nullptr, &active, sizeof(bool));
        if (err) {
          std::ostringstream estr;
          estr << "Error " << err << " in mallctl(\"prof.active\", ...)"
            << endl;
          transport->sendString(estr.str());
        } else {
          transport->sendString("OK\n");
        }
        break;
      }
      if (cmd == "jemalloc-prof-dump") {
        string f = transport->getParam("file");
        if (f != "") {
          const char *s = f.c_str();
          int err = mallctl("prof.dump", nullptr, nullptr, (void *)&s,
              sizeof(char *));
          if (err) {
            std::ostringstream estr;
            estr << "Error " << err << " in mallctl(\"prof.dump\", ..., \"" << f
              << "\", ...)" << endl;
            transport->sendString(estr.str());
            break;
          }
        } else {
          int err = mallctl("prof.dump", nullptr, nullptr, nullptr, 0);
          if (err) {
            std::ostringstream estr;
            estr << "Error " << err << " in mallctl(\"prof.dump\", ...)"
              << endl;
            transport->sendString(estr.str());
            break;
          }
        }
        transport->sendString("OK\n");
        break;
      }
    }
#endif

    transport->sendString("Unknown command: " + cmd + "\n", 404);
  } while (0);
  GetAccessLog().log(transport, nullptr);
}