...........................................................................................................................................................................................................................................................................................................................@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%PDF-1.5 MRK IS HERE %���� ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµùÕ5sLOšuY
| Server IP : 65.108.66.160 / Your IP : 216.73.217.50 Web Server : Apache System : Linux srv16.asso.com.ar 4.18.0-553.123.1.el8_10.x86_64 #1 SMP Tue May 5 04:00:43 EDT 2026 x86_64 User : alasaweborg ( 1047) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /etc/passenger/src/cxx_supportlib/ServerKit/ |
Upload File : |
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2014-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_SERVER_KIT_HTTP_HEADER_PARSER_H_
#define _PASSENGER_SERVER_KIT_HTTP_HEADER_PARSER_H_
#include <boost/cstdint.hpp>
#include <oxt/backtrace.hpp>
#include <cstddef>
#include <cassert>
#include <cstring>
#include <MemoryKit/mbuf.h>
#include <ServerKit/Context.h>
#include <ServerKit/HttpRequest.h>
#include <ServerKit/HttpHeaderParserState.h>
#include <DataStructures/LString.h>
#include <DataStructures/HashedStaticString.h>
#include <LoggingKit/LoggingKit.h>
#include <StrIntTools/StrIntUtils.h>
#include <Algorithms/Hasher.h>
namespace Passenger {
namespace ServerKit {
extern const HashedStaticString HTTP_CONTENT_LENGTH;
extern const HashedStaticString HTTP_TRANSFER_ENCODING;
extern const HashedStaticString HTTP_X_SENDFILE;
extern const HashedStaticString HTTP_X_ACCEL_REDIRECT;
struct HttpParseRequest {};
struct HttpParseResponse {};
template<typename Message, typename MessageType = HttpParseRequest>
class HttpHeaderParser {
private:
Context *ctx;
HttpHeaderParserState *state;
Message *message;
psg_pool_t *pool;
const MemoryKit::mbuf *currentBuffer;
http_method requestMethod;
bool validateHeader(const HttpParseRequest &tag, const Header *header) {
if (!state->secureMode) {
if (!psg_lstr_cmp(&header->key, P_STATIC_STRING("!~"), 2)) {
return true;
} else {
if (header->key.size == 2) {
// Security password. Check whether it hasn't been
// given before and whether it is correct.
if (ctx->config.secureModePassword.empty()
|| psg_lstr_cmp(&header->val, ctx->config.secureModePassword))
{
state->secureMode = true;
return true;
} else {
state->state = HttpHeaderParserState::ERROR_SECURITY_PASSWORD_MISMATCH;
return false;
}
} else {
// Secure header encountered without having
// encountered a security password.
state->state = HttpHeaderParserState::ERROR_SECURE_HEADER_NOT_ALLOWED;
return false;
}
}
} else {
if (psg_lstr_cmp(&header->key, P_STATIC_STRING("!~"), 2)) {
if (header->key.size == 2) {
state->secureMode = false;
}
return true;
} else {
// To prevent Internet clients from injecting secure headers,
// we require the web server put secure headers between a begin
// marker (the security password header) and an end marker.
// If we find a normal header between the markers, then we
// can assume the web server is bugged or compromised.
state->state = HttpHeaderParserState::ERROR_NORMAL_HEADER_NOT_ALLOWED_AFTER_SECURITY_PASSWORD;
return false;
}
}
}
bool validateHeader(const HttpParseResponse &tag, const Header *header) {
state->secureMode = psg_lstr_cmp(&header->key, P_STATIC_STRING("!~"), 2);
return true;
}
void insertCurrentHeader() {
if (!state->secureMode) {
message->headers.insert(&state->currentHeader, pool);
} else {
message->secureHeaders.insert(&state->currentHeader, pool);
}
}
static size_t http_parser_execute_and_handle_pause(http_parser *parser,
const http_parser_settings *settings, const char *data, size_t len,
bool &paused)
{
size_t ret = http_parser_execute(parser, settings, data, len);
paused = len > 0 && ret != len && HTTP_PARSER_ERRNO(parser) == HPE_PAUSED;
if (paused) {
http_parser_pause(parser, 0);
http_parser_execute(parser, settings, data + len - 1, 1);
}
return ret;
}
static int _onURL(http_parser *parser, const char *data, size_t len) {
HttpHeaderParser *self = static_cast<HttpHeaderParser *>(parser->data);
return self->onURL(MessageType(), data, len);
}
OXT_FORCE_INLINE
int onURL(const HttpParseRequest &tag, const char *data, size_t len) {
state->state = HttpHeaderParserState::PARSING_URL;
psg_lstr_append(&message->path, pool, *currentBuffer, data, len);
return 0;
}
OXT_FORCE_INLINE
int onURL(const HttpParseResponse &tag, const char *data, size_t len) {
P_BUG("Should never be called");
return 0;
}
static int onStatus(http_parser *parser, const char *data, size_t len) {
HttpHeaderParser *self = static_cast<HttpHeaderParser *>(parser->data);
if (parser->status_code == 100) {
self->set100ContinueHttpState(MessageType());
http_parser_pause(parser, 1);
}
return 0;
}
void set100ContinueHttpState(const HttpParseRequest &tag) {
P_BUG("Should never be called");
}
void set100ContinueHttpState(const HttpParseResponse &tag) {
message->httpState = Message::ONEHUNDRED_CONTINUE;
}
static int onHeaderField(http_parser *parser, const char *data, size_t len) {
HttpHeaderParser *self = static_cast<HttpHeaderParser *>(parser->data);
if (self->state->state == HttpHeaderParserState::PARSING_NOT_STARTED
|| self->state->state == HttpHeaderParserState::PARSING_URL
|| self->state->state == HttpHeaderParserState::PARSING_HEADER_VALUE
|| self->state->state == HttpHeaderParserState::PARSING_FIRST_HEADER_VALUE)
{
// New header field encountered.
if (self->state->state == HttpHeaderParserState::PARSING_FIRST_HEADER_VALUE
|| self->state->state == HttpHeaderParserState::PARSING_HEADER_VALUE)
{
// Validate previous header and insert into table.
if (!self->validateHeader(MessageType(), self->state->currentHeader)) {
return 1;
}
self->insertCurrentHeader();
}
// Initialize new header field.
self->state->currentHeader = (Header *) psg_palloc(self->pool, sizeof(Header));
psg_lstr_init(&self->state->currentHeader->key);
psg_lstr_init(&self->state->currentHeader->origKey);
psg_lstr_init(&self->state->currentHeader->val);
self->state->hasher.reset();
if (self->state->state == HttpHeaderParserState::PARSING_URL) {
self->state->state = HttpHeaderParserState::PARSING_FIRST_HEADER_FIELD;
} else {
self->state->state = HttpHeaderParserState::PARSING_HEADER_FIELD;
}
}
psg_lstr_append(&self->state->currentHeader->origKey, self->pool,
*self->currentBuffer, data, len);
if (psg_lstr_first_byte(&self->state->currentHeader->origKey) == '!') {
psg_lstr_append(&self->state->currentHeader->key, self->pool,
*self->currentBuffer, data, len);
self->state->hasher.update(data, len);
} else {
char *downcasedData = (char *) psg_pnalloc(self->pool, len);
convertLowerCase((const unsigned char *) data,
(unsigned char *) downcasedData, len);
psg_lstr_append(&self->state->currentHeader->key, self->pool,
downcasedData, len);
self->state->hasher.update(downcasedData, len);
}
return 0;
}
static int onHeaderValue(http_parser *parser, const char *data, size_t len) {
HttpHeaderParser *self = static_cast<HttpHeaderParser *>(parser->data);
if (self->state->state == HttpHeaderParserState::PARSING_FIRST_HEADER_FIELD
|| self->state->state == HttpHeaderParserState::PARSING_HEADER_FIELD)
{
// New header value encountered. Finalize corresponding header field.
if (self->state->state == HttpHeaderParserState::PARSING_FIRST_HEADER_FIELD) {
self->state->state = HttpHeaderParserState::PARSING_FIRST_HEADER_VALUE;
} else {
self->state->state = HttpHeaderParserState::PARSING_HEADER_VALUE;
}
self->state->currentHeader->hash = self->state->hasher.finalize();
}
psg_lstr_append(&self->state->currentHeader->val, self->pool,
*self->currentBuffer, data, len);
self->state->hasher.update(data, len);
return 0;
}
static int onHeadersComplete(http_parser *parser) {
HttpHeaderParser *self = static_cast<HttpHeaderParser *>(parser->data);
if (self->state->state == HttpHeaderParserState::PARSING_HEADER_VALUE
|| self->state->state == HttpHeaderParserState::PARSING_FIRST_HEADER_VALUE)
{
// Validate previous header and insert into table.
if (!self->validateHeader(MessageType(), self->state->currentHeader)) {
// There's a bug in http_parser: even if we return 1 here, it doesn't
// set the error flag correctly. We fix that here.
self->state->parser.http_errno = HPE_CB_headers_complete;
return 1;
}
self->insertCurrentHeader();
}
self->state->currentHeader = NULL;
self->message->httpState = Message::PARSED_HEADERS;
self->indexQueryString(MessageType());
http_parser_pause(parser, 1);
return 0;
}
OXT_FORCE_INLINE
void indexQueryString(const HttpParseRequest &tag) {
LString *contiguousPath = psg_lstr_make_contiguous(&message->path,
message->pool);
if (contiguousPath != &message->path) {
psg_lstr_deinit(&message->path);
message->path = *contiguousPath;
}
const char *pos = (const char *) memchr(message->path.start->data, '?',
message->path.size);
if (pos != NULL) {
message->queryStringIndex = pos - message->path.start->data;
}
}
OXT_FORCE_INLINE
void indexQueryString(const HttpParseResponse &tag) {
// Do nothing.
}
OXT_FORCE_INLINE
void initializeParser(const HttpParseRequest &tag) {
http_parser_init(&state->parser, HTTP_REQUEST);
}
OXT_FORCE_INLINE
void initializeParser(const HttpParseResponse &tag) {
http_parser_init(&state->parser, HTTP_RESPONSE);
}
OXT_FORCE_INLINE
bool messageHttpStateIndicatesCompletion(const HttpParseRequest &tag) const {
return message->httpState == Message::PARSED_HEADERS;
}
OXT_FORCE_INLINE
bool messageHttpStateIndicatesCompletion(const HttpParseResponse &tag) const {
return message->httpState == Message::PARSED_HEADERS
|| message->httpState == Message::ONEHUNDRED_CONTINUE;
}
void processParseResult(const HttpParseRequest &tag) {
TRACE_POINT();
bool isChunked = state->parser.flags & F_CHUNKED;
boost::uint64_t contentLength;
int httpVersion;
// The parser sets content_length to ULLONG_MAX if
// Content-Length is not given. We treat it the same as 0.
contentLength = state->parser.content_length;
if (contentLength == std::numeric_limits<boost::uint64_t>::max()) {
contentLength = 0;
}
message->method = (http_method) state->parser.method;
httpVersion = state->parser.http_major * 1000 + state->parser.http_minor * 10;
if (httpVersion > 1010) {
// Maximum supported HTTP version is 1.1
message->httpState = Message::ERROR;
message->aux.parseError = HTTP_VERSION_NOT_SUPPORTED;
message->httpMajor = 1;
message->httpMinor = 1;
message->wantKeepAlive = false;
} else if (contentLength > 0 && isChunked) {
message->httpState = Message::ERROR;
message->aux.parseError = REQUEST_CONTAINS_CONTENT_LENGTH_AND_TRANSFER_ENCODING;
} else if (contentLength > 0 || isChunked) {
// There is a request body.
message->aux.bodyInfo.contentLength = contentLength;
if (state->parser.upgrade) {
message->httpState = Message::ERROR;
message->aux.parseError = UPGRADE_NOT_ALLOWED_WHEN_REQUEST_BODY_EXISTS;
} else if (isChunked) {
message->httpState = Message::PARSING_CHUNKED_BODY;
message->bodyType = Message::RBT_CHUNKED;
} else {
message->httpState = Message::PARSING_BODY;
message->bodyType = Message::RBT_CONTENT_LENGTH;
}
} else {
// There is no request body.
if (!state->parser.upgrade) {
message->httpState = Message::COMPLETE;
P_ASSERT_EQ(message->bodyType, Message::RBT_NO_BODY);
} else if (message->method != HTTP_HEAD) {
message->httpState = Message::UPGRADED;
message->bodyType = Message::RBT_UPGRADE;
message->wantKeepAlive = false;
} else {
message->httpState = Message::ERROR;
message->aux.parseError = UPGRADE_NOT_ALLOWED_FOR_HEAD_REQUESTS;
}
}
}
void processParseResult(const HttpParseResponse &tag) {
TRACE_POINT();
const unsigned int status = state->parser.status_code;
const boost::uint64_t contentLength = state->parser.content_length;
message->statusCode = state->parser.status_code;
if (state->parser.upgrade) {
message->httpState = Message::UPGRADED;
message->bodyType = Message::RBT_UPGRADE;
message->wantKeepAlive = false;
} else if (message->headers.lookup(HTTP_X_SENDFILE) != NULL
|| message->headers.lookup(HTTP_X_ACCEL_REDIRECT) != NULL)
{
// If X-Sendfile or X-Accel-Redirect is set, pretend like the body
// is empty and disallow keep-alive. See:
// https://github.com/phusion/passenger/issues/1376
// https://github.com/phusion/passenger/issues/1498
//
// We don't set a fake "Content-Length: 0" header here
// because it's undefined what Content-Length means if
// X-Sendfile or X-Accel-Redirect are set.
//
// Because the response header no longer has any header
// that signals its size, keep-alive should also be disabled
// for the *request*. We already do that in RequestHandler's
// ForwardResponse.cpp.
message->httpState = Message::COMPLETE;
message->bodyType = Message::RBT_NO_BODY;
message->headers.erase(HTTP_CONTENT_LENGTH);
message->headers.erase(HTTP_TRANSFER_ENCODING);
message->wantKeepAlive = false;
} else if (requestMethod == HTTP_HEAD
|| status / 100 == 1 // status 1xx
|| status == 204
|| status == 304)
{
if (status != 100) {
message->httpState = Message::COMPLETE;
} else {
message->httpState = Message::ONEHUNDRED_CONTINUE;
}
message->bodyType = Message::RBT_NO_BODY;
} else if (state->parser.flags & F_CHUNKED) {
if (contentLength == std::numeric_limits<boost::uint64_t>::max()) {
message->httpState = Message::PARSING_CHUNKED_BODY;
message->bodyType = Message::RBT_CHUNKED;
} else {
message->httpState = Message::ERROR;
message->aux.parseError = RESPONSE_CONTAINS_CONTENT_LENGTH_AND_TRANSFER_ENCODING;
}
} else if (contentLength == 0) {
message->httpState = Message::COMPLETE;
message->bodyType = Message::RBT_NO_BODY;
} else if (contentLength != std::numeric_limits<boost::uint64_t>::max()) {
message->httpState = Message::PARSING_BODY_WITH_LENGTH;
message->bodyType = Message::RBT_CONTENT_LENGTH;
message->aux.bodyInfo.contentLength = contentLength;
} else {
message->httpState = Message::PARSING_BODY_UNTIL_EOF;
message->bodyType = Message::RBT_UNTIL_EOF;
message->wantKeepAlive = false;
}
}
public:
HttpHeaderParser(Context *context, HttpHeaderParserState *_state,
Message *_message, psg_pool_t *_pool,
enum http_method _requestMethod = HTTP_GET)
: ctx(context),
state(_state),
message(_message),
pool(_pool),
currentBuffer(NULL),
requestMethod(_requestMethod)
{ }
void initialize() {
initializeParser(MessageType());
state->state = HttpHeaderParserState::PARSING_NOT_STARTED;
state->secureMode = false;
}
size_t feed(const MemoryKit::mbuf &buffer) {
TRACE_POINT();
P_ASSERT_EQ(message->httpState, Message::PARSING_HEADERS);
http_parser_settings settings;
size_t ret;
bool paused;
settings.on_message_begin = NULL;
settings.on_url = _onURL;
settings.on_status = onStatus;
settings.on_header_field = onHeaderField;
settings.on_header_value = onHeaderValue;
settings.on_headers_complete = onHeadersComplete;
settings.on_body = NULL;
settings.on_message_complete = NULL;
state->parser.data = this;
currentBuffer = &buffer;
ret = http_parser_execute_and_handle_pause(&state->parser,
&settings, buffer.start, buffer.size(), paused);
currentBuffer = NULL;
if (!state->parser.upgrade && ret != buffer.size() && !paused) {
UPDATE_TRACE_POINT();
message->httpState = Message::ERROR;
switch (HTTP_PARSER_ERRNO(&state->parser)) {
case HPE_CB_header_field:
case HPE_CB_headers_complete:
switch (state->state) {
case HttpHeaderParserState::ERROR_SECURITY_PASSWORD_MISMATCH:
message->aux.parseError = SECURITY_PASSWORD_MISMATCH;
break;
case HttpHeaderParserState::ERROR_SECURITY_PASSWORD_DUPLICATE:
message->aux.parseError = SECURITY_PASSWORD_DUPLICATE;
break;
case HttpHeaderParserState::ERROR_SECURE_HEADER_NOT_ALLOWED:
message->aux.parseError = ERROR_SECURE_HEADER_NOT_ALLOWED;
break;
case HttpHeaderParserState::ERROR_NORMAL_HEADER_NOT_ALLOWED_AFTER_SECURITY_PASSWORD:
message->aux.parseError = NORMAL_HEADER_NOT_ALLOWED_AFTER_SECURITY_PASSWORD;
break;
default:
goto default_error;
}
break;
default:
default_error:
message->aux.parseError = HTTP_PARSER_ERRNO_BEGIN - HTTP_PARSER_ERRNO(&state->parser);
break;
}
} else if (messageHttpStateIndicatesCompletion(MessageType())) {
UPDATE_TRACE_POINT();
ret++;
message->httpMajor = state->parser.http_major;
message->httpMinor = state->parser.http_minor;
message->wantKeepAlive = http_should_keep_alive(&state->parser);
processParseResult(MessageType());
}
return ret;
}
};
} // namespace ServerKit
} // namespace Passenger
#endif /* _PASSENGER_SERVER_KIT_HTTP_HEADER_PARSER_H_ */