<commit_before>/*
 *  Phusion Passenger - http://www.modrails.com/
 *  Copyright (C) 2008  Phusion
 *
 *  Phusion Passenger is a trademark of <NAME> & <NAME>.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _PASSENGER_DIRECTORY_MAPPER_H_
#define _PASSENGER_DIRECTORY_MAPPER_H_

#include <string>
#include <set>
#include <cstring>

#include <oxt/backtrace.hpp>

#include "CachedFileStat.h"
#include "Configuration.h"
#include "Utils.h"

// The Apache/APR headers *must* come after the Boost headers, otherwise
// compilation will fail on OpenBSD.
#include <httpd.h>
#include <http_core.h>

namespace Passenger {

using namespace std;
using namespace oxt;

/**
 * Utility class for determining URI-to-application directory mappings.
 * Given a URI, it will determine whether that URI belongs to a Phusion
 * Passenger-handled application, what the base URI of that application is,
 * and what the associated 'public' directory is.
 *
 * @note This class is not thread-safe, but is reentrant.
 * @ingroup Core
 */
class DirectoryMapper {
public:
	enum ApplicationType {
		NONE,
		RAILS,
		RACK,
		WSGI
	};

private:
	DirConfig *config;
	request_rec *r;
	CachedMultiFileStat *mstat;
	unsigned int throttleRate;
	bool baseURIKnown;
	const char *baseURI;
	ApplicationType appType;
	
	inline bool shouldAutoDetectRails() {
		return config->autoDetectRails == DirConfig::ENABLED ||
			config->autoDetectRails == DirConfig::UNSET;
	}
	
	inline bool shouldAutoDetectRack() {
		return config->autoDetectRack == DirConfig::ENABLED ||
			config->autoDetectRack == DirConfig::UNSET;
	}
	
	inline bool shouldAutoDetectWSGI() {
		return config->autoDetectWSGI == DirConfig::ENABLED ||
			config->autoDetectWSGI == DirConfig::UNSET;
	}
	
public:
	/**
	 * Create a new DirectoryMapper object.
	 *
	 * @param mstat A CachedMultiFileStat object used for statting files.
	 * @param throttleRate A throttling rate for mstat.
	 * @warning Do not use this object after the destruction of <tt>r</tt>,
	 *          <tt>config</tt> or <tt>mstat</tt>.
	 */
	DirectoryMapper(request_rec *r, DirConfig *config,
	                CachedMultiFileStat *mstat, unsigned int throttleRate) {
		this->r = r;
		this->config = config;
		this->mstat = mstat;
		this->throttleRate = throttleRate;
		appType = NONE;
		baseURIKnown = false;
		baseURI = NULL;
	}
	
	/**
	 * Determine whether the given HTTP request falls under one of the specified
	 * RailsBaseURIs or RackBaseURIs. If yes, then the first matching base URI will
	 * be returned.
	 *
	 * If Rails/Rack autodetection was enabled in the configuration, and the document
	 * root seems to be a valid Rails/Rack 'public' folder, then this method will
	 * return "/".
	 *
	 * Otherwise, NULL will be returned.
	 *
	 * @throws FileSystemException This method might also examine the filesystem in
	 *            order to detect the application's type. During that process, a
	 *            FileSystemException might be thrown.
	 * @warning The return value may only be used as long as <tt>config</tt>
	 *          hasn't been destroyed.
	 */
	const char *getBaseURI() {
		TRACE_POINT();
		if (baseURIKnown) {
			return baseURI;
		}
		
		set<string>::const_iterator it;
		const char *uri = r->uri;
		size_t uri_len = strlen(uri);
		
		if (uri_len == 0 || uri[0] != '/') {
			baseURIKnown = true;
			return NULL;
		}
		
		UPDATE_TRACE_POINT();
		for (it = config->railsBaseURIs.begin(); it != config->railsBaseURIs.end(); it++) {
			const string &base(*it);
			if (  base == "/"
			 || ( uri_len == base.size() && memcmp(uri, base.c_str(), uri_len) == 0 )
			 || ( uri_len  > base.size() && memcmp(uri, base.c_str(), base.size()) == 0
			                             && uri[base.size()] == '/' )
			) {
				baseURIKnown = true;
				baseURI = base.c_str();
				appType = RAILS;
				return baseURI;
			}
		}
		
		UPDATE_TRACE_POINT();
		for (it = config->rackBaseURIs.begin(); it != config->rackBaseURIs.end(); it++) {
			const string &base(*it);
			if (  base == "/"
			 || ( uri_len == base.size() && memcmp(uri, base.c_str(), uri_len) == 0 )
			 || ( uri_len  > base.size() && memcmp(uri, base.c_str(), base.size()) == 0
			                             && uri[base.size()] == '/' )
			) {
				baseURIKnown = true;
				baseURI = base.c_str();
				appType = RACK;
				return baseURI;
			}
		}
		
		UPDATE_TRACE_POINT();
		if (shouldAutoDetectRails()
		 && verifyRailsDir(canonicalizePath(config->getAppRoot(ap_document_root(r))), mstat, throttleRate)) {
			baseURIKnown = true;
			baseURI = "/";
			appType = RAILS;
			return baseURI;
		}
		
		UPDATE_TRACE_POINT();
		if (shouldAutoDetectRack()
		 && verifyRackDir(canonicalizePath(config->getAppRoot(ap_document_root(r))), mstat, throttleRate)) {
			baseURIKnown = true;
			baseURI = "/";
			appType = RACK;
			return baseURI;
		}
		
		UPDATE_TRACE_POINT();
		if (shouldAutoDetectWSGI()
		 && verifyWSGIDir(canonicalizePath(config->getAppRoot(ap_document_root(r))), mstat, throttleRate)) {
			baseURIKnown = true;
			baseURI = "/";
			appType = WSGI;
			return baseURI;
		}
		
		baseURIKnown = true;
		return NULL;
	}
	
	/**
	 * Returns the filename of the 'public' directory of the Rails/Rack application
	 * that's associated with the HTTP request.
	 *
	 * Returns an empty string if the document root of the HTTP request
	 * cannot be determined, or if it isn't a valid folder.
	 *
	 * @throws FileSystemException An error occured while examening the filesystem.
	 */
	string getPublicDirectory() {
		if (!baseURIKnown) {<commit_msg>Optimization: do not canonicalize paths when autodetecting Rails/Rack/WSGI applications. Path canonicalization is unnecessary here.<commit_after>/*
 *  Phusion Passenger - http://www.modrails.com/
 *  Copyright (C) 2008  Phusion
 *
 *  Phusion Passenger is a trademark of <NAME> & <NAME>.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef _PASSENGER_DIRECTORY_MAPPER_H_
#define _PASSENGER_DIRECTORY_MAPPER_H_

#include <string>
#include <set>
#include <cstring>

#include <oxt/backtrace.hpp>

#include "CachedFileStat.h"
#include "Configuration.h"
#include "Utils.h"

// The Apache/APR headers *must* come after the Boost headers, otherwise
// compilation will fail on OpenBSD.
#include <httpd.h>
#include <http_core.h>

namespace Passenger {

using namespace std;
using namespace oxt;

/**
 * Utility class for determining URI-to-application directory mappings.
 * Given a URI, it will determine whether that URI belongs to a Phusion
 * Passenger-handled application, what the base URI of that application is,
 * and what the associated 'public' directory is.
 *
 * @note This class is not thread-safe, but is reentrant.
 * @ingroup Core
 */
class DirectoryMapper {
public:
	enum ApplicationType {
		NONE,
		RAILS,
		RACK,
		WSGI
	};

private:
	DirConfig *config;
	request_rec *r;
	CachedMultiFileStat *mstat;
	unsigned int throttleRate;
	bool baseURIKnown;
	const char *baseURI;
	ApplicationType appType;
	
	inline bool shouldAutoDetectRails() {
		return config->autoDetectRails == DirConfig::ENABLED ||
			config->autoDetectRails == DirConfig::UNSET;
	}
	
	inline bool shouldAutoDetectRack() {
		return config->autoDetectRack == DirConfig::ENABLED ||
			config->autoDetectRack == DirConfig::UNSET;
	}
	
	inline bool shouldAutoDetectWSGI() {
		return config->autoDetectWSGI == DirConfig::ENABLED ||
			config->autoDetectWSGI == DirConfig::UNSET;
	}
	
public:
	/**
	 * Create a new DirectoryMapper object.
	 *
	 * @param mstat A CachedMultiFileStat object used for statting files.
	 * @param throttleRate A throttling rate for mstat.
	 * @warning Do not use this object after the destruction of <tt>r</tt>,
	 *          <tt>config</tt> or <tt>mstat</tt>.
	 */
	DirectoryMapper(request_rec *r, DirConfig *config,
	                CachedMultiFileStat *mstat, unsigned int throttleRate) {
		this->r = r;
		this->config = config;
		this->mstat = mstat;
		this->throttleRate = throttleRate;
		appType = NONE;
		baseURIKnown = false;
		baseURI = NULL;
	}
	
	/**
	 * Determine whether the given HTTP request falls under one of the specified
	 * RailsBaseURIs or RackBaseURIs. If yes, then the first matching base URI will
	 * be returned.
	 *
	 * If Rails/Rack autodetection was enabled in the configuration, and the document
	 * root seems to be a valid Rails/Rack 'public' folder, then this method will
	 * return "/".
	 *
	 * Otherwise, NULL will be returned.
	 *
	 * @throws FileSystemException This method might also examine the filesystem in
	 *            order to detect the application's type. During that process, a
	 *            FileSystemException might be thrown.
	 * @warning The return value may only be used as long as <tt>config</tt>
	 *          hasn't been destroyed.
	 */
	const char *getBaseURI() {
		TRACE_POINT();
		if (baseURIKnown) {
			return baseURI;
		}
		
		set<string>::const_iterator it;
		const char *uri = r->uri;
		size_t uri_len = strlen(uri);
		
		if (uri_len == 0 || uri[0] != '/') {
			baseURIKnown = true;
			return NULL;
		}
		
		UPDATE_TRACE_POINT();
		for (it = config->railsBaseURIs.begin(); it != config->railsBaseURIs.end(); it++) {
			const string &base(*it);
			if (  base == "/"
			 || ( uri_len == base.size() && memcmp(uri, base.c_str(), uri_len) == 0 )
			 || ( uri_len  > base.size() && memcmp(uri, base.c_str(), base.size()) == 0
			                             && uri[base.size()] == '/' )
			) {
				baseURIKnown = true;
				baseURI = base.c_str();
				appType = RAILS;
				return baseURI;
			}
		}
		
		UPDATE_TRACE_POINT();
		for (it = config->rackBaseURIs.begin(); it != config->rackBaseURIs.end(); it++) {
			const string &base(*it);
			if (  base == "/"
			 || ( uri_len == base.size() && memcmp(uri, base.c_str(), uri_len) == 0 )
			 || ( uri_len  > base.size() && memcmp(uri, base.c_str(), base.size()) == 0
			                             && uri[base.size()] == '/' )
			) {
				baseURIKnown = true;
				baseURI = base.c_str();
				appType = RACK;
				return baseURI;
			}
		}
		
		UPDATE_TRACE_POINT();
		if (shouldAutoDetectRails()
		 && verifyRailsDir(config->getAppRoot(ap_document_root(r)), mstat, throttleRate)) {
			baseURIKnown = true;
			baseURI = "/";
			appType = RAILS;
			return baseURI;
		}
		
		UPDATE_TRACE_POINT();
		if (shouldAutoDetectRack()
		 && verifyRackDir(config->getAppRoot(ap_document_root(r)), mstat, throttleRate)) {
			baseURIKnown = true;
			baseURI = "/";
			appType = RACK;
			return baseURI;
		}
		
		UPDATE_TRACE_POINT();
		if (shouldAutoDetectWSGI()
		 && verifyWSGIDir(config->getAppRoot(ap_document_root(r)), mstat, throttleRate)) {
			baseURIKnown = true;
			baseURI = "/";
			appType = WSGI;
			return baseURI;
		}
		
		baseURIKnown = true;
		return NULL;
	}
	
	/**
	 * Returns the filename of the 'public' directory of the Rails/Rack application
	 * that's associated with the HTTP request.
	 *
	 * Returns an empty string if the document root of the HTTP request
	 * cannot be determined, or if it isn't a valid folder.
	 *
	 * @throws FileSystemException An error occured while examening the filesystem.
	 */
	string getPublicDirectory() {
		if (!baseURIKnown) {
{'fraction_non_alphanumeric': 0.11394557823129252, 'fraction_numerical': 0.0035633300939423387, 'mean_word_length': 3.3513037350246653, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4797117', 'n_tokens_mistral': 4353, 'n_tokens_neox': 3792, 'n_words': 1223}