• Main Page
  • Modules
  • Classes
  • Files
  • File List

DirectoryMapper.h

00001 /*
00002  *  Phusion Passenger - http://www.modrails.com/
00003  *  Copyright (c) 2008, 2009 Phusion
00004  *
00005  *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
00006  *
00007  *  Permission is hereby granted, free of charge, to any person obtaining a copy
00008  *  of this software and associated documentation files (the "Software"), to deal
00009  *  in the Software without restriction, including without limitation the rights
00010  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00011  *  copies of the Software, and to permit persons to whom the Software is
00012  *  furnished to do so, subject to the following conditions:
00013  *
00014  *  The above copyright notice and this permission notice shall be included in
00015  *  all copies or substantial portions of the Software.
00016  *
00017  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00020  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00022  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00023  *  THE SOFTWARE.
00024  */
00025 #ifndef _PASSENGER_DIRECTORY_MAPPER_H_
00026 #define _PASSENGER_DIRECTORY_MAPPER_H_
00027 
00028 #include <string>
00029 #include <set>
00030 #include <cstring>
00031 
00032 #include <oxt/backtrace.hpp>
00033 
00034 #include "CachedFileStat.hpp"
00035 #include "Configuration.h"
00036 #include "Utils.h"
00037 
00038 // The Apache/APR headers *must* come after the Boost headers, otherwise
00039 // compilation will fail on OpenBSD.
00040 #include <httpd.h>
00041 #include <http_core.h>
00042 
00043 namespace Passenger {
00044 
00045 using namespace std;
00046 using namespace oxt;
00047 
00048 /**
00049  * Utility class for determining URI-to-application directory mappings.
00050  * Given a URI, it will determine whether that URI belongs to a Phusion
00051  * Passenger-handled application, what the base URI of that application is,
00052  * and what the associated 'public' directory is.
00053  *
00054  * @note This class is not thread-safe, but is reentrant.
00055  * @ingroup Core
00056  */
00057 class DirectoryMapper {
00058 public:
00059         enum ApplicationType {
00060                 NONE,
00061                 RAILS,
00062                 RACK,
00063                 WSGI
00064         };
00065 
00066 private:
00067         DirConfig *config;
00068         request_rec *r;
00069         CachedFileStat *cstat;
00070         unsigned int throttleRate;
00071         bool baseURIKnown;
00072         const char *baseURI;
00073         ApplicationType appType;
00074         
00075         inline bool shouldAutoDetectRails() {
00076                 return config->autoDetectRails == DirConfig::ENABLED ||
00077                         config->autoDetectRails == DirConfig::UNSET;
00078         }
00079         
00080         inline bool shouldAutoDetectRack() {
00081                 return config->autoDetectRack == DirConfig::ENABLED ||
00082                         config->autoDetectRack == DirConfig::UNSET;
00083         }
00084         
00085         inline bool shouldAutoDetectWSGI() {
00086                 return config->autoDetectWSGI == DirConfig::ENABLED ||
00087                         config->autoDetectWSGI == DirConfig::UNSET;
00088         }
00089         
00090 public:
00091         /**
00092          * Create a new DirectoryMapper object.
00093          *
00094          * @param cstat A CachedFileStat object used for statting files.
00095          * @param throttleRate A throttling rate for cstat.
00096          * @warning Do not use this object after the destruction of <tt>r</tt>,
00097          *          <tt>config</tt> or <tt>cstat</tt>.
00098          */
00099         DirectoryMapper(request_rec *r, DirConfig *config,
00100                         CachedFileStat *cstat, unsigned int throttleRate) {
00101                 this->r = r;
00102                 this->config = config;
00103                 this->cstat = cstat;
00104                 this->throttleRate = throttleRate;
00105                 appType = NONE;
00106                 baseURIKnown = false;
00107                 baseURI = NULL;
00108         }
00109         
00110         /**
00111          * Determine whether the given HTTP request falls under one of the specified
00112          * RailsBaseURIs or RackBaseURIs. If yes, then the first matching base URI will
00113          * be returned.
00114          *
00115          * If Rails/Rack autodetection was enabled in the configuration, and the document
00116          * root seems to be a valid Rails/Rack 'public' folder, then this method will
00117          * return "/".
00118          *
00119          * Otherwise, NULL will be returned.
00120          *
00121          * @throws FileSystemException This method might also examine the filesystem in
00122          *            order to detect the application's type. During that process, a
00123          *            FileSystemException might be thrown.
00124          * @warning The return value may only be used as long as <tt>config</tt>
00125          *          hasn't been destroyed.
00126          */
00127         const char *getBaseURI() {
00128                 TRACE_POINT();
00129                 if (baseURIKnown) {
00130                         return baseURI;
00131                 }
00132                 
00133                 set<string>::const_iterator it;
00134                 const char *uri = r->uri;
00135                 size_t uri_len = strlen(uri);
00136                 
00137                 if (uri_len == 0 || uri[0] != '/') {
00138                         baseURIKnown = true;
00139                         return NULL;
00140                 }
00141                 
00142                 UPDATE_TRACE_POINT();
00143                 for (it = config->railsBaseURIs.begin(); it != config->railsBaseURIs.end(); it++) {
00144                         const string &base(*it);
00145                         if (  base == "/"
00146                          || ( uri_len == base.size() && memcmp(uri, base.c_str(), uri_len) == 0 )
00147                          || ( uri_len  > base.size() && memcmp(uri, base.c_str(), base.size()) == 0
00148                                                      && uri[base.size()] == '/' )
00149                         ) {
00150                                 baseURIKnown = true;
00151                                 baseURI = base.c_str();
00152                                 appType = RAILS;
00153                                 return baseURI;
00154                         }
00155                 }
00156                 
00157                 UPDATE_TRACE_POINT();
00158                 for (it = config->rackBaseURIs.begin(); it != config->rackBaseURIs.end(); it++) {
00159                         const string &base(*it);
00160                         if (  base == "/"
00161                          || ( uri_len == base.size() && memcmp(uri, base.c_str(), uri_len) == 0 )
00162                          || ( uri_len  > base.size() && memcmp(uri, base.c_str(), base.size()) == 0
00163                                                      && uri[base.size()] == '/' )
00164                         ) {
00165                                 baseURIKnown = true;
00166                                 baseURI = base.c_str();
00167                                 appType = RACK;
00168                                 return baseURI;
00169                         }
00170                 }
00171                 
00172                 UPDATE_TRACE_POINT();
00173                 if (shouldAutoDetectRack()
00174                  && verifyRackDir(config->getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
00175                         baseURIKnown = true;
00176                         baseURI = "/";
00177                         appType = RACK;
00178                         return baseURI;
00179                 }
00180                 
00181                 UPDATE_TRACE_POINT();
00182                 if (shouldAutoDetectRails()
00183                  && verifyRailsDir(config->getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
00184                         baseURIKnown = true;
00185                         baseURI = "/";
00186                         appType = RAILS;
00187                         return baseURI;
00188                 }
00189                 
00190                 UPDATE_TRACE_POINT();
00191                 if (shouldAutoDetectWSGI()
00192                  && verifyWSGIDir(config->getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
00193                         baseURIKnown = true;
00194                         baseURI = "/";
00195                         appType = WSGI;
00196                         return baseURI;
00197                 }
00198                 
00199                 baseURIKnown = true;
00200                 return NULL;
00201         }
00202         
00203         /**
00204          * Returns the filename of the 'public' directory of the Rails/Rack application
00205          * that's associated with the HTTP request.
00206          *
00207          * Returns an empty string if the document root of the HTTP request
00208          * cannot be determined, or if it isn't a valid folder.
00209          *
00210          * @throws FileSystemException An error occured while examening the filesystem.
00211          */
00212         string getPublicDirectory() {
00213                 if (!baseURIKnown) {
00214                         getBaseURI();
00215                 }
00216                 if (baseURI == NULL) {
00217                         return "";
00218                 }
00219                 
00220                 const char *docRoot = ap_document_root(r);
00221                 size_t len = strlen(docRoot);
00222                 if (len > 0) {
00223                         string path;
00224                         if (docRoot[len - 1] == '/') {
00225                                 path.assign(docRoot, len - 1);
00226                         } else {
00227                                 path.assign(docRoot, len);
00228                         }
00229                         if (strcmp(baseURI, "/") != 0) {
00230                                 /* Application is deployed in a sub-URI.
00231                                  * This is probably a symlink, so let's resolve it.
00232                                  */
00233                                 path.append(baseURI);
00234                                 path = resolveSymlink(path);
00235                         }
00236                         return path;
00237                 } else {
00238                         return "";
00239                 }
00240         }
00241         
00242         /**
00243          * Returns the application type that's associated with the HTTP request.
00244          *
00245          * @throws FileSystemException An error occured while examening the filesystem.
00246          */
00247         ApplicationType getApplicationType() {
00248                 if (!baseURIKnown) {
00249                         getBaseURI();
00250                 }
00251                 return appType;
00252         }
00253         
00254         /**
00255          * Returns the application type (as a string) that's associated
00256          * with the HTTP request.
00257          *
00258          * @throws FileSystemException An error occured while examening the filesystem.
00259          */
00260         const char *getApplicationTypeString() {
00261                 if (!baseURIKnown) {
00262                         getBaseURI();
00263                 }
00264                 switch (appType) {
00265                 case RAILS:
00266                         return "rails";
00267                 case RACK:
00268                         return "rack";
00269                 case WSGI:
00270                         return "wsgi";
00271                 default:
00272                         return NULL;
00273                 };
00274         }
00275         
00276         /**
00277          * Returns the environment under which the application should be spawned.
00278          *
00279          * @throws FileSystemException An error occured while examening the filesystem.
00280          */
00281         const char *getEnvironment() {
00282                 switch (getApplicationType()) {
00283                 case RAILS:
00284                         return config->getRailsEnv();
00285                 case RACK:
00286                         return config->getRackEnv();
00287                 default:
00288                         return "production";
00289                 }
00290         }
00291 };
00292 
00293 } // namespace Passenger
00294 
00295 #endif /* _PASSENGER_DIRECTORY_MAPPER_H_ */
00296 

Generated on Mon Jan 18 2016 18:22:10 for Passenger by  doxygen 1.7.1