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); }
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); }