1
0
Fork 0

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:
Simon 2019-11-26 01:01:38 +08:00 committed by KVS85
parent b94755c61e
commit 45cccf8537
11 changed files with 157 additions and 192 deletions

View File

@ -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>(

View File

@ -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;
} }

View File

@ -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);

View File

@ -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>

View File

@ -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;

View File

@ -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();
}
} }
} }
} }

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -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) {

View File

@ -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,