mirror of https://gitee.com/bigwinds/arangodb
287 lines
9.7 KiB
C++
287 lines
9.7 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2019 ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Simon Grätzer
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "Utils.h"
|
|
|
|
#include "Agency/AgencyFeature.h"
|
|
#include "Agency/Agent.h"
|
|
#include "Basics/Common.h"
|
|
#include "Basics/NumberUtils.h"
|
|
#include "Cluster/ClusterFeature.h"
|
|
#include "Cluster/ClusterInfo.h"
|
|
#include "Logger/LogMacros.h"
|
|
#include "Network/Methods.h"
|
|
#include "Network/NetworkFeature.h"
|
|
#include "VocBase/ticks.h"
|
|
|
|
#include <velocypack/velocypack-aliases.h>
|
|
|
|
namespace arangodb {
|
|
namespace network {
|
|
|
|
int resolveDestination(NetworkFeature const& feature, DestinationId const& dest,
|
|
network::EndpointSpec& spec) {
|
|
|
|
// Now look up the actual endpoint:
|
|
if (!feature.server().hasFeature<ClusterFeature>()) {
|
|
return TRI_ERROR_SHUTTING_DOWN;
|
|
}
|
|
auto& ci = feature.server().getFeature<ClusterFeature>().clusterInfo();
|
|
return resolveDestination(ci, dest, spec);
|
|
}
|
|
|
|
int resolveDestination(ClusterInfo& ci, DestinationId const& dest,
|
|
network::EndpointSpec& spec) {
|
|
using namespace arangodb;
|
|
|
|
if (dest.find("tcp://") == 0 || dest.find("ssl://") == 0) {
|
|
spec.endpoint = dest;
|
|
return TRI_ERROR_NO_ERROR; // all good
|
|
}
|
|
|
|
// This sets result.shardId, result.serverId and result.endpoint,
|
|
// depending on what dest is. Note that if a shardID is given, the
|
|
// responsible server is looked up, if a serverID is given, the endpoint
|
|
// is looked up, both can fail and immediately lead to a CL_COMM_ERROR
|
|
// state.
|
|
|
|
if (dest.compare(0, 6, "shard:", 6) == 0) {
|
|
spec.shardId = dest.substr(6);
|
|
{
|
|
std::shared_ptr<std::vector<ServerID>> resp = ci.getResponsibleServer(spec.shardId);
|
|
if (!resp->empty()) {
|
|
spec.serverId = (*resp)[0];
|
|
} else {
|
|
LOG_TOPIC("60ee8", ERR, Logger::CLUSTER)
|
|
<< "cannot find responsible server for shard '" << spec.shardId << "'";
|
|
return TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE;
|
|
}
|
|
}
|
|
LOG_TOPIC("64670", DEBUG, Logger::CLUSTER) << "Responsible server: " << spec.serverId;
|
|
} else if (dest.compare(0, 7, "server:", 7) == 0) {
|
|
spec.serverId = dest.substr(7);
|
|
} else {
|
|
std::string errorMessage = "did not understand destination '" + dest + "'";
|
|
LOG_TOPIC("77a84", ERR, Logger::COMMUNICATION)
|
|
<< "did not understand destination '" << dest << "'";
|
|
return TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE;
|
|
}
|
|
|
|
spec.endpoint = ci.getServerEndpoint(spec.serverId);
|
|
if (spec.endpoint.empty()) {
|
|
if (spec.serverId.find(',') != std::string::npos) {
|
|
TRI_ASSERT(false);
|
|
}
|
|
std::string errorMessage =
|
|
"did not find endpoint of server '" + spec.serverId + "'";
|
|
LOG_TOPIC("f29ef", ERR, Logger::COMMUNICATION)
|
|
<< "did not find endpoint of server '" << spec.serverId << "'";
|
|
return TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE;
|
|
}
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
/// @brief extract the error code form the body
|
|
int errorCodeFromBody(arangodb::velocypack::Slice body, int defaultErrorCode) {
|
|
if (body.isObject()) {
|
|
VPackSlice num = body.get(StaticStrings::ErrorNum);
|
|
if (num.isNumber()) {
|
|
// we found an error number, so let's use it!
|
|
return num.getNumericValue<int>();
|
|
}
|
|
}
|
|
return defaultErrorCode;
|
|
}
|
|
|
|
Result resultFromBody(std::shared_ptr<arangodb::velocypack::Buffer<uint8_t>> const& body,
|
|
int defaultError) {
|
|
// read the error number from the response and use it if present
|
|
if (body && !body->empty()) {
|
|
return resultFromBody(VPackSlice(body->data()), defaultError);
|
|
}
|
|
return Result(defaultError);
|
|
}
|
|
|
|
Result resultFromBody(std::shared_ptr<arangodb::velocypack::Builder> const& body,
|
|
int defaultError) {
|
|
// read the error number from the response and use it if present
|
|
if (body) {
|
|
return resultFromBody(body->slice(), defaultError);
|
|
}
|
|
|
|
return Result(defaultError);
|
|
}
|
|
|
|
Result resultFromBody(arangodb::velocypack::Slice slice, int defaultError) {
|
|
// read the error number from the response and use it if present
|
|
if (slice.isObject()) {
|
|
VPackSlice num = slice.get(StaticStrings::ErrorNum);
|
|
VPackSlice msg = slice.get(StaticStrings::ErrorMessage);
|
|
if (num.isNumber()) {
|
|
if (msg.isString()) {
|
|
// found an error number and an error message, so let's use it!
|
|
return Result(num.getNumericValue<int>(), msg.copyString());
|
|
}
|
|
// we found an error number, so let's use it!
|
|
return Result(num.getNumericValue<int>());
|
|
}
|
|
}
|
|
return Result(defaultError);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Extract all error baby-style error codes and store them in a map
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void errorCodesFromHeaders(network::Headers headers,
|
|
std::unordered_map<int, size_t>& errorCounter,
|
|
bool includeNotFound) {
|
|
auto const& codes = headers.find(StaticStrings::ErrorCodes);
|
|
if (codes != headers.end()) {
|
|
auto parsedCodes = VPackParser::fromJson(codes->second);
|
|
VPackSlice codesSlice = parsedCodes->slice();
|
|
if (!codesSlice.isObject()) {
|
|
return;
|
|
}
|
|
|
|
for (auto code : VPackObjectIterator(codesSlice)) {
|
|
VPackValueLength codeLength;
|
|
char const* codeString = code.key.getString(codeLength);
|
|
int codeNr = NumberUtils::atoi_zero<int>(codeString, codeString + codeLength);
|
|
if (includeNotFound || codeNr != TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
|
|
errorCounter[codeNr] += code.value.getNumericValue<size_t>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
int toArangoErrorCodeInternal(fuerte::Error err) {
|
|
// This function creates an error code from a fuerte::Error,
|
|
// but only if it is a communication error. If the communication
|
|
// was successful and there was an HTTP error code, this function
|
|
// returns TRI_ERROR_NO_ERROR.
|
|
// If TRI_ERROR_NO_ERROR is returned, then the result was CL_COMM_RECEIVED
|
|
// and .answer can safely be inspected.
|
|
|
|
switch (err) {
|
|
case fuerte::Error::NoError:
|
|
return TRI_ERROR_NO_ERROR;
|
|
|
|
case fuerte::Error::CouldNotConnect:
|
|
return TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE;
|
|
|
|
case fuerte::Error::CloseRequested:
|
|
case fuerte::Error::ConnectionClosed:
|
|
return TRI_ERROR_CLUSTER_CONNECTION_LOST;
|
|
|
|
case fuerte::Error::Timeout: // No reply, we give up:
|
|
return TRI_ERROR_CLUSTER_TIMEOUT;
|
|
|
|
case fuerte::Error::Canceled:
|
|
return TRI_ERROR_REQUEST_CANCELED;
|
|
|
|
case fuerte::Error::QueueCapacityExceeded: // there is no result
|
|
case fuerte::Error::ReadError:
|
|
case fuerte::Error::WriteError:
|
|
case fuerte::Error::ProtocolError:
|
|
return TRI_ERROR_CLUSTER_CONNECTION_LOST;
|
|
|
|
case fuerte::Error::VstUnauthorized:
|
|
return TRI_ERROR_FORBIDDEN;
|
|
}
|
|
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
} // namespace
|
|
|
|
fuerte::RestVerb arangoRestVerbToFuerte(rest::RequestType verb) {
|
|
switch (verb) {
|
|
case rest::RequestType::DELETE_REQ:
|
|
return fuerte::RestVerb::Delete;
|
|
case rest::RequestType::GET:
|
|
return fuerte::RestVerb::Get;
|
|
case rest::RequestType::POST:
|
|
return fuerte::RestVerb::Post;
|
|
case rest::RequestType::PUT:
|
|
return fuerte::RestVerb::Put;
|
|
case rest::RequestType::HEAD:
|
|
return fuerte::RestVerb::Head;
|
|
case rest::RequestType::PATCH:
|
|
return fuerte::RestVerb::Patch;
|
|
case rest::RequestType::OPTIONS:
|
|
return fuerte::RestVerb::Options;
|
|
case rest::RequestType::ILLEGAL:
|
|
return fuerte::RestVerb::Illegal;
|
|
}
|
|
|
|
return fuerte::RestVerb::Illegal;
|
|
}
|
|
|
|
rest::RequestType fuerteRestVerbToArango(fuerte::RestVerb verb) {
|
|
switch (verb) {
|
|
case fuerte::RestVerb::Illegal:
|
|
return rest::RequestType::ILLEGAL;
|
|
case fuerte::RestVerb::Delete:
|
|
return rest::RequestType::DELETE_REQ;
|
|
case fuerte::RestVerb::Get:
|
|
return rest::RequestType::GET;
|
|
case fuerte::RestVerb::Post:
|
|
return rest::RequestType::POST;
|
|
case fuerte::RestVerb::Put:
|
|
return rest::RequestType::PUT;
|
|
case fuerte::RestVerb::Head:
|
|
return rest::RequestType::HEAD;
|
|
case fuerte::RestVerb::Patch:
|
|
return rest::RequestType::PATCH;
|
|
case fuerte::RestVerb::Options:
|
|
return rest::RequestType::OPTIONS;
|
|
}
|
|
|
|
return rest::RequestType::ILLEGAL;
|
|
}
|
|
|
|
int fuerteToArangoErrorCode(network::Response const& res) {
|
|
LOG_TOPIC_IF("abcde", ERR, Logger::COMMUNICATION, res.error != fuerte::Error::NoError)
|
|
<< "communication error: '" << fuerte::to_string(res.error)
|
|
<< "' from destination '" << res.destination << "'";
|
|
return toArangoErrorCodeInternal(res.error);
|
|
}
|
|
|
|
int fuerteToArangoErrorCode(fuerte::Error err) {
|
|
LOG_TOPIC_IF("abcdf", ERR, Logger::COMMUNICATION, err != fuerte::Error::NoError)
|
|
<< "communication error: '" << fuerte::to_string(err) << "'";
|
|
return toArangoErrorCodeInternal(err);
|
|
}
|
|
|
|
std::string fuerteToArangoErrorMessage(network::Response const& res) {
|
|
return TRI_errno_string(fuerteToArangoErrorCode(res));
|
|
}
|
|
|
|
std::string fuerteToArangoErrorMessage(fuerte::Error err) {
|
|
return TRI_errno_string(fuerteToArangoErrorCode(err));
|
|
}
|
|
|
|
} // namespace network
|
|
} // namespace arangodb
|