Your IP : 3.145.43.92
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2011-2018 Phusion Holding B.V.
*
* "Passenger", "Phusion Passenger" and "Union Station" are registered
* trademarks of Phusion Holding B.V.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef _PASSENGER_CORE_CONTROLLER_H_
#define _PASSENGER_CORE_CONTROLLER_H_
//#define DEBUG_CC_EVENT_LOOP_BLOCKING
#define CC_BENCHMARK_POINT(client, req, value) \
do { \
if (OXT_UNLIKELY(mainConfig.benchmarkMode == value)) { \
writeBenchmarkResponse(&client, &req); \
return; \
} \
} while (false)
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/cstdint.hpp>
#include <oxt/macros.hpp>
#include <ev++.h>
#include <ostream>
#if defined(__GLIBCXX__) || defined(__APPLE__)
#include <cxxabi.h>
#define CXX_ABI_API_AVAILABLE
#endif
#include <sys/types.h>
#include <sys/uio.h>
#include <utility>
#include <typeinfo>
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <cassert>
#include <cctype>
#include <LoggingKit/LoggingKit.h>
#include <IOTools/MessageSerialization.h>
#include <Constants.h>
#include <ConfigKit/ConfigKit.h>
#include <ServerKit/Errors.h>
#include <ServerKit/HttpServer.h>
#include <ServerKit/HttpHeaderParser.h>
#include <MemoryKit/palloc.h>
#include <DataStructures/LString.h>
#include <DataStructures/StringKeyTable.h>
#include <WrapperRegistry/Registry.h>
#include <StaticString.h>
#include <Utils.h>
#include <StrIntTools/StrIntUtils.h>
#include <IOTools/IOUtils.h>
#include <JsonTools/JsonUtils.h>
#include <Utils/HttpConstants.h>
#include <Utils/Timer.h>
#include <Core/Controller/Config.h>
#include <Core/Controller/Client.h>
#include <Core/Controller/AppResponse.h>
#include <Core/Controller/TurboCaching.h>
namespace Passenger {
using namespace std;
using namespace boost;
using namespace oxt;
using namespace ApplicationPool2;
namespace ServerKit {
extern const HashedStaticString HTTP_COOKIE;
extern const HashedStaticString HTTP_SET_COOKIE;
}
namespace Core {
class Controller: public ServerKit::HttpServer<Controller, Client> {
private:
typedef ServerKit::HttpServer<Controller, Client> ParentClass;
typedef ServerKit::Channel Channel;
typedef ServerKit::FdSinkChannel FdSinkChannel;
typedef ServerKit::FdSourceChannel FdSourceChannel;
typedef ServerKit::FileBufferedChannel FileBufferedChannel;
typedef ServerKit::FileBufferedFdSinkChannel FileBufferedFdSinkChannel;
// If you change this value, make sure that Request::sessionCheckoutTry
// has enough bits.
static const unsigned int MAX_SESSION_CHECKOUT_TRY = 10;
ControllerMainConfig mainConfig;
ControllerRequestConfigPtr requestConfig;
StringKeyTable< boost::shared_ptr<Options> > poolOptionsCache;
HashedStaticString PASSENGER_APP_GROUP_NAME;
HashedStaticString PASSENGER_ENV_VARS;
HashedStaticString PASSENGER_MAX_REQUESTS;
HashedStaticString PASSENGER_SHOW_VERSION_IN_HEADER;
HashedStaticString PASSENGER_STICKY_SESSIONS;
HashedStaticString PASSENGER_STICKY_SESSIONS_COOKIE_NAME;
HashedStaticString PASSENGER_STICKY_SESSIONS_COOKIE_ATTRIBUTES;
HashedStaticString PASSENGER_REQUEST_OOB_WORK;
HashedStaticString REMOTE_ADDR;
HashedStaticString REMOTE_PORT;
HashedStaticString REMOTE_USER;
HashedStaticString FLAGS;
HashedStaticString HTTP_COOKIE;
HashedStaticString HTTP_DATE;
HashedStaticString HTTP_HOST;
HashedStaticString HTTP_CONTENT_LENGTH;
HashedStaticString HTTP_CONTENT_TYPE;
HashedStaticString HTTP_EXPECT;
HashedStaticString HTTP_CONNECTION;
HashedStaticString HTTP_STATUS;
HashedStaticString HTTP_TRANSFER_ENCODING;
friend class TurboCaching<Request>;
friend class ResponseCache<Request>;
struct ev_check checkWatcher;
TurboCaching<Request> turboCaching;
ConfigKit::Store *singleAppModeConfig;
#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
struct ev_prepare prepareWatcher;
ev_tstamp timeBeforeBlocking;
#endif
/****** Initialization and shutdown ******/
void preinitialize();
/****** Stage: initialize request ******/
struct RequestAnalysis;
void initializeFlags(Client *client, Request *req, RequestAnalysis &analysis);
bool respondFromTurboCache(Client *client, Request *req);
void initializePoolOptions(Client *client, Request *req, RequestAnalysis &analysis);
void fillPoolOptionsFromConfigCaches(Options &options, psg_pool_t *pool,
const ControllerRequestConfigPtr &requestConfigCache);
static void fillPoolOption(Request *req, StaticString &field,
const HashedStaticString &name);
static void fillPoolOption(Request *req, int &field,
const HashedStaticString &name);
static void fillPoolOption(Request *req, bool &field,
const HashedStaticString &name);
static void fillPoolOption(Request *req, unsigned int &field,
const HashedStaticString &name);
static void fillPoolOption(Request *req, unsigned long &field,
const HashedStaticString &name);
static void fillPoolOption(Request *req, long &field,
const HashedStaticString &name);
static void fillPoolOptionSecToMsec(Request *req, unsigned int &field,
const HashedStaticString &name);
void createNewPoolOptions(Client *client, Request *req,
const HashedStaticString &appGroupName);
void setStickySessionId(Client *client, Request *req);
const LString *getStickySessionCookieName(Request *req);
/****** Stage: buffering body ******/
void beginBufferingBody(Client *client, Request *req);
Channel::Result whenBufferingBody_onRequestBody(Client *client, Request *req,
const MemoryKit::mbuf &buffer, int errcode);
static void _bodyBufferFlushed(FileBufferedChannel *_channel);
/****** Stage: checkout session ******/
void checkoutSession(Client *client, Request *req);
static void sessionCheckedOut(const AbstractSessionPtr &session,
const ExceptionPtr &e, void *userData);
void sessionCheckedOutFromAnotherThread(Client *client, Request *req,
AbstractSessionPtr session, ExceptionPtr e);
void sessionCheckedOutFromEventLoopThread(Client *client, Request *req,
const AbstractSessionPtr &session, const ExceptionPtr &e);
void maybeSend100Continue(Client *client, Request *req);
void initiateSession(Client *client, Request *req);
static void checkoutSessionLater(Request *req);
void reportSessionCheckoutError(Client *client, Request *req,
const ExceptionPtr &e);
int lookupCodeFromHeader(Request *req, const char* header, int statusCode);
void writeRequestQueueFullExceptionErrorResponse(Client *client,
Request *req, const boost::shared_ptr<RequestQueueFullException> &e);
void writeSpawnExceptionErrorResponse(Client *client, Request *req,
const boost::shared_ptr<SpawningKit::SpawnException> &e);
void writeOtherExceptionErrorResponse(Client *client, Request *req,
const ExceptionPtr &e);
void endRequestWithErrorResponse(Client **c, Request **r,
const SpawningKit::SpawnException &e, int statusCode);
const LString* customErrorPageEnabled(Request *req);
bool friendlyErrorPagesEnabled(Request *req);
/****** Stage: send request to application ******/
struct SessionProtocolWorkingState;
struct HttpHeaderConstructionCache;
void sendHeaderToApp(Client *client, Request *req);
void sendHeaderToAppWithSessionProtocol(Client *client, Request *req);
static void sendBodyToAppWhenAppSinkIdle(Channel *_channel, unsigned int size);
unsigned int determineMaxHeaderSizeForSessionProtocol(Request *req,
SessionProtocolWorkingState &state, string delta_monotonic);
bool constructHeaderForSessionProtocol(Request *req, char * restrict buffer,
unsigned int &size, const SessionProtocolWorkingState &state, string delta_monotonic);
void sendHeaderToAppWithHttpProtocol(Client *client, Request *req);
bool constructHeaderBuffersForHttpProtocol(Request *req, struct iovec *buffers,
unsigned int maxbuffers, unsigned int & restrict_ref nbuffers,
unsigned int & restrict_ref dataSize, HttpHeaderConstructionCache &cache);
bool sendHeaderToAppWithHttpProtocolAndWritev(Request *req, ssize_t &bytesWritten,
HttpHeaderConstructionCache &cache);
void sendHeaderToAppWithHttpProtocolWithBuffering(Request *req, unsigned int offset,
HttpHeaderConstructionCache &cache);
void sendBodyToApp(Client *client, Request *req);
void maybeHalfCloseAppSinkBecauseRequestBodyEndReached(Client *client, Request *req);
Channel::Result whenSendingRequest_onRequestBody(Client *client, Request *req,
const MemoryKit::mbuf &buffer, int errcode);
static void resumeRequestBodyChannelWhenAppSinkIdle(Channel *_channel,
unsigned int size);
void startBodyChannel(Client *client, Request *req);
void stopBodyChannel(Client *client, Request *req);
void logAppSocketWriteError(Client *client, int errcode);
/****** Stage: forward application response to client ******/
static Channel::Result _onAppSourceData(Channel *_channel,
const MemoryKit::mbuf &buffer, int errcode);
Channel::Result onAppSourceData(Client *client, Request *req,
const MemoryKit::mbuf &buffer, int errcode);
void onAppResponseBegin(Client *client, Request *req);
void prepareAppResponseCaching(Client *client, Request *req);
void onAppResponse100Continue(Client *client, Request *req);
bool constructHeaderBuffersForResponse(Request *req, struct iovec *buffers,
unsigned int maxbuffers, unsigned int & restrict_ref nbuffers,
unsigned int & restrict_ref dataSize,
unsigned int & restrict_ref nCacheableBuffers);
unsigned int constructDateHeaderBuffersForResponse(char *dateStr,
unsigned int bufsize);
bool sendResponseHeaderWithWritev(Client *client, Request *req,
ssize_t &bytesWritten);
void sendResponseHeaderWithBuffering(Client *client, Request *req,
unsigned int offset);
void logResponseHeaders(Client *client, Request *req, struct iovec *buffers,
unsigned int nbuffers, unsigned int dataSize);
void markHeaderBuffersForTurboCaching(Client *client, Request *req,
struct iovec *buffers, unsigned int nbuffers);
static ServerKit::HttpHeaderParser<AppResponse, ServerKit::HttpParseResponse>
createAppResponseHeaderParser(ServerKit::Context *ctx, Request *req);
static ServerKit::HttpChunkedBodyParser createAppResponseChunkedBodyParser(
Request *req);
static unsigned int formatAppResponseChunkedBodyParserLoggingPrefix(char *buf,
unsigned int bufsize, void *userData);
void prepareAppResponseChunkedBodyParsing(Client *client, Request *req);
void writeResponseAndMarkForTurboCaching(Client *client, Request *req,
const MemoryKit::mbuf &buffer);
void markResponsePartForTurboCaching(Client *client, Request *req,
const MemoryKit::mbuf &buffer);
void maybeThrottleAppSource(Client *client, Request *req);
static void _outputBuffersFlushed(FileBufferedChannel *_channel);
void outputBuffersFlushed(Client *client, Request *req);
static void _outputDataFlushed(FileBufferedChannel *_channel);
void outputDataFlushed(Client *client, Request *req);
void handleAppResponseBodyEnd(Client *client, Request *req);
OXT_FORCE_INLINE void keepAliveAppConnection(Client *client, Request *req);
void storeAppResponseInTurboCache(Client *client, Request *req);
/***** Hooks ******/
static Channel::Result onBodyBufferData(Channel *_channel,
const MemoryKit::mbuf &buffer, int errcode);
#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
static void onEventLoopPrepare(EV_P_ struct ev_prepare *w, int revents);
#endif
static void onEventLoopCheck(EV_P_ struct ev_check *w, int revents);
/****** Internal utility functions ******/
void disconnectWithClientSocketWriteError(Client **client, int e);
void disconnectWithAppSocketIncompleteResponseError(Client **client);
void disconnectWithAppSocketReadError(Client **client, int e);
void disconnectWithAppSocketWriteError(Client **client, int e);
void endRequestWithAppSocketIncompleteResponse(Client **client,
Request **req);
void endRequestWithAppSocketReadError(Client **client, Request **req,
int e);
ServerKit::HeaderTable getHeadersWithContentType(Request *req);
const string getFormattedMessage(Request *req, const StaticString &body);
void endRequestWithSimpleResponse(Client **c, Request **r,
const StaticString &body, int code = 200);
void endRequestAsBadGateway(Client **client, Request **req);
void writeBenchmarkResponse(Client **client, Request **req,
bool end = true);
bool getBoolOption(Request *req, const HashedStaticString &name,
bool defaultValue = false);
template<typename Number> static Number clamp(Number value,
Number min, Number max);
static void gatherBuffers(char * restrict dest, unsigned int size,
const struct iovec *buffers, unsigned int nbuffers);
static LString *resolveSymlink(const StaticString &path, psg_pool_t *pool);
void parseCookieHeader(psg_pool_t *pool, const LString *headerValue,
vector< pair<StaticString, StaticString> > &cookies) const;
#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
void reportLargeTimeDiff(Client *client, const char *name,
ev_tstamp fromTime, ev_tstamp toTime);
#endif
protected:
/****** Stage: initialize request ******/
virtual void onRequestBegin(Client *client, Request *req);
/****** Hooks ******/
virtual void onClientAccepted(Client *client);
virtual void onRequestObjectCreated(Client *client, Request *req);
virtual void deinitializeClient(Client *client);
virtual void reinitializeRequest(Client *client, Request *req);
virtual void deinitializeRequest(Client *client, Request *req);
void reinitializeAppResponse(Client *client, Request *req);
void deinitializeAppResponse(Client *client, Request *req);
virtual Channel::Result onRequestBody(Client *client, Request *req,
const MemoryKit::mbuf &buffer, int errcode);
virtual void onNextRequestEarlyReadError(Client *client, Request *req, int errcode);
virtual bool shouldDisconnectClientOnShutdown(Client *client);
virtual bool shouldAutoDechunkBody(Client *client, Request *req);
virtual bool supportsUpgrade(Client *client, Request *req);
/****** Marked virtual so that unit tests can mock these ******/
virtual void asyncGetFromApplicationPool(Request *req,
ApplicationPool2::GetCallback callback);
public:
typedef ControllerConfigChangeRequest ConfigChangeRequest;
// Dependencies
ResourceLocator *resourceLocator;
WrapperRegistry::Registry *wrapperRegistry;
PoolPtr appPool;
/****** Initialization and shutdown ******/
Controller(ServerKit::Context *context,
const ControllerSchema &schema,
const Json::Value &initialConfig,
const ConfigKit::Translator &translator1 = ConfigKit::DummyTranslator(),
const ControllerSingleAppModeSchema *singleAppModeSchema = NULL,
const Json::Value *_singleAppModeConfig = NULL,
const ConfigKit::Translator &translator2 = ConfigKit::DummyTranslator()
)
: ParentClass(context, schema, initialConfig, translator1),
mainConfig(config),
requestConfig(new ControllerRequestConfig(config)),
poolOptionsCache(4),
turboCaching(),
singleAppModeConfig(NULL),
resourceLocator(NULL)
/**************************/
{
if (mainConfig.singleAppMode) {
singleAppModeConfig = new ConfigKit::Store(*singleAppModeSchema,
*_singleAppModeConfig, translator2);
}
preinitialize();
}
virtual ~Controller();
virtual void initialize();
/****** Hooks ******/
virtual unsigned int getClientName(const Client *client, char *buf, size_t size) const;
virtual StaticString getServerName() const;
/****** Configuration handling ******/
bool prepareConfigChange(const Json::Value &updates,
vector<ConfigKit::Error> &errors, ControllerConfigChangeRequest &req);
void commitConfigChange(ControllerConfigChangeRequest &req)
BOOST_NOEXCEPT_OR_NOTHROW;
/****** State and configuration ******/
unsigned int getThreadNumber() const; // Thread-safe
virtual Json::Value inspectStateAsJson() const;
virtual Json::Value inspectClientStateAsJson(const Client *client) const;
virtual Json::Value inspectRequestStateAsJson(const Request *req) const;
/****** Miscellaneous *******/
void disconnectLongRunningConnections(const StaticString &gupid);
};
} // namespace Core
} // namespace Passenger
#endif /* _PASSENGER_CORE_CONTROLLER_H_ */