示例#1
0
void LibEventServer::onResponse(int worker, evhttp_request *request,
                                int code, LibEventTransport *transport) {
  int nwritten = 0;
  bool skip_sync = false;

  if (request->evcon == nullptr) {
    evhttp_request_free(request);
    return;
  }

#ifdef _EVENT_USE_OPENSSL
  skip_sync = evhttp_is_connection_ssl(request->evcon);
#endif

  int totalSize = 0;

  if (RuntimeOption::LibEventSyncSend && !skip_sync) {
    const char *reason = HttpProtocol::GetReasonString(code);
    timespec begin, end;
    Timer::GetMonotonicTime(begin);
#ifdef EVHTTP_SYNC_SEND_REPORT_TOTAL_LEN
    nwritten = evhttp_send_reply_sync(request, code, reason, nullptr, &totalSize);
#else
    nwritten = evhttp_send_reply_sync_begin(request, code, reason, nullptr);
#endif
    Timer::GetMonotonicTime(end);
    int64_t delay = gettime_diff_us(begin, end);
    transport->onFlushBegin(totalSize);
    transport->onFlushProgress(nwritten, delay);
  }
  m_responseQueue.enqueue(worker, request, code, nwritten);
}
示例#2
0
PooledCurlHandle* CurlHandlePool::fetch() {
  struct timespec ts;
  gettime(CLOCK_REALTIME, &ts);
  ts.tv_sec += m_connGetTimeout / 1000;
  ts.tv_nsec += 1000000 * (m_connGetTimeout % 1000);

  Lock lock(m_mutex);
  m_statsFetches += 1;
  // wait until the user-specified timeout for an available handle
  if (m_handleStack.empty()) {
    m_statsEmpty += 1;
    do {
      if (ETIMEDOUT == pthread_cond_timedwait(&m_cond, &m_mutex.getRaw(),
                                              &ts)
      ) {
        m_statsFetchUs += m_connGetTimeout * 1000;
        SystemLib::throwRuntimeExceptionObject(
          "Timeout reached waiting for an available pooled curl connection!");
      }
    } while (m_handleStack.empty());
  }

  PooledCurlHandle* ret = m_handleStack.top();
  assertx(ret);
  m_handleStack.pop();

  struct timespec after;
  gettime(CLOCK_REALTIME, &after);
  m_statsFetchUs += m_connGetTimeout * 1000 - gettime_diff_us(after, ts);

  return ret;
}
// Should be called outside m_lock
void ConcurrentTableSharedStore::purgeExpired() {
  if ((atomic_add(m_purgeCounter, (uint64)1) %
       RuntimeOption::ApcPurgeFrequency) != 0) return;
  time_t now = time(NULL);
  ExpirationPair tmp;
  struct timespec tsBegin, tsEnd;
  gettime(CLOCK_MONOTONIC, &tsBegin);
  int i = 0;
  while (RuntimeOption::ApcPurgeRate < 0 || i < RuntimeOption::ApcPurgeRate) {
    if (!m_expQueue.try_pop(tmp)) {
      break;
    }
    if (tmp.second > now) {
      m_expQueue.push(tmp);
      break;
    }
    if (RuntimeOption::ApcUseFileStorage &&
        strcmp(tmp.first, RuntimeOption::ApcFileStorageFlagKey.c_str()) == 0) {
      s_apc_file_storage.adviseOut();
      addToExpirationQueue(RuntimeOption::ApcFileStorageFlagKey.c_str(),
                           time(NULL) +
                           RuntimeOption::ApcFileStorageAdviseOutPeriod);
      continue;
    }
    m_expMap.erase(tmp.first);
    eraseImpl(tmp.first, true);
    free((void *)tmp.first);
    ++i;
  }
  gettime(CLOCK_MONOTONIC, &tsEnd);
  int64 elapsed = gettime_diff_us(tsBegin, tsEnd);
  SharedStoreStats::addPurgingTime(elapsed);
  // Size could be inaccurate, but for stats reporting, it is good enough
  SharedStoreStats::setExpireQueueSize(m_expQueue.size());
}
// Should be called outside m_lock
void ConcurrentTableSharedStore::purgeExpired() {
  if (m_purgeCounter.fetch_add(1, std::memory_order_relaxed) %
      apcExtension::PurgeFrequency != 0) {
    return;
  }
  time_t now = time(nullptr);
  ExpirationPair tmp;
  struct timespec tsBegin, tsEnd;
  Timer::GetMonotonicTime(tsBegin);
  int i = 0;
  while (apcExtension::PurgeRate < 0 || i < apcExtension::PurgeRate) {
    if (!m_expQueue.try_pop(tmp)) {
      break;
    }
    if (tmp.second > now) {
      m_expQueue.push(tmp);
      break;
    }
    if (apcExtension::UseFileStorage &&
        strcmp(tmp.first, apcExtension::FileStorageFlagKey.c_str()) == 0) {
      s_apc_file_storage.adviseOut();
      addToExpirationQueue(apcExtension::FileStorageFlagKey.c_str(),
                           time(nullptr) +
                           apcExtension::FileStorageAdviseOutPeriod);
      continue;
    }
    m_expMap.erase(tmp.first);
    eraseImpl(tmp.first, true);
    free((void *)tmp.first);
    ++i;
  }
  Timer::GetMonotonicTime(tsEnd);
  int64_t elapsed = gettime_diff_us(tsBegin, tsEnd);
  SharedStoreStats::addPurgingTime(elapsed);
  // Size could be inaccurate, but for stats reporting, it is good enough
  SharedStoreStats::setExpireQueueSize(m_expQueue.size());
}
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);
}