Arquivos
hhvm/hphp/runtime/base/server/request_uri.cpp
T
Paul Tarjan d3e6ade96f fix all the $_SERVER urls - take 2
I needed to `m_origPathInfo.reset()` in `RequestURI::clear`
2013-06-25 11:42:33 -07:00

360 linhas
12 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/request_uri.h"
#include "hphp/runtime/base/server/virtual_host.h"
#include "hphp/runtime/base/server/transport.h"
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/runtime/base/server/static_content_cache.h"
#include "hphp/runtime/base/string_util.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
RequestURI::RequestURI(const VirtualHost *vhost, Transport *transport,
const std::string &sourceRoot,
const std::string &pathTranslation)
: m_rewritten(false), m_defaultDoc(false), m_done(false),
m_forbidden(false), m_ext(nullptr) {
if (!process(vhost, transport, sourceRoot, pathTranslation,
transport->getServerObject()) ||
(m_forbidden && RuntimeOption::ForbiddenAs404)) {
m_forbidden = false; // put down forbidden flag since we are redirecting
if (!RuntimeOption::ErrorDocument404.empty()) {
String redirectURL(RuntimeOption::ErrorDocument404);
if (m_queryString != "") {
if (redirectURL.find('?') == -1) {
redirectURL += "?";
} else {
// has query in 404 string
redirectURL += "&";
}
redirectURL += m_queryString;
}
if (process(vhost, transport, sourceRoot, pathTranslation,
redirectURL.data())) {
// 404 redirection succeed
return;
}
}
transport->sendString("Not Found", 404);
transport->onSendEnd();
m_done = true;
}
}
RequestURI::RequestURI(const std::string & rpcFunc)
: m_rewritten(false), m_defaultDoc(false), m_done(false) {
m_originalURL = m_rewrittenURL = m_resolvedURL = String(rpcFunc);
}
bool RequestURI::process(const VirtualHost *vhost, Transport *transport,
const string &sourceRoot,
const string &pathTranslation, const char *url) {
splitURL(url, m_originalURL, m_queryString);
m_originalURL = StringUtil::UrlDecode(m_originalURL, false);
// Fast path for files that exist
String canon(Util::canonicalize(m_originalURL.c_str(), m_originalURL.size()),
AttachString);
if (virtualFileExists(vhost, sourceRoot, pathTranslation, canon)) {
m_rewrittenURL = canon;
m_resolvedURL = canon;
return true;
}
if (!rewriteURL(vhost, transport, pathTranslation, sourceRoot)) {
// Redirection
m_done = true;
return true;
}
if (!resolveURL(vhost, pathTranslation, sourceRoot)) {
// Can't find
return false;
}
return true;
}
void RequestURI::splitURL(String surl, String &base, String &querys) {
const char *url = surl.c_str();
const char *query = strchr(url, '?');
const char *fragment = strchr(url, '#');
if (fragment) {
// ignore everything after the #
if (query && fragment > query) {
base = String(url, query - url, CopyString);
++query; // skipping ?
querys = String(query, fragment - query, CopyString);
} else {
base = String(url, fragment - url, CopyString);
querys = "";
}
} else if (query) {
base = String(url, query - url, CopyString);
++query; // skipping ?
querys = String(query, CopyString);
} else {
base = String(url, CopyString);
querys = "";
}
}
/**
* Precondition: m_originalURL and m_queryString are set
* Postcondition: Output is false and we are redirecting OR
* m_rewrittenURL is set and m_queryString is updated if needed
*/
bool RequestURI::rewriteURL(const VirtualHost *vhost, Transport *transport,
const string &pathTranslation,
const string &sourceRoot) {
bool qsa = false;
int redirect = 0;
string host = transport->getHeader("host");
m_rewrittenURL = m_originalURL;
if (vhost->rewriteURL(host, m_rewrittenURL, qsa, redirect)) {
m_rewritten = true;
if (qsa && !m_queryString.empty()) {
m_rewrittenURL += (m_rewrittenURL.find('?') < 0) ? "?" : "&";
m_rewrittenURL += m_queryString;
}
if (redirect) {
if (m_rewrittenURL.substr(0, 7) != "http://" &&
m_rewrittenURL.substr(0, 8) != "https://") {
PrependSlash(m_rewrittenURL);
}
transport->redirect(m_rewrittenURL.c_str(), redirect, "rewriteURL");
return false;
}
splitURL(m_rewrittenURL, m_rewrittenURL, m_queryString);
}
m_rewrittenURL = String(
Util::canonicalize(m_rewrittenURL.c_str(), m_rewrittenURL.size()),
AttachString);
if (!m_rewritten && m_rewrittenURL.charAt(0) == '/') {
// A un-rewritten URL is always relative, so remove prepending /
m_rewrittenURL = m_rewrittenURL.substr(1);
}
// If the URL refers to a folder but does not end
// with a slash, then we need to redictect
String url = m_rewrittenURL;
if (!url.empty() &&
url.charAt(url.length() - 1) != '/') {
if (virtualFolderExists(vhost, sourceRoot, pathTranslation, url)) {
url += "/";
m_rewritten = true;
String queryStr;
m_rewrittenURL = m_originalURL;
m_rewrittenURL += "/";
if (!m_queryString.empty()) {
m_rewrittenURL += "?";
m_rewrittenURL += m_queryString;
}
if (m_rewrittenURL.substr(0, 7) != "http://" &&
m_rewrittenURL.substr(0, 8) != "https://") {
PrependSlash(m_rewrittenURL);
}
transport->redirect(m_rewrittenURL.c_str(), 301, "rewriteURL");
return false;
}
}
return true;
}
/**
* Precondition: m_rewrittenURL is set
* Postcondition: Output is true and m_path and m_absolutePath are set OR
* output is false and no file was found
*/
bool RequestURI::resolveURL(const VirtualHost *vhost,
const string &pathTranslation,
const string &sourceRoot) {
String startURL;
if (m_rewritten) {
startURL = m_rewrittenURL;
} else {
startURL = m_originalURL;
}
startURL = String(
Util::canonicalize(startURL.c_str(), startURL.size(), false),
AttachString);
m_resolvedURL = startURL;
while (!virtualFileExists(vhost, sourceRoot, pathTranslation,
m_resolvedURL)) {
int pos = m_resolvedURL.rfind('/');
if (pos <= 0) {
// when none of the <subpath> exists, we give up, and try default doc
m_resolvedURL = startURL;
if (!m_resolvedURL.empty() &&
m_resolvedURL.charAt(m_resolvedURL.length() - 1) != '/') {
m_resolvedURL += "/";
}
m_resolvedURL += String(RuntimeOption::DefaultDocument);
m_origPathInfo.reset();
if (virtualFileExists(vhost, sourceRoot, pathTranslation,
m_resolvedURL)) {
m_defaultDoc = true;
return true;
}
return false;
}
m_resolvedURL = startURL.substr(0, pos);
m_origPathInfo = startURL.substr(pos);
}
m_pathInfo = String(
Util::canonicalize(m_origPathInfo.c_str(), m_origPathInfo.size()),
AttachString);
return true;
}
bool RequestURI::virtualFileExists(const VirtualHost *vhost,
const string &sourceRoot,
const string &pathTranslation,
CStrRef filename) {
if (filename.empty() || filename.charAt(filename.length() - 1) == '/') {
return false;
}
String canon(Util::canonicalize(filename.c_str(), filename.size()),
AttachString);
if (!vhost->getDocumentRoot().empty()) {
string fullname = canon.data();
while (fullname[0] == '/') {
fullname = fullname.substr(1);
}
fullname = pathTranslation + fullname;
m_path = fullname;
m_absolutePath = String(sourceRoot) + m_path;
processExt();
if (StaticContentCache::TheFileCache && !fullname.empty() &&
StaticContentCache::TheFileCache->fileExists(fullname.c_str())) {
return true;
}
struct stat st;
return RuntimeOption::AllowedFiles.find(fullname.c_str()) !=
RuntimeOption::AllowedFiles.end() ||
(stat(m_absolutePath.c_str(), &st) == 0 &&
(st.st_mode & S_IFMT) == S_IFREG);
}
m_path = canon;
m_absolutePath = String(sourceRoot) + canon;
processExt();
return true;
}
bool RequestURI::virtualFolderExists(const VirtualHost *vhost,
const string &sourceRoot,
const string &pathTranslation,
CStrRef foldername) {
if (!vhost->getDocumentRoot().empty()) {
string fullname = foldername.data();
// If there is a trailing slash, remove it
if (fullname.size() > 0 && fullname[fullname.size()-1] == '/') {
fullname = fullname.substr(fullname.size()-1);
}
if (fullname[0] == '/') {
fullname = fullname.substr(1);
} else {
fullname = pathTranslation + fullname;
}
m_path = fullname;
m_absolutePath = String(sourceRoot) + m_path;
processExt();
if (StaticContentCache::TheFileCache && !fullname.empty() &&
StaticContentCache::TheFileCache->dirExists(fullname.c_str())) {
return true;
}
const vector<string> &allowedDirectories =
VirtualHost::GetAllowedDirectories();
if (find(allowedDirectories.begin(),
allowedDirectories.end(),
fullname.c_str()) != allowedDirectories.end()) {
return true;
}
struct stat st;
return (stat(m_absolutePath.c_str(), &st) == 0 &&
(st.st_mode & S_IFMT) == S_IFDIR);
}
m_path = foldername;
m_absolutePath = String(sourceRoot) + foldername;
processExt();
return true;
}
void RequestURI::processExt() {
m_ext = parseExt(m_path);
if (RuntimeOption::ForbiddenFileExtensions.empty()) {
return;
}
if (m_ext &&
RuntimeOption::ForbiddenFileExtensions.find(m_ext) !=
RuntimeOption::ForbiddenFileExtensions.end()) {
m_forbidden = true;
}
}
/*
* Parse file extension from a path
*/
const char *RequestURI::parseExt(CStrRef s) {
int pos = s.rfind('.');
if (pos == -1) {
return nullptr;
}
if (s.find('/', pos) != -1) {
// '/' after '.' is not extension, e.g., "./foo" "../bar"
return nullptr;
}
return s.data() + pos + 1;
}
void RequestURI::PrependSlash(String &s) {
if (!s.empty() && s.charAt(0) != '/') {
s = String("/") + s;
}
}
void RequestURI::dump() {
m_originalURL.dump();
m_queryString.dump();
m_rewrittenURL.dump();
m_resolvedURL.dump();
m_pathInfo.dump();
m_origPathInfo.dump();
m_absolutePath.dump();
m_path.dump();
}
void RequestURI::clear() {
m_originalURL.reset();
m_queryString.reset();
m_rewrittenURL.reset();
m_resolvedURL.reset();
m_pathInfo.reset();
m_origPathInfo.reset();
m_absolutePath.reset();
m_path.reset();
}
///////////////////////////////////////////////////////////////////////////////
}