Your IP : 18.221.221.171
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2014-2017 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_SERVER_KIT_HTTP_REQUEST_H_
#define _PASSENGER_SERVER_KIT_HTTP_REQUEST_H_
#include <psg_sysqueue.h>
#include <boost/cstdint.hpp>
#include <boost/atomic.hpp>
#include <ServerKit/llhttp.h>
#include <ServerKit/Hooks.h>
#include <ServerKit/Client.h>
#include <ServerKit/HeaderTable.h>
#include <ServerKit/FileBufferedChannel.h>
#include <ServerKit/HttpHeaderParserState.h>
#include <ServerKit/HttpChunkedBodyParserState.h>
#include <MemoryKit/palloc.h>
#include <DataStructures/LString.h>
namespace Passenger {
namespace ServerKit {
class BaseHttpRequest {
public:
enum HttpState {
/** The request headers are still being parsed. */
PARSING_HEADERS,
/** Internal state used by the parser. Users should never see this state. */
PARSED_HEADERS,
/** The request headers have been parsed, and there is no body. */
COMPLETE,
/** The request headers have been parsed, and we are now receiving/parsing the body,
* which does not have the chunked transfer-encoding. */
PARSING_BODY,
/** The request headers have been parsed, and we are now receiving/parsing the body,
* which has the chunked transfer-encoding. */
PARSING_CHUNKED_BODY,
/** The request headers have been parsed, and the connection has been upgraded. */
UPGRADED,
// The following states are recognized as 'ended'.
/** An error occurred. */
ERROR,
/**
* The request has been ended. We've deinitialized the request object, and we're now
* waiting for output to be flushed before transitioning to WAITING_FOR_REFERENCES.
* In this state, the client object's `currentRequest` field still points to this
* request.
*/
FLUSHING_OUTPUT,
/**
* The request has ended. We've deinitialized the request object, and we're now
* waiting until all references to this request object are gone. In this state,
* the client object's `currentRequest` field no longer points to this request.
*/
WAITING_FOR_REFERENCES,
/** This request object is in the freelist. */
IN_FREELIST
};
// Enum values are deliberately chosen so that hasBody() can be branchless.
enum BodyType {
/** The request has no body and the connection will not be upgraded. */
RBT_NO_BODY = 0,
/** The connection has been upgraded. */
RBT_UPGRADE = 1,
/** The request body's size is determined by the Content-Length header. */
RBT_CONTENT_LENGTH = 2,
/** The request body's size is determined by the chunked Transfer-Encoding. */
RBT_CHUNKED = 4
};
boost::uint8_t httpMajor;
boost::uint8_t httpMinor;
HttpState httpState: 5;
BodyType bodyType: 3;
llhttp_method_t method: 6;
bool wantKeepAlive: 1;
bool responseBegun: 1;
bool detectingNextRequestEarlyReadError: 1;
boost::atomic<int> refcount;
BaseClient *client;
union {
// If httpState == PARSING_HEADERS
HttpHeaderParserState *headerParser;
// If httpState == PARSING_CHUNKED_BODY
HttpChunkedBodyParserState chunkedBodyParser;
} parserState;
psg_pool_t *pool;
Hooks hooks;
// Guaranteed to be contiguous.
LString path;
HeaderTable headers;
// We separate headers and secure headers because the number of normal
// headers is variable, but the number of secure headers is more or less
// constant.
HeaderTable secureHeaders;
// HttpServer feeds all body data received via client->input to bodyChannel
Channel bodyChannel;
union {
/** Length of the message body. Only use when httpState != ERROR. */
union {
// If bodyType == RBT_CONTENT_LENGTH. Guaranteed to be > 0.
boost::uint64_t contentLength;
// If bodyType == RBT_CHUNKED
bool endChunkReached;
} bodyInfo;
/** If a request parsing error occurred, the error code is stored here.
* Only use if httpState == ERROR.
*/
int parseError;
} aux;
boost::uint64_t bodyAlreadyRead;
ev_tstamp lastDataReceiveTime;
ev_tstamp lastDataSendTime;
/**
* The start index of the '?' character in `path`. -1 when it doesn't exist.
*/
int queryStringIndex;
/* When a body error is encountered and bodyChannel is not immediately available,
* the error code is temporarily stored here.
*/
int bodyError;
/**
* When a request body read error, or a client socket EOF, has been detected
* after the current request body has already fully received, the error code is
* temporarily stored here so that it may be processed at the next request. The
* error is not passed to the bodyChannel immediately because it isn't an error
* part of the current request's body. But users of HttpServer can still query
* this field to see that an error is imminent, and may choose to abort early.
*
* The value is either the body read error code, or EARLY_EOF_DETECTED. The
* latter means that a client socket EOF has been detected.
*
* A value of 0 means that everthing is ok.
*/
int nextRequestEarlyReadError;
BaseHttpRequest()
: refcount(1),
client(NULL),
pool(NULL),
headers(16),
secureHeaders(32),
bodyAlreadyRead(0)
{
psg_lstr_init(&path);
aux.bodyInfo.contentLength = 0; // Sets the entire union to 0.
}
const char *getHttpStateString() const {
switch (httpState) {
case PARSING_HEADERS:
return "PARSING_HEADERS";
case PARSED_HEADERS:
return "PARSED_HEADERS";
case COMPLETE:
return "COMPLETE";
case PARSING_BODY:
return "PARSING_BODY";
case PARSING_CHUNKED_BODY:
return "PARSING_CHUNKED_BODY";
case UPGRADED:
return "UPGRADED";
case ERROR:
return "ERROR";
case FLUSHING_OUTPUT:
return "FLUSHING_OUTPUT";
case WAITING_FOR_REFERENCES:
return "WAITING_FOR_REFERENCES";
case IN_FREELIST:
return "IN_FREELIST";
default:
return "UNKNOWN";
}
}
const char *getBodyTypeString() const {
switch (bodyType) {
case RBT_NO_BODY:
return "NO_BODY";
case RBT_UPGRADE:
return "UPGRADE";
case RBT_CONTENT_LENGTH:
return "CONTENT_LENGTH";
case RBT_CHUNKED:
return "CHUNKED";
default:
return "UNKNOWN";
}
}
bool bodyFullyRead() const {
switch (bodyType) {
case RBT_NO_BODY:
return true;
case RBT_UPGRADE:
return false;
case RBT_CONTENT_LENGTH:
return bodyAlreadyRead >= aux.bodyInfo.contentLength;
case RBT_CHUNKED:
return aux.bodyInfo.endChunkReached;
default:
return false;
}
}
bool hasBody() const {
return bodyType & (RBT_CONTENT_LENGTH | RBT_CHUNKED);
}
bool upgraded() const {
return bodyType == RBT_UPGRADE;
}
// Not mutually exclusive with ended(). If a request has begun() and is ended(),
// then it just means that it hasn't been reinitialized for the next request yet.
bool begun() const {
return (int) httpState >= COMPLETE;
}
bool ended() const {
// Branchless OR.
return ((int) httpState >= (int) ERROR) | !client->connected();
}
StaticString getPathWithoutQueryString() const {
if (queryStringIndex == -1) {
return StaticString(path.start->data, path.size);
} else {
return StaticString(path.start->data, queryStringIndex);
}
}
StaticString getQueryString() const {
if (queryStringIndex == -1) {
return StaticString();
} else {
return StaticString(path.start->data + queryStringIndex + 1,
path.size - queryStringIndex - 1);
}
}
};
#define DEFINE_SERVER_KIT_BASE_HTTP_REQUEST_FOOTER(RequestType) \
public: \
union { \
STAILQ_ENTRY(RequestType) freeRequest; \
LIST_ENTRY(RequestType) lingeringRequest; \
} nextRequest
class HttpRequest: public BaseHttpRequest {
public:
DEFINE_SERVER_KIT_BASE_HTTP_REQUEST_FOOTER(Passenger::ServerKit::HttpRequest);
};
} // namespace ServerKit
} // namespace Passenger
#endif /* _PASSENGER_SERVER_KIT_HTTP_REQUEST_H_ */