void McRequest::Keys::update(folly::StringPiece key) { keyWithoutRoute = key; if (!key.empty()) { if (*key.begin() == '/') { size_t pos = 1; for (int i = 0; i < 2; ++i) { pos = key.find('/', pos); if (pos == std::string::npos) { break; } ++pos; } if (pos != std::string::npos) { keyWithoutRoute.advance(pos); routingPrefix.reset(key.begin(), pos); } } } routingKey = keyWithoutRoute; size_t pos = keyWithoutRoute.find("|#|"); if (pos != std::string::npos) { routingKey.reset(keyWithoutRoute.begin(), pos); } routingKeyHash = getMemcacheKeyHashValue(routingKey); }
const std::vector<McrouterRouteHandlePtr>* RouteHandleMap::getTargetsForKeyFast(folly::StringPiece prefix, folly::StringPiece key) const { const std::vector<McrouterRouteHandlePtr>* result = nullptr; if (prefix.empty()) { // empty prefix => route to default route result = &defaultRouteMap_->getTargetsForKey(key); } else if (prefix == "/*/*/") { // route to all routes result = &allRoutes_->getTargetsForKey(key); } else { auto starPos = prefix.find("*"); if (starPos == std::string::npos) { // no stars at all auto it = byRoute_.find(prefix); result = it == byRoute_.end() ? &emptyV_ : &it->second->getTargetsForKey(key); } else if (prefix.endsWith("/*/") && starPos == prefix.size() - 2) { // route to all clusters of some region (/region/*/) auto region = prefix.subpiece(1, prefix.size() - 4); auto it = byRegion_.find(region); result = it == byRegion_.end() ? &emptyV_ : &it->second->getTargetsForKey(key); } } if (sendInvalidRouteToDefault_ && result != nullptr && result->empty()) { return &defaultRouteMap_->getTargetsForKey(key); } return result; }
const std::vector<McrouterRouteHandlePtr>* RouteHandleMap::getTargetsForKeyFast(folly::StringPiece prefix, folly::StringPiece key) const { // empty prefix => route to default route if (prefix.empty()) { return &getBySingleRoute(defaultRoute_, key); } // route to all routes if (prefix == "/*/*/") { return &allRoutes_->getTargetsForKey(key); } auto starPos = prefix.find("*"); if (starPos == std::string::npos) { // no stars at all return &getBySingleRoute(prefix, key); } if (prefix.endsWith("/*/") && starPos == prefix.size() - 2) { // route to all clusters of some region (/region/*/) auto region = prefix.subpiece(1, prefix.size() - 4); auto it = byRegion_.find(region); if (it == byRegion_.end()) { return &emptyV_; } return &it->second->getTargetsForKey(key); } // no other types supported return nullptr; }
// Starts tracing using the given profiler static void start_tracing(XDebugProfiler* profiler, folly::StringPiece filename = folly::StringPiece(), int64_t options = 0) { // Add ini settings if (XDEBUG_GLOBAL(TraceOptions)) { options |= k_XDEBUG_TRACE_APPEND; } if (XDEBUG_GLOBAL(TraceFormat) == 1) { options |= k_XDEBUG_TRACE_COMPUTERIZED; } if (XDEBUG_GLOBAL(TraceFormat) == 2) { options |= k_XDEBUG_TRACE_HTML; } // If no filename is passed, php5 xdebug stores in the default output // directory with the default file name. folly::StringPiece dirname; if (filename.empty()) { auto& default_dirname = XDEBUG_GLOBAL(TraceOutputDir); auto& default_filename = XDEBUG_GLOBAL(TraceOutputName); dirname = folly::StringPiece(default_dirname); filename = folly::StringPiece(default_filename); } auto const suffix = !(options & k_XDEBUG_TRACE_NAKED_FILENAME); auto abs_filename = format_filename(dirname, filename, suffix); profiler->enableTracing(abs_filename, options); }
void Keys::update(folly::StringPiece key) { keyWithoutRoute_ = key; if (!key.empty()) { if (*key.begin() == '/') { size_t pos = 1; for (int i = 0; i < 2; ++i) { pos = key.find('/', pos); if (pos == std::string::npos) { break; } ++pos; } if (pos != std::string::npos) { keyWithoutRoute_.advance(pos); routingPrefix_.reset(key.begin(), pos); } } } routingKey_ = keyWithoutRoute_; size_t pos = keyWithoutRoute_.find("|#|"); if (pos != std::string::npos) { routingKey_.reset(keyWithoutRoute_.begin(), pos); } routingKeyHash_ = 0; }
std::vector<std::string> LoggerDB::processConfigString( folly::StringPiece config) { std::vector<std::string> errors; if (config.empty()) { return errors; } std::vector<StringPiece> pieces; folly::split(",", config, pieces); for (const auto& p : pieces) { auto idx = p.rfind('='); if (idx == folly::StringPiece::npos) { errors.emplace_back( folly::sformat("missing '=' in logger configuration: \"{}\"", p)); continue; } auto category = p.subpiece(0, idx); auto level_str = p.subpiece(idx + 1); LogLevel level; try { level = stringToLogLevel(level_str); } catch (const std::exception&) { errors.emplace_back(folly::sformat( "invalid log level \"{}\" for category \"{}\"", level_str, category)); continue; } setLevel(category, level); } return errors; }
// Simplify a path -- as much as we can while not moving data around... void simplifyPath(folly::StringPiece& sp) { // Strip leading slashes and useless patterns (./), leaving one initial // slash. for (;;) { if (sp.empty()) { return; } // Strip leading slashes, leaving one. while (sp.startsWith("//")) { sp.advance(1); } if (sp.startsWith("/./")) { // Note 2, not 3, to keep it absolute sp.advance(2); continue; } if (sp.removePrefix("./")) { // Also remove any subsequent slashes to avoid making this path absolute. while (sp.startsWith('/')) { sp.advance(1); } continue; } break; } // Strip trailing slashes and useless patterns (/.). for (;;) { if (sp.empty()) { return; } // Strip trailing slashes, except when this is the root path. while (sp.size() > 1 && sp.removeSuffix('/')) { } if (sp.removeSuffix("/.")) { continue; } break; } }
// Simplify a path -- as much as we can while not moving data around... void simplifyPath(folly::StringPiece& sp) { // Strip leading slashes and useless patterns (./), leaving one initial // slash. for (;;) { if (sp.empty()) { return; } // Strip leading slashes, leaving one. while (sp.startsWith("//")) { sp.advance(1); } if (sp.startsWith("/./")) { // Note 2, not 3, to keep it absolute sp.advance(2); continue; } if (sp.removePrefix("./")) { continue; } break; } // Strip trailing slashes and useless patterns (/.). for (;;) { if (sp.empty()) { return; } // Strip trailing slashes while (sp.removeSuffix('/')) { } if (sp.removeSuffix("/.")) { continue; } break; } }
void RouteHandleMap::foreachRoutePolicy(folly::StringPiece prefix, std::function<void(const std::shared_ptr<RoutePolicyMap>&)> f) const { // if no route is provided or the default route matches the glob // then stick at the start so that we always send to the local cluster first if (prefix.empty() || match_pattern_route(prefix, defaultRoute_)) { auto it = byRoute_.find(defaultRoute_); if (it != byRoute_.end()) { f(it->second); } } if (prefix.empty()) { return; } bool selectAll = (prefix == "/*/*/"); for (const auto& it : byRoute_) { if (it.first != defaultRoute_.str() && (selectAll || match_pattern_route(prefix, it.first))) { f(it.second); } } }
std::shared_ptr<AccessPoint> AccessPoint::create(folly::StringPiece apString, mc_protocol_t defaultProtocol, bool defaultUseSsl, uint16_t portOverride) { if (apString.empty()) { return nullptr; } folly::StringPiece host; if (apString[0] == '[') { // IPv6 auto closing = apString.find(']'); if (closing == std::string::npos) { return nullptr; } host = apString.subpiece(1, closing - 1); apString.advance(closing + 1); } else { // IPv4 or hostname auto colon = apString.find(':'); if (colon == std::string::npos) { host = apString; apString = ""; } else { host = apString.subpiece(0, colon); apString.advance(colon); } } if (host.empty()) { return nullptr; } try { folly::StringPiece port, protocol, encr; parseParts(apString, port, protocol, encr); return std::make_shared<AccessPoint>( host, portOverride != 0 ? portOverride : folly::to<uint16_t>(port), protocol.empty() ? defaultProtocol : parseProtocol(protocol), encr.empty() ? defaultUseSsl : parseSsl(encr)); } catch (const std::exception&) { return nullptr; } }
size_t McrouterClient::send( const mcrouter_msg_t* requests, size_t nreqs, folly::StringPiece ipAddr /* = folly::StringPiece() */ ) { assert(!disconnected_); if (nreqs == 0) { return 0; } size_t id = 0; auto makeNextPreq = [this, requests, &id, ipAddr]() { auto cb = [ this, context = requests[id].context, req = McMsgRef::cloneRef(requests[id].req) ](ProxyRequestContext&, McReply&& reply) mutable { this->onReply(std::move(reply), std::move(req), context); }; auto preq = createLegacyProxyRequestContext( *proxy_, McMsgRef::cloneRef(requests[id].req), std::move(cb)); preq->requester_ = self_; if (!ipAddr.empty()) { preq->setUserIpAddress(ipAddr); } ++id; return preq; }; auto cancelRemaining = [this, requests, &id, nreqs]() { for (; id < nreqs; ++id) { mcrouter_msg_t error_reply; error_reply.req = requests[id].req; error_reply.reply = McReply(mc_res_local_error); error_reply.context = requests[id].context; callbacks_.on_reply(&error_reply, arg_); } }; auto res = sendMultiImpl(nreqs, std::move(makeNextPreq), std::move(cancelRemaining)); return res ? nreqs : 0; }
/* * Writes a file path to a file while folding any consecutive forward slashes. */ void write_path(int fd, folly::StringPiece path) { while (!path.empty()) { auto const pos = path.find('/'); if (pos == std::string::npos) { folly::writeNoInt(fd, path.data(), path.size()); break; } auto const left = path.subpiece(0, pos + 1); folly::writeNoInt(fd, left.data(), left.size()); auto right = path.subpiece(pos); while (!right.empty() && right[0] == '/') { right = right.subpiece(1); } path = right; } }
FizzContextAndVerifier createClientFizzContextAndVerifier( std::string certData, std::string keyData, folly::StringPiece pemCaPath) { // global session cache static auto SESSION_CACHE = std::make_shared<fizz::client::SynchronizedLruPskCache>(100); initSSL(); auto ctx = std::make_shared<fizz::client::FizzClientContext>(); ctx->setSupportedVersions({fizz::ProtocolVersion::tls_1_3}); ctx->setPskCache(SESSION_CACHE); if (!certData.empty() && !keyData.empty()) { auto cert = fizz::CertUtils::makeSelfCert(std::move(certData), std::move(keyData)); ctx->setClientCertificate(std::move(cert)); } std::shared_ptr<fizz::DefaultCertificateVerifier> verifier; if (!pemCaPath.empty()) { verifier = fizz::DefaultCertificateVerifier::createFromCAFile( fizz::VerificationContext::Client, pemCaPath.str()); } return FizzContextAndVerifier(std::move(ctx), std::move(verifier)); }
std::shared_ptr<AccessPoint> AccessPoint::create( folly::StringPiece apString, mc_protocol_t defaultProtocol, SecurityMech defaultMech, uint16_t portOverride, bool defaultCompressed) { if (apString.empty()) { return nullptr; } folly::StringPiece host; bool unixDomainSocket = false; if (apString[0] == '[') { // IPv6 auto closing = apString.find(']'); if (closing == std::string::npos) { return nullptr; } host = apString.subpiece(1, closing - 1); apString.advance(closing + 1); } else { // IPv4 or hostname or UNIX domain socket if (apString.subpiece(0, 5) == "unix:") { // Unix domain socket unixDomainSocket = true; apString.advance(5); } auto colon = apString.find(':'); if (colon == std::string::npos) { host = apString; apString = ""; } else { host = apString.subpiece(0, colon); apString.advance(colon); } } if (host.empty()) { return nullptr; } try { folly::StringPiece port, protocol, encr, comp; if (unixDomainSocket) { port = "0"; parseParts(apString, protocol, encr, comp); // Unix Domain Sockets with SSL is not supported. if (!encr.empty() && parseSecurityMech(encr) != SecurityMech::NONE) { return nullptr; } } else { parseParts(apString, port, protocol, encr, comp); } return std::make_shared<AccessPoint>( host, portOverride != 0 ? portOverride : folly::to<uint16_t>(port), protocol.empty() ? defaultProtocol : parseProtocol(protocol), encr.empty() ? defaultMech : parseSecurityMech(encr), comp.empty() ? defaultCompressed : parseCompressed(comp), unixDomainSocket); } catch (const std::exception&) { return nullptr; } }
std::shared_ptr<fizz::server::FizzServerContext> createFizzServerContext( folly::StringPiece pemCertPath, folly::StringPiece certData, folly::StringPiece pemKeyPath, folly::StringPiece keyData, folly::StringPiece pemCaPath, bool requireClientVerification, wangle::TLSTicketKeySeeds* ticketKeySeeds) { initSSL(); auto certMgr = std::make_unique<fizz::server::CertManager>(); try { auto selfCert = fizz::CertUtils::makeSelfCert(certData.str(), keyData.str()); // add the default cert certMgr->addCert(std::move(selfCert), true); } catch (const std::exception& ex) { LOG_FAILURE( "SSLCert", failure::Category::kBadEnvironment, "Failed to create self cert from \"{}\" and \"{}\". ex: {}", pemCertPath, pemKeyPath, ex.what()); return nullptr; } auto ctx = std::make_shared<fizz::server::FizzServerContext>(); ctx->setSupportedVersions({fizz::ProtocolVersion::tls_1_3}); ctx->setSupportedPskModes( {fizz::PskKeyExchangeMode::psk_ke, fizz::PskKeyExchangeMode::psk_dhe_ke}); ctx->setVersionFallbackEnabled(true); ctx->setCertManager(std::move(certMgr)); if (!pemCaPath.empty()) { auto verifier = fizz::DefaultCertificateVerifier::createFromCAFile( fizz::VerificationContext::Server, pemCaPath.str()); ctx->setClientCertVerifier(std::move(verifier)); ctx->setClientAuthMode(fizz::server::ClientAuthMode::Optional); } if (requireClientVerification) { ctx->setClientAuthMode(fizz::server::ClientAuthMode::Required); } // set ticket seeds if (ticketKeySeeds) { std::vector<folly::ByteRange> ticketSecrets; for (const auto& secret : ticketKeySeeds->currentSeeds) { ticketSecrets.push_back(folly::StringPiece(secret)); } for (const auto& secret : ticketKeySeeds->oldSeeds) { ticketSecrets.push_back(folly::StringPiece(secret)); } for (const auto& secret : ticketKeySeeds->newSeeds) { ticketSecrets.push_back(folly::StringPiece(secret)); } auto cipher = std::make_shared<fizz::server::AES128TicketCipher>(); cipher->setTicketSecrets(std::move(ticketSecrets)); cipher->setValidity(std::chrono::seconds(kSessionLifeTime)); ctx->setTicketCipher(std::move(cipher)); } // TODO: allow for custom FizzFactory return ctx; }
size_t McrouterClient::send( const mcrouter_msg_t* requests, size_t nreqs, folly::StringPiece ipAddr /* = folly::StringPiece() */ ) { assert(!disconnected_); auto router = router_.lock(); if (nreqs == 0 || !router) { return 0; } auto makePreq = [this, &requests, ipAddr](size_t i) { auto cb = [this, context = requests[i].context, req = McMsgRef::cloneRef(requests[i].req)] (ProxyRequestContext&, McReply&& reply) mutable { this->onReply(std::move(reply), std::move(req), context); }; auto preq = createLegacyProxyRequestContext( *proxy_, McMsgRef::cloneRef(requests[i].req), std::move(cb)); preq->requester_ = self_; if (!ipAddr.empty()) { preq->setUserIpAddress(ipAddr); } return preq; }; if (maxOutstanding_ == 0) { if (sameThread_) { for (size_t i = 0; i < nreqs; i++) { sendSameThread(makePreq(i)); } } else { for (size_t i = 0; i < nreqs; i++) { sendRemoteThread(makePreq(i)); } } } else if (maxOutstandingError_) { for(size_t begin = 0; begin < nreqs;) { auto end = begin + counting_sem_lazy_nonblocking(&outstandingReqsSem_, nreqs - begin); if (begin == end) { for (size_t i = begin; i < nreqs; ++i) { mcrouter_msg_t error_reply; error_reply.req = requests[i].req; error_reply.reply = McReply(mc_res_local_error); error_reply.context = requests[i].context; callbacks_.on_reply(&error_reply, arg_); } break; } if (sameThread_) { for (size_t i = begin; i < end; i++) { sendSameThread(makePreq(i)); } } else { for (size_t i = begin; i < end; i++) { sendRemoteThread(makePreq(i)); } } begin = end; } } else { assert(!sameThread_); size_t i = 0; size_t n = 0; while (i < nreqs) { n += counting_sem_lazy_wait(&outstandingReqsSem_, nreqs - n); for (size_t j = i; j < n; ++j) { sendRemoteThread(makePreq(j)); } i = n; } } return nreqs; }
void XDebugServer::parseInput(folly::StringPiece in, String& cmd, Array& args) { // Always start with a blank array args = Array::Create(); // Find the first space in the command. Everything before is assumed to be the // command string auto ptr = strchr(const_cast<char*>(in.data()), ' '); if (ptr != nullptr) { size_t size = ptr - in.data(); cmd = String::attach(StringData::Make(in.data(), size, CopyString)); } else if (!in.empty() && in[0] != '\0') { // There are no spaces, the entire string is the command cmd = String::attach(StringData::Make(in.data(), CopyString)); return; } else { throw_exn(Error::Parse); } // Loop starting after the space until the end of the string char opt; bool escaped = false; char* value = nullptr; ParseState state = ParseState::NORMAL; do { ptr++; switch (state) { // A new option which is prefixed with "-" is expected case ParseState::NORMAL: if (*ptr != '-') { throw_exn(Error::Parse); } else { state = ParseState::OPT_FOLLOWS; } break; // The option key follows case ParseState::OPT_FOLLOWS: opt = *ptr; state = ParseState::SEP_FOLLOWS; break; // Expect a " " separator to follow case ParseState::SEP_FOLLOWS: if (*ptr != ' ') { throw_exn(Error::Parse); } else { state = ParseState::VALUE_FOLLOWS_FIRST_CHAR; value = ptr + 1; } break; // Expect the option value's first character to follow. This character // could be either '"'or '-' case ParseState::VALUE_FOLLOWS_FIRST_CHAR: if (*ptr == '"' && opt != '-') { value = ptr + 1; state = ParseState::QUOTED; } else { state = ParseState::VALUE_FOLLOWS; } break; // The option's value should follow case ParseState::VALUE_FOLLOWS: if ((*ptr == ' ' && opt != '-') || *ptr == '\0') { if (args[opt].isNull()) { size_t size = ptr - value; StringData* val_data = StringData::Make(value, size, CopyString); args.set(opt, String::attach(val_data)); state = ParseState::NORMAL; } else { throw_exn(Error::DupArg); } } break; // State when we are within a quoted string case ParseState::QUOTED: // if the quote is escaped, remain in ParseState::QUOTED. This // will also handle other escaped chars, or an instance of // an escaped slash followed by a quote: \\" if (*ptr == '\\') { escaped = !escaped; break; } else if (*ptr != '"') { break; } else if (escaped) { escaped = false; break; } // Need to strip slashes before adding option if (args[opt].isNull()) { size_t size = ptr - value; StringData* val_data = StringData::Make(value, size, CopyString); args.set(opt, HHVM_FN(stripcslashes)(String::attach(val_data))); state = ParseState::SKIP_CHAR; } else { throw_exn(Error::DupArg); } break; // Do nothing case ParseState::SKIP_CHAR: state = ParseState::NORMAL; break; } } while (*ptr != '\0'); }
// Helper used to create an absolute filename using the passed // directory and xdebug-specific format string static String format_filename(folly::StringPiece dir, folly::StringPiece formatFile, bool addSuffix) { // Create a string buffer and append the directory name auto const formatlen = formatFile.size(); StringBuffer buf(formatlen * 2); // Slightly larger than formatlen if (!dir.empty()) { buf.append(dir); buf.append('/'); } // Append the filename auto globals = get_global_variables()->asArrayData(); for (int pos = 0; pos < formatlen; pos++) { auto c = formatFile[pos]; if (c != '%' || pos + 1 == formatlen) { buf.append(c); continue; } c = formatFile[++pos]; switch (c) { // crc32 of current working directory case 'c': { auto const crc32 = HHVM_FN(crc32)(g_context->getCwd()); buf.append(crc32); break; } // process id case 'p': buf.append(getpid()); break; // Random number case 'r': buf.printf("%lx", (long)HHVM_FN(rand)()); break; // Script name case 's': { auto server = globals->get(s_SERVER).toArray(); if (server.exists(s_SCRIPT_NAME) && server[s_SCRIPT_NAME].isString()) { const String scriptname(server[s_SCRIPT_NAME].toString(), CopyString); replace_special_chars(scriptname.get()); buf.append(scriptname); } break; } // Timestamp (seconds) case 't': { auto const sec = (int64_t)time(nullptr); if (sec != -1) { buf.append(sec); } break; } // Timestamp (microseconds) case 'u': { struct timeval tv; if (gettimeofday(&tv, 0) != -1) { buf.printf("%ld_%ld", long(tv.tv_sec), long(tv.tv_usec)); } break; } // $_SERVER['HTTP_HOST'] case 'H': { Array server = globals->get(s_SERVER).toArray(); if (server.exists(s_HTTP_HOST) && server[s_HTTP_HOST].isString()) { const String hostname(server[s_HTTP_HOST].toString(), CopyString); replace_special_chars(hostname.get()); buf.append(hostname); } break; } // $_SERVER['REQUEST_URI'] case 'R': { auto server = globals->get(s_SERVER).toArray(); if (globals->exists(s_REQUEST_URI)) { const String requri(server[s_REQUEST_URI].toString(), CopyString); replace_special_chars(requri.get()); buf.append(requri); } break; } // $_SERVER['UNIQUE_ID'] case 'U': { auto server = globals->get(s_SERVER).toArray(); if (server.exists(s_UNIQUE_ID) && server[s_UNIQUE_ID].isString()) { const String uniqueid(server[s_UNIQUE_ID].toString(), CopyString); replace_special_chars(uniqueid.get()); buf.append(uniqueid); } break; } // session id case 'S': { // First we grab the session name from the ini settings, then the id // from the cookies String session_name; if (IniSetting::Get(s_SESSION_NAME, session_name)) { auto cookies = globals->get(s_COOKIE).toArray(); if (cookies.exists(session_name) && cookies[session_name].isString()) { const String sessionstr(cookies[session_name].toString(), CopyString); replace_special_chars(sessionstr.get()); buf.append(sessionstr); } break; } } // Literal case '%': buf.append('%'); break; default: buf.append('%'); buf.append(c); break; } } // Optionally add .xt file extension if (addSuffix) { buf.append(".xt"); } return buf.copy(); }