mirror of https://gitee.com/bigwinds/arangodb
Make some changes to Comm Tasks (#10520)
* Make some changes to VST handlers * clear response body after sending * less code
This commit is contained in:
parent
b94755c61e
commit
45cccf8537
|
@ -296,10 +296,10 @@ struct ClusterCommResult {
|
||||||
auto const& headers = response->headers();
|
auto const& headers = response->headers();
|
||||||
auto errorCodes = headers.find(StaticStrings::ErrorCodes);
|
auto errorCodes = headers.find(StaticStrings::ErrorCodes);
|
||||||
if (errorCodes != headers.end()) {
|
if (errorCodes != headers.end()) {
|
||||||
request->setHeaderV2(StaticStrings::ErrorCodes, errorCodes->second);
|
request->setHeaderV2(std::string(StaticStrings::ErrorCodes),
|
||||||
|
std::string(errorCodes->second));
|
||||||
}
|
}
|
||||||
request->setHeaderV2(StaticStrings::ResponseCode,
|
|
||||||
GeneralResponse::responseString(answer_code));
|
|
||||||
answer.reset(request);
|
answer.reset(request);
|
||||||
TRI_ASSERT(response != nullptr);
|
TRI_ASSERT(response != nullptr);
|
||||||
result = std::make_shared<httpclient::SimpleHttpCommunicatorResult>(
|
result = std::make_shared<httpclient::SimpleHttpCommunicatorResult>(
|
||||||
|
|
|
@ -389,29 +389,26 @@ void CommTask::executeRequest(std::unique_ptr<GeneralRequest> request,
|
||||||
// --SECTION-- statistics handling protected methods
|
// --SECTION-- statistics handling protected methods
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
void CommTask::setStatistics(uint64_t id, RequestStatistics* stat) {
|
|
||||||
std::lock_guard<std::mutex> guard(_statisticsMutex);
|
|
||||||
|
|
||||||
if (stat == nullptr) {
|
|
||||||
auto it = _statisticsMap.find(id);
|
|
||||||
if (it != _statisticsMap.end()) {
|
|
||||||
it->second->release();
|
|
||||||
_statisticsMap.erase(it);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto result = _statisticsMap.insert({id, stat});
|
|
||||||
if (!result.second) {
|
|
||||||
result.first->second->release();
|
|
||||||
result.first->second = stat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RequestStatistics* CommTask::acquireStatistics(uint64_t id) {
|
RequestStatistics* CommTask::acquireStatistics(uint64_t id) {
|
||||||
RequestStatistics* stat = RequestStatistics::acquire();
|
RequestStatistics* stat = RequestStatistics::acquire();
|
||||||
setStatistics(id, stat);
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(_statisticsMutex);
|
||||||
|
if (stat == nullptr) {
|
||||||
|
auto it = _statisticsMap.find(id);
|
||||||
|
if (it != _statisticsMap.end()) {
|
||||||
|
it->second->release();
|
||||||
|
_statisticsMap.erase(it);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto result = _statisticsMap.insert({id, stat});
|
||||||
|
if (!result.second) {
|
||||||
|
result.first->second->release();
|
||||||
|
result.first->second = stat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,6 @@ protected:
|
||||||
void executeRequest(std::unique_ptr<GeneralRequest>,
|
void executeRequest(std::unique_ptr<GeneralRequest>,
|
||||||
std::unique_ptr<GeneralResponse>);
|
std::unique_ptr<GeneralResponse>);
|
||||||
|
|
||||||
void setStatistics(uint64_t, RequestStatistics*);
|
|
||||||
RequestStatistics* acquireStatistics(uint64_t);
|
RequestStatistics* acquireStatistics(uint64_t);
|
||||||
RequestStatistics* statistics(uint64_t);
|
RequestStatistics* statistics(uint64_t);
|
||||||
RequestStatistics* stealStatistics(uint64_t);
|
RequestStatistics* stealStatistics(uint64_t);
|
||||||
|
|
|
@ -82,6 +82,7 @@ int HttpCommTask<T>::on_message_began(llhttp_t* p) {
|
||||||
self->_origin.clear();
|
self->_origin.clear();
|
||||||
self->_request = std::make_unique<HttpRequest>(self->_connectionInfo, /*header*/ nullptr,
|
self->_request = std::make_unique<HttpRequest>(self->_connectionInfo, /*header*/ nullptr,
|
||||||
0, self->_allowMethodOverride);
|
0, self->_allowMethodOverride);
|
||||||
|
self->_response.reset();
|
||||||
self->_lastHeaderWasValue = false;
|
self->_lastHeaderWasValue = false;
|
||||||
self->_shouldKeepAlive = false;
|
self->_shouldKeepAlive = false;
|
||||||
self->_messageDone = false;
|
self->_messageDone = false;
|
||||||
|
@ -175,16 +176,18 @@ int HttpCommTask<T>::on_header_complete(llhttp_t* p) {
|
||||||
<< "received a 100-continue request";
|
<< "received a 100-continue request";
|
||||||
char const* response = "HTTP/1.1 100 Continue\r\n\r\n";
|
char const* response = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||||
auto buff = asio_ns::buffer(response, strlen(response));
|
auto buff = asio_ns::buffer(response, strlen(response));
|
||||||
asio_ns::async_write(self->_protocol->socket, buff, [self = self->shared_from_this()](asio_ns::error_code const& ec, std::size_t) {
|
asio_ns::async_write(self->_protocol->socket, buff,
|
||||||
if (ec) {
|
[self = self->shared_from_this()](asio_ns::error_code const& ec,
|
||||||
static_cast<HttpCommTask<T>*>(self.get())->close();
|
std::size_t) {
|
||||||
}
|
if (ec) {
|
||||||
});
|
static_cast<HttpCommTask<T>*>(self.get())->close();
|
||||||
|
}
|
||||||
|
});
|
||||||
return HPE_OK;
|
return HPE_OK;
|
||||||
}
|
}
|
||||||
if (self->_request->requestType() == RequestType::HEAD) {
|
if (self->_request->requestType() == RequestType::HEAD) {
|
||||||
// Assume that request/response has no body, proceed parsing next message
|
// Assume that request/response has no body, proceed parsing next message
|
||||||
return 1; // 1 is defined by parser
|
return 1; // 1 is defined by parser
|
||||||
}
|
}
|
||||||
return HPE_OK;
|
return HPE_OK;
|
||||||
}
|
}
|
||||||
|
@ -237,8 +240,7 @@ HttpCommTask<T>::~HttpCommTask() = default;
|
||||||
template <SocketType T>
|
template <SocketType T>
|
||||||
void HttpCommTask<T>::start() {
|
void HttpCommTask<T>::start() {
|
||||||
this->_protocol->setNonBlocking(true);
|
this->_protocol->setNonBlocking(true);
|
||||||
asio_ns::post(this->_protocol->context.io_context,
|
asio_ns::post(this->_protocol->context.io_context, [self = this->shared_from_this()] {
|
||||||
[self = this->shared_from_this()] {
|
|
||||||
auto* thisPtr = static_cast<HttpCommTask<T>*>(self.get());
|
auto* thisPtr = static_cast<HttpCommTask<T>*>(self.get());
|
||||||
thisPtr->checkVSTPrefix();
|
thisPtr->checkVSTPrefix();
|
||||||
});
|
});
|
||||||
|
@ -250,7 +252,7 @@ void HttpCommTask<T>::addSimpleResponse(rest::ResponseCode code,
|
||||||
rest::ContentType respType, uint64_t /*messageId*/,
|
rest::ContentType respType, uint64_t /*messageId*/,
|
||||||
velocypack::Buffer<uint8_t>&& buffer) {
|
velocypack::Buffer<uint8_t>&& buffer) {
|
||||||
try {
|
try {
|
||||||
auto resp = std::make_unique<HttpResponse>(code, /*buffer*/nullptr);
|
auto resp = std::make_unique<HttpResponse>(code, /*buffer*/ nullptr);
|
||||||
resp->setContentType(respType);
|
resp->setContentType(respType);
|
||||||
if (!buffer.empty()) {
|
if (!buffer.empty()) {
|
||||||
resp->setPayload(std::move(buffer), true, VPackOptions::Defaults);
|
resp->setPayload(std::move(buffer), true, VPackOptions::Defaults);
|
||||||
|
@ -265,9 +267,8 @@ void HttpCommTask<T>::addSimpleResponse(rest::ResponseCode code,
|
||||||
|
|
||||||
template <SocketType T>
|
template <SocketType T>
|
||||||
bool HttpCommTask<T>::readCallback(asio_ns::error_code ec) {
|
bool HttpCommTask<T>::readCallback(asio_ns::error_code ec) {
|
||||||
|
|
||||||
llhttp_errno_t err = HPE_OK;
|
llhttp_errno_t err = HPE_OK;
|
||||||
if (ec) { // got a connection error
|
if (ec) { // got a connection error
|
||||||
if (ec == asio_ns::error::misc_errors::eof) {
|
if (ec == asio_ns::error::misc_errors::eof) {
|
||||||
err = llhttp_finish(&_parser);
|
err = llhttp_finish(&_parser);
|
||||||
} else {
|
} else {
|
||||||
|
@ -275,7 +276,7 @@ bool HttpCommTask<T>::readCallback(asio_ns::error_code ec) {
|
||||||
<< "Error while reading from socket: '" << ec.message() << "'";
|
<< "Error while reading from socket: '" << ec.message() << "'";
|
||||||
err = HPE_INVALID_EOF_STATE;
|
err = HPE_INVALID_EOF_STATE;
|
||||||
}
|
}
|
||||||
} else { // Inspect the received data
|
} else { // Inspect the received data
|
||||||
|
|
||||||
size_t parsedBytes = 0;
|
size_t parsedBytes = 0;
|
||||||
for (auto const& buffer : this->_protocol->buffer.data()) {
|
for (auto const& buffer : this->_protocol->buffer.data()) {
|
||||||
|
@ -299,7 +300,7 @@ bool HttpCommTask<T>::readCallback(asio_ns::error_code ec) {
|
||||||
if (err == HPE_PAUSED_UPGRADE) {
|
if (err == HPE_PAUSED_UPGRADE) {
|
||||||
this->addSimpleResponse(rest::ResponseCode::NOT_IMPLEMENTED,
|
this->addSimpleResponse(rest::ResponseCode::NOT_IMPLEMENTED,
|
||||||
rest::ContentType::UNSET, 1, VPackBuffer<uint8_t>());
|
rest::ContentType::UNSET, 1, VPackBuffer<uint8_t>());
|
||||||
return false; // stop read loop
|
return false; // stop read loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,16 +308,16 @@ bool HttpCommTask<T>::readCallback(asio_ns::error_code ec) {
|
||||||
TRI_ASSERT(err == HPE_PAUSED);
|
TRI_ASSERT(err == HPE_PAUSED);
|
||||||
_messageDone = false;
|
_messageDone = false;
|
||||||
processRequest();
|
processRequest();
|
||||||
return false; // stop read loop
|
return false; // stop read loop
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err != HPE_OK && err != HPE_USER) {
|
if (err != HPE_OK && err != HPE_USER) {
|
||||||
if (err == HPE_INVALID_EOF_STATE) {
|
if (err == HPE_INVALID_EOF_STATE) {
|
||||||
LOG_TOPIC("595fd", TRACE, Logger::REQUESTS)
|
LOG_TOPIC("595fd", TRACE, Logger::REQUESTS)
|
||||||
<< "Connection closed by peer, with ptr " << this;
|
<< "Connection closed by peer, with ptr " << this;
|
||||||
} else {
|
} else {
|
||||||
LOG_TOPIC("595fe", TRACE, Logger::REQUESTS)
|
LOG_TOPIC("595fe", TRACE, Logger::REQUESTS)
|
||||||
<< "HTTP parse failure: '" << llhttp_get_error_reason(&_parser) << "'";
|
<< "HTTP parse failure: '" << llhttp_get_error_reason(&_parser) << "'";
|
||||||
}
|
}
|
||||||
this->close();
|
this->close();
|
||||||
}
|
}
|
||||||
|
@ -327,12 +328,11 @@ bool HttpCommTask<T>::readCallback(asio_ns::error_code ec) {
|
||||||
namespace {
|
namespace {
|
||||||
static constexpr const char* vst10 = "VST/1.0\r\n\r\n";
|
static constexpr const char* vst10 = "VST/1.0\r\n\r\n";
|
||||||
static constexpr const char* vst11 = "VST/1.1\r\n\r\n";
|
static constexpr const char* vst11 = "VST/1.1\r\n\r\n";
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
template <SocketType T>
|
template <SocketType T>
|
||||||
void HttpCommTask<T>::checkVSTPrefix() {
|
void HttpCommTask<T>::checkVSTPrefix() {
|
||||||
auto cb = [self = this->shared_from_this()]
|
auto cb = [self = this->shared_from_this()](asio_ns::error_code const& ec, size_t nread) {
|
||||||
(asio_ns::error_code const& ec, size_t nread) {
|
|
||||||
auto* thisPtr = static_cast<HttpCommTask<T>*>(self.get());
|
auto* thisPtr = static_cast<HttpCommTask<T>*>(self.get());
|
||||||
if (ec || nread < 11) {
|
if (ec || nread < 11) {
|
||||||
thisPtr->close();
|
thisPtr->close();
|
||||||
|
@ -342,27 +342,25 @@ void HttpCommTask<T>::checkVSTPrefix() {
|
||||||
|
|
||||||
auto bg = asio_ns::buffers_begin(thisPtr->_protocol->buffer.data());
|
auto bg = asio_ns::buffers_begin(thisPtr->_protocol->buffer.data());
|
||||||
if (std::equal(::vst10, ::vst10 + 11, bg, bg + 11)) {
|
if (std::equal(::vst10, ::vst10 + 11, bg, bg + 11)) {
|
||||||
|
thisPtr->_protocol->buffer.consume(11); // remove VST/1.0 prefix
|
||||||
thisPtr->_protocol->buffer.consume(11); // remove VST/1.0 prefix
|
|
||||||
auto commTask =
|
auto commTask =
|
||||||
std::make_unique<VstCommTask<T>>(thisPtr->_server, thisPtr->_connectionInfo,
|
std::make_unique<VstCommTask<T>>(thisPtr->_server, thisPtr->_connectionInfo,
|
||||||
std::move(thisPtr->_protocol),
|
std::move(thisPtr->_protocol),
|
||||||
fuerte::vst::VST1_0);
|
fuerte::vst::VST1_0);
|
||||||
thisPtr->_server.registerTask(std::move(commTask));
|
thisPtr->_server.registerTask(std::move(commTask));
|
||||||
return; // vst 1.0
|
return; // vst 1.0
|
||||||
|
|
||||||
} else if (std::equal(::vst11, ::vst11 + 11, bg, bg + 11)) {
|
} else if (std::equal(::vst11, ::vst11 + 11, bg, bg + 11)) {
|
||||||
|
thisPtr->_protocol->buffer.consume(11); // remove VST/1.1 prefix
|
||||||
thisPtr->_protocol->buffer.consume(11); // remove VST/1.1 prefix
|
|
||||||
auto commTask =
|
auto commTask =
|
||||||
std::make_unique<VstCommTask<T>>(thisPtr->_server, thisPtr->_connectionInfo,
|
std::make_unique<VstCommTask<T>>(thisPtr->_server, thisPtr->_connectionInfo,
|
||||||
std::move(thisPtr->_protocol),
|
std::move(thisPtr->_protocol),
|
||||||
fuerte::vst::VST1_1);
|
fuerte::vst::VST1_1);
|
||||||
thisPtr->_server.registerTask(std::move(commTask));
|
thisPtr->_server.registerTask(std::move(commTask));
|
||||||
return; // vst 1.1
|
return; // vst 1.1
|
||||||
}
|
}
|
||||||
|
|
||||||
thisPtr->asyncReadSome(); // continue reading
|
thisPtr->asyncReadSome(); // continue reading
|
||||||
};
|
};
|
||||||
auto buffs = this->_protocol->buffer.prepare(GeneralCommTask<T>::ReadBlockSize);
|
auto buffs = this->_protocol->buffer.prepare(GeneralCommTask<T>::ReadBlockSize);
|
||||||
asio_ns::async_read(this->_protocol->socket, buffs,
|
asio_ns::async_read(this->_protocol->socket, buffs,
|
||||||
|
@ -437,8 +435,8 @@ void HttpCommTask<T>::processRequest() {
|
||||||
|
|
||||||
// unzip / deflate
|
// unzip / deflate
|
||||||
if (!handleContentEncoding(*_request)) {
|
if (!handleContentEncoding(*_request)) {
|
||||||
this->addErrorResponse(rest::ResponseCode::BAD, _request->contentTypeResponse(), 1,
|
this->addErrorResponse(rest::ResponseCode::BAD, _request->contentTypeResponse(),
|
||||||
TRI_ERROR_BAD_PARAMETER, "decoding error");
|
1, TRI_ERROR_BAD_PARAMETER, "decoding error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,12 +455,12 @@ bool allowCredentials(std::string const& origin) {
|
||||||
bool allowCredentials = false;
|
bool allowCredentials = false;
|
||||||
if (origin.empty()) {
|
if (origin.empty()) {
|
||||||
return allowCredentials;
|
return allowCredentials;
|
||||||
} // else handle origin headers
|
} // else handle origin headers
|
||||||
|
|
||||||
// if the request asks to allow credentials, we'll check against the
|
// if the request asks to allow credentials, we'll check against the
|
||||||
// configured whitelist of origins
|
// configured whitelist of origins
|
||||||
std::vector<std::string> const& accessControlAllowOrigins =
|
std::vector<std::string> const& accessControlAllowOrigins =
|
||||||
GeneralServerFeature::accessControlAllowOrigins();
|
GeneralServerFeature::accessControlAllowOrigins();
|
||||||
|
|
||||||
if (!accessControlAllowOrigins.empty()) {
|
if (!accessControlAllowOrigins.empty()) {
|
||||||
if (accessControlAllowOrigins[0] == "*") {
|
if (accessControlAllowOrigins[0] == "*") {
|
||||||
|
@ -487,8 +485,7 @@ bool allowCredentials(std::string const& origin) {
|
||||||
}
|
}
|
||||||
return allowCredentials;
|
return allowCredentials;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// handle an OPTIONS request
|
/// handle an OPTIONS request
|
||||||
template <SocketType T>
|
template <SocketType T>
|
||||||
|
@ -523,7 +520,7 @@ void HttpCommTask<T>::processCorsOptions() {
|
||||||
resp->setHeaderNCIfNotSet(StaticStrings::AccessControlMaxAge, StaticStrings::N1800);
|
resp->setHeaderNCIfNotSet(StaticStrings::AccessControlMaxAge, StaticStrings::N1800);
|
||||||
}
|
}
|
||||||
|
|
||||||
_request.reset(); // forge the request
|
_request.reset(); // forge the request
|
||||||
sendResponse(std::move(resp), nullptr);
|
sendResponse(std::move(resp), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,7 +644,7 @@ void HttpCommTask<T>::sendResponse(std::unique_ptr<GeneralResponse> baseRes,
|
||||||
// the request contained an Origin header. We have to send back the
|
// the request contained an Origin header. We have to send back the
|
||||||
// access-control-allow-origin header now
|
// access-control-allow-origin header now
|
||||||
LOG_TOPIC("ae603", DEBUG, arangodb::Logger::REQUESTS)
|
LOG_TOPIC("ae603", DEBUG, arangodb::Logger::REQUESTS)
|
||||||
<< "handling CORS response";
|
<< "handling CORS response";
|
||||||
|
|
||||||
// send back original value of "Origin" header
|
// send back original value of "Origin" header
|
||||||
response.setHeaderNCIfNotSet(StaticStrings::AccessControlAllowOrigin, _origin);
|
response.setHeaderNCIfNotSet(StaticStrings::AccessControlAllowOrigin, _origin);
|
||||||
|
@ -668,22 +665,20 @@ void HttpCommTask<T>::sendResponse(std::unique_ptr<GeneralResponse> baseRes,
|
||||||
response.setHeaderNCIfNotSet(StaticStrings::XContentTypeOptions, StaticStrings::NoSniff);
|
response.setHeaderNCIfNotSet(StaticStrings::XContentTypeOptions, StaticStrings::NoSniff);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO lease buffers
|
_header.clear();
|
||||||
auto header = std::make_shared<VPackBuffer<uint8_t>>();
|
_header.reserve(220);
|
||||||
header->reserve(220);
|
|
||||||
|
|
||||||
header->append(TRI_CHAR_LENGTH_PAIR("HTTP/1.1 "));
|
_header.append(TRI_CHAR_LENGTH_PAIR("HTTP/1.1 "));
|
||||||
header->append(GeneralResponse::responseString(response.responseCode()));
|
_header.append(GeneralResponse::responseString(response.responseCode()));
|
||||||
header->append("\r\n", 2);
|
_header.append("\r\n", 2);
|
||||||
|
|
||||||
bool seenServerHeader = false;
|
bool seenServerHeader = false;
|
||||||
//bool seenConnectionHeader = false;
|
// bool seenConnectionHeader = false;
|
||||||
for (auto const& it : response.headers()) {
|
for (auto const& it : response.headers()) {
|
||||||
std::string const& key = it.first;
|
std::string const& key = it.first;
|
||||||
size_t const keyLength = key.size();
|
size_t const keyLength = key.size();
|
||||||
// ignore content-length
|
// ignore content-length
|
||||||
if (key == StaticStrings::ContentLength ||
|
if (key == StaticStrings::ContentLength || key == StaticStrings::Connection ||
|
||||||
key == StaticStrings::Connection ||
|
|
||||||
key == StaticStrings::TransferEncoding) {
|
key == StaticStrings::TransferEncoding) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -693,7 +688,7 @@ void HttpCommTask<T>::sendResponse(std::unique_ptr<GeneralResponse> baseRes,
|
||||||
}
|
}
|
||||||
|
|
||||||
// reserve enough space for header name + ": " + value + "\r\n"
|
// reserve enough space for header name + ": " + value + "\r\n"
|
||||||
header->reserve(key.size() + 2 + it.second.size() + 2);
|
_header.reserve(key.size() + 2 + it.second.size() + 2);
|
||||||
|
|
||||||
char const* p = key.data();
|
char const* p = key.data();
|
||||||
char const* end = p + keyLength;
|
char const* end = p + keyLength;
|
||||||
|
@ -701,11 +696,11 @@ void HttpCommTask<T>::sendResponse(std::unique_ptr<GeneralResponse> baseRes,
|
||||||
while (p < end) {
|
while (p < end) {
|
||||||
if (capState == 1) {
|
if (capState == 1) {
|
||||||
// upper case
|
// upper case
|
||||||
header->push_back(StringUtils::toupper(*p));
|
_header.push_back(StringUtils::toupper(*p));
|
||||||
capState = 0;
|
capState = 0;
|
||||||
} else if (capState == 0) {
|
} else if (capState == 0) {
|
||||||
// normal case
|
// normal case
|
||||||
header->push_back(StringUtils::tolower(*p));
|
_header.push_back(StringUtils::tolower(*p));
|
||||||
if (*p == '-') {
|
if (*p == '-') {
|
||||||
capState = 1;
|
capState = 1;
|
||||||
} else if (*p == ':') {
|
} else if (*p == ':') {
|
||||||
|
@ -713,19 +708,19 @@ void HttpCommTask<T>::sendResponse(std::unique_ptr<GeneralResponse> baseRes,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// output as is
|
// output as is
|
||||||
header->push_back(*p);
|
_header.push_back(*p);
|
||||||
}
|
}
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
|
||||||
header->append(": ", 2);
|
_header.append(": ", 2);
|
||||||
header->append(it.second);
|
_header.append(it.second);
|
||||||
header->append("\r\n", 2);
|
_header.append("\r\n", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add "Server" response header
|
// add "Server" response header
|
||||||
if (!seenServerHeader && !HttpResponse::HIDE_PRODUCT_HEADER) {
|
if (!seenServerHeader && !HttpResponse::HIDE_PRODUCT_HEADER) {
|
||||||
header->append(TRI_CHAR_LENGTH_PAIR("Server: ArangoDB\r\n"));
|
_header.append(TRI_CHAR_LENGTH_PAIR("Server: ArangoDB\r\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn on the keepAlive timer
|
// turn on the keepAlive timer
|
||||||
|
@ -733,42 +728,42 @@ void HttpCommTask<T>::sendResponse(std::unique_ptr<GeneralResponse> baseRes,
|
||||||
if (_shouldKeepAlive && secs > 0) {
|
if (_shouldKeepAlive && secs > 0) {
|
||||||
int64_t millis = static_cast<int64_t>(secs * 1000);
|
int64_t millis = static_cast<int64_t>(secs * 1000);
|
||||||
this->_protocol->timer.expires_after(std::chrono::milliseconds(millis));
|
this->_protocol->timer.expires_after(std::chrono::milliseconds(millis));
|
||||||
this->_protocol->timer.async_wait([self = CommTask::weak_from_this()] (asio_ns::error_code ec) {
|
this->_protocol->timer.async_wait([self = CommTask::weak_from_this()](asio_ns::error_code ec) {
|
||||||
std::shared_ptr<CommTask> s;
|
std::shared_ptr<CommTask> s;
|
||||||
if (ec || !(s = self.lock())) { // was canceled / deallocated
|
if (ec || !(s = self.lock())) { // was canceled / deallocated
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG_TOPIC("5c1e0", INFO, Logger::REQUESTS)
|
LOG_TOPIC("5c1e0", INFO, Logger::REQUESTS)
|
||||||
<< "keep alive timeout, closing stream!";
|
<< "keep alive timeout, closing stream!";
|
||||||
s->close();
|
s->close();
|
||||||
});
|
});
|
||||||
|
|
||||||
header->append(TRI_CHAR_LENGTH_PAIR("Connection: Keep-Alive\r\n"));
|
_header.append(TRI_CHAR_LENGTH_PAIR("Connection: Keep-Alive\r\n"));
|
||||||
} else {
|
} else {
|
||||||
header->append(TRI_CHAR_LENGTH_PAIR("Connection: Close\r\n"));
|
_header.append(TRI_CHAR_LENGTH_PAIR("Connection: Close\r\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// add "Content-Type" header
|
// add "Content-Type" header
|
||||||
switch (response.contentType()) {
|
switch (response.contentType()) {
|
||||||
case ContentType::UNSET:
|
case ContentType::UNSET:
|
||||||
case ContentType::JSON:
|
case ContentType::JSON:
|
||||||
header->append(TRI_CHAR_LENGTH_PAIR(
|
_header.append(TRI_CHAR_LENGTH_PAIR(
|
||||||
"Content-Type: application/json; charset=utf-8\r\n"));
|
"Content-Type: application/json; charset=utf-8\r\n"));
|
||||||
break;
|
break;
|
||||||
case ContentType::VPACK:
|
case ContentType::VPACK:
|
||||||
header->append(
|
_header.append(
|
||||||
TRI_CHAR_LENGTH_PAIR("Content-Type: application/x-velocypack\r\n"));
|
TRI_CHAR_LENGTH_PAIR("Content-Type: application/x-velocypack\r\n"));
|
||||||
break;
|
break;
|
||||||
case ContentType::TEXT:
|
case ContentType::TEXT:
|
||||||
header->append(
|
_header.append(
|
||||||
TRI_CHAR_LENGTH_PAIR("Content-Type: text/plain; charset=utf-8\r\n"));
|
TRI_CHAR_LENGTH_PAIR("Content-Type: text/plain; charset=utf-8\r\n"));
|
||||||
break;
|
break;
|
||||||
case ContentType::HTML:
|
case ContentType::HTML:
|
||||||
header->append(
|
_header.append(
|
||||||
TRI_CHAR_LENGTH_PAIR("Content-Type: text/html; charset=utf-8\r\n"));
|
TRI_CHAR_LENGTH_PAIR("Content-Type: text/html; charset=utf-8\r\n"));
|
||||||
break;
|
break;
|
||||||
case ContentType::DUMP:
|
case ContentType::DUMP:
|
||||||
header->append(TRI_CHAR_LENGTH_PAIR(
|
_header.append(TRI_CHAR_LENGTH_PAIR(
|
||||||
"Content-Type: application/x-arango-dump; charset=utf-8\r\n"));
|
"Content-Type: application/x-arango-dump; charset=utf-8\r\n"));
|
||||||
break;
|
break;
|
||||||
case ContentType::CUSTOM: // don't do anything
|
case ContentType::CUSTOM: // don't do anything
|
||||||
|
@ -776,62 +771,76 @@ void HttpCommTask<T>::sendResponse(std::unique_ptr<GeneralResponse> baseRes,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& it : response.cookies()) {
|
for (auto const& it : response.cookies()) {
|
||||||
header->append(TRI_CHAR_LENGTH_PAIR("Set-Cookie: "));
|
_header.append(TRI_CHAR_LENGTH_PAIR("Set-Cookie: "));
|
||||||
header->append(it);
|
_header.append(it);
|
||||||
header->append("\r\n", 2);
|
_header.append("\r\n", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t len = response.bodySize();
|
size_t len = response.bodySize();
|
||||||
header->append(TRI_CHAR_LENGTH_PAIR("Content-Length: "));
|
_header.append(TRI_CHAR_LENGTH_PAIR("Content-Length: "));
|
||||||
header->append(std::to_string(len));
|
_header.append(std::to_string(len));
|
||||||
header->append("\r\n\r\n", 4);
|
_header.append("\r\n\r\n", 4);
|
||||||
|
|
||||||
std::shared_ptr<basics::StringBuffer> body = response.stealBody();
|
TRI_ASSERT(_response == nullptr);
|
||||||
|
_response = response.stealBody();
|
||||||
// append write buffer and statistics
|
// append write buffer and statistics
|
||||||
double const totalTime = RequestStatistics::ELAPSED_SINCE_READ_START(stat);
|
double const totalTime = RequestStatistics::ELAPSED_SINCE_READ_START(stat);
|
||||||
|
|
||||||
// and give some request information
|
// and give some request information
|
||||||
LOG_TOPIC("8f555", DEBUG, Logger::REQUESTS)
|
LOG_TOPIC("8f555", DEBUG, Logger::REQUESTS)
|
||||||
<< "\"http-request-end\",\"" << (void*)this << "\",\"" << this->_connectionInfo.clientAddress
|
<< "\"http-request-end\",\"" << (void*)this << "\",\""
|
||||||
<< "\",\"" << GeneralRequest::translateMethod(::llhttpToRequestType(&_parser)) << "\",\""
|
<< this->_connectionInfo.clientAddress << "\",\""
|
||||||
<< static_cast<int>(response.responseCode()) << "\"," << Logger::FIXED(totalTime, 6);
|
<< GeneralRequest::translateMethod(::llhttpToRequestType(&_parser))
|
||||||
|
<< "\",\"" << static_cast<int>(response.responseCode()) << "\","
|
||||||
|
<< Logger::FIXED(totalTime, 6);
|
||||||
|
|
||||||
|
if constexpr (SocketType::Ssl == T) {
|
||||||
|
this->_protocol->context.io_context.dispatch([self = this->shared_from_this(), stat]() mutable {
|
||||||
|
auto* thisPtr = static_cast<HttpCommTask<T>*>(self.get());
|
||||||
|
thisPtr->writeResponse(stat);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
writeResponse(stat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called on IO context thread
|
||||||
|
template <SocketType T>
|
||||||
|
void HttpCommTask<T>::writeResponse(RequestStatistics* stat) {
|
||||||
|
TRI_ASSERT(!_header.empty());
|
||||||
|
|
||||||
RequestStatistics::SET_WRITE_START(stat);
|
RequestStatistics::SET_WRITE_START(stat);
|
||||||
|
|
||||||
this->_protocol->context.io_context.post([this, self = this->shared_from_this(), header = std::move(header), body = std::move(body), stat] () mutable {
|
std::array<asio_ns::const_buffer, 2> buffers;
|
||||||
std::array<asio_ns::const_buffer, 2> buffers;
|
buffers[0] = asio_ns::buffer(_header.data(), _header.size());
|
||||||
buffers[0] = asio_ns::buffer(header->data(), header->size());
|
if (HTTP_HEAD != _parser.method) {
|
||||||
if (HTTP_HEAD != _parser.method) {
|
buffers[1] = asio_ns::buffer(_response->data(), _response->size());
|
||||||
buffers[1] = asio_ns::buffer(body->data(), body->size());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME measure performance w/o sync write
|
// FIXME measure performance w/o sync write
|
||||||
asio_ns::async_write(this->_protocol->socket, buffers,
|
asio_ns::async_write(this->_protocol->socket, buffers,
|
||||||
[self = std::move(self),
|
[self = this->shared_from_this(), stat](asio_ns::error_code ec, size_t nwrite) {
|
||||||
h = std::move(header),
|
auto* thisPtr = static_cast<HttpCommTask<T>*>(self.get());
|
||||||
b = std::move(body),
|
RequestStatistics::SET_WRITE_END(stat);
|
||||||
stat](asio_ns::error_code ec, size_t nwrite) {
|
RequestStatistics::ADD_SENT_BYTES(stat, nwrite);
|
||||||
auto* thisPtr = static_cast<HttpCommTask<T>*>(self.get());
|
|
||||||
RequestStatistics::SET_WRITE_END(stat);
|
thisPtr->_response.reset();
|
||||||
RequestStatistics::ADD_SENT_BYTES(stat, h->size() + b->size());
|
|
||||||
llhttp_errno_t err = llhttp_get_errno(&thisPtr->_parser);
|
|
||||||
if (ec || !thisPtr->_shouldKeepAlive || err != HPE_PAUSED) {
|
|
||||||
if (ec) {
|
|
||||||
LOG_TOPIC("2b6b4", DEBUG, arangodb::Logger::REQUESTS)
|
|
||||||
<< "asio write error: '" << ec.message() << "'";
|
|
||||||
}
|
|
||||||
thisPtr->close();
|
|
||||||
} else { // ec == HPE_PAUSED
|
|
||||||
|
|
||||||
llhttp_resume(&thisPtr->_parser);
|
llhttp_errno_t err = llhttp_get_errno(&thisPtr->_parser);
|
||||||
thisPtr->asyncReadSome();
|
if (ec || !thisPtr->_shouldKeepAlive || err != HPE_PAUSED) {
|
||||||
|
if (ec) {
|
||||||
|
LOG_TOPIC("2b6b4", DEBUG, arangodb::Logger::REQUESTS)
|
||||||
|
<< "asio write error: '" << ec.message() << "'";
|
||||||
}
|
}
|
||||||
if (stat != nullptr) {
|
thisPtr->close();
|
||||||
stat->release();
|
} else { // ec == HPE_PAUSED
|
||||||
}
|
llhttp_resume(&thisPtr->_parser);
|
||||||
});
|
thisPtr->asyncReadSome();
|
||||||
});
|
}
|
||||||
|
if (stat != nullptr) {
|
||||||
|
stat->release();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <SocketType T>
|
template <SocketType T>
|
||||||
|
|
|
@ -33,6 +33,10 @@
|
||||||
namespace arangodb {
|
namespace arangodb {
|
||||||
class HttpRequest;
|
class HttpRequest;
|
||||||
|
|
||||||
|
namespace basics {
|
||||||
|
class StringBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
namespace rest {
|
namespace rest {
|
||||||
|
|
||||||
template <SocketType T>
|
template <SocketType T>
|
||||||
|
@ -84,17 +88,23 @@ private:
|
||||||
ResponseCode handleAuthHeader(HttpRequest& request);
|
ResponseCode handleAuthHeader(HttpRequest& request);
|
||||||
/// decompress content
|
/// decompress content
|
||||||
bool handleContentEncoding(HttpRequest&);
|
bool handleContentEncoding(HttpRequest&);
|
||||||
|
|
||||||
|
// called on IO context thread
|
||||||
|
void writeResponse(RequestStatistics* stat);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// the node http-parser
|
/// the node http-parser
|
||||||
llhttp_t _parser;
|
llhttp_t _parser;
|
||||||
llhttp_settings_t _parserSettings;
|
llhttp_settings_t _parserSettings;
|
||||||
|
|
||||||
|
velocypack::Buffer<uint8_t> _header;
|
||||||
|
|
||||||
// ==== parser state ====
|
// ==== parser state ====
|
||||||
std::string _lastHeaderField;
|
std::string _lastHeaderField;
|
||||||
std::string _lastHeaderValue;
|
std::string _lastHeaderValue;
|
||||||
std::string _origin; // value of the HTTP origin header the client sent (if
|
std::string _origin; // value of the HTTP origin header the client sent (if
|
||||||
std::unique_ptr<HttpRequest> _request;
|
std::unique_ptr<HttpRequest> _request;
|
||||||
|
std::unique_ptr<basics::StringBuffer> _response;
|
||||||
bool _lastHeaderWasValue;
|
bool _lastHeaderWasValue;
|
||||||
bool _shouldKeepAlive; /// keep connection open
|
bool _shouldKeepAlive; /// keep connection open
|
||||||
bool _messageDone;
|
bool _messageDone;
|
||||||
|
|
|
@ -334,9 +334,14 @@ void VstCommTask<T>::sendResponse(std::unique_ptr<GeneralResponse> baseRes, Requ
|
||||||
bool expected = _writing.load();
|
bool expected = _writing.load();
|
||||||
if (false == expected) {
|
if (false == expected) {
|
||||||
if (_writing.compare_exchange_strong(expected, true)) {
|
if (_writing.compare_exchange_strong(expected, true)) {
|
||||||
this->_protocol->context.io_context.post([this, self = this->shared_from_this()]{
|
// we managed to start writing
|
||||||
this->doWrite(); // we managed to start writing
|
if constexpr (SocketType::Ssl == T) {
|
||||||
});
|
this->_protocol->context.io_context.post([self = this->shared_from_this()]() {
|
||||||
|
static_cast<VstCommTask<T>*>(self.get())->doWrite();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
doWrite();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,58 +89,6 @@ var shardList = function (dbName, collectionName) {
|
||||||
return shards;
|
return shards;
|
||||||
};
|
};
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
|
||||||
// / @brief wait for a distributed response
|
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
var waitForDistributedResponse = function (data, numberOfRequests, ignoreHttpErrors) {
|
|
||||||
var received = [];
|
|
||||||
try {
|
|
||||||
while (received.length < numberOfRequests) {
|
|
||||||
var result = global.ArangoClusterComm.wait(data);
|
|
||||||
var status = result.status;
|
|
||||||
|
|
||||||
if (status === 'ERROR') {
|
|
||||||
raiseError(arangodb.errors.ERROR_INTERNAL.code,
|
|
||||||
'received an error from a DB server: ' + JSON.stringify(result));
|
|
||||||
} else if (status === 'TIMEOUT') {
|
|
||||||
raiseError(arangodb.errors.ERROR_CLUSTER_TIMEOUT.code,
|
|
||||||
arangodb.errors.ERROR_CLUSTER_TIMEOUT.message);
|
|
||||||
} else if (status === 'DROPPED') {
|
|
||||||
raiseError(arangodb.errors.ERROR_INTERNAL.code,
|
|
||||||
'the operation was dropped');
|
|
||||||
} else if (status === 'RECEIVED') {
|
|
||||||
received.push(result);
|
|
||||||
|
|
||||||
if (result.headers && result.headers.hasOwnProperty('x-arango-response-code')) {
|
|
||||||
var code = parseInt(result.headers['x-arango-response-code'].substr(0, 3), 10);
|
|
||||||
result.statusCode = code;
|
|
||||||
|
|
||||||
if (code >= 400 && !ignoreHttpErrors) {
|
|
||||||
var body;
|
|
||||||
|
|
||||||
try {
|
|
||||||
body = JSON.parse(result.body);
|
|
||||||
} catch (err) {
|
|
||||||
raiseError(arangodb.errors.ERROR_INTERNAL.code,
|
|
||||||
'error parsing JSON received from a DB server: ' + err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
raiseError(body.errorNum,
|
|
||||||
body.errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// something else... wait without GC
|
|
||||||
require('internal').wait(0.1, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
global.ArangoClusterComm.drop(data);
|
|
||||||
}
|
|
||||||
return received;
|
|
||||||
};
|
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief whether or not clustering is enabled
|
// / @brief whether or not clustering is enabled
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -493,7 +441,6 @@ exports.isCoordinator = isCoordinator;
|
||||||
exports.role = role;
|
exports.role = role;
|
||||||
exports.shardList = shardList;
|
exports.shardList = shardList;
|
||||||
exports.status = status;
|
exports.status = status;
|
||||||
exports.wait = waitForDistributedResponse;
|
|
||||||
exports.endpointToURL = endpointToURL;
|
exports.endpointToURL = endpointToURL;
|
||||||
exports.shardDistribution = shardDistribution;
|
exports.shardDistribution = shardDistribution;
|
||||||
exports.collectionShardDistribution = collectionShardDistribution;
|
exports.collectionShardDistribution = collectionShardDistribution;
|
||||||
|
|
|
@ -201,7 +201,6 @@ std::string const StaticStrings::PotentialDirtyRead(
|
||||||
"x-arango-potential-dirty-read");
|
"x-arango-potential-dirty-read");
|
||||||
std::string const StaticStrings::RequestForwardedTo(
|
std::string const StaticStrings::RequestForwardedTo(
|
||||||
"x-arango-request-forwarded-to");
|
"x-arango-request-forwarded-to");
|
||||||
std::string const StaticStrings::ResponseCode("x-arango-response-code");
|
|
||||||
std::string const StaticStrings::Server("server");
|
std::string const StaticStrings::Server("server");
|
||||||
std::string const StaticStrings::TransferEncoding("transfer-encoding");
|
std::string const StaticStrings::TransferEncoding("transfer-encoding");
|
||||||
std::string const StaticStrings::TransactionBody("x-arango-trx-body");
|
std::string const StaticStrings::TransactionBody("x-arango-trx-body");
|
||||||
|
|
|
@ -186,7 +186,6 @@ class StaticStrings {
|
||||||
static std::string const Origin;
|
static std::string const Origin;
|
||||||
static std::string const PotentialDirtyRead;
|
static std::string const PotentialDirtyRead;
|
||||||
static std::string const RequestForwardedTo;
|
static std::string const RequestForwardedTo;
|
||||||
static std::string const ResponseCode;
|
|
||||||
static std::string const Server;
|
static std::string const Server;
|
||||||
static std::string const TransferEncoding;
|
static std::string const TransferEncoding;
|
||||||
static std::string const TransactionBody;
|
static std::string const TransactionBody;
|
||||||
|
|
|
@ -534,7 +534,7 @@ void HttpRequest::parseUrl(const char* path, size_t length) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequest::setHeaderV2(std::string key, std::string value) {
|
void HttpRequest::setHeaderV2(std::string&& key, std::string&& value) {
|
||||||
StringUtils::tolowerInPlace(key); // always lowercase key
|
StringUtils::tolowerInPlace(key); // always lowercase key
|
||||||
|
|
||||||
if (key == StaticStrings::ContentLength) {
|
if (key == StaticStrings::ContentLength) {
|
||||||
|
|
|
@ -102,7 +102,7 @@ class HttpRequest final : public GeneralRequest {
|
||||||
|
|
||||||
/// @brief parse an existing url
|
/// @brief parse an existing url
|
||||||
void parseUrl(char const* start, size_t len);
|
void parseUrl(char const* start, size_t len);
|
||||||
void setHeaderV2(std::string key, std::string value);
|
void setHeaderV2(std::string&& key, std::string&& value);
|
||||||
|
|
||||||
static HttpRequest* createHttpRequest(ContentType contentType,
|
static HttpRequest* createHttpRequest(ContentType contentType,
|
||||||
char const* body, int64_t contentLength,
|
char const* body, int64_t contentLength,
|
||||||
|
|
Loading…
Reference in New Issue