Commit b724ff91 authored by yangjian's avatar yangjian

Update AccessAndMobilitySubscriptionData: add database and update UDR code structure

parent 717b2796
......@@ -8,16 +8,6 @@ include(ExternalProject)
set(EXTERNAL_INSTALL_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/external)
ExternalProject_Add(PISTACHE
GIT_REPOSITORY https://github.com/oktal/pistache.git
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_LOCATION}
)
ExternalProject_Add(NLOHMANN
GIT_REPOSITORY https://github.com/nlohmann/json.git
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_LOCATION}
)
include_directories(${EXTERNAL_INSTALL_LOCATION}/include)
link_directories(${EXTERNAL_INSTALL_LOCATION}/lib)
......@@ -33,5 +23,4 @@ file(GLOB SRCS
)
add_executable(${PROJECT_NAME} ${SRCS} )
add_dependencies(${PROJECT_NAME} PISTACHE NLOHMANN)
target_link_libraries(${PROJECT_NAME} pistache pthread mysqlclient)
/**
* Nudr_DataRepository API OpenAPI file
* Unified Data Repository Service. © 2020, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
* Unified Data Repository Service. © 2020, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
*
* The version of the OpenAPI document: 2.1.2
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
......@@ -12,7 +12,7 @@
/*
* AuthenticationStatusDocumentApi.h
*
*
*
*/
#ifndef AuthenticationStatusDocumentApi_H_
......@@ -47,10 +47,10 @@ private:
void setupRoutes();
void create_authentication_status_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
void delete_authentication_status_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
void query_authentication_status_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
void delete_authentication_status_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
void query_authentication_status_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
void authentication_status_document_api_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
std::shared_ptr<Pistache::Rest::Router> router;
......@@ -59,13 +59,13 @@ private:
/// To store the Authentication Status data of a UE
/// </summary>
/// <remarks>
///
///
/// </remarks>
/// <param name="ueId">UE id</param>
/// <param name="authEvent"> (optional)</param>
virtual void create_authentication_status(const std::string &ueId, const AuthEvent &authEvent, Pistache::Http::ResponseWriter &response) = 0;
virtual void delete_authentication_status(const std::string &ueId, Pistache::Http::ResponseWriter &response) = 0;
virtual void query_authentication_status(const std::string &ueId, const Pistache::Optional<std::vector<std::string>> &fields, const Pistache::Optional<std::string> &supportedFeatures, Pistache::Http::ResponseWriter &response) = 0;
virtual void delete_authentication_status(const std::string &ueId, Pistache::Http::ResponseWriter &response) = 0;
virtual void query_authentication_status(const std::string &ueId, const Pistache::Optional<std::vector<std::string>> &fields, const Pistache::Optional<std::string> &supportedFeatures, Pistache::Http::ResponseWriter &response) = 0;
};
......
This diff is collapsed.
This diff is collapsed.
/*
Copyright (C) 2019-2020, Kip Warner.
Released under the terms of Apache License 2.0.
*/
// Multiple include protection...
#ifndef _BASE_64_H_
#define _BASE_64_H_
// Includes...
// Build environment configuration...
#include <pistache/config.h>
// Standard C++ / POSIX system headers...
#include <cstddef>
#include <string>
#include <vector>
#if __cplusplus < 201703L
namespace std {
typedef uint8_t byte;
}
#endif
// A class for performing decoding to raw bytes from base 64 encoding...
class Base64Decoder {
// Public methods...
public:
// Constructor...
explicit Base64Decoder(const std::string &Base64EncodedString)
: m_Base64EncodedString(Base64EncodedString) {}
// Calculate length of decoded raw bytes from that would be generated if
// the base 64 encoded input buffer was decoded. This is not a static
// method because we need to examine the string...
std::vector<std::byte>::size_type CalculateDecodedSize() const;
// Decode base 64 encoding into raw bytes...
const std::vector<std::byte> &Decode();
// Get raw decoded data...
const std::vector<std::byte> &GetRawDecodedData() const noexcept {
return m_DecodedData;
}
// Protected methods...
protected:
// Convert an octet character to corresponding sextet, provided it can
// safely be represented as such. Otherwise return 0xff...
std::byte DecodeCharacter(const unsigned char Character) const;
// Protected attributes...
protected:
// Base 64 encoded string to decode...
const std::string &m_Base64EncodedString;
// Decoded raw data...
std::vector<std::byte> m_DecodedData;
};
// A class for performing base 64 encoding from raw bytes...
class Base64Encoder {
// Public methods...
public:
// Construct encoder to encode from a raw input buffer...
explicit Base64Encoder(const std::vector<std::byte> &InputBuffer)
: m_InputBuffer(InputBuffer) {}
// Calculate length of base 64 string that would need to be generated
// for raw data of a given length...
static std::string::size_type CalculateEncodedSize(
const std::vector<std::byte>::size_type DecodedSize) noexcept;
// Encode raw data input buffer to base 64...
const std::string &Encode() noexcept;
// Encode a string into base 64 format...
static std::string EncodeString(const std::string &StringInput);
// Get the encoded data...
const std::string &GetBase64EncodedString() const noexcept {
return m_Base64EncodedString;
}
// Protected methods...
protected:
// Encode single binary byte to 6-bit base 64 character...
unsigned char EncodeByte(const std::byte Byte) const;
// Protected attributes...
protected:
// Raw bytes to encode to base 64 string...
const std::vector<std::byte> &m_InputBuffer;
// Base64 encoded string...
std::string m_Base64EncodedString;
};
// Multiple include protection...
#endif
/*
Mathieu Stefani, 29 janvier 2016
The Http client
*/
#pragma once
#include <pistache/async.h>
#include <pistache/http.h>
#include <pistache/os.h>
#include <pistache/reactor.h>
#include <pistache/timer_pool.h>
#include <pistache/view.h>
#include <atomic>
#include <chrono>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
namespace Pistache {
namespace Http {
namespace Default {
constexpr int Threads = 1;
constexpr int MaxConnectionsPerHost = 8;
constexpr bool KeepAlive = true;
constexpr size_t MaxResponseSize = std::numeric_limits<uint32_t>::max();
} // namespace Default
class Transport;
struct Connection : public std::enable_shared_from_this<Connection> {
using OnDone = std::function<void()>;
explicit Connection(size_t maxResponseSize);
struct RequestData {
RequestData(Async::Resolver resolve, Async::Rejection reject,
const Http::Request &request, OnDone onDone)
: resolve(std::move(resolve)), reject(std::move(reject)),
request(request), onDone(std::move(onDone)) {}
Async::Resolver resolve;
Async::Rejection reject;
Http::Request request;
OnDone onDone;
};
enum State : uint32_t { Idle, Used };
enum ConnectionState { NotConnected, Connecting, Connected };
void connect(const Address &addr);
void close();
bool isIdle() const;
bool tryUse();
void setAsIdle();
bool isConnected() const;
bool hasTransport() const;
void associateTransport(const std::shared_ptr<Transport> &transport);
Async::Promise<Response> perform(const Http::Request &request, OnDone onDone);
Async::Promise<Response> asyncPerform(const Http::Request &request,
OnDone onDone);
void performImpl(const Http::Request &request, Async::Resolver resolve,
Async::Rejection reject, OnDone onDone);
Fd fd() const;
void handleResponsePacket(const char *buffer, size_t totalBytes);
void handleError(const char *error);
void handleTimeout();
std::string dump() const;
private:
void processRequestQueue();
struct RequestEntry {
RequestEntry(Async::Resolver resolve, Async::Rejection reject,
std::shared_ptr<TimerPool::Entry> timer, OnDone onDone)
: resolve(std::move(resolve)), reject(std::move(reject)),
timer(std::move(timer)), onDone(std::move(onDone)) {}
Async::Resolver resolve;
Async::Rejection reject;
std::shared_ptr<TimerPool::Entry> timer;
OnDone onDone;
};
Fd fd_;
struct sockaddr_in saddr;
std::unique_ptr<RequestEntry> requestEntry;
std::atomic<uint32_t> state_;
std::atomic<ConnectionState> connectionState_;
std::shared_ptr<Transport> transport_;
Queue<RequestData> requestsQueue;
TimerPool timerPool_;
ResponseParser parser;
};
class ConnectionPool {
public:
ConnectionPool() = default;
void init(size_t maxConnsPerHost, size_t maxResponseSize);
std::shared_ptr<Connection> pickConnection(const std::string &domain);
static void releaseConnection(const std::shared_ptr<Connection> &connection);
size_t usedConnections(const std::string &domain) const;
size_t idleConnections(const std::string &domain) const;
size_t availableConnections(const std::string &domain) const;
void closeIdleConnections(const std::string &domain);
void shutdown();
private:
using Connections = std::vector<std::shared_ptr<Connection>>;
using Lock = std::mutex;
using Guard = std::lock_guard<Lock>;
mutable Lock connsLock;
std::unordered_map<std::string, Connections> conns;
size_t maxConnectionsPerHost;
size_t maxResponseSize;
};
class Client;
class RequestBuilder {
public:
friend class Client;
RequestBuilder &method(Method method);
RequestBuilder &resource(const std::string &val);
RequestBuilder &params(const Uri::Query &query);
RequestBuilder &header(const std::shared_ptr<Header::Header> &header);
template <typename H, typename... Args>
typename std::enable_if<Header::IsHeader<H>::value, RequestBuilder &>::type
header(Args &&... args) {
return header(std::make_shared<H>(std::forward<Args>(args)...));
}
RequestBuilder &cookie(const Cookie &cookie);
RequestBuilder &body(const std::string &val);
RequestBuilder &body(std::string &&val);
RequestBuilder &timeout(std::chrono::milliseconds val);
Async::Promise<Response> send();
private:
explicit RequestBuilder(Client *const client) : client_(client), request_() {}
Client *const client_;
Request request_;
};
class Client {
public:
friend class RequestBuilder;
struct Options {
friend class Client;
Options()
: threads_(Default::Threads),
maxConnectionsPerHost_(Default::MaxConnectionsPerHost),
keepAlive_(Default::KeepAlive),
maxResponseSize_(Default::MaxResponseSize) {}
Options &threads(int val);
Options &keepAlive(bool val);
Options &maxConnectionsPerHost(int val);
Options &maxResponseSize(size_t val);
private:
int threads_;
int maxConnectionsPerHost_;
bool keepAlive_;
size_t maxResponseSize_;
};
Client();
~Client();
static Options options();
void init(const Options &options = Options());
RequestBuilder get(const std::string &resource);
RequestBuilder post(const std::string &resource);
RequestBuilder put(const std::string &resource);
RequestBuilder patch(const std::string &resource);
RequestBuilder del(const std::string &resource);
void shutdown();
private:
std::shared_ptr<Aio::Reactor> reactor_;
ConnectionPool pool;
Aio::Reactor::Key transportKey;
std::atomic<uint64_t> ioIndex;
using Lock = std::mutex;
using Guard = std::lock_guard<Lock>;
Lock queuesLock;
std::unordered_map<std::string,
MPMCQueue<std::shared_ptr<Connection::RequestData>, 2048>>
requestsQueues;
bool stopProcessPequestsQueues;
private:
RequestBuilder prepareRequest(const std::string &resource,
Http::Method method);
Async::Promise<Response> doRequest(Http::Request request);
void processRequestQueue();
};
} // namespace Http
} // namespace Pistache
/* common.h
Mathieu Stefani, 12 August 2015
A collection of macro / utilities / constants
*/
#pragma once
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <cstring>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#define TRY(...) \
do { \
auto ret = __VA_ARGS__; \
if (ret < 0) { \
const char *str = #__VA_ARGS__; \
std::ostringstream oss; \
oss << str << ": "; \
if (errno == 0) { \
oss << gai_strerror(static_cast<int>(ret)); \
} else { \
oss << strerror(errno); \
} \
oss << " (" << __FILE__ << ":" << __LINE__ << ")"; \
throw std::runtime_error(oss.str()); \
} \
} while (0)
#define TRY_RET(...) \
[&]() { \
auto ret = __VA_ARGS__; \
if (ret < 0) { \
const char *str = #__VA_ARGS__; \
std::ostringstream oss; \
oss << str << ": " << strerror(errno); \
oss << " (" << __FILE__ << ":" << __LINE__ << ")"; \
throw std::runtime_error(oss.str()); \
} \
return ret; \
}(); \
(void)0
struct PrintException {
void operator()(std::exception_ptr exc) const {
try {
std::rethrow_exception(exc);
} catch (const std::exception &e) {
std::cerr << "An exception occured: " << e.what() << std::endl;
}
}
};
#define unreachable() __builtin_unreachable()
// Until we require C++17 compiler with [[maybe_unused]]
#define UNUSED(x) (void)(x);
#pragma once
#include <cstddef>
#include <cstdint>
#include <limits>
// Allow compile-time overload
namespace Pistache {
namespace Const {
static constexpr size_t MaxBacklog = 128;
static constexpr size_t MaxEvents = 1024;
static constexpr size_t MaxBuffer = 4096;
static constexpr size_t DefaultWorkers = 1;
static constexpr size_t DefaultTimerPoolSize = 128;
// Defined from CMakeLists.txt in project root
static constexpr size_t DefaultMaxRequestSize = 4096;
static constexpr size_t DefaultMaxResponseSize =
std::numeric_limits<uint32_t>::max();
static constexpr size_t ChunkSize = 1024;
static constexpr uint16_t HTTP_STANDARD_PORT = 80;
} // namespace Const
} // namespace Pistache
/*
Mathieu Stefani, 16 janvier 2016
Representation of a Cookie as per http://tools.ietf.org/html/rfc6265
*/
#pragma once
#include <ctime>
#include <list>
#include <map>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <pistache/http_defs.h>
#include <pistache/optional.h>
namespace Pistache {
namespace Http {
struct Cookie {
friend std::ostream &operator<<(std::ostream &os, const Cookie &cookie);
Cookie(std::string name, std::string value);
std::string name;
std::string value;
Optional<std::string> path;
Optional<std::string> domain;
Optional<FullDate> expires;
Optional<int> maxAge;
bool secure;
bool httpOnly;
std::map<std::string, std::string> ext;
static Cookie fromRaw(const char *str, size_t len);
static Cookie fromString(const std::string &str);
private:
void write(std::ostream &os) const;
};
std::ostream &operator<<(std::ostream &os, const Cookie &cookie);
class CookieJar {
public:
using HashMapCookies =
std::unordered_map<std::string, Cookie>; // "value" -> Cookie
using Storage = std::unordered_map<
std::string, HashMapCookies>; // "name" -> Hashmap("value" -> Cookie)
struct iterator : std::iterator<std::bidirectional_iterator_tag, Cookie> {
explicit iterator(const Storage::const_iterator &_iterator)
: iter_storage(_iterator), iter_cookie_values(), iter_storage_end() {}
iterator(const Storage::const_iterator &_iterator,
const Storage::const_iterator &end)
: iter_storage(_iterator), iter_cookie_values(), iter_storage_end(end) {
if (iter_storage != iter_storage_end) {
iter_cookie_values = iter_storage->second.begin();
}
}
Cookie operator*() const {
return iter_cookie_values->second; // return iter_storage->second;
}
const Cookie *operator->() const { return &(iter_cookie_values->second); }
iterator &operator++() {
++iter_cookie_values;
if (iter_cookie_values == iter_storage->second.end()) {
++iter_storage;
if (iter_storage != iter_storage_end)
iter_cookie_values = iter_storage->second.begin();
}
return *this;
}
iterator operator++(int) {
iterator ret(iter_storage, iter_storage_end);
++iter_cookie_values;
if (iter_cookie_values == iter_storage->second.end()) {
++iter_storage;
if (iter_storage != iter_storage_end) // this check is important
iter_cookie_values = iter_storage->second.begin();
}
return ret;
}
bool operator!=(iterator other) const {
return iter_storage != other.iter_storage;
}
bool operator==(iterator other) const {
return iter_storage == other.iter_storage;
}
private:
Storage::const_iterator iter_storage;
HashMapCookies::const_iterator iter_cookie_values;
Storage::const_iterator
iter_storage_end; // we need to know where main hashmap ends.
};
CookieJar();
void add(const Cookie &cookie);
void removeAllCookies();
void addFromRaw(const char *str, size_t len);
Cookie get(const std::string &name) const;
bool has(const std::string &name) const;
iterator begin() const { return iterator(cookies.begin(), cookies.end()); }
iterator end() const { return iterator(cookies.end()); }
private:
Storage cookies;
};
} // namespace Http
} // namespace Pistache
This diff is collapsed.
/*
Mathieu Stefani, 22 janvier 2016
An Http endpoint
*/
#pragma once
#include <pistache/http.h>
#include <pistache/listener.h>
#include <pistache/net.h>
namespace Pistache {
namespace Http {
class Endpoint {
public:
struct Options {
friend class Endpoint;
Options &threads(int val);
Options &threadsName(const std::string &val);
Options &flags(Flags<Tcp::Options> flags);
Options &flags(Tcp::Options tcp_opts) {
flags(Flags<Tcp::Options>(tcp_opts));
return *this;
}
Options &backlog(int val);
Options &maxRequestSize(size_t val);
Options &maxResponseSize(size_t val);
Options &logger(PISTACHE_STRING_LOGGER_T logger);
[[deprecated("Replaced by maxRequestSize(val)")]] Options &
maxPayload(size_t val);
private:
int threads_;
std::string threadsName_;
Flags<Tcp::Options> flags_;
int backlog_;
size_t maxRequestSize_;
size_t maxResponseSize_;
PISTACHE_STRING_LOGGER_T logger_;
Options();
};
Endpoint();
explicit Endpoint(const Address &addr);
template <typename... Args> void initArgs(Args &&... args) {
listener.init(std::forward<Args>(args)...);
}
void init(const Options &options = Options());
void setHandler(const std::shared_ptr<Handler> &handler);
void bind();
void bind(const Address &addr);
void serve();
void serveThreaded();
void shutdown();
/*!
* \brief Use SSL on this endpoint
*
* \param[in] cert Server certificate path
* \param[in] key Server key path
* \param[in] use_compression Wether or not use compression on the encryption
*
* Setup the SSL configuration for an endpoint. In order to do that, this
* function will init OpenSSL constants and load *all* algorithms. It will
* then load the server certificate and key, in order to use it later.
* *If the private key does not match the certificate, an exception will
* be thrown*
*
* \note use_compression is false by default to mitigate BREACH[1] and
* CRIME[2] vulnerabilities
* \note This function will throw an exception if pistache has not been
* compiled with PISTACHE_USE_SSL
*
* [1] https://en.wikipedia.org/wiki/BREACH
* [2] https://en.wikipedia.org/wiki/CRIME
*/
void useSSL(const std::string &cert, const std::string &key, bool use_compression = false);
/*!
* \brief Use SSL certificate authentication on this endpoint
*
* \param[in] ca_file Certificate Authority file
* \param[in] ca_path Certificate Authority path
* \param[in] cb OpenSSL verify callback[1]
*
* Change the SSL configuration in order to only accept verified client
* certificates. The function 'useSSL' *should* be called before this
* function.
* Due to the way we actually doesn't expose any OpenSSL internal types, the
* callback function is Cpp generic. The 'real' callback will be:
*
* int callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
*
* It is up to the caller to cast the second argument to an appropriate
* pointer:
*
* int store_callback(int preverify_ok, void *ctx) {
* X509_STORE_CTX *x509_ctx = (X509_STORE_CTX *)ctx;
*
* [...]
*
* if (all_good)
* return 1;
* return 0;
* }
*
* [...]
*
* endpoint->useSSLAuth(ca_file, ca_path, &store_callback);
*
* See the documentation[1] for more information about this callback.
*
* \sa useSSL
* \note This function will throw an exception if pistache has not been
* compiled with PISTACHE_USE_SSL
*
* [1] https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_verify.html
*/
void useSSLAuth(std::string ca_file, std::string ca_path = "",
int (*cb)(int, void *) = NULL);
bool isBound() const { return listener.isBound(); }
Port getPort() const { return listener.getPort(); }
Async::Promise<Tcp::Listener::Load>
requestLoad(const Tcp::Listener::Load &old);
static Options options();
private:
template <typename Method> void serveImpl(Method method) {
#define CALL_MEMBER_FN(obj, pmf) ((obj).*(pmf))
if (!handler_)
throw std::runtime_error("Must call setHandler() prior to serve()");
listener.setHandler(handler_);
listener.bind();
CALL_MEMBER_FN(listener, method)();
#undef CALL_MEMBER_FN
}
std::shared_ptr<Handler> handler_;
Tcp::Listener listener;
size_t maxRequestSize_ = Const::DefaultMaxRequestSize;
size_t maxResponseSize_ = Const::DefaultMaxResponseSize;
PISTACHE_STRING_LOGGER_T logger_ = PISTACHE_NULL_STRING_LOGGER;
};
template <typename Handler>
void listenAndServe(Address addr,
const Endpoint::Options &options = Endpoint::options()) {
Endpoint endpoint(addr);
endpoint.init(options);
endpoint.setHandler(make_handler<Handler>());
endpoint.serve();
}
} // namespace Http
} // namespace Pistache
#pragma once
#include <stdexcept>
namespace Pistache {
namespace Tcp {
class SocketError : public std::runtime_error {
public:
explicit SocketError(const char *what_arg) : std::runtime_error(what_arg) {}
};
class ServerError : public std::runtime_error {
public:
explicit ServerError(const char *what_arg) : std::runtime_error(what_arg) {}
};
} // namespace Tcp
} // namespace Pistache
\ No newline at end of file
/* flags.h
Mathieu Stefani, 18 August 2015
Make it easy to have bitwise operators for scoped or unscoped enumerations
*/
#pragma once
#include <climits>
#include <iostream>
#include <type_traits>
namespace Pistache {
// Looks like gcc 4.6 does not implement std::underlying_type
namespace detail {
template <size_t N> struct TypeStorage;
template <> struct TypeStorage<sizeof(uint8_t)> { typedef uint8_t Type; };
template <> struct TypeStorage<sizeof(uint16_t)> { typedef uint16_t Type; };
template <> struct TypeStorage<sizeof(uint32_t)> { typedef uint32_t Type; };
template <> struct TypeStorage<sizeof(uint64_t)> { typedef uint64_t Type; };
template <typename T> struct UnderlyingType {
typedef typename TypeStorage<sizeof(T)>::Type Type;
};
template <typename Enum> struct HasNone {
template <typename U>
static auto test(U *) -> decltype(U::None, std::true_type());
template <typename U> static auto test(...) -> std::false_type;
static constexpr bool value =
std::is_same<decltype(test<Enum>(0)), std::true_type>::value;
};
} // namespace detail
template <typename T> class Flags {
public:
typedef typename detail::UnderlyingType<T>::Type Type;
static_assert(std::is_enum<T>::value, "Flags only works with enumerations");
static_assert(detail::HasNone<T>::value, "The enumartion needs a None value");
static_assert(static_cast<Type>(T::None) == 0, "None should be 0");
Flags() : val(T::None) {}
explicit Flags(T _val) : val(_val) {}
#define DEFINE_BITWISE_OP_CONST(Op) \
Flags<T> operator Op(T rhs) const { \
return Flags<T>( \
static_cast<T>(static_cast<Type>(val) Op static_cast<Type>(rhs))); \
} \
\
Flags<T> operator Op(Flags<T> rhs) const { \
return Flags<T>( \
static_cast<T>(static_cast<Type>(val) Op static_cast<Type>(rhs.val))); \
}
DEFINE_BITWISE_OP_CONST(|)
DEFINE_BITWISE_OP_CONST(&)
DEFINE_BITWISE_OP_CONST(^)
#undef DEFINE_BITWISE_OP_CONST
#define DEFINE_BITWISE_OP(Op) \
Flags<T> &operator Op##=(T rhs) { \
val = static_cast<T>(static_cast<Type>(val) Op static_cast<Type>(rhs)); \
return *this; \
} \
\
Flags<T> &operator Op##=(Flags<T> rhs) { \
val = \
static_cast<T>(static_cast<Type>(val) Op static_cast<Type>(rhs.val)); \
return *this; \
}
DEFINE_BITWISE_OP(|)
DEFINE_BITWISE_OP(&)
DEFINE_BITWISE_OP(^)
#undef DEFINE_BITWISE_OP
bool hasFlag(T flag) const {
return static_cast<Type>(val) & static_cast<Type>(flag);
}
Flags<T> &setFlag(T flag) {
*this |= flag;
return *this;
}
Flags<T> &toggleFlag(T flag) { return *this ^= flag; }
operator T() const { return val; }
private:
T val;
};
} // namespace Pistache
#define DEFINE_BITWISE_OP(Op, T) \
inline T operator Op(T lhs, T rhs) { \
typedef Pistache::detail::UnderlyingType<T>::Type UnderlyingType; \
return static_cast<T>(static_cast<UnderlyingType>(lhs) \
Op static_cast<UnderlyingType>(rhs)); \
}
#define DECLARE_FLAGS_OPERATORS(T) \
DEFINE_BITWISE_OP(&, T) \
DEFINE_BITWISE_OP(|, T)
template <typename T>
std::ostream &operator<<(std::ostream &os, Pistache::Flags<T> flags) {
typedef typename Pistache::detail::UnderlyingType<T>::Type UnderlyingType;
auto val = static_cast<UnderlyingType>(static_cast<T>(flags));
for (ssize_t i = (sizeof(UnderlyingType) * CHAR_BIT) - 1; i >= 0; --i) {
os << ((val >> i) & 0x1);
}
return os;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* http_headers.h
Mathieu Stefani, 19 August 2015
A list of HTTP headers
*/
#pragma once
#include <algorithm>
#include <functional>
#include <memory>
#include <unordered_map>
#include <vector>
#include <pistache/http_header.h>
#include <pistache/type_checkers.h>
namespace Pistache {
namespace Http {
namespace Header {
std::string toLowercase(std::string str);
struct LowercaseHash {
size_t operator()(const std::string &key) const {
return std::hash<std::string>{}(toLowercase(key));
}
};
bool LowercaseEqualStatic(const std::string &dynamic,
const std::string &statik);
struct LowercaseEqual {
bool operator()(const std::string &left, const std::string &right) const {
return std::equal(left.begin(), left.end(), right.begin(), right.end(),
[](const char &a, const char &b) {
return std::tolower(a) == std::tolower(b);
});
};
};
class Collection {
public:
Collection() : headers(), rawHeaders() {}
template <typename H>
typename std::enable_if<IsHeader<H>::value, std::shared_ptr<const H>>::type
get() const {
return std::static_pointer_cast<const H>(get(H::Name));
}
template <typename H>
typename std::enable_if<IsHeader<H>::value, std::shared_ptr<H>>::type get() {
return std::static_pointer_cast<H>(get(H::Name));
}
template <typename H>
typename std::enable_if<IsHeader<H>::value, std::shared_ptr<const H>>::type
tryGet() const {
return std::static_pointer_cast<const H>(tryGet(H::Name));
}
template <typename H>
typename std::enable_if<IsHeader<H>::value, std::shared_ptr<H>>::type
tryGet() {
return std::static_pointer_cast<H>(tryGet(H::Name));
}
Collection &add(const std::shared_ptr<Header> &header);
Collection &addRaw(const Raw &raw);
template <typename H, typename... Args>
typename std::enable_if<IsHeader<H>::value, Collection &>::type
add(Args &&... args) {
return add(std::make_shared<H>(std::forward<Args>(args)...));
}
template <typename H>
typename std::enable_if<IsHeader<H>::value, bool>::type remove() {
return remove(H::Name);
}
std::shared_ptr<const Header> get(const std::string &name) const;
std::shared_ptr<Header> get(const std::string &name);
Raw getRaw(const std::string &name) const;
std::shared_ptr<const Header> tryGet(const std::string &name) const;
std::shared_ptr<Header> tryGet(const std::string &name);
Optional<Raw> tryGetRaw(const std::string &name) const;
template <typename H>
typename std::enable_if<IsHeader<H>::value, bool>::type has() const {
return has(H::Name);
}
bool has(const std::string &name) const;
std::vector<std::shared_ptr<Header>> list() const;
const std::unordered_map<std::string, Raw, LowercaseHash, LowercaseEqual> &
rawList() const {
return rawHeaders;
}
bool remove(const std::string &name);
void clear();
private:
std::pair<bool, std::shared_ptr<Header>>
getImpl(const std::string &name) const;
std::unordered_map<std::string, std::shared_ptr<Header>, LowercaseHash,
LowercaseEqual>
headers;
std::unordered_map<std::string, Raw, LowercaseHash, LowercaseEqual>
rawHeaders;
};
class Registry {
public:
Registry(const Registry &) = delete;
Registry &operator=(const Registry &) = delete;
static Registry &instance();
template <typename H, REQUIRES(IsHeader<H>::value)> void registerHeader() {
registerHeader(H::Name, []() -> std::unique_ptr<Header> {
return std::unique_ptr<Header>(new H());
});
}
std::vector<std::string> headersList();
std::unique_ptr<Header> makeHeader(const std::string &name);
bool isRegistered(const std::string &name);
private:
Registry();
~Registry();
using RegistryFunc = std::function<std::unique_ptr<Header>()>;
using RegistryStorageType = std::unordered_map<std::string, RegistryFunc,
LowercaseHash, LowercaseEqual>;
void registerHeader(const std::string &name, RegistryFunc func);
RegistryStorageType registry;
};
template <typename H> struct Registrar {
static_assert(IsHeader<H>::value, "Registrar only works with header types");
Registrar() { Registry::instance().registerHeader<H>(); }
};
/* Crazy macro machinery to generate a unique variable name
* Don't touch it !
*/
#define CAT(a, b) CAT_I(a, b)
#define CAT_I(a, b) a##b
#define UNIQUE_NAME(base) CAT(base, __LINE__)
#define RegisterHeader(Header) \
Registrar<Header> UNIQUE_NAME(CAT(CAT_I(__, Header), __))
} // namespace Header
} // namespace Http
} // namespace Pistache
/*
Mathieu Stefani, 28 février 2016
A collection of sample iterator adapters
*/
#pragma once
namespace Pistache {
template <typename Map> struct FlatMapIteratorAdapter {
typedef typename Map::key_type Key;
typedef typename Map::mapped_type Value;
typedef typename Map::const_iterator const_iterator;
explicit FlatMapIteratorAdapter(const_iterator _it) : it(_it) {}
FlatMapIteratorAdapter &operator++() {
++it;
return *this;
}
const Value &operator*() { return it->second; }
bool operator==(FlatMapIteratorAdapter other) { return other.it == it; }
bool operator!=(FlatMapIteratorAdapter other) { return !(*this == other); }
private:
const_iterator it;
};
template <typename Map>
FlatMapIteratorAdapter<Map>
makeFlatMapIterator(const Map &, typename Map::const_iterator it) {
return FlatMapIteratorAdapter<Map>(it);
}
} // namespace Pistache
/* listener.h
Mathieu Stefani, 12 August 2015
A TCP Listener
*/
#pragma once
#include <pistache/async.h>
#include <pistache/config.h>
#include <pistache/flags.h>
#include <pistache/log.h>
#include <pistache/net.h>
#include <pistache/os.h>
#include <pistache/reactor.h>
#include <pistache/ssl_wrappers.h>
#include <pistache/tcp.h>
#include <sys/resource.h>
#include <memory>
#include <thread>
#include <vector>
#ifdef PISTACHE_USE_SSL
#include <openssl/ssl.h>
#endif /* PISTACHE_USE_SSL */
namespace Pistache {
namespace Tcp {
class Peer;
class Transport;
void setSocketOptions(Fd fd, Flags<Options> options);
class Listener {
public:
struct Load {
using TimePoint = std::chrono::system_clock::time_point;
double global;
std::vector<double> workers;
std::vector<rusage> raw;
TimePoint tick;
};
Listener() = default;
~Listener();
explicit Listener(const Address &address);
void init(size_t workers,
Flags<Options> options = Flags<Options>(Options::None),
const std::string &workersName = "",
int backlog = Const::MaxBacklog,
PISTACHE_STRING_LOGGER_T logger = PISTACHE_NULL_STRING_LOGGER);
void setHandler(const std::shared_ptr<Handler> &handler);
void bind();
void bind(const Address &address);
bool isBound() const;
Port getPort() const;
void run();
void runThreaded();
void shutdown();
Async::Promise<Load> requestLoad(const Load &old);
Options options() const;
Address address() const;
void pinWorker(size_t worker, const CpuSet &set);
void setupSSL(const std::string &cert_path, const std::string &key_path,
bool use_compression);
void setupSSLAuth(const std::string &ca_file, const std::string &ca_path,
int (*cb)(int, void *));
private:
Address addr_;
int listen_fd = -1;
int backlog_ = Const::MaxBacklog;
NotifyFd shutdownFd;
Polling::Epoll poller;
Flags<Options> options_;
std::thread acceptThread;
size_t workers_ = Const::DefaultWorkers;
std::string workersName_;
std::shared_ptr<Handler> handler_;
Aio::Reactor reactor_;
Aio::Reactor::Key transportKey;
void handleNewConnection();
int acceptConnection(struct sockaddr_in &peer_addr) const;
void dispatchPeer(const std::shared_ptr<Peer> &peer);
bool useSSL_ = false;
ssl::SSLCtxPtr ssl_ctx_ = nullptr;
PISTACHE_STRING_LOGGER_T logger_ = PISTACHE_NULL_STRING_LOGGER;
};
} // namespace Tcp
} // namespace Pistache
/* log.h
Michael Ellison, 27 May 2020
Logging macro definitions - use these to log messages in Pistache.
*/
#pragma once
#include <pistache/string_logger.h>
#ifndef PISTACHE_LOG_STRING_FATAL
#define PISTACHE_LOG_STRING_FATAL(logger, message) do {\
if (logger && logger->isEnabledFor(::Pistache::Log::Level::FATAL)) { \
std::ostringstream oss_; \
oss_ << message; \
logger->log(::Pistache::Log::Level::FATAL, oss_.str()); \
} \
} while (0)
#endif
#ifndef PISTACHE_LOG_STRING_ERROR
#define PISTACHE_LOG_STRING_ERROR(logger, message) do {\
if (logger && logger->isEnabledFor(::Pistache::Log::Level::ERROR)) { \
std::ostringstream oss_; \
oss_ << message; \
logger->log(::Pistache::Log::Level::ERROR, oss_.str()); \
} \
} while (0)
#endif
#ifndef PISTACHE_LOG_STRING_WARN
#define PISTACHE_LOG_STRING_WARN(logger, message) do {\
if (logger && logger->isEnabledFor(::Pistache::Log::Level::WARN)) { \
std::ostringstream oss_; \
oss_ << message; \
logger->log(::Pistache::Log::Level::WARN, oss_.str()); \
} \
} while (0)
#endif
#ifndef PISTACHE_LOG_STRING_INFO
#define PISTACHE_LOG_STRING_INFO(logger, message) do {\
if (logger && logger->isEnabledFor(::Pistache::Log::Level::INFO)) { \
std::ostringstream oss_; \
oss_ << message; \
logger->log(::Pistache::Log::Level::INFO, oss_.str()); \
} \
} while (0)
#endif
#ifndef PISTACHE_LOG_STRING_DEBUG
#define PISTACHE_LOG_STRING_DEBUG(logger, message) do {\
if (logger && logger->isEnabledFor(::Pistache::Log::Level::DEBUG)) { \
std::ostringstream oss_; \
oss_ << message; \
logger->log(::Pistache::Log::Level::DEBUG, oss_.str()); \
} \
} while (0)
#endif
#ifndef PISTACHE_LOG_STRING_TRACE
#ifndef NDEBUG // Only enable trace logging in debug builds.
#define PISTACHE_LOG_STRING_TRACE(logger, message) do {\
if (logger && logger->isEnabledFor(::Pistache::Log::Level::TRACE)) { \
std::ostringstream oss_; \
oss_ << message; \
logger->log(::Pistache::Log::Level::TRACE, oss_.str()); \
} \
} while (0)
#else
#define PISTACHE_LOG_STRING_TRACE(logger, message) do {\
if (0) { \
std::ostringstream oss_; \
oss_ << message; \
logger->log(::Pistache::Log::Level::TRACE, oss_.str()); \
} \
} while (0)
#endif
#endif
#ifndef PISTACHE_STRING_LOGGER_T
#define PISTACHE_STRING_LOGGER_T \
std::shared_ptr<::Pistache::Log::StringLogger>
#endif
#ifndef PISTACHE_DEFAULT_STRING_LOGGER
#define PISTACHE_DEFAULT_STRING_LOGGER \
std::make_shared<::Pistache::Log::StringToStreamLogger>(::Pistache::Log::Level::WARN)
#endif
#ifndef PISTACHE_NULL_STRING_LOGGER
#define PISTACHE_NULL_STRING_LOGGER \
nullptr
#endif
/* mailbox.h
Mathieu Stefani, 12 August 2015
Copyright (c) 2014 Datacratic. All rights reserved.
A simple lock-free Mailbox implementation
*/
#pragma once
#include <atomic>
#include <stdexcept>
#include <array>
#include <sys/eventfd.h>
#include <unistd.h>
#include <pistache/common.h>
#include <pistache/os.h>
namespace Pistache {
static constexpr size_t CachelineSize = 64;
typedef char cacheline_pad_t[CachelineSize];
template <typename T> class Mailbox {
public:
Mailbox() { data.store(nullptr); }
virtual ~Mailbox() {}
const T *get() const {
if (isEmpty()) {
throw std::runtime_error("Can not retrieve mail from empty mailbox");
}
return data.load();
}
virtual T *post(T *newData) {
T *old = data.load();
while (!data.compare_exchange_weak(old, newData)) {
}
return old;
}
virtual T *clear() { return data.exchange(nullptr); }
bool isEmpty() const { return data == nullptr; }
private:
std::atomic<T *> data;
};
template <typename T> class PollableMailbox : public Mailbox<T> {
public:
PollableMailbox() : event_fd(-1) {}
~PollableMailbox() {
if (event_fd != -1)
close(event_fd);
}
bool isBound() const { return event_fd != -1; }
Polling::Tag bind(Polling::Epoll &poller) {
using namespace Polling;
if (isBound()) {
throw std::runtime_error("The mailbox has already been bound");
}
event_fd = TRY_RET(eventfd(0, EFD_NONBLOCK));
Tag tag_(event_fd);
poller.addFd(event_fd, Flags<Polling::NotifyOn>(NotifyOn::Read), tag_);
return tag_;
}
T *post(T *newData) {
auto *_ret = Mailbox<T>::post(newData);
if (isBound()) {
uint64_t val = 1;
TRY(write(event_fd, &val, sizeof val));
}
return _ret;
}
T *clear() {
auto ret = Mailbox<T>::clear();
if (isBound()) {
uint64_t val;
for (;;) {
ssize_t bytes = read(event_fd, &val, sizeof val);
if (bytes == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
else {
// TODO
}
}
}
}
return ret;
}
Polling::Tag tag() const {
if (!isBound())
throw std::runtime_error("Can not retrieve tag of an unbound mailbox");
return Polling::Tag(event_fd);
}
void unbind(Polling::Epoll &poller) {
if (event_fd == -1) {
throw std::runtime_error("The mailbox is not bound");
}
poller.removeFd(event_fd);
close(event_fd), event_fd = -1;
}
private:
int event_fd;
};
/*
* An unbounded MPSC lock-free queue. Usefull for efficient cross-thread message
passing.
* push() and pop() are wait-free.
* Might replace the Mailbox implementation below
* Design comes from
http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue
*/
template <typename T> class Queue {
public:
struct Entry {
friend class Queue;
Entry() : storage(), next(nullptr) {}
template <class U> explicit Entry(U &&u) : storage(), next(nullptr) {
new (&storage) T(std::forward<U>(u));
}
const T &data() const { return *reinterpret_cast<const T *>(&storage); }
T &data() { return *reinterpret_cast<T *>(&storage); }
private:
typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type Storage;
Storage storage;
std::atomic<Entry *> next;
};
Queue() : head(), tail(nullptr) {
auto *sentinel = new Entry;
sentinel->next = nullptr;
head.store(sentinel, std::memory_order_relaxed);
tail = sentinel;
}
virtual ~Queue() {
while (!empty()) {
Entry *e = pop();
e->data().~T();
delete e;
}
delete tail;
}
template <typename U> void push(U &&u) {
Entry *entry = new Entry(std::forward<U>(u));
// @Note: we're using SC atomics here (exchange will issue a full fence),
// but I don't think we should bother relaxing them for now
auto *prev = head.exchange(entry);
prev->next = entry;
}
virtual Entry *pop() {
auto *res = tail;
auto *next = res->next.load(std::memory_order_acquire);
if (next) {
// Since it's Single-Consumer, the store does not need to be atomic
tail = next;
new (&res->storage) T(std::move(next->data()));
return res;
}
return nullptr;
}
bool empty() { return head == tail; }
std::unique_ptr<T> popSafe() {
std::unique_ptr<T> object;
std::unique_ptr<Entry> entry(pop());
if (entry) {
object.reset(new T(std::move(entry->data())));
entry->data().~T();
}
return object;
}
private:
std::atomic<Entry *> head;
Entry *tail;
};
template <typename T> class PollableQueue : public Queue<T> {
public:
typedef typename Queue<T>::Entry Entry;
PollableQueue() : event_fd(-1) {}
~PollableQueue() {
if (event_fd != -1)
close(event_fd);
}
bool isBound() const { return event_fd != -1; }
Polling::Tag bind(Polling::Epoll &poller) {
using namespace Polling;
if (isBound()) {
throw std::runtime_error("The queue has already been bound");
}
event_fd = TRY_RET(eventfd(0, EFD_NONBLOCK));
Tag tag_(event_fd);
poller.addFd(event_fd, Flags<Polling::NotifyOn>(NotifyOn::Read), tag_);
return tag_;
}
template <class U> void push(U &&u) {
Queue<T>::push(std::forward<U>(u));
if (isBound()) {
uint64_t val = 1;
TRY(write(event_fd, &val, sizeof val));
}
}
Entry *pop() override {
auto ret = Queue<T>::pop();
if (isBound()) {
uint64_t val;
for (;;) {
ssize_t bytes = read(event_fd, &val, sizeof val);
if (bytes == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
else {
// TODO
}
}
}
}
return ret;
}
Polling::Tag tag() const {
if (!isBound())
throw std::runtime_error("Can not retrieve tag of an unbound mailbox");
return Polling::Tag(event_fd);
}
void unbind(Polling::Epoll &poller) {
if (event_fd == -1) {
throw std::runtime_error("The mailbox is not bound");
}
poller.removeFd(event_fd);
close(event_fd), event_fd = -1;
}
private:
int event_fd;
};
// A Multi-Producer Multi-Consumer bounded queue
// taken from
// http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
template <typename T, size_t Size> class MPMCQueue {
static_assert(Size >= 2 && ((Size & (Size - 1)) == 0),
"The size must be a power of 2");
static constexpr size_t Mask = Size - 1;
public:
MPMCQueue(const MPMCQueue &other) = delete;
MPMCQueue &operator=(const MPMCQueue &other) = delete;
/*
* Note that you should not move a queue. This is somehow needed for gcc 4.7,
* otherwise the client won't compile
* @Investigate why
*/
MPMCQueue(MPMCQueue &&other) { *this = std::move(other); }
MPMCQueue &operator=(MPMCQueue &&other) {
for (size_t i = 0; i < Size; ++i) {
cells_[i].sequence.store(
other.cells_[i].sequence.load(std::memory_order_relaxed),
std::memory_order_relaxed);
cells_[i].data = std::move(other.cells_[i].data);
}
enqueueIndex.store(other.enqueueIndex.load(), std::memory_order_relaxed);
dequeueIndex.store(other.enqueueIndex.load(), std::memory_order_relaxed);
return *this;
}
MPMCQueue() : cells_(), enqueueIndex(), dequeueIndex() {
for (size_t i = 0; i < Size; ++i) {
cells_[i].sequence.store(i, std::memory_order_relaxed);
}
enqueueIndex.store(0, std::memory_order_relaxed);
dequeueIndex.store(0, std::memory_order_relaxed);
}
template <typename U> bool enqueue(U &&data) {
Cell *target;
size_t index = enqueueIndex.load(std::memory_order_relaxed);
for (;;) {
target = cell(index);
size_t seq = target->sequence.load(std::memory_order_acquire);
auto diff =
static_cast<std::intptr_t>(seq) - static_cast<std::intptr_t>(index);
if (diff == 0) {
if (enqueueIndex.compare_exchange_weak(index, index + 1,
std::memory_order_relaxed))
break;
}
else if (diff < 0)
return false;
else {
index = enqueueIndex.load(std::memory_order_relaxed);
}
}
target->data = std::forward<U>(data);
target->sequence.store(index + 1, std::memory_order_release);
return true;
}
bool dequeue(T &data) {
Cell *target;
size_t index = dequeueIndex.load(std::memory_order_relaxed);
for (;;) {
target = cell(index);
size_t seq = target->sequence.load(std::memory_order_acquire);
auto diff = static_cast<std::intptr_t>(seq) -
static_cast<std::intptr_t>(index + 1);
if (diff == 0) {
if (dequeueIndex.compare_exchange_weak(index, index + 1,
std::memory_order_relaxed))
break;
} else if (diff < 0)
return false;
else {
index = dequeueIndex.load(std::memory_order_relaxed);
}
}
data = target->data;
target->sequence.store(index + Mask + 1, std::memory_order_release);
return true;
}
private:
struct Cell {
Cell() : sequence(), data() {}
std::atomic<size_t> sequence;
T data;
};
size_t cellIndex(size_t index) const { return index & Mask; }
Cell *cell(size_t index) { return &cells_[cellIndex(index)]; }
std::array<Cell, Size> cells_;
cacheline_pad_t pad0;
std::atomic<size_t> enqueueIndex;
cacheline_pad_t pad1;
std::atomic<size_t> dequeueIndex;
};
} // namespace Pistache
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment