mirror of https://gitee.com/bigwinds/arangodb
fix crash when pressing CTRL+c in arangosh (#6187)
This commit is contained in:
parent
2aa879a53b
commit
cb12be3e4e
|
@ -52,17 +52,6 @@ class Connection : public std::enable_shared_from_this<Connection> {
|
|||
/// @brief Send a request to the server and wait into a response it received.
|
||||
std::unique_ptr<Response> sendRequest(std::unique_ptr<Request> r);
|
||||
|
||||
/// @brief sed request synchronously, only save to use if the
|
||||
virtual std::unique_ptr<Response> sendRequestSync(std::unique_ptr<Request> r) {
|
||||
return sendRequest(std::move(r));
|
||||
}
|
||||
|
||||
// Send a request to the server and wait into a response it received.
|
||||
/*std::unique_ptr<Response> sendRequest(Request const& r) {
|
||||
std::unique_ptr<Request> copy(new Request(r));
|
||||
return sendRequest(std::move(copy));
|
||||
}*/
|
||||
|
||||
// Send a request to the server and return immediately.
|
||||
// When a response is received or an error occurs, the corresponding
|
||||
// callbackis called. The callback is executed on a specific
|
||||
|
@ -70,13 +59,6 @@ class Connection : public std::enable_shared_from_this<Connection> {
|
|||
virtual MessageID sendRequest(std::unique_ptr<Request> r,
|
||||
RequestCallback cb) = 0;
|
||||
|
||||
// Send a request to the server and return immediately.
|
||||
// When a response is received or an error occurs, the corresponding
|
||||
// callback is called.
|
||||
/*MessageID sendRequest(Request const& r, RequestCallback cb) {
|
||||
std::unique_ptr<Request> copy(new Request(r));
|
||||
return sendRequest(std::move(copy), cb);
|
||||
}*/
|
||||
|
||||
// Return the number of requests that have not yet finished.
|
||||
virtual std::size_t requestsLeft() const = 0;
|
||||
|
@ -84,9 +66,8 @@ class Connection : public std::enable_shared_from_this<Connection> {
|
|||
/// @brief connection state
|
||||
virtual State state() const = 0;
|
||||
|
||||
/// @brief Activate the connection.
|
||||
virtual void startConnection() = 0;
|
||||
virtual void shutdownConnection(const ErrorCondition) = 0;
|
||||
/// @brief cancel the connection, unusable afterwards
|
||||
virtual void cancel() = 0;
|
||||
|
||||
std::string endpoint() const;
|
||||
|
||||
|
@ -94,6 +75,9 @@ class Connection : public std::enable_shared_from_this<Connection> {
|
|||
Connection(detail::ConnectionConfiguration const& conf)
|
||||
: _config(conf) {}
|
||||
|
||||
/// @brief Activate the connection.
|
||||
virtual void startConnection() = 0;
|
||||
|
||||
// Invoke the configured ConnectionFailureCallback (if any)
|
||||
void onFailure(Error errorCode, const std::string& errorMessage) {
|
||||
if (_config._onFailure) {
|
||||
|
|
|
@ -38,8 +38,10 @@ struct Socket<SocketType::Tcp> {
|
|||
: resolver(ctx), socket(ctx) {}
|
||||
|
||||
~Socket() {
|
||||
try {
|
||||
resolver.cancel();
|
||||
shutdown();
|
||||
} catch(...) {}
|
||||
}
|
||||
|
||||
template<typename CallbackT>
|
||||
|
@ -63,10 +65,10 @@ struct Socket<SocketType::Tcp> {
|
|||
}
|
||||
void shutdown() {
|
||||
if (socket.is_open()) {
|
||||
asio_ns::error_code error; // prevents exceptions
|
||||
socket.cancel(error);
|
||||
socket.shutdown(asio_ns::ip::tcp::socket::shutdown_both, error);
|
||||
socket.close(error);
|
||||
asio_ns::error_code ec; // prevents exceptions
|
||||
socket.cancel(ec);
|
||||
socket.shutdown(asio_ns::ip::tcp::socket::shutdown_both, ec);
|
||||
socket.close(ec);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,8 +83,10 @@ struct Socket<fuerte::SocketType::Ssl> {
|
|||
: resolver(ctx), socket(ctx, loop.sslContext()) {}
|
||||
|
||||
~Socket() {
|
||||
try {
|
||||
resolver.cancel();
|
||||
shutdown();
|
||||
} catch(...) {}
|
||||
}
|
||||
|
||||
template<typename CallbackT>
|
||||
|
@ -124,12 +128,10 @@ struct Socket<fuerte::SocketType::Ssl> {
|
|||
resolver.async_resolve({config._host, config._port}, rcb);
|
||||
}
|
||||
void shutdown() {
|
||||
//socket.cancel();
|
||||
if (socket.lowest_layer().is_open()) {
|
||||
asio_ns::error_code error;
|
||||
socket.shutdown(error);
|
||||
socket.lowest_layer().shutdown(asio_ns::ip::tcp::socket::shutdown_both, error);
|
||||
socket.lowest_layer().close(error);
|
||||
if (socket.next_layer().is_open()) {
|
||||
asio_ns::error_code ec;
|
||||
socket.next_layer().shutdown(asio_ns::ip::tcp::socket::shutdown_both, ec);
|
||||
socket.shutdown(ec);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ std::shared_ptr<Connection> ConnectionBuilder::connect(EventLoopService& loop) {
|
|||
#endif
|
||||
}
|
||||
if (!result) {
|
||||
throw std::logic_error("unsupported socket type");
|
||||
throw std::logic_error("unsupported socket or protocol type");
|
||||
}
|
||||
|
||||
// Start the connection implementation
|
||||
|
|
|
@ -104,7 +104,7 @@ HttpConnection<ST>::HttpConnection(EventLoopService& loop,
|
|||
_state(Connection::State::Disconnected),
|
||||
_numQueued(0),
|
||||
_active(false),
|
||||
_queue(1024) {
|
||||
_queue() {
|
||||
// initialize http parsing code
|
||||
_parserSettings.on_message_begin = ::on_message_began;
|
||||
_parserSettings.on_status = ::on_status;
|
||||
|
@ -141,7 +141,12 @@ MessageID HttpConnection<ST>::sendRequest(std::unique_ptr<Request> req,
|
|||
RequestCallback cb) {
|
||||
static std::atomic<uint64_t> ticketId(1);
|
||||
|
||||
assert(req);
|
||||
Connection::State state = _state.load(std::memory_order_acquire);
|
||||
if (state == Connection::State::Failed) {
|
||||
cb(errorToInt(ErrorCondition::Canceled), std::move(req), nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// construct RequestItem
|
||||
std::unique_ptr<RequestItem> item(new RequestItem());
|
||||
// requestItem->_response later
|
||||
|
@ -153,13 +158,12 @@ MessageID HttpConnection<ST>::sendRequest(std::unique_ptr<Request> req,
|
|||
// Prepare a new request
|
||||
uint64_t id = item->_messageID;
|
||||
if (!_queue.push(item.get())) {
|
||||
FUERTE_LOG_ERROR << "connection queue capactiy exceeded\n";
|
||||
throw std::length_error("connection queue capactiy exceeded");
|
||||
FUERTE_LOG_ERROR << "connection queue capacity exceeded\n";
|
||||
throw std::length_error("connection queue capacity exceeded");
|
||||
}
|
||||
item.release();
|
||||
_numQueued.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
Connection::State state = _state.load(std::memory_order_acquire);
|
||||
if (state == Connection::State::Connected) {
|
||||
FUERTE_LOG_HTTPTRACE << "sendRequest (http): start sending & reading\n";
|
||||
startWriting();
|
||||
|
@ -173,7 +177,6 @@ MessageID HttpConnection<ST>::sendRequest(std::unique_ptr<Request> req,
|
|||
// Activate this connection.
|
||||
template <SocketType ST>
|
||||
void HttpConnection<ST>::startConnection() {
|
||||
|
||||
// start connecting only if state is disconnected
|
||||
Connection::State exp = Connection::State::Disconnected;
|
||||
if (!_state.compare_exchange_strong(exp, Connection::State::Connecting)) {
|
||||
|
@ -195,19 +198,38 @@ void HttpConnection<ST>::startConnection() {
|
|||
});
|
||||
}
|
||||
|
||||
/// @brief cancel the connection, unusable afterwards
|
||||
template <SocketType ST>
|
||||
void HttpConnection<ST>::cancel() {
|
||||
std::weak_ptr<Connection> self = shared_from_this();
|
||||
asio_ns::post(*_io_context, [self, this] {
|
||||
auto s = self.lock();
|
||||
if (s) {
|
||||
shutdownConnection(ErrorCondition::Canceled);
|
||||
_state.store(State::Failed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// shutdown the connection and cancel all pending messages.
|
||||
template<SocketType ST>
|
||||
void HttpConnection<ST>::shutdownConnection(const ErrorCondition ec) {
|
||||
FUERTE_LOG_CALLBACKS << "shutdownConnection\n";
|
||||
|
||||
_state.store(State::Disconnected, std::memory_order_release);
|
||||
if (_state.load() != State::Failed) {
|
||||
_state.store(State::Disconnected);
|
||||
}
|
||||
|
||||
// cancel timeouts
|
||||
try {
|
||||
_timeout.cancel(); // cancel timeouts
|
||||
_timeout.cancel();
|
||||
} catch (...) {
|
||||
// cancel() may throw, but we are not allowed to throw here
|
||||
// as we may be called from the dtor
|
||||
}
|
||||
_protocol.shutdown(); // Close socket
|
||||
|
||||
// Close socket
|
||||
_protocol.shutdown();
|
||||
_active.store(false); // no IO operations running
|
||||
|
||||
RequestItem* item = nullptr;
|
||||
|
@ -335,7 +357,7 @@ void HttpConnection<ST>::asyncWriteNextRequest() {
|
|||
return;
|
||||
}
|
||||
// a request got queued in-between last minute
|
||||
_active.store(true, std::memory_order_release);
|
||||
_active.store(true);
|
||||
}
|
||||
std::shared_ptr<http::RequestItem> item(ptr);
|
||||
_numQueued.fetch_sub(1, std::memory_order_relaxed);
|
||||
|
@ -505,115 +527,23 @@ template<SocketType ST>
|
|||
void HttpConnection<ST>::setTimeout(std::chrono::milliseconds millis) {
|
||||
if (millis.count() == 0) {
|
||||
_timeout.cancel();
|
||||
return; // do
|
||||
return;
|
||||
}
|
||||
assert(millis.count() > 0);
|
||||
_timeout.expires_after(millis);
|
||||
auto self = shared_from_this();
|
||||
_timeout.async_wait([this, self] (asio_ns::error_code const& e) {
|
||||
if (e == asio_ns::error::operation_aborted) {
|
||||
// timer was canceled
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e) { // expired
|
||||
std::weak_ptr<Connection> self = shared_from_this();
|
||||
_timeout.async_wait([self, this] (asio_ns::error_code const& ec) {
|
||||
if (!ec) {
|
||||
auto s = self.lock();
|
||||
if (s) {
|
||||
FUERTE_LOG_DEBUG << "HTTP-Request timeout\n";
|
||||
restartConnection(ErrorCondition::Timeout);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// @brief sed request synchronously, only save to use if the
|
||||
template<SocketType ST>
|
||||
std::unique_ptr<Response> HttpConnection<ST>::sendRequestSync(std::unique_ptr<Request> req) {
|
||||
int max = 1024;
|
||||
Connection::State state = _state.load(std::memory_order_acquire);
|
||||
while (state != State::Connected && max-- > 0) {
|
||||
if (state == State::Failed) {
|
||||
return nullptr;
|
||||
} else if (state == State::Disconnected) {
|
||||
startConnection();
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
state = _state.load(std::memory_order_acquire);
|
||||
}
|
||||
if (state != State::Connected) {
|
||||
throw ErrorCondition::CouldNotConnect;
|
||||
}
|
||||
|
||||
RequestItem item;
|
||||
// requestItem->_response later
|
||||
item._requestHeader = buildRequestBody(*req);
|
||||
item._request = std::move(req);
|
||||
item._response.reset(new Response());
|
||||
|
||||
setTimeout(item._request->timeout());
|
||||
std::vector<asio_ns::const_buffer> buffers(2);
|
||||
buffers.emplace_back(item._requestHeader.data(),
|
||||
item._requestHeader.size());
|
||||
// GET and HEAD have no payload
|
||||
if (item._request->header.restVerb != RestVerb::Get &&
|
||||
item._request->header.restVerb != RestVerb::Head) {
|
||||
buffers.emplace_back(item._request->payload());
|
||||
}
|
||||
asio_ns::error_code ec;
|
||||
asio_ns::write(_protocol.socket, buffers, ec);
|
||||
if (ec) {
|
||||
auto err = checkEOFError(ec, ErrorCondition::WriteError);;
|
||||
shutdownConnection(err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
http_parser_init(&_parser, HTTP_RESPONSE);
|
||||
_parser.data = static_cast<void*>(&item);
|
||||
|
||||
while (!item.message_complete) {
|
||||
// reserve 32kB in output buffer
|
||||
auto mutableBuff = _receiveBuffer.prepare(READ_BLOCK_SIZE);
|
||||
|
||||
size_t transferred = _protocol.socket.read_some(mutableBuff, ec);
|
||||
if (ec) {
|
||||
auto err = checkEOFError(ec, ErrorCondition::ReadError);;
|
||||
shutdownConnection(err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Inspect the data we've received so far.
|
||||
auto cursor = asio_ns::buffer_cast<const char*>(_receiveBuffer.data()); // no copy
|
||||
|
||||
/* Start up / continue the parser.
|
||||
* Note we pass recved==0 to signal that EOF has been received.
|
||||
*/
|
||||
size_t nparsed = http_parser_execute(&_parser, &_parserSettings,
|
||||
cursor, transferred);
|
||||
|
||||
if (_parser.upgrade || nparsed != transferred) {
|
||||
/* Handle error. Usually just close the connection. */
|
||||
FUERTE_LOG_ERROR << "Invalid HTTP response in parser\n";
|
||||
shutdownConnection(ErrorCondition::ProtocolError); // will cleanup _inFlight
|
||||
throw ErrorCondition::ProtocolError;
|
||||
}
|
||||
|
||||
// item.message_complete may have been set by the call to http_parser_execute!
|
||||
if (item.message_complete) {
|
||||
//_timeout.cancel(); // got response in time
|
||||
// Remove consumed data from receive buffer.
|
||||
_receiveBuffer.consume(nparsed);
|
||||
|
||||
// thread-safe access on IO-Thread
|
||||
if (!item._responseBuffer.empty()) {
|
||||
item._response->setPayload(std::move(item._responseBuffer), 0);
|
||||
}
|
||||
if (!item.should_keep_alive) {
|
||||
shutdownConnection(ErrorCondition::CloseRequested);
|
||||
}
|
||||
return std::move(item._response);
|
||||
}
|
||||
}
|
||||
|
||||
throw ErrorCondition::Timeout;
|
||||
}
|
||||
|
||||
template class arangodb::fuerte::v1::http::HttpConnection<SocketType::Tcp>;
|
||||
template class arangodb::fuerte::v1::http::HttpConnection<SocketType::Ssl>;
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
|
|
|
@ -55,9 +55,6 @@ class HttpConnection final : public fuerte::Connection {
|
|||
/// Start an asynchronous request.
|
||||
MessageID sendRequest(std::unique_ptr<Request>, RequestCallback) override;
|
||||
|
||||
/// @brief sed request synchronously, only save to use if the
|
||||
std::unique_ptr<Response> sendRequestSync(std::unique_ptr<Request> r) override;
|
||||
|
||||
// Return the number of unfinished requests.
|
||||
size_t requestsLeft() const override {
|
||||
return _numQueued.load(std::memory_order_acquire);
|
||||
|
@ -71,10 +68,14 @@ class HttpConnection final : public fuerte::Connection {
|
|||
// Activate this connection
|
||||
void startConnection() override;
|
||||
|
||||
// called on shutdown, always call superclass
|
||||
void shutdownConnection(const ErrorCondition) override;
|
||||
/// @brief cancel the connection, unusable afterwards
|
||||
void cancel() override;
|
||||
|
||||
private:
|
||||
|
||||
// shutdown connection, cancel async operations
|
||||
void shutdownConnection(const ErrorCondition);
|
||||
|
||||
// restart connection
|
||||
void restartConnection(const ErrorCondition);
|
||||
|
||||
|
@ -124,7 +125,8 @@ class HttpConnection final : public fuerte::Connection {
|
|||
std::atomic<bool> _active;
|
||||
|
||||
/// elements to send out
|
||||
boost::lockfree::queue<fuerte::v1::http::RequestItem*> _queue;
|
||||
boost::lockfree::queue<fuerte::v1::http::RequestItem*,
|
||||
boost::lockfree::capacity<1024>> _queue;
|
||||
|
||||
/// cached authentication header
|
||||
std::string _authHeader;
|
||||
|
|
|
@ -49,7 +49,7 @@ VstConnection<ST>::VstConnection(
|
|||
_timeout(*_io_context),
|
||||
_state(Connection::State::Disconnected),
|
||||
_loopState(0),
|
||||
_writeQueue(1024){}
|
||||
_writeQueue() {}
|
||||
|
||||
template<SocketType ST>
|
||||
VstConnection<ST>::~VstConnection() {
|
||||
|
@ -62,6 +62,12 @@ static std::atomic<MessageID> vstMessageId(1);
|
|||
template<SocketType ST>
|
||||
MessageID VstConnection<ST>::sendRequest(std::unique_ptr<Request> req,
|
||||
RequestCallback cb) {
|
||||
Connection::State state = _state.load(std::memory_order_acquire);
|
||||
if (state == Connection::State::Failed) {
|
||||
cb(errorToInt(ErrorCondition::Canceled), std::move(req), nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// it does not matter if IDs are reused on different connections
|
||||
uint64_t mid = vstMessageId.fetch_add(1, std::memory_order_relaxed);
|
||||
// Create RequestItem from parameters
|
||||
|
@ -74,14 +80,13 @@ MessageID VstConnection<ST>::sendRequest(std::unique_ptr<Request> req,
|
|||
|
||||
// Add item to send queue
|
||||
if (!_writeQueue.push(item.get())) {
|
||||
FUERTE_LOG_ERROR << "connection queue capactiy exceeded\n";
|
||||
throw std::length_error("connection queue capactiy exceeded");
|
||||
FUERTE_LOG_ERROR << "connection queue capacity exceeded\n";
|
||||
throw std::length_error("connection queue capacity exceeded");
|
||||
}
|
||||
item.release();
|
||||
// WRITE_LOOP_ACTIVE, READ_LOOP_ACTIVE are synchronized via cmpxchg
|
||||
uint32_t loop = _loopState.fetch_add(WRITE_LOOP_QUEUE_INC, std::memory_order_seq_cst);
|
||||
|
||||
Connection::State state = _state.load(std::memory_order_acquire);
|
||||
if (state == Connection::State::Connected) {
|
||||
FUERTE_LOG_VSTTRACE << "sendRequest (vst): start sending & reading\n";
|
||||
if (!(loop & WRITE_LOOP_ACTIVE)) {
|
||||
|
@ -103,7 +108,6 @@ void VstConnection<ST>::startConnection() {
|
|||
FUERTE_LOG_ERROR << "already resolving endpoint\n";
|
||||
return;
|
||||
}
|
||||
|
||||
auto self = shared_from_this();
|
||||
_protocol.connect(_config, [self, this](asio_ns::error_code const& ec) {
|
||||
if (ec) {
|
||||
|
@ -117,18 +121,34 @@ void VstConnection<ST>::startConnection() {
|
|||
});
|
||||
}
|
||||
|
||||
/// @brief cancel the connection, unusable afterwards
|
||||
template <SocketType ST>
|
||||
void VstConnection<ST>::cancel() {
|
||||
std::weak_ptr<Connection> self = shared_from_this();
|
||||
asio_ns::post(*_io_context, [self, this] {
|
||||
auto s = self.lock();
|
||||
if (s) {
|
||||
shutdownConnection(ErrorCondition::Canceled);
|
||||
_state.store(State::Failed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// shutdown the connection and cancel all pending messages.
|
||||
template <SocketType ST>
|
||||
void VstConnection<ST>::shutdownConnection(const ErrorCondition ec) {
|
||||
FUERTE_LOG_CALLBACKS << "shutdownConnection\n";
|
||||
|
||||
_state.store(State::Disconnected, std::memory_order_release);
|
||||
if (_state.load() != State::Failed) {
|
||||
_state.store(State::Disconnected);
|
||||
}
|
||||
|
||||
// cancel timeouts
|
||||
try {
|
||||
_timeout.cancel();
|
||||
} catch (...) {
|
||||
// cancel() may throw
|
||||
// cancel() may throw, but we are not allowed to throw here
|
||||
// as we may be called from the dtor
|
||||
}
|
||||
|
||||
// Close socket
|
||||
|
@ -199,10 +219,9 @@ void VstConnection<ST>::finishInitialization() {
|
|||
auto self = shared_from_this();
|
||||
asio_ns::async_write(_protocol.socket,
|
||||
asio_ns::buffer(vstHeader, strlen(vstHeader)),
|
||||
[this, self](asio_ns::error_code const& ec, std::size_t transferred) {
|
||||
[self, this](asio_ns::error_code const& ec, std::size_t transferred) {
|
||||
if (ec) {
|
||||
FUERTE_LOG_ERROR << ec.message() << "\n";
|
||||
_state.store(Connection::State::Disconnected, std::memory_order_release);
|
||||
shutdownConnection(ErrorCondition::CouldNotConnect);
|
||||
onFailure(errorToInt(ErrorCondition::CouldNotConnect),
|
||||
"unable to initialize connection: error=" + ec.message());
|
||||
|
@ -242,7 +261,7 @@ void VstConnection<ST>::sendAuthenticationRequest() {
|
|||
item->prepareForNetwork(_vstVersion, header, asio_ns::const_buffer(0,0));
|
||||
|
||||
auto self = shared_from_this();
|
||||
item->_callback = [this, self](Error error, std::unique_ptr<Request>,
|
||||
item->_callback = [self, this](Error error, std::unique_ptr<Request>,
|
||||
std::unique_ptr<Response> resp) {
|
||||
if (error || resp->statusCode() != StatusOK) {
|
||||
_state.store(State::Failed, std::memory_order_release);
|
||||
|
@ -255,7 +274,7 @@ void VstConnection<ST>::sendAuthenticationRequest() {
|
|||
|
||||
// actually send auth request
|
||||
asio_ns::post(*_io_context, [this, self, item] {
|
||||
auto cb = [this, self, item](asio_ns::error_code const& ec,
|
||||
auto cb = [self, item, this](asio_ns::error_code const& ec,
|
||||
std::size_t transferred) {
|
||||
if (ec) {
|
||||
asyncWriteCallback(ec, transferred, std::move(item)); // error handling
|
||||
|
@ -286,7 +305,7 @@ void VstConnection<ST>::startWriting() {
|
|||
std::memory_order_seq_cst)) {
|
||||
FUERTE_LOG_TRACE << "startWriting (vst): starting write\n";
|
||||
auto self = shared_from_this(); // only one thread can get here per connection
|
||||
asio_ns::post(*_io_context, [this, self] {
|
||||
asio_ns::post(*_io_context, [self, this] {
|
||||
asyncWriteNextRequest();
|
||||
});
|
||||
return;
|
||||
|
@ -328,7 +347,7 @@ void VstConnection<ST>::asyncWriteNextRequest() {
|
|||
setTimeout(); // prepare request / connection timeouts
|
||||
|
||||
auto self = shared_from_this();
|
||||
auto cb = [this, self, item](asio_ns::error_code const& ec, std::size_t transferred) {
|
||||
auto cb = [self, item, this](asio_ns::error_code const& ec, std::size_t transferred) {
|
||||
asyncWriteCallback(ec, transferred, std::move(item));
|
||||
};
|
||||
asio_ns::async_write(_protocol.socket, item->_requestBuffers, cb);
|
||||
|
@ -404,7 +423,7 @@ void VstConnection<ST>::startReading() {
|
|||
while (!(state & READ_LOOP_ACTIVE)) {
|
||||
if (_loopState.compare_exchange_weak(state, state | READ_LOOP_ACTIVE)) {
|
||||
auto self = shared_from_this(); // only one thread can get here per connection
|
||||
asio_ns::post(*_io_context, [this, self] {
|
||||
asio_ns::post(*_io_context, [self, this] {
|
||||
asyncReadSome();
|
||||
});
|
||||
return;
|
||||
|
@ -444,7 +463,7 @@ void VstConnection<ST>::asyncReadSome() {
|
|||
#endif
|
||||
|
||||
auto self = shared_from_this();
|
||||
auto cb = [this, self](asio_ns::error_code const& ec, size_t transferred) {
|
||||
auto cb = [self, this](asio_ns::error_code const& ec, size_t transferred) {
|
||||
// received data is "committed" from output sequence to input sequence
|
||||
_receiveBuffer.commit(transferred);
|
||||
asyncReadCallback(ec, transferred);
|
||||
|
@ -613,11 +632,15 @@ void VstConnection<ST>::setTimeout() {
|
|||
}
|
||||
|
||||
_timeout.expires_at(expires);
|
||||
auto self = shared_from_this();
|
||||
_timeout.async_wait([this, self](asio_ns::error_code const& ec) {
|
||||
std::weak_ptr<Connection> self = shared_from_this();
|
||||
_timeout.async_wait([self, this](asio_ns::error_code const& ec) {
|
||||
if (ec) { // was canceled
|
||||
return;
|
||||
}
|
||||
auto s = self.lock();
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cancel expired requests
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
|
|
@ -72,11 +72,14 @@ class VstConnection final : public Connection {
|
|||
/// Activate the connection.
|
||||
void startConnection() override final;
|
||||
|
||||
/// called on shutdown, always call superclass
|
||||
void shutdownConnection(const ErrorCondition) override;
|
||||
/// @brief cancel the connection, unusable afterwards
|
||||
void cancel() override final;
|
||||
|
||||
private:
|
||||
|
||||
/// shutdown connection, cancel async operations
|
||||
void shutdownConnection(const ErrorCondition);
|
||||
|
||||
void restartConnection(const ErrorCondition);
|
||||
|
||||
void finishInitialization();
|
||||
|
@ -151,7 +154,8 @@ class VstConnection final : public Connection {
|
|||
static_assert((WRITE_LOOP_ACTIVE & READ_LOOP_ACTIVE) == 0, "");
|
||||
|
||||
/// elements to send out
|
||||
boost::lockfree::queue<vst::RequestItem*> _writeQueue;
|
||||
boost::lockfree::queue<vst::RequestItem*,
|
||||
boost::lockfree::capacity<1024>> _writeQueue;
|
||||
};
|
||||
|
||||
}}}} // namespace arangodb::fuerte::v1::vst
|
||||
|
|
|
@ -192,6 +192,7 @@ GeneralCommTask::RequestFlow GeneralCommTask::prepareExecution(
|
|||
break; // continue with auth check
|
||||
}
|
||||
// intentional fallthrough
|
||||
[[gnu::fallthrough]];
|
||||
}
|
||||
case ServerState::Mode::TRYAGAIN: {
|
||||
if (path.find("/_admin/shutdown") == std::string::npos &&
|
||||
|
|
|
@ -62,6 +62,10 @@ V8ClientConnection::V8ClientConnection()
|
|||
_vpackOptions(VPackOptions::Defaults) {
|
||||
_vpackOptions.buildUnindexedObjects = true;
|
||||
_vpackOptions.buildUnindexedArrays = true;
|
||||
_builder.onFailure([this](int error, std::string const& msg) {
|
||||
_lastHttpReturnCode = 503;
|
||||
_lastErrorMessage = msg;
|
||||
});
|
||||
}
|
||||
|
||||
V8ClientConnection::~V8ClientConnection() {
|
||||
|
@ -69,34 +73,15 @@ V8ClientConnection::~V8ClientConnection() {
|
|||
_loop.forceStop();
|
||||
}
|
||||
|
||||
void V8ClientConnection::init(ClientFeature* client) {
|
||||
_requestTimeout = std::chrono::duration<double>(client->requestTimeout());
|
||||
_username = client->username();
|
||||
_password = client->password();
|
||||
_databaseName = client->databaseName();
|
||||
void V8ClientConnection::createConnection() {
|
||||
|
||||
fuerte::ConnectionBuilder builder;
|
||||
builder.endpoint(client->endpoint());
|
||||
if (!client->username().empty()) {
|
||||
builder.user(client->username()).password(client->password());
|
||||
builder.authenticationType(fuerte::AuthenticationType::Basic);
|
||||
} else if (!client->jwtSecret().empty()) {
|
||||
builder.jwtToken(fuerte::jwt::generateInternalToken(client->jwtSecret(), "arangosh"));
|
||||
builder.authenticationType(fuerte::AuthenticationType::Jwt);
|
||||
}
|
||||
builder.onFailure([this](int error, std::string const& msg) {
|
||||
_lastHttpReturnCode = 505;
|
||||
_lastErrorMessage = msg;
|
||||
});
|
||||
|
||||
shutdownConnection();
|
||||
_connection = builder.connect(_loop);
|
||||
_connection = _builder.connect(_loop);
|
||||
|
||||
fuerte::StringMap params{{"details","true"}};
|
||||
auto req = fuerte::createRequest(fuerte::RestVerb::Get, "/_api/version", params);
|
||||
req->header.database = _databaseName;
|
||||
try {
|
||||
auto res = _connection->sendRequestSync(std::move(req));
|
||||
auto res = _connection->sendRequest(std::move(req));
|
||||
_lastHttpReturnCode = res->statusCode();
|
||||
|
||||
if (_lastHttpReturnCode == 200) {
|
||||
|
@ -140,19 +125,16 @@ void V8ClientConnection::init(ClientFeature* client) {
|
|||
}
|
||||
} catch (fuerte::ErrorCondition const& e) { // connection error
|
||||
_lastErrorMessage = fuerte::to_string(e);
|
||||
_lastHttpReturnCode = 505;
|
||||
_lastHttpReturnCode = 503;
|
||||
}
|
||||
}
|
||||
|
||||
void V8ClientConnection::setInterrupted(bool interrupted) {
|
||||
if (_connection) {
|
||||
if (interrupted) {
|
||||
shutdownConnection();
|
||||
} else {
|
||||
if (_connection->state() == fuerte::Connection::State::Disconnected) {
|
||||
_connection->startConnection();
|
||||
}
|
||||
}
|
||||
if (interrupted && _connection.get() != nullptr) {
|
||||
_connection->cancel();
|
||||
_connection.reset();
|
||||
} else if (!interrupted && _connection.get() == nullptr) {
|
||||
createConnection();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,12 +150,34 @@ std::string V8ClientConnection::endpointSpecification() const {
|
|||
}
|
||||
|
||||
void V8ClientConnection::connect(ClientFeature* client) {
|
||||
this->init(client);
|
||||
TRI_ASSERT(client);
|
||||
|
||||
_requestTimeout = std::chrono::duration<double>(client->requestTimeout());
|
||||
_databaseName = client->databaseName();
|
||||
_builder.endpoint(client->endpoint());
|
||||
if (!client->username().empty()) {
|
||||
_builder.user(client->username()).password(client->password());
|
||||
_builder.authenticationType(fuerte::AuthenticationType::Basic);
|
||||
} else if (!client->jwtSecret().empty()) {
|
||||
_builder.jwtToken(fuerte::jwt::generateInternalToken(client->jwtSecret(), "arangosh"));
|
||||
_builder.authenticationType(fuerte::AuthenticationType::Jwt);
|
||||
}
|
||||
createConnection();
|
||||
}
|
||||
|
||||
void V8ClientConnection::reconnect(ClientFeature* client) {
|
||||
_requestTimeout = std::chrono::duration<double>(client->requestTimeout());
|
||||
_databaseName = client->databaseName();
|
||||
_builder.endpoint(client->endpoint());
|
||||
if (!client->username().empty()) {
|
||||
_builder.user(client->username()).password(client->password());
|
||||
_builder.authenticationType(fuerte::AuthenticationType::Basic);
|
||||
} else if (!client->jwtSecret().empty()) {
|
||||
_builder.jwtToken(fuerte::jwt::generateInternalToken(client->jwtSecret(), "arangosh"));
|
||||
_builder.authenticationType(fuerte::AuthenticationType::Jwt);
|
||||
}
|
||||
try {
|
||||
init(client);
|
||||
createConnection();
|
||||
} catch (...) {
|
||||
std::string errorMessage = "error in '" + client->endpoint() + "'";
|
||||
throw errorMessage;
|
||||
|
@ -186,7 +190,7 @@ void V8ClientConnection::reconnect(ClientFeature* client) {
|
|||
<< "'" << endpointSpecification() << "', "
|
||||
<< "version " << _version << " [" << _mode << "], "
|
||||
<< "database '" << _databaseName << "', "
|
||||
<< "username: '" << _username << "'";
|
||||
<< "username: '" << client->username() << "'";
|
||||
} else {
|
||||
if (client->getWarnConnect()) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
|
@ -1353,7 +1357,7 @@ v8::Local<v8::Value> V8ClientConnection::requestData(
|
|||
_lastHttpReturnCode = 0;
|
||||
if (!_connection) {
|
||||
_lastErrorMessage = "not connected";
|
||||
_lastHttpReturnCode = 505;
|
||||
_lastHttpReturnCode = 503;
|
||||
return v8::Null(isolate);
|
||||
}
|
||||
|
||||
|
@ -1403,7 +1407,7 @@ v8::Local<v8::Value> V8ClientConnection::requestData(
|
|||
|
||||
std::unique_ptr<fuerte::Response> response;
|
||||
try {
|
||||
response = _connection->sendRequestSync(std::move(req));
|
||||
response = _connection->sendRequest(std::move(req));
|
||||
} catch (fuerte::ErrorCondition const& ec) {
|
||||
return handleResult(isolate, nullptr, ec);
|
||||
}
|
||||
|
@ -1455,10 +1459,10 @@ v8::Local<v8::Value> V8ClientConnection::requestDataRaw(
|
|||
|
||||
std::unique_ptr<fuerte::Response> response;
|
||||
try {
|
||||
response = _connection->sendRequestSync(std::move(req));
|
||||
response = _connection->sendRequest(std::move(req));
|
||||
} catch (fuerte::ErrorCondition const& e) {
|
||||
_lastErrorMessage.assign(fuerte::to_string(e));
|
||||
_lastHttpReturnCode = 505;
|
||||
_lastHttpReturnCode = 503;
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> result = v8::Object::New(isolate);
|
||||
|
@ -1756,7 +1760,7 @@ void V8ClientConnection::initServer(v8::Isolate* isolate,
|
|||
|
||||
void V8ClientConnection::shutdownConnection() {
|
||||
if (_connection) {
|
||||
_connection->shutdownConnection(fuerte::ErrorCondition::Canceled);
|
||||
_connection->cancel();
|
||||
_connection.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "Basics/Common.h"
|
||||
#include "Basics/StringRef.h"
|
||||
|
||||
#include <fuerte/connection.h>
|
||||
#include <fuerte/loop.h>
|
||||
#include <fuerte/types.h>
|
||||
#include <v8.h>
|
||||
|
@ -58,7 +59,7 @@ class V8ClientConnection {
|
|||
~V8ClientConnection();
|
||||
|
||||
public:
|
||||
void setInterrupted(bool value);
|
||||
void setInterrupted(bool interrupted);
|
||||
bool isConnected();
|
||||
|
||||
void connect(ClientFeature*);
|
||||
|
@ -66,8 +67,8 @@ class V8ClientConnection {
|
|||
|
||||
std::string const& databaseName() const { return _databaseName; }
|
||||
void setDatabaseName(std::string const& value) { _databaseName = value; }
|
||||
std::string const& username() const { return _username; }
|
||||
std::string const& password() const { return _password; }
|
||||
std::string username() const { return _builder.user(); }
|
||||
std::string password() const { return _builder.password(); }
|
||||
int lastHttpReturnCode() const { return _lastHttpReturnCode; }
|
||||
std::string lastErrorMessage() const { return _lastErrorMessage; }
|
||||
std::string const& version() const { return _version; }
|
||||
|
@ -107,7 +108,8 @@ class V8ClientConnection {
|
|||
ClientFeature*);
|
||||
|
||||
private:
|
||||
void init(ClientFeature*);
|
||||
|
||||
void createConnection();
|
||||
|
||||
v8::Local<v8::Value> requestData(
|
||||
v8::Isolate* isolate, fuerte::RestVerb verb,
|
||||
|
@ -130,8 +132,6 @@ class V8ClientConnection {
|
|||
|
||||
private:
|
||||
std::string _databaseName;
|
||||
std::string _username;
|
||||
std::string _password;
|
||||
std::chrono::duration<double> _requestTimeout;
|
||||
|
||||
int _lastHttpReturnCode;
|
||||
|
@ -140,8 +140,9 @@ class V8ClientConnection {
|
|||
std::string _version;
|
||||
std::string _mode;
|
||||
|
||||
std::shared_ptr<fuerte::Connection> _connection;
|
||||
fuerte::EventLoopService _loop;
|
||||
fuerte::ConnectionBuilder _builder;
|
||||
std::shared_ptr<fuerte::Connection> _connection;
|
||||
velocypack::Options _vpackOptions;
|
||||
};
|
||||
} // namespace arangodb
|
||||
|
|
|
@ -282,10 +282,10 @@ bool V8ShellFeature::printHello(V8ClientConnection* v8connection) {
|
|||
}
|
||||
|
||||
// the result is wrapped in a Javascript variable SYS_ARANGO
|
||||
std::unique_ptr<V8ClientConnection> V8ShellFeature::setup(
|
||||
std::shared_ptr<V8ClientConnection> V8ShellFeature::setup(
|
||||
v8::Local<v8::Context>& context, bool createConnection,
|
||||
std::vector<std::string> const& positionals, bool* promptError) {
|
||||
std::unique_ptr<V8ClientConnection> v8connection;
|
||||
std::shared_ptr<V8ClientConnection> v8connection;
|
||||
|
||||
ClientFeature* client = nullptr;
|
||||
|
||||
|
@ -293,10 +293,6 @@ std::unique_ptr<V8ClientConnection> V8ShellFeature::setup(
|
|||
client = server()->getFeature<ClientFeature>("Client");
|
||||
|
||||
if (client != nullptr && client->isEnabled()) {
|
||||
/*auto jwtSecret = client->jwtSecret();
|
||||
if (!jwtSecret.empty()) {
|
||||
V8ClientConnection::setJwtSecret(jwtSecret);
|
||||
}*/
|
||||
v8connection = std::make_unique<V8ClientConnection>();
|
||||
v8connection->connect(client);
|
||||
} else {
|
||||
|
@ -338,7 +334,7 @@ int V8ShellFeature::runShell(std::vector<std::string> const& positionals) {
|
|||
|
||||
if (v8connection != nullptr) {
|
||||
v8LineEditor.setSignalFunction(
|
||||
[&v8connection]() { v8connection->setInterrupted(true); });
|
||||
[v8connection]() { v8connection->setInterrupted(true); });
|
||||
}
|
||||
|
||||
v8LineEditor.open(_console->autoComplete());
|
||||
|
@ -910,8 +906,8 @@ void V8ShellFeature::initGlobals() {
|
|||
auto ctx = ArangoGlobalContext::CONTEXT;
|
||||
|
||||
if (ctx == nullptr) {
|
||||
LOG_TOPIC(ERR, arangodb::Logger::FIXME)
|
||||
<< "failed to get global context. ";
|
||||
LOG_TOPIC(FATAL, arangodb::Logger::FIXME)
|
||||
<< "failed to get global context";
|
||||
FATAL_ERROR_EXIT();
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ class V8ShellFeature final : public application_features::ApplicationFeature {
|
|||
void initGlobals();
|
||||
void initMode(ShellFeature::RunMode, std::vector<std::string> const&);
|
||||
void loadModules(ShellFeature::RunMode);
|
||||
std::unique_ptr<V8ClientConnection> setup(v8::Local<v8::Context>& context, bool,
|
||||
std::shared_ptr<V8ClientConnection> setup(v8::Local<v8::Context>& context, bool,
|
||||
std::vector<std::string> const&,
|
||||
bool* promptError = nullptr);
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
#include "V8LineEditor.h"
|
||||
|
||||
#include "Basics/Mutex.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
#include "Basics/StringUtils.h"
|
||||
#include "Basics/tri-strings.h"
|
||||
#include "Logger/Logger.h"
|
||||
|
@ -31,14 +33,17 @@
|
|||
#include "V8/v8-utils.h"
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb;
|
||||
|
||||
namespace {
|
||||
static arangodb::Mutex singletonMutex;
|
||||
static arangodb::V8LineEditor* singleton = nullptr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief the active instance of the editor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::atomic<V8LineEditor*> SINGLETON(nullptr);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief signal handler for CTRL-C
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -53,7 +58,8 @@ static bool SignalHandler(DWORD eventType) {
|
|||
case CTRL_LOGOFF_EVENT:
|
||||
case CTRL_SHUTDOWN_EVENT: {
|
||||
// get the instance of the console
|
||||
auto instance = SINGLETON.load();
|
||||
MUTEX_LOCKER(mutex, ::singletonMutex);
|
||||
auto instance = ::singleton;
|
||||
|
||||
if (instance != nullptr) {
|
||||
if (instance->isExecutingCommand()) {
|
||||
|
@ -77,7 +83,8 @@ static bool SignalHandler(DWORD eventType) {
|
|||
|
||||
static void SignalHandler(int /*signal*/) {
|
||||
// get the instance of the console
|
||||
auto instance = SINGLETON.load();
|
||||
MUTEX_LOCKER(mutex, ::singletonMutex);
|
||||
auto instance = ::singleton;
|
||||
|
||||
if (instance != nullptr) {
|
||||
if (instance->isExecutingCommand()) {
|
||||
|
@ -375,8 +382,12 @@ V8LineEditor::V8LineEditor(v8::Isolate* isolate,
|
|||
_context(context),
|
||||
_executingCommand(false) {
|
||||
// register global instance
|
||||
TRI_ASSERT(SINGLETON.load() == nullptr);
|
||||
SINGLETON.store(this);
|
||||
|
||||
{
|
||||
MUTEX_LOCKER(mutex, ::singletonMutex);
|
||||
TRI_ASSERT(::singleton == nullptr);
|
||||
::singleton = this;
|
||||
}
|
||||
|
||||
// create shell
|
||||
_shell = ShellBase::buildShell(history, new V8Completer());
|
||||
|
@ -409,6 +420,7 @@ V8LineEditor::V8LineEditor(v8::Isolate* isolate,
|
|||
|
||||
V8LineEditor::~V8LineEditor() {
|
||||
// unregister global instance
|
||||
TRI_ASSERT(SINGLETON.load() != nullptr);
|
||||
SINGLETON.store(nullptr);
|
||||
MUTEX_LOCKER(mutex, ::singletonMutex);
|
||||
TRI_ASSERT(::singleton != nullptr);
|
||||
::singleton = nullptr;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue