Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
// 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);
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
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;
}
Exemplo n.º 7
0
// 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;
  }
}
Exemplo n.º 8
0
// 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;
  }
}
Exemplo n.º 9
0
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);
    }
  }
}
Exemplo n.º 10
0
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;
    }
}
Exemplo n.º 11
0
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;
}
Exemplo n.º 12
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;
  }
}
Exemplo n.º 13
0
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));
}
Exemplo n.º 14
0
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;
  }
}
Exemplo n.º 15
0
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;
}
Exemplo n.º 16
0
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;
}
Exemplo n.º 17
0
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');
}
Exemplo n.º 18
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();
}