fd5d218e8e
Drop the request when it's been sitting on the select queue longer than the virtual host timeout allows for. This is orthogonal to the expiration timeout on the queue itself. This can be useful if we ever allow select queue timeout to be configured to be higher than the virtual host request timeout.
454 linhas
15 KiB
C++
454 linhas
15 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
#include "hphp/runtime/base/server/virtual_host.h"
|
|
|
|
#include "hphp/runtime/base/comparisons.h"
|
|
#include "hphp/runtime/base/preg.h"
|
|
#include "hphp/runtime/base/runtime_option.h"
|
|
#include "hphp/runtime/base/comparisons.h"
|
|
#include "hphp/runtime/base/timeout_thread.h"
|
|
#include "hphp/runtime/base/string_util.h"
|
|
#include "hphp/util/util.h"
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
VirtualHost &VirtualHost::GetDefault() {
|
|
// VirtualHost acquires global mutexes in its constructor, so we allocate
|
|
// s_default_vhost lazily to ensure that all of the global mutexes have
|
|
// been initialized before we enter the constructor.
|
|
static VirtualHost s_default_vhost;
|
|
return s_default_vhost;
|
|
}
|
|
|
|
void VirtualHost::SetCurrent(VirtualHost *vhost) {
|
|
g_context->setVirtualHost(vhost ? vhost : &VirtualHost::GetDefault());
|
|
}
|
|
|
|
const VirtualHost *VirtualHost::GetCurrent() {
|
|
const VirtualHost *ret = g_context->getVirtualHost();
|
|
if (!ret) ret = &VirtualHost::GetDefault();
|
|
return ret;
|
|
}
|
|
|
|
int64_t VirtualHost::GetMaxPostSize() {
|
|
const VirtualHost *vh = GetCurrent();
|
|
assert(vh);
|
|
if (vh->m_runtimeOption.maxPostSize != -1) {
|
|
return vh->m_runtimeOption.maxPostSize;
|
|
}
|
|
return RuntimeOption::MaxPostSize;
|
|
}
|
|
|
|
int64_t VirtualHost::GetUploadMaxFileSize() {
|
|
const VirtualHost *vh = GetCurrent();
|
|
assert(vh);
|
|
if (vh->m_runtimeOption.uploadMaxFileSize != -1) {
|
|
return vh->m_runtimeOption.uploadMaxFileSize;
|
|
}
|
|
return RuntimeOption::UploadMaxFileSize;
|
|
}
|
|
|
|
const vector<string> &VirtualHost::GetAllowedDirectories() {
|
|
const VirtualHost *vh = GetCurrent();
|
|
assert(vh);
|
|
if (!vh->m_runtimeOption.allowedDirectories.empty()) {
|
|
return vh->m_runtimeOption.allowedDirectories;
|
|
}
|
|
return RuntimeOption::AllowedDirectories;
|
|
}
|
|
|
|
void VirtualHost::SortAllowedDirectories(std::vector<std::string>& dirs) {
|
|
/*
|
|
Make sure corresponding realpath's are also allowed
|
|
*/
|
|
for (unsigned int i = 0, s = dirs.size(); i < s; i++) {
|
|
string &directory = dirs[i];
|
|
char resolved_path[PATH_MAX];
|
|
if (realpath(directory.c_str(), resolved_path) &&
|
|
directory != resolved_path) {
|
|
dirs.push_back(resolved_path);
|
|
}
|
|
}
|
|
/*
|
|
sort so we can use upper_bound to find the right prefix,
|
|
rather than using a linear scan (and so we can remove
|
|
duplicates, etc below)
|
|
*/
|
|
std::sort(dirs.begin(), dirs.end());
|
|
/*
|
|
AllowedDirectories is a list of prefixes, so if x is a substring
|
|
of y, we dont need y (also remove any duplicates).
|
|
*/
|
|
dirs.erase(std::unique(dirs.begin(), dirs.end(),
|
|
[](const std::string &a, const std::string &b) {
|
|
if (a.size() < b.size()) {
|
|
return !b.compare(0, a.size(), a);
|
|
}
|
|
return !a.compare(0, b.size(), b);
|
|
}),
|
|
dirs.end());
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void VirtualHost::initRuntimeOption(Hdf overwrite) {
|
|
int requestTimeoutSeconds =
|
|
overwrite["Server.RequestTimeoutSeconds"].getInt32(-1);
|
|
int64_t maxPostSize =
|
|
overwrite["Server.MaxPostSize"].getInt32(-1);
|
|
if (maxPostSize != -1) maxPostSize *= (1LL << 20);
|
|
int64_t uploadMaxFileSize =
|
|
overwrite["Server.Upload.UploadMaxFileSize"].getInt32(-1);
|
|
if (uploadMaxFileSize != -1) uploadMaxFileSize *= (1LL << 20);
|
|
overwrite["Server.AllowedDirectories"].
|
|
get(m_runtimeOption.allowedDirectories);
|
|
m_runtimeOption.requestTimeoutSeconds = requestTimeoutSeconds;
|
|
m_runtimeOption.maxPostSize = maxPostSize;
|
|
m_runtimeOption.uploadMaxFileSize = uploadMaxFileSize;
|
|
}
|
|
|
|
void VirtualHost::addAllowedDirectories(const std::vector<std::string>& dirs) {
|
|
if (!m_runtimeOption.allowedDirectories.empty()) {
|
|
m_runtimeOption.allowedDirectories.insert(
|
|
m_runtimeOption.allowedDirectories.end(),
|
|
dirs.begin(), dirs.end());
|
|
SortAllowedDirectories(m_runtimeOption.allowedDirectories);
|
|
}
|
|
}
|
|
|
|
void VirtualHost::setRequestTimeoutSeconds() const {
|
|
if (m_runtimeOption.requestTimeoutSeconds != -1) {
|
|
TimeoutThread::DeferTimeout(m_runtimeOption.requestTimeoutSeconds);
|
|
}
|
|
}
|
|
|
|
int VirtualHost::getRequestTimeoutSeconds() const {
|
|
return m_runtimeOption.requestTimeoutSeconds;
|
|
}
|
|
|
|
VirtualHost::VirtualHost() : m_disabled(false) {
|
|
Hdf empty;
|
|
initRuntimeOption(empty);
|
|
}
|
|
|
|
VirtualHost::VirtualHost(Hdf vh) : m_disabled(false) {
|
|
init(vh);
|
|
}
|
|
|
|
void VirtualHost::init(Hdf vh) {
|
|
m_name = vh.getName();
|
|
|
|
const char *prefix = vh["Prefix"].get("");
|
|
const char *pattern = vh["Pattern"].get("");
|
|
const char *pathTranslation = vh["PathTranslation"].get("");
|
|
Hdf overwrite = vh["overwrite"];
|
|
initRuntimeOption(overwrite);
|
|
|
|
if (prefix) m_prefix = prefix;
|
|
if (pattern) {
|
|
m_pattern = Util::format_pattern(pattern, true);
|
|
if (!m_pattern.empty()) {
|
|
m_pattern += "i"; // case-insensitive
|
|
}
|
|
}
|
|
if (pathTranslation) {
|
|
m_pathTranslation = pathTranslation;
|
|
if (!m_pathTranslation.empty() &&
|
|
m_pathTranslation[m_pathTranslation.length() - 1] != '/') {
|
|
m_pathTranslation += '/';
|
|
}
|
|
}
|
|
m_disabled = vh["Disabled"].getBool(false);
|
|
|
|
m_documentRoot = RuntimeOption::SourceRoot + m_pathTranslation;
|
|
if (!m_documentRoot.empty() &&
|
|
m_documentRoot[m_documentRoot.length() - 1] == '/') {
|
|
m_documentRoot = m_documentRoot.substr(0, m_documentRoot.length() - 1);
|
|
}
|
|
|
|
Hdf rewriteRules = vh["RewriteRules"];
|
|
for (Hdf hdf = rewriteRules.firstChild(); hdf.exists(); hdf = hdf.next()) {
|
|
RewriteRule dummy;
|
|
m_rewriteRules.push_back(dummy);
|
|
RewriteRule &rule = m_rewriteRules.back();
|
|
rule.pattern = Util::format_pattern(hdf["pattern"].getString(""), true);
|
|
rule.to = hdf["to"].getString("");
|
|
rule.qsa = hdf["qsa"].getBool(false);
|
|
rule.redirect = hdf["redirect"].getInt16(0);
|
|
rule.encode_backrefs = hdf["encode_backrefs"].getBool(false);
|
|
|
|
if (rule.pattern.empty() || rule.to.empty()) {
|
|
throw InvalidArgumentException("rewrite rule", "(empty pattern or to)");
|
|
}
|
|
Hdf rewriteConds = hdf["conditions"];
|
|
for (Hdf chdf = rewriteConds.firstChild(); chdf.exists();
|
|
chdf = chdf.next()) {
|
|
RewriteCond dummy;
|
|
rule.rewriteConds.push_back(dummy);
|
|
RewriteCond &cond = rule.rewriteConds.back();
|
|
cond.pattern = Util::format_pattern(chdf["pattern"].getString(""), true);
|
|
if (cond.pattern.empty()) {
|
|
throw InvalidArgumentException("rewrite rule", "(empty cond pattern)");
|
|
}
|
|
const char *type = chdf["type"].get();
|
|
if (type) {
|
|
if (strcasecmp(type, "host") == 0) {
|
|
cond.type = RewriteCond::Type::Host;
|
|
} else if (strcasecmp(type, "request") == 0) {
|
|
cond.type = RewriteCond::Type::Request;
|
|
} else {
|
|
throw InvalidArgumentException("rewrite rule",
|
|
"(invalid cond type)");
|
|
}
|
|
} else {
|
|
cond.type = RewriteCond::Type::Request;
|
|
}
|
|
cond.negate = chdf["negate"].getBool(false);
|
|
}
|
|
|
|
}
|
|
|
|
if (vh["IpBlockMap"].firstChild().exists()) {
|
|
Hdf ipblocks = vh["IpBlockMap"];
|
|
m_ipBlocks = IpBlockMapPtr(new IpBlockMap(ipblocks));
|
|
}
|
|
|
|
Hdf logFilters = vh["LogFilters"];
|
|
for (Hdf hdf = logFilters.firstChild(); hdf.exists(); hdf = hdf.next()) {
|
|
QueryStringFilter filter;
|
|
filter.urlPattern = Util::format_pattern(hdf["url"].getString(""), true);
|
|
filter.replaceWith = hdf["value"].getString("");
|
|
filter.replaceWith = "\\1=" + filter.replaceWith;
|
|
|
|
string pattern = hdf["pattern"].getString("");
|
|
vector<string> names;
|
|
hdf["params"].get(names);
|
|
|
|
if (pattern.empty()) {
|
|
for (unsigned int i = 0; i < names.size(); i++) {
|
|
if (pattern.empty()) {
|
|
pattern = "(?<=[&\?])(";
|
|
} else {
|
|
pattern += "|";
|
|
}
|
|
pattern += names[i];
|
|
}
|
|
if (!pattern.empty()) {
|
|
pattern += ")=.*?(?=(&|$))";
|
|
pattern = Util::format_pattern(pattern, false);
|
|
}
|
|
} else if (!names.empty()) {
|
|
throw InvalidArgumentException
|
|
("log filter", "(cannot specify both params and pattern)");
|
|
}
|
|
|
|
filter.namePattern = pattern;
|
|
m_queryStringFilters.push_back(filter);
|
|
}
|
|
|
|
vh["ServerVariables"].get(m_serverVars);
|
|
m_serverName = vh["ServerName"].getString();
|
|
}
|
|
|
|
bool VirtualHost::match(const string &host) const {
|
|
if (!m_pattern.empty()) {
|
|
Variant ret = preg_match(String(m_pattern.c_str(), m_pattern.size(),
|
|
AttachLiteral),
|
|
String(host.c_str(), host.size(),
|
|
AttachLiteral));
|
|
return ret.toInt64() > 0;
|
|
} else if (!m_prefix.empty()) {
|
|
return strncasecmp(host.c_str(), m_prefix.c_str(), m_prefix.size()) == 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int get_backref(const char **s) {
|
|
assert('0' <= **s && **s <= '9');
|
|
int val = **s - '0';
|
|
*s += 1;
|
|
if ('0' <= **s && **s <= '9') {
|
|
val = val * 10 + **s - '0';
|
|
*s += 1;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
bool VirtualHost::rewriteURL(CStrRef host, String &url, bool &qsa,
|
|
int &redirect) const {
|
|
String normalized = url;
|
|
if (normalized.empty() || normalized.charAt(0) != '/') {
|
|
normalized = String("/") + normalized;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < m_rewriteRules.size(); i++) {
|
|
const RewriteRule &rule = m_rewriteRules[i];
|
|
|
|
bool passed = true;
|
|
for (vector<RewriteCond>::const_iterator it = rule.rewriteConds.begin();
|
|
it != rule.rewriteConds.end(); ++it) {
|
|
String subject;
|
|
if (it->type == RewriteCond::Type::Request) {
|
|
subject = normalized;
|
|
} else {
|
|
subject = host;
|
|
}
|
|
Variant ret = preg_match(String(it->pattern.c_str(), it->pattern.size(),
|
|
AttachLiteral), subject);
|
|
if (!same(ret, it->negate ? 0 : 1)) {
|
|
passed = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!passed) continue;
|
|
Variant matches;
|
|
int count = preg_match(rule.pattern.c_str(),
|
|
normalized,
|
|
matches).toInt64();
|
|
if (count > 0) {
|
|
const char *s = rule.to.c_str();
|
|
StringBuffer ret;
|
|
while (*s) {
|
|
int backref = -1;
|
|
if (*s == '\\') {
|
|
if ('0' <= s[1] && s[1] <= '9') {
|
|
s++;
|
|
backref = get_backref(&s);
|
|
} else if (s[1] == '\\') {
|
|
s++;
|
|
}
|
|
} else if (*s == '$') {
|
|
if (s[1] == '{') {
|
|
const char *t = s+2;
|
|
if ('0' <= *t && *t <= '9') {
|
|
backref = get_backref(&t);
|
|
if (*t != '}') {
|
|
backref = -1;
|
|
} else {
|
|
s = t+1;
|
|
}
|
|
}
|
|
} else if ('0' <= s[1] && s[1] <= '9') {
|
|
s++;
|
|
backref = get_backref(&s);
|
|
}
|
|
}
|
|
if (backref >= 0) {
|
|
String br = matches[backref].toString();
|
|
if (rule.encode_backrefs) {
|
|
br = StringUtil::UrlEncode(br);
|
|
}
|
|
ret.append(br);
|
|
} else {
|
|
ret.append(s, 1);
|
|
s++;
|
|
}
|
|
}
|
|
|
|
url = ret.detach();
|
|
qsa = rule.qsa;
|
|
redirect = rule.redirect;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool VirtualHost::isBlocking(const std::string &command,
|
|
const std::string &ip) const {
|
|
if (!m_ipBlocks) {
|
|
return RuntimeOption::IpBlocks->isBlocking(command, ip);
|
|
}
|
|
return m_ipBlocks->isBlocking(command, ip);
|
|
}
|
|
|
|
std::string VirtualHost::serverName(const std::string &host) const {
|
|
if (!m_serverName.empty()) {
|
|
return m_serverName;
|
|
}
|
|
|
|
if (!RuntimeOption::DefaultServerNameSuffix.empty()) {
|
|
if (!m_pattern.empty()) {
|
|
Variant matches;
|
|
Variant ret = preg_match(String(m_pattern.c_str(), m_pattern.size(),
|
|
AttachLiteral),
|
|
String(host.c_str(), host.size(),
|
|
AttachLiteral),
|
|
matches);
|
|
if (ret.toInt64() > 0) {
|
|
String prefix = matches[1].toString();
|
|
if (prefix.empty()) {
|
|
prefix = matches[0].toString();
|
|
}
|
|
if (!prefix.empty()) {
|
|
return std::string(prefix.data()) +
|
|
RuntimeOption::DefaultServerNameSuffix;
|
|
}
|
|
}
|
|
} else if (!m_prefix.empty()) {
|
|
return m_prefix + RuntimeOption::DefaultServerNameSuffix;
|
|
}
|
|
}
|
|
|
|
return RuntimeOption::Host;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// query string filter
|
|
|
|
std::string VirtualHost::filterUrl(const std::string &url) const {
|
|
assert(!m_queryStringFilters.empty());
|
|
|
|
for (unsigned int i = 0; i < m_queryStringFilters.size(); i++) {
|
|
const QueryStringFilter &filter = m_queryStringFilters[i];
|
|
|
|
bool match = true;
|
|
if (!filter.urlPattern.empty()) {
|
|
Variant ret = preg_match(String(filter.urlPattern.c_str(),
|
|
filter.urlPattern.size(),
|
|
AttachLiteral),
|
|
String(url.c_str(), url.size(),
|
|
AttachLiteral));
|
|
match = (ret.toInt64() > 0);
|
|
}
|
|
|
|
if (match) {
|
|
if (filter.namePattern.empty()) {
|
|
return "";
|
|
}
|
|
|
|
Variant ret;
|
|
int count = preg_replace(ret, filter.namePattern.c_str(),
|
|
String(filter.replaceWith.c_str(),
|
|
filter.replaceWith.size(),
|
|
AttachLiteral),
|
|
String(url.c_str(), url.size(),
|
|
AttachLiteral));
|
|
if (!same(ret, false) && count > 0) {
|
|
return ret.toString().data();
|
|
}
|
|
|
|
return url;
|
|
}
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|