Merge branch 'master' into cache-digest
This commit is contained in:
commit
4bfd9b182e
17
README.rst
17
README.rst
|
@ -176,6 +176,23 @@ To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
|
|||
applications were not built, then using ``--enable-app`` may find
|
||||
that cause, such as the missing dependency.
|
||||
|
||||
Notes for building on Windows (MSVC)
|
||||
------------------------------------
|
||||
|
||||
The easiest way to build native Windows nghttp2 dll is use
|
||||
[cmake](https://cmake.org/). The free version of [Visual C++ Build
|
||||
Tools](http://landinghub.visualstudio.com/visual-cpp-build-tools)
|
||||
works fine.
|
||||
|
||||
1. Install cmake for windows
|
||||
2. Open "Visual C++ ... Native Build Tool Command Prompt", and inside
|
||||
nghttp2 directly, run ``cmake``.
|
||||
3. Then run ``cmake --build`` to build library.
|
||||
4. nghttp2.dll, nghttp2.lib, nghttp2.exp are placed under lib directory.
|
||||
|
||||
Note that the above steps most likely produce nghttp2 library only.
|
||||
No bundled applications are compiled.
|
||||
|
||||
Notes for building on Windows (Mingw/Cygwin)
|
||||
--------------------------------------------
|
||||
|
||||
|
|
|
@ -695,9 +695,6 @@ int main(int argc, char **argv) {
|
|||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, 0);
|
||||
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
OPENSSL_config(NULL);
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
|
|
|
@ -594,9 +594,6 @@ int main(int argc, char **argv) {
|
|||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
OPENSSL_config(NULL);
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
|
|
|
@ -781,9 +781,6 @@ int main(int argc, char **argv) {
|
|||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
OPENSSL_config(NULL);
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
|
|
|
@ -386,8 +386,9 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
|||
faddr_(faddr),
|
||||
worker_(worker),
|
||||
left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN),
|
||||
affinity_hash_(-1),
|
||||
should_close_after_write_(false) {
|
||||
affinity_hash_(0),
|
||||
should_close_after_write_(false),
|
||||
affinity_hash_computed_(false) {
|
||||
|
||||
++worker_->get_worker_stat()->num_connections;
|
||||
|
||||
|
@ -674,23 +675,21 @@ void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn) {
|
|||
}
|
||||
|
||||
namespace {
|
||||
uint32_t hash32(const StringRef &s) {
|
||||
/* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */
|
||||
uint32_t h = 2166136261u;
|
||||
size_t i;
|
||||
// Computes 32bits hash for session affinity for IP address |ip|.
|
||||
uint32_t compute_affinity_from_ip(const StringRef &ip) {
|
||||
int rv;
|
||||
std::array<uint8_t, 32> buf;
|
||||
|
||||
for (i = 0; i < s.size(); ++i) {
|
||||
h ^= s[i];
|
||||
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
|
||||
rv = util::sha256(buf.data(), ip);
|
||||
if (rv != 0) {
|
||||
// Not sure when sha256 failed. Just fall back to another
|
||||
// function.
|
||||
return util::hash32(ip);
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int32_t calculate_affinity_from_ip(const StringRef &ip) {
|
||||
return hash32(ip) & 0x7fffffff;
|
||||
return (static_cast<uint32_t>(buf[0]) << 24) |
|
||||
(static_cast<uint32_t>(buf[1]) << 16) |
|
||||
(static_cast<uint32_t>(buf[2]) << 8) | static_cast<uint32_t>(buf[3]);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -918,11 +917,22 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
|||
auto &shared_addr = group->shared_addr;
|
||||
|
||||
if (shared_addr->affinity == AFFINITY_IP) {
|
||||
if (affinity_hash_ == -1) {
|
||||
affinity_hash_ = calculate_affinity_from_ip(StringRef{ipaddr_});
|
||||
if (!affinity_hash_computed_) {
|
||||
affinity_hash_ = compute_affinity_from_ip(StringRef{ipaddr_});
|
||||
affinity_hash_computed_ = true;
|
||||
}
|
||||
|
||||
auto idx = affinity_hash_ % shared_addr->addrs.size();
|
||||
const auto &affinity_hash = shared_addr->affinity_hash;
|
||||
|
||||
auto it = std::lower_bound(
|
||||
std::begin(affinity_hash), std::end(affinity_hash), affinity_hash_,
|
||||
[](const AffinityHash &lhs, uint32_t rhs) { return lhs.hash < rhs; });
|
||||
|
||||
if (it == std::end(affinity_hash)) {
|
||||
it = std::begin(affinity_hash);
|
||||
}
|
||||
|
||||
auto idx = (*it).idx;
|
||||
|
||||
auto &addr = shared_addr->addrs[idx];
|
||||
if (addr.proto == PROTO_HTTP2) {
|
||||
|
|
|
@ -174,8 +174,11 @@ private:
|
|||
Worker *worker_;
|
||||
// The number of bytes of HTTP/2 client connection header to read
|
||||
size_t left_connhd_len_;
|
||||
int32_t affinity_hash_;
|
||||
// hash for session affinity using client IP
|
||||
uint32_t affinity_hash_;
|
||||
bool should_close_after_write_;
|
||||
// true if affinity_hash_ is computed
|
||||
bool affinity_hash_computed_;
|
||||
ReadBuf rb_;
|
||||
};
|
||||
|
||||
|
|
|
@ -2830,12 +2830,51 @@ StringRef strproto(shrpx_proto proto) {
|
|||
assert(0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Consistent hashing method described in
|
||||
// https://github.com/RJ/ketama. Generate 160 32-bit hashes per |s|,
|
||||
// which is usually backend address. The each hash is associated to
|
||||
// index of backend address. When all hashes for every backend
|
||||
// address are calculated, sort it in ascending order of hash. To
|
||||
// choose the index, compute 32-bit hash based on client IP address,
|
||||
// and do lower bound search in the array. The returned index is the
|
||||
// backend to use.
|
||||
int compute_affinity_hash(std::vector<AffinityHash> &res, size_t idx,
|
||||
const StringRef &s) {
|
||||
int rv;
|
||||
std::array<uint8_t, 32> buf;
|
||||
|
||||
for (auto i = 0; i < 20; ++i) {
|
||||
auto t = s.str();
|
||||
t += i;
|
||||
|
||||
rv = util::sha256(buf.data(), StringRef{t});
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto h = (static_cast<uint32_t>(buf[4 * i]) << 24) |
|
||||
(static_cast<uint32_t>(buf[4 * i + 1]) << 16) |
|
||||
(static_cast<uint32_t>(buf[4 * i + 2]) << 8) |
|
||||
static_cast<uint32_t>(buf[4 * i + 3]);
|
||||
|
||||
res.emplace_back(idx, h);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Configures the following member in |config|:
|
||||
// conn.downstream_router, conn.downstream.addr_groups,
|
||||
// conn.downstream.addr_group_catch_all.
|
||||
int configure_downstream_group(Config *config, bool http2_proxy,
|
||||
bool numeric_addr_only,
|
||||
const TLSConfig &tlsconf) {
|
||||
int rv;
|
||||
|
||||
auto &downstreamconf = *config->conn.downstream;
|
||||
auto &addr_groups = downstreamconf.addr_groups;
|
||||
auto &routerconf = downstreamconf.router;
|
||||
|
@ -2959,6 +2998,25 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
|||
<< util::to_numeric_addr(&addr.addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (g.affinity == AFFINITY_IP) {
|
||||
size_t idx = 0;
|
||||
for (auto &addr : g.addrs) {
|
||||
auto p = reinterpret_cast<uint8_t *>(&addr.addr.su);
|
||||
rv = compute_affinity_hash(g.affinity_hash, idx,
|
||||
StringRef{p, addr.addr.len});
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
|
||||
std::sort(std::begin(g.affinity_hash), std::end(g.affinity_hash),
|
||||
[](const AffinityHash &lhs, const AffinityHash &rhs) {
|
||||
return lhs.hash < rhs.hash;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -370,12 +370,24 @@ struct DownstreamAddrConfig {
|
|||
bool tls;
|
||||
};
|
||||
|
||||
// Mapping hash to idx which is an index into
|
||||
// DownstreamAddrGroupConfig::addrs.
|
||||
struct AffinityHash {
|
||||
AffinityHash(size_t idx, uint32_t hash) : idx(idx), hash(hash) {}
|
||||
|
||||
size_t idx;
|
||||
uint32_t hash;
|
||||
};
|
||||
|
||||
struct DownstreamAddrGroupConfig {
|
||||
DownstreamAddrGroupConfig(const StringRef &pattern)
|
||||
: pattern(pattern.c_str(), pattern.size()), affinity(AFFINITY_NONE) {}
|
||||
|
||||
ImmutableString pattern;
|
||||
std::vector<DownstreamAddrConfig> addrs;
|
||||
// Bunch of session affinity hash. Only used if affinity ==
|
||||
// AFFINITY_IP.
|
||||
std::vector<AffinityHash> affinity_hash;
|
||||
// Session affinity
|
||||
shrpx_session_affinity affinity;
|
||||
};
|
||||
|
|
|
@ -110,12 +110,8 @@ void serial_event_async_cb(struct ev_loop *loop, ev_async *w, int revent) {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
std::random_device rd;
|
||||
} // namespace
|
||||
|
||||
ConnectionHandler::ConnectionHandler(struct ev_loop *loop)
|
||||
: gen_(rd()),
|
||||
ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
|
||||
: gen_(gen),
|
||||
single_worker_(nullptr),
|
||||
loop_(loop),
|
||||
tls_ticket_key_memcached_get_retry_count_(0),
|
||||
|
@ -736,6 +732,14 @@ ConnectionHandler::get_tls_ticket_key_memcached_dispatcher() const {
|
|||
return tls_ticket_key_memcached_dispatcher_.get();
|
||||
}
|
||||
|
||||
// Use the similar backoff algorithm described in
|
||||
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
|
||||
namespace {
|
||||
constexpr size_t MAX_BACKOFF_EXP = 10;
|
||||
constexpr auto MULTIPLIER = 3.2;
|
||||
constexpr auto JITTER = 0.2;
|
||||
} // namespace
|
||||
|
||||
void ConnectionHandler::on_tls_ticket_key_network_error(ev_timer *w) {
|
||||
if (++tls_ticket_key_memcached_get_retry_count_ >=
|
||||
get_config()->tls.ticket.memcached.max_retry) {
|
||||
|
@ -746,15 +750,19 @@ void ConnectionHandler::on_tls_ticket_key_network_error(ev_timer *w) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto dist = std::uniform_int_distribution<int>(
|
||||
1, std::min(60, 1 << tls_ticket_key_memcached_get_retry_count_));
|
||||
auto t = dist(gen_);
|
||||
auto base_backoff = util::int_pow(
|
||||
MULTIPLIER,
|
||||
std::min(MAX_BACKOFF_EXP, tls_ticket_key_memcached_get_retry_count_));
|
||||
auto dist = std::uniform_real_distribution<>(-JITTER * base_backoff,
|
||||
JITTER * base_backoff);
|
||||
|
||||
auto backoff = base_backoff + dist(gen_);
|
||||
|
||||
LOG(WARN)
|
||||
<< "Memcached: tls ticket get failed due to network error, retrying in "
|
||||
<< t << " seconds";
|
||||
<< backoff << " seconds";
|
||||
|
||||
ev_timer_set(w, t, 0.);
|
||||
ev_timer_set(w, backoff, 0.);
|
||||
ev_timer_start(loop_, w);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <sys/socket.h>
|
||||
#endif // HAVE_SYS_SOCKET_H
|
||||
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
|
@ -100,7 +101,7 @@ struct SerialEvent {
|
|||
|
||||
class ConnectionHandler {
|
||||
public:
|
||||
ConnectionHandler(struct ev_loop *loop);
|
||||
ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen);
|
||||
~ConnectionHandler();
|
||||
int handle_connection(int fd, sockaddr *addr, int addrlen,
|
||||
const UpstreamAddr *faddr);
|
||||
|
|
|
@ -56,7 +56,7 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
|||
auto mconn = static_cast<MemcachedConnection *>(conn->data);
|
||||
|
||||
if (mconn->on_read() != 0) {
|
||||
mconn->disconnect();
|
||||
mconn->reconnect_or_fail();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
|||
auto mconn = static_cast<MemcachedConnection *>(conn->data);
|
||||
|
||||
if (mconn->on_write() != 0) {
|
||||
mconn->disconnect();
|
||||
mconn->reconnect_or_fail();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -88,22 +88,25 @@ void connectcb(struct ev_loop *loop, ev_io *w, int revents) {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
constexpr ev_tstamp write_timeout = 10.;
|
||||
constexpr ev_tstamp read_timeout = 10.;
|
||||
constexpr auto write_timeout = 10_s;
|
||||
constexpr auto read_timeout = 10_s;
|
||||
|
||||
MemcachedConnection::MemcachedConnection(const Address *addr,
|
||||
struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
||||
const StringRef &sni_name,
|
||||
MemchunkPool *mcpool)
|
||||
MemchunkPool *mcpool,
|
||||
std::mt19937 &gen)
|
||||
: conn_(loop, -1, nullptr, mcpool, write_timeout, read_timeout, {}, {},
|
||||
connectcb, readcb, timeoutcb, this, 0, 0., PROTO_MEMCACHED),
|
||||
do_read_(&MemcachedConnection::noop),
|
||||
do_write_(&MemcachedConnection::noop),
|
||||
sni_name_(sni_name.str()),
|
||||
connect_blocker_(gen, loop, [] {}, [] {}),
|
||||
parse_state_{},
|
||||
addr_(addr),
|
||||
ssl_ctx_(ssl_ctx),
|
||||
sendsum_(0),
|
||||
try_count_(0),
|
||||
connected_(false) {}
|
||||
|
||||
MemcachedConnection::~MemcachedConnection() { disconnect(); }
|
||||
|
@ -201,6 +204,8 @@ int MemcachedConnection::initiate_connection() {
|
|||
|
||||
int MemcachedConnection::connected() {
|
||||
if (!util::check_socket_connected(conn_.fd)) {
|
||||
connect_blocker_.on_failure();
|
||||
|
||||
conn_.wlimit.stopw();
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -214,10 +219,7 @@ int MemcachedConnection::connected() {
|
|||
MCLOG(INFO, this) << "connected to memcached server";
|
||||
}
|
||||
|
||||
connected_ = true;
|
||||
|
||||
conn_.rlimit.startw();
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
|
||||
ev_set_cb(&conn_.wev, writecb);
|
||||
|
||||
|
@ -228,6 +230,12 @@ int MemcachedConnection::connected() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
ev_timer_stop(conn_.loop, &conn_.wt);
|
||||
|
||||
connected_ = true;
|
||||
|
||||
connect_blocker_.on_success();
|
||||
|
||||
do_read_ = &MemcachedConnection::read_clear;
|
||||
do_write_ = &MemcachedConnection::write_clear;
|
||||
|
||||
|
@ -248,6 +256,7 @@ int MemcachedConnection::tls_handshake() {
|
|||
}
|
||||
|
||||
if (rv < 0) {
|
||||
connect_blocker_.on_failure();
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -259,6 +268,7 @@ int MemcachedConnection::tls_handshake() {
|
|||
|
||||
if (!tlsconf.insecure &&
|
||||
ssl::check_cert(conn_.tls.ssl, addr_, StringRef(sni_name_)) != 0) {
|
||||
connect_blocker_.on_failure();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -270,6 +280,13 @@ int MemcachedConnection::tls_handshake() {
|
|||
}
|
||||
}
|
||||
|
||||
ev_timer_stop(conn_.loop, &conn_.rt);
|
||||
ev_timer_stop(conn_.loop, &conn_.wt);
|
||||
|
||||
connected_ = true;
|
||||
|
||||
connect_blocker_.on_success();
|
||||
|
||||
do_read_ = &MemcachedConnection::read_tls;
|
||||
do_write_ = &MemcachedConnection::write_tls;
|
||||
|
||||
|
@ -325,7 +342,9 @@ int MemcachedConnection::read_tls() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
if (ev_is_active(&conn_.rt)) {
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
auto nread = conn_.read_tls(recvbuf_.last, recvbuf_.wleft());
|
||||
|
@ -386,7 +405,9 @@ int MemcachedConnection::read_clear() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
if (ev_is_active(&conn_.rt)) {
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
auto nread = conn_.read_clear(recvbuf_.last, recvbuf_.wleft());
|
||||
|
@ -535,9 +556,16 @@ int MemcachedConnection::parse_packet() {
|
|||
}
|
||||
}
|
||||
|
||||
// We require at least one complete response to clear try count.
|
||||
try_count_ = 0;
|
||||
|
||||
auto req = std::move(recvq_.front());
|
||||
recvq_.pop_front();
|
||||
|
||||
if (sendq_.empty() && recvq_.empty()) {
|
||||
ev_timer_stop(conn_.loop, &conn_.rt);
|
||||
}
|
||||
|
||||
if (!req->canceled && req->cb) {
|
||||
req->cb(req.get(), MemcachedResult(parse_state_.status_code,
|
||||
std::move(parse_state_.value)));
|
||||
|
@ -635,6 +663,13 @@ void MemcachedConnection::drain_send_queue(size_t nwrite) {
|
|||
recvq_.push_back(std::move(sendq_.front()));
|
||||
sendq_.pop_front();
|
||||
}
|
||||
|
||||
// start read timer only when we wait for responses.
|
||||
if (recvq_.empty()) {
|
||||
ev_timer_stop(conn_.loop, &conn_.rt);
|
||||
} else if (!ev_is_active(&conn_.rt)) {
|
||||
ev_timer_again(conn_.loop, &conn_.rt);
|
||||
}
|
||||
}
|
||||
|
||||
size_t MemcachedConnection::serialized_size(MemcachedRequest *req) {
|
||||
|
@ -676,6 +711,10 @@ void MemcachedConnection::make_request(MemcachedSendbuf *sendbuf,
|
|||
}
|
||||
|
||||
int MemcachedConnection::add_request(std::unique_ptr<MemcachedRequest> req) {
|
||||
if (connect_blocker_.blocked()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sendq_.push_back(std::move(req));
|
||||
|
||||
if (connected_) {
|
||||
|
@ -684,6 +723,7 @@ int MemcachedConnection::add_request(std::unique_ptr<MemcachedRequest> req) {
|
|||
}
|
||||
|
||||
if (conn_.fd == -1 && initiate_connection() != 0) {
|
||||
connect_blocker_.on_failure();
|
||||
disconnect();
|
||||
return -1;
|
||||
}
|
||||
|
@ -696,4 +736,50 @@ void MemcachedConnection::signal_write() { conn_.wlimit.startw(); }
|
|||
|
||||
int MemcachedConnection::noop() { return 0; }
|
||||
|
||||
void MemcachedConnection::reconnect_or_fail() {
|
||||
if (!connected_ || (recvq_.empty() && sendq_.empty())) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr size_t MAX_TRY_COUNT = 3;
|
||||
|
||||
if (++try_count_ >= MAX_TRY_COUNT) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
MCLOG(INFO, this) << "Tried " << MAX_TRY_COUNT
|
||||
<< " times, and all failed. Aborting";
|
||||
}
|
||||
try_count_ = 0;
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<MemcachedRequest>> q;
|
||||
q.reserve(recvq_.size() + sendq_.size());
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
MCLOG(INFO, this) << "Retry connection, enqueue "
|
||||
<< recvq_.size() + sendq_.size() << " request(s) again";
|
||||
}
|
||||
|
||||
q.insert(std::end(q), std::make_move_iterator(std::begin(recvq_)),
|
||||
std::make_move_iterator(std::end(recvq_)));
|
||||
q.insert(std::end(q), std::make_move_iterator(std::begin(sendq_)),
|
||||
std::make_move_iterator(std::end(sendq_)));
|
||||
|
||||
recvq_.clear();
|
||||
sendq_.clear();
|
||||
|
||||
disconnect();
|
||||
|
||||
sendq_.insert(std::end(sendq_), std::make_move_iterator(std::begin(q)),
|
||||
std::make_move_iterator(std::end(q)));
|
||||
|
||||
if (initiate_connection() != 0) {
|
||||
connect_blocker_.on_failure();
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include "shrpx_connection.h"
|
||||
#include "shrpx_ssl.h"
|
||||
#include "shrpx_connect_blocker.h"
|
||||
#include "buffer.h"
|
||||
#include "network.h"
|
||||
|
||||
|
@ -96,7 +97,7 @@ class MemcachedConnection {
|
|||
public:
|
||||
MemcachedConnection(const Address *addr, struct ev_loop *loop,
|
||||
SSL_CTX *ssl_ctx, const StringRef &sni_name,
|
||||
MemchunkPool *mcpool);
|
||||
MemchunkPool *mcpool, std::mt19937 &gen);
|
||||
~MemcachedConnection();
|
||||
|
||||
void disconnect();
|
||||
|
@ -126,6 +127,8 @@ public:
|
|||
|
||||
int noop();
|
||||
|
||||
void reconnect_or_fail();
|
||||
|
||||
private:
|
||||
Connection conn_;
|
||||
std::deque<std::unique_ptr<MemcachedRequest>> recvq_;
|
||||
|
@ -134,11 +137,13 @@ private:
|
|||
std::function<int(MemcachedConnection &)> do_read_, do_write_;
|
||||
std::string sni_name_;
|
||||
ssl::TLSSessionCache tls_session_cache_;
|
||||
ConnectBlocker connect_blocker_;
|
||||
MemcachedParseState parse_state_;
|
||||
const Address *addr_;
|
||||
SSL_CTX *ssl_ctx_;
|
||||
// Sum of the bytes to be transmitted in sendbufv_.
|
||||
size_t sendsum_;
|
||||
size_t try_count_;
|
||||
bool connected_;
|
||||
Buffer<8_k> recvbuf_;
|
||||
};
|
||||
|
|
|
@ -33,10 +33,11 @@ namespace shrpx {
|
|||
MemcachedDispatcher::MemcachedDispatcher(const Address *addr,
|
||||
struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
||||
const StringRef &sni_name,
|
||||
MemchunkPool *mcpool)
|
||||
MemchunkPool *mcpool,
|
||||
std::mt19937 &gen)
|
||||
: loop_(loop),
|
||||
mconn_(make_unique<MemcachedConnection>(addr, loop_, ssl_ctx, sni_name,
|
||||
mcpool)) {}
|
||||
mcpool, gen)) {}
|
||||
|
||||
MemcachedDispatcher::~MemcachedDispatcher() {}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "shrpx.h"
|
||||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
|
@ -45,7 +46,7 @@ class MemcachedDispatcher {
|
|||
public:
|
||||
MemcachedDispatcher(const Address *addr, struct ev_loop *loop,
|
||||
SSL_CTX *ssl_ctx, const StringRef &sni_name,
|
||||
MemchunkPool *mcpool);
|
||||
MemchunkPool *mcpool, std::mt19937 &gen);
|
||||
~MemcachedDispatcher();
|
||||
|
||||
int add_request(std::unique_ptr<MemcachedRequest> req);
|
||||
|
|
|
@ -147,7 +147,7 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
|||
session_cache_memcached_dispatcher_ = make_unique<MemcachedDispatcher>(
|
||||
&session_cacheconf.memcached.addr, loop,
|
||||
tls_session_cache_memcached_ssl_ctx,
|
||||
StringRef{session_cacheconf.memcached.host}, &mcpool_);
|
||||
StringRef{session_cacheconf.memcached.host}, &mcpool_, randgen_);
|
||||
}
|
||||
|
||||
replace_downstream_config(std::move(downstreamconf));
|
||||
|
@ -192,6 +192,7 @@ void Worker::replace_downstream_config(
|
|||
|
||||
shared_addr->addrs.resize(src.addrs.size());
|
||||
shared_addr->affinity = src.affinity;
|
||||
shared_addr->affinity_hash = src.affinity_hash;
|
||||
|
||||
size_t num_http1 = 0;
|
||||
size_t num_http2 = 0;
|
||||
|
|
|
@ -127,6 +127,9 @@ struct WeightedPri {
|
|||
|
||||
struct SharedDownstreamAddr {
|
||||
std::vector<DownstreamAddr> addrs;
|
||||
// Bunch of session affinity hash. Only used if affinity ==
|
||||
// AFFINITY_IP.
|
||||
std::vector<AffinityHash> affinity_hash;
|
||||
// List of Http2Session which is not fully utilized (i.e., the
|
||||
// server advertized maximum concurrency is not reached). We will
|
||||
// coalesce as much stream as possible in one Http2Session to fully
|
||||
|
|
|
@ -379,6 +379,10 @@ void nb_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
|
|||
} // namespace
|
||||
#endif // HAVE_NEVERBLEED
|
||||
|
||||
namespace {
|
||||
std::random_device rd;
|
||||
} // namespace
|
||||
|
||||
int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
if (reopen_log_files() != 0) {
|
||||
LOG(FATAL) << "Failed to open log file";
|
||||
|
@ -387,7 +391,9 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
|||
|
||||
auto loop = EV_DEFAULT;
|
||||
|
||||
ConnectionHandler conn_handler(loop);
|
||||
auto gen = std::mt19937(rd());
|
||||
|
||||
ConnectionHandler conn_handler(loop, gen);
|
||||
|
||||
for (auto &addr : get_config()->conn.listener.addrs) {
|
||||
conn_handler.add_acceptor(make_unique<AcceptHandler>(&addr, &conn_handler));
|
||||
|
@ -435,7 +441,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
|||
conn_handler.set_tls_ticket_key_memcached_dispatcher(
|
||||
make_unique<MemcachedDispatcher>(
|
||||
&ticketconf.memcached.addr, loop, ssl_ctx,
|
||||
StringRef{memcachedconf.host}, &mcpool));
|
||||
StringRef{memcachedconf.host}, &mcpool, gen));
|
||||
|
||||
ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0.,
|
||||
0.);
|
||||
|
|
56
src/util.cc
56
src/util.cc
|
@ -54,8 +54,11 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "ssl_compat.h"
|
||||
#include "timegm.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
@ -1325,6 +1328,59 @@ double int_pow(double x, size_t y) {
|
|||
return res;
|
||||
}
|
||||
|
||||
uint32_t hash32(const StringRef &s) {
|
||||
/* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */
|
||||
uint32_t h = 2166136261u;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < s.size(); ++i) {
|
||||
h ^= s[i];
|
||||
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
#if !OPENSSL_101_API
|
||||
namespace {
|
||||
EVP_MD_CTX *EVP_MD_CTX_new(void) { return EVP_MD_CTX_create(); }
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); }
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
int sha256(uint8_t *res, const StringRef &s) {
|
||||
int rv;
|
||||
|
||||
auto ctx = EVP_MD_CTX_new();
|
||||
if (ctx == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto ctx_deleter = defer(EVP_MD_CTX_free, ctx);
|
||||
|
||||
rv = EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
|
||||
if (rv != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = EVP_DigestUpdate(ctx, s.c_str(), s.size());
|
||||
if (rv != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int mdlen = 32;
|
||||
|
||||
rv = EVP_DigestFinal_ex(ctx, res, &mdlen);
|
||||
if (rv != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
} // namespace nghttp2
|
||||
|
|
|
@ -680,6 +680,12 @@ OutputIterator copy_lit(OutputIterator it, CharT(&s)[N]) {
|
|||
// Returns x**y
|
||||
double int_pow(double x, size_t y);
|
||||
|
||||
uint32_t hash32(const StringRef &s);
|
||||
|
||||
// Computes SHA-256 of |s|, and stores it in |buf|. This function
|
||||
// returns 0 if it succeeds, or -1.
|
||||
int sha256(uint8_t *buf, const StringRef &s);
|
||||
|
||||
} // namespace util
|
||||
|
||||
} // namespace nghttp2
|
||||
|
|
Loading…
Reference in New Issue