commit
7398e57174
|
@ -28,6 +28,7 @@ addons:
|
|||
- libevent-dev
|
||||
- libjansson-dev
|
||||
- libjemalloc-dev
|
||||
- libc-ares-dev
|
||||
- cmake
|
||||
- cmake-data
|
||||
before_install:
|
||||
|
|
|
@ -59,6 +59,7 @@ find_package(PythonInterp)
|
|||
# Auto-detection of features that can be toggled
|
||||
find_package(OpenSSL 1.0.1)
|
||||
find_package(Libev 4.11)
|
||||
find_package(Libcares 1.7.5)
|
||||
find_package(ZLIB 1.2.3)
|
||||
if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND)
|
||||
set(ENABLE_APP_DEFAULT ON)
|
||||
|
@ -207,6 +208,14 @@ if(LIBEVENT_FOUND)
|
|||
# Must both link the core and openssl libraries.
|
||||
set(LIBEVENT_OPENSSL_LIBRARIES ${LIBEVENT_LIBRARIES})
|
||||
endif()
|
||||
# libc-ares (for src)
|
||||
set(HAVE_LIBCARES ${LIBCARES_FOUND})
|
||||
if(LIBCARES_FOUND)
|
||||
set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR})
|
||||
else()
|
||||
set(LIBCARES_INCLUDE_DIRS "")
|
||||
set(LIBCARES_LIBRARIES "")
|
||||
endif()
|
||||
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||
set(HAVE_JANSSON ${JANSSON_FOUND})
|
||||
# libxml2 (for src/nghttp)
|
||||
|
@ -499,6 +508,7 @@ message(STATUS "summary of build options:
|
|||
OpenSSL: ${HAVE_OPENSSL} (LIBS='${OPENSSL_LIBRARIES}')
|
||||
Libxml2: ${HAVE_LIBXML2} (LIBS='${LIBXML2_LIBRARIES}')
|
||||
Libev: ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
|
||||
Libc-ares: ${HAVE_LIBCARES} (LIBS='${LIBCARES_LIBRARIES}')
|
||||
Libevent(SSL): ${HAVE_LIBEVENT_OPENSSL} (LIBS='${LIBEVENT_OPENSSL_LIBRARIES}')
|
||||
Spdylay: ${HAVE_SPDYLAY} (LIBS='${SPDYLAY_LIBRARIES}')
|
||||
Jansson: ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}')
|
||||
|
|
|
@ -45,7 +45,8 @@ EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
|||
cmake/Version.cmake \
|
||||
cmake/FindCython.cmake \
|
||||
cmake/FindLibevent.cmake \
|
||||
cmake/FindJansson.cmake
|
||||
cmake/FindJansson.cmake \
|
||||
cmake/FindLibcares.cmake
|
||||
|
||||
.PHONY: clang-format
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ are required:
|
|||
* OpenSSL >= 1.0.1
|
||||
* libev >= 4.11
|
||||
* zlib >= 1.2.3
|
||||
* libc-ares >= 1.7.5
|
||||
|
||||
ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
|
||||
LibreSSL >= 2.2.0 can be used instead of OpenSSL, but OpenSSL has more
|
||||
|
@ -115,7 +116,7 @@ If you are using Ubuntu 14.04 LTS (trusty) or Debian 7.0 (wheezy) and above run
|
|||
|
||||
sudo apt-get install g++ make binutils autoconf automake autotools-dev libtool pkg-config \
|
||||
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
|
||||
libjemalloc-dev cython python3-dev python-setuptools
|
||||
libc-ares-dev libjemalloc-dev cython python3-dev python-setuptools
|
||||
|
||||
From Ubuntu 15.10, spdylay has been available as a package named
|
||||
`libspdylay-dev`. For the earlier Ubuntu release, you need to build
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# - Try to find libcares
|
||||
# Once done this will define
|
||||
# LIBCARES_FOUND - System has libcares
|
||||
# LIBCARES_INCLUDE_DIRS - The libcares include directories
|
||||
# LIBCARES_LIBRARIES - The libraries needed to use libcares
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBCARES QUIET libcares)
|
||||
|
||||
find_path(LIBCARES_INCLUDE_DIR
|
||||
NAMES ares.h
|
||||
HINTS ${PC_LIBCARES_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(LIBCARES_LIBRARY
|
||||
NAMES cares
|
||||
HINTS ${PC_LIBCARES_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(LIBCARES_INCLUDE_DIR)
|
||||
set(_version_regex "^#define[ \t]+ARES_VERSION_STR[ \t]+\"([^\"]+)\".*")
|
||||
file(STRINGS "${LIBCARES_INCLUDE_DIR}/ares_version.h"
|
||||
LIBCARES_VERSION REGEX "${_version_regex}")
|
||||
string(REGEX REPLACE "${_version_regex}" "\\1"
|
||||
LIBCARES_VERSION "${LIBCARES_VERSION}")
|
||||
unset(_version_regex)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LIBCARES_FOUND to TRUE
|
||||
# if all listed variables are TRUE and the requested version matches.
|
||||
find_package_handle_standard_args(Libcares REQUIRED_VARS
|
||||
LIBCARES_LIBRARY LIBCARES_INCLUDE_DIR
|
||||
VERSION_VAR LIBCARES_VERSION)
|
||||
|
||||
if(LIBCARES_FOUND)
|
||||
set(LIBCARES_LIBRARIES ${LIBCARES_LIBRARY})
|
||||
set(LIBCARES_INCLUDE_DIRS ${LIBCARES_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LIBCARES_INCLUDE_DIR LIBCARES_LIBRARY)
|
35
configure.ac
35
configure.ac
|
@ -370,6 +370,13 @@ if test "x${have_openssl}" = "xno"; then
|
|||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# c-ares (for src)
|
||||
PKG_CHECK_MODULES([LIBCARES], [libcares >= 1.7.5], [have_libcares=yes],
|
||||
[have_libcares=no])
|
||||
if test "x${have_libcares}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBCARES_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# libevent_openssl (for examples)
|
||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||
|
@ -479,13 +486,14 @@ if test "x${request_asio_lib}" = "xyes"; then
|
|||
fi
|
||||
fi
|
||||
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
||||
# and libev
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL,
|
||||
# libev, and libc-ares.
|
||||
enable_app=no
|
||||
if test "x${request_app}" != "xno" &&
|
||||
test "x${have_zlib}" = "xyes" &&
|
||||
test "x${have_openssl}" = "xyes" &&
|
||||
test "x${have_libev}" = "xyes"; then
|
||||
test "x${have_libev}" = "xyes" &&
|
||||
test "x${have_libcares}" = "xyes"; then
|
||||
enable_app=yes
|
||||
fi
|
||||
|
||||
|
@ -640,6 +648,26 @@ AC_SYS_LARGEFILE
|
|||
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
|
||||
[have_struct_tm_tm_gmtoff=no], [[#include <time.h>]])
|
||||
|
||||
AC_CHECK_MEMBER([struct sockaddr_in.sin_len],
|
||||
[AC_DEFINE([HAVE_SOCKADDR_IN_SIN_LEN],[1],
|
||||
[Define to 1 if struct sockaddr_in has sin_len member.])],
|
||||
[],
|
||||
[[
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
]])
|
||||
|
||||
AC_CHECK_MEMBER([struct sockaddr_in6.sin6_len],
|
||||
[AC_DEFINE([HAVE_SOCKADDR_IN6_SIN6_LEN],[1],
|
||||
[Define to 1 if struct sockaddr_in6 has sin6_len member.])],
|
||||
[],
|
||||
[[
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
]])
|
||||
|
||||
if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
|
||||
AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1],
|
||||
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
|
||||
|
@ -890,6 +918,7 @@ AC_MSG_NOTICE([summary of build options:
|
|||
OpenSSL: ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}')
|
||||
Libxml2: ${have_libxml2} (CFLAGS='${XML_CPPFLAGS}' LIBS='${XML_LIBS}')
|
||||
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
|
||||
Libc-ares ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
|
||||
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
|
||||
Spdylay: ${have_spdylay} (CFLAGS='${LIBSPDYLAY_CFLAGS}' LIBS='${LIBSPDYLAY_LIBS}')
|
||||
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
|
||||
|
|
|
@ -1767,10 +1767,10 @@ The replacement is done instantly without breaking existing
|
|||
connections or requests. It also avoids any process creation as is
|
||||
the case with hot swapping with signals.
|
||||
|
||||
The one limitation is that only numeric IP address is allowd in
|
||||
:option:`backend <--backend>` in request body while non numeric
|
||||
hostname is allowed in command-line or configuration file is read
|
||||
using :option:`--conf`.
|
||||
The one limitation is that only numeric IP address is allowed in
|
||||
:option:`backend <--backend>` in request body unless "dns" parameter
|
||||
is missing while non numeric hostname is allowed in command-line or
|
||||
configuration file is read using :option:`--conf`.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
|
|
@ -149,6 +149,9 @@ OPTIONS = [
|
|||
"ecdh-curves",
|
||||
"tls-sct-dir",
|
||||
"backend-connect-timeout",
|
||||
"dns-cache-timeout",
|
||||
"dns-lookup-timeout",
|
||||
"dns-max-try",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
|
|
@ -1369,6 +1369,42 @@ func TestH2H1ProxyProtocolV1InvalidID(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestH2H1ExternalDNS tests that DNS resolution using external DNS
|
||||
// with HTTP/1 backend works.
|
||||
func TestH2H1ExternalDNS(t *testing.T) {
|
||||
st := newServerTester([]string{"--external-dns"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1ExternalDNS",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1DNS tests that DNS resolution without external DNS with
|
||||
// HTTP/1 backend works.
|
||||
func TestH2H1DNS(t *testing.T) {
|
||||
st := newServerTester([]string{"--dns"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1DNS",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
|
@ -1845,6 +1881,42 @@ func TestH2H2RespPhaseReturn(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestH2H2ExternalDNS tests that DNS resolution using external DNS
|
||||
// with HTTP/2 backend works.
|
||||
func TestH2H2ExternalDNS(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--external-dns"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2ExternalDNS",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H2DNS tests that DNS resolution without external DNS with
|
||||
// HTTP/2 backend works.
|
||||
func TestH2H2DNS(t *testing.T) {
|
||||
st := newServerTester([]string{"--http2-bridge", "--dns"}, t, noopHandler)
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H2DNS",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2APIBackendconfig exercise backendconfig API endpoint routine
|
||||
// for successful case.
|
||||
func TestH2APIBackendconfig(t *testing.T) {
|
||||
|
|
|
@ -101,10 +101,17 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
|||
args := []string{}
|
||||
|
||||
backendTLS := false
|
||||
dns := false
|
||||
externalDNS := false
|
||||
for _, k := range src_args {
|
||||
switch k {
|
||||
case "--http2-bridge":
|
||||
backendTLS = true
|
||||
case "--dns":
|
||||
dns = true
|
||||
case "--external-dns":
|
||||
dns = true
|
||||
externalDNS = true
|
||||
default:
|
||||
args = append(args, k)
|
||||
}
|
||||
|
@ -117,7 +124,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
|||
ts.TLS = new(tls.Config)
|
||||
ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2")
|
||||
ts.StartTLS()
|
||||
args = append(args, "-k", "--backend-tls")
|
||||
args = append(args, "-k")
|
||||
} else {
|
||||
ts.Start()
|
||||
}
|
||||
|
@ -134,9 +141,23 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
|
|||
|
||||
// URL.Host looks like "127.0.0.1:8080", but we want
|
||||
// "127.0.0.1,8080"
|
||||
b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1)
|
||||
b := "-b"
|
||||
if !externalDNS {
|
||||
b += fmt.Sprintf("%v;", strings.Replace(backendURL.Host, ":", ",", -1))
|
||||
} else {
|
||||
sep := strings.LastIndex(backendURL.Host, ":")
|
||||
if sep == -1 {
|
||||
t.Fatalf("backendURL.Host %v does not contain separator ':'", backendURL.Host)
|
||||
}
|
||||
// We use awesome service xip.io.
|
||||
b += fmt.Sprintf("%v.xip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
|
||||
}
|
||||
|
||||
if backendTLS {
|
||||
b += ";;proto=h2;tls"
|
||||
b += ";proto=h2;tls"
|
||||
}
|
||||
if dns {
|
||||
b += ";dns"
|
||||
}
|
||||
|
||||
noTLS := "no-tls"
|
||||
|
|
|
@ -19,6 +19,7 @@ include_directories(
|
|||
${LIBXML2_INCLUDE_DIRS}
|
||||
${LIBEV_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
${LIBCARES_INCLUDE_DIRS}
|
||||
${JANSSON_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
@ -31,6 +32,7 @@ link_libraries(
|
|||
${LIBXML2_LIBRARIES}
|
||||
${LIBEV_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${LIBCARES_LIBRARIES}
|
||||
${JANSSON_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${APP_LIBRARIES}
|
||||
|
@ -112,6 +114,9 @@ if(ENABLE_APP)
|
|||
shrpx_api_downstream_connection.cc
|
||||
shrpx_health_monitor_downstream_connection.cc
|
||||
shrpx_exec.cc
|
||||
shrpx_dns_resolver.cc
|
||||
shrpx_dual_dns_resolver.cc
|
||||
shrpx_dns_tracker.cc
|
||||
xsi_strerror.c
|
||||
)
|
||||
if(HAVE_SPDYLAY)
|
||||
|
|
|
@ -44,6 +44,7 @@ AM_CPPFLAGS = \
|
|||
@XML_CPPFLAGS@ \
|
||||
@LIBEV_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@LIBCARES_CFLAGS@ \
|
||||
@JANSSON_CFLAGS@ \
|
||||
@ZLIB_CFLAGS@ \
|
||||
@DEFS@
|
||||
|
@ -55,6 +56,7 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
|
|||
@XML_LIBS@ \
|
||||
@LIBEV_LIBS@ \
|
||||
@OPENSSL_LIBS@ \
|
||||
@LIBCARES_LIBS@ \
|
||||
@JANSSON_LIBS@ \
|
||||
@ZLIB_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
|
@ -139,6 +141,9 @@ NGHTTPX_SRCS = \
|
|||
shrpx_health_monitor_downstream_connection.cc \
|
||||
shrpx_health_monitor_downstream_connection.h \
|
||||
shrpx_exec.cc shrpx_exec.h \
|
||||
shrpx_dns_resolver.cc shrpx_dns_resolver.h \
|
||||
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \
|
||||
shrpx_dns_tracker.cc shrpx_dns_tracker.h \
|
||||
buffer.h memchunk.h template.h allocator.h \
|
||||
xsi_strerror.c xsi_strerror.h
|
||||
|
||||
|
|
64
src/shrpx.cc
64
src/shrpx.cc
|
@ -1486,6 +1486,14 @@ void fill_default_config(Config *config) {
|
|||
|
||||
auto &apiconf = config->api;
|
||||
apiconf.max_request_body = 16_k;
|
||||
|
||||
auto &dnsconf = config->dns;
|
||||
{
|
||||
auto &timeoutconf = dnsconf.timeout;
|
||||
timeoutconf.cache = 10_s;
|
||||
timeoutconf.lookup = 5_s;
|
||||
}
|
||||
dnsconf.max_try = 2;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -1587,13 +1595,13 @@ Connections:
|
|||
Several parameters <PARAM> are accepted after <PATTERN>.
|
||||
The parameters are delimited by ";". The available
|
||||
parameters are: "proto=<PROTO>", "tls",
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>", and
|
||||
"affinity=<METHOD>". The parameter consists of keyword,
|
||||
and optionally followed by "=" and value. For example,
|
||||
the parameter "proto=h2" consists of the keyword "proto"
|
||||
and value "h2". The parameter "tls" consists of the
|
||||
keyword "tls" without value. Each parameter is
|
||||
described as follows.
|
||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
||||
"affinity=<METHOD>", and "dns". The parameter consists
|
||||
of keyword, and optionally followed by "=" and value.
|
||||
For example, the parameter "proto=h2" consists of the
|
||||
keyword "proto" and value "h2". The parameter "tls"
|
||||
consists of the keyword "tls" without value. Each
|
||||
parameter is described as follows.
|
||||
|
||||
The backend application protocol can be specified using
|
||||
optional "proto" parameter, and in the form of
|
||||
|
@ -1642,6 +1650,14 @@ Connections:
|
|||
break if one of the backend gets unreachable, or backend
|
||||
settings are reloaded or replaced by API.
|
||||
|
||||
By default, name resolution of backend host name is done
|
||||
at start up, or reloading configuration. If "dns"
|
||||
parameter is given, name resolution takes place
|
||||
dynamically. This is useful if backend address changes
|
||||
frequently. If "dns" is given, name resolution of
|
||||
backend host name at start up, or reloading
|
||||
configuration is skipped.
|
||||
|
||||
Since ";" and ":" are used as delimiter, <PATTERN> must
|
||||
not contain these characters. Since ";" has special
|
||||
meaning in shell, the option value must be quoted.
|
||||
|
@ -2368,6 +2384,25 @@ API:
|
|||
Default: )"
|
||||
<< util::utos_unit(config->api.max_request_body) << R"(
|
||||
|
||||
DNS:
|
||||
--dns-cache-timeout=<DURATION>
|
||||
Set duration that cached DNS results remain valid. Note
|
||||
that nghttpx caches the unsuccessful results as well.
|
||||
Default: )"
|
||||
<< util::duration_str(config->dns.timeout.cache) << R"(
|
||||
--dns-lookup-timeout=<DURATION>
|
||||
Set timeout that DNS server is given to respond to the
|
||||
initial DNS query. For the 2nd and later queries,
|
||||
server is given time based on this timeout, and it is
|
||||
scaled linearly.
|
||||
Default: )"
|
||||
<< util::duration_str(config->dns.timeout.lookup) << R"(
|
||||
--dns-max-try=<N>
|
||||
Set the number of DNS query before nghttpx gives up name
|
||||
lookup.
|
||||
Default: )"
|
||||
<< config->dns.max_try << R"(
|
||||
|
||||
Debug:
|
||||
--frontend-http2-dump-request-header=<PATH>
|
||||
Dumps request headers received by HTTP/2 frontend to the
|
||||
|
@ -3027,6 +3062,9 @@ int main(int argc, char **argv) {
|
|||
{SHRPX_OPT_TLS_SCT_DIR.c_str(), required_argument, &flag, 141},
|
||||
{SHRPX_OPT_BACKEND_CONNECT_TIMEOUT.c_str(), required_argument, &flag,
|
||||
142},
|
||||
{SHRPX_OPT_DNS_CACHE_TIMEOUT.c_str(), required_argument, &flag, 143},
|
||||
{SHRPX_OPT_DNS_LOOKUP_TIMEOUT.c_str(), required_argument, &flag, 144},
|
||||
{SHRPX_OPT_DNS_MAX_TRY.c_str(), required_argument, &flag, 145},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -3700,6 +3738,18 @@ int main(int argc, char **argv) {
|
|||
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECT_TIMEOUT,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 143:
|
||||
// --dns-cache-timeout
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_DNS_CACHE_TIMEOUT, StringRef{optarg});
|
||||
break;
|
||||
case 144:
|
||||
// --dns-lookup-timeou
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_DNS_LOOKUP_TIMEOUT, StringRef{optarg});
|
||||
break;
|
||||
case 145:
|
||||
// --dns-max-try
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_DNS_MAX_TRY, StringRef{optarg});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -728,6 +728,7 @@ struct DownstreamParams {
|
|||
shrpx_proto proto;
|
||||
shrpx_session_affinity affinity;
|
||||
bool tls;
|
||||
bool dns;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
@ -801,6 +802,8 @@ int parse_downstream_params(DownstreamParams &out,
|
|||
LOG(ERROR) << "backend: affinity: value must be either none or ip";
|
||||
return -1;
|
||||
}
|
||||
} else if (util::strieq_l("dns", param)) {
|
||||
out.dns = true;
|
||||
} else if (!param.empty()) {
|
||||
LOG(ERROR) << "backend: " << param << ": unknown keyword";
|
||||
return -1;
|
||||
|
@ -841,11 +844,17 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (addr.host_unix && params.dns) {
|
||||
LOG(ERROR) << "backend: dns: cannot be used for UNIX domain socket";
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.fall = params.fall;
|
||||
addr.rise = params.rise;
|
||||
addr.proto = params.proto;
|
||||
addr.tls = params.tls;
|
||||
addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
|
||||
addr.dns = params.dns;
|
||||
|
||||
auto &routerconf = downstreamconf.router;
|
||||
auto &router = routerconf.router;
|
||||
|
@ -1361,6 +1370,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("dns-max-tr", name, 10)) {
|
||||
return SHRPX_OPTID_DNS_MAX_TRY;
|
||||
}
|
||||
if (util::strieq_l("http2-prox", name, 10)) {
|
||||
return SHRPX_OPTID_HTTP2_PROXY;
|
||||
}
|
||||
|
@ -1522,6 +1534,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("dns-cache-timeou", name, 16)) {
|
||||
return SHRPX_OPTID_DNS_CACHE_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("worker-read-burs", name, 16)) {
|
||||
return SHRPX_OPTID_WORKER_READ_BURST;
|
||||
}
|
||||
|
@ -1536,6 +1551,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("dns-lookup-timeou", name, 17)) {
|
||||
return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
|
||||
}
|
||||
if (util::strieq_l("worker-write-burs", name, 17)) {
|
||||
return SHRPX_OPTID_WORKER_WRITE_BURST;
|
||||
}
|
||||
|
@ -3090,6 +3108,24 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
|
||||
return 0;
|
||||
#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||
case SHRPX_OPTID_DNS_CACHE_TIMEOUT:
|
||||
return parse_duration(&config->dns.timeout.cache, opt, optarg);
|
||||
case SHRPX_OPTID_DNS_LOOKUP_TIMEOUT:
|
||||
return parse_duration(&config->dns.timeout.lookup, opt, optarg);
|
||||
case SHRPX_OPTID_DNS_MAX_TRY: {
|
||||
int n;
|
||||
if (parse_uint(&n, opt, optarg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n > 5) {
|
||||
LOG(ERROR) << opt << ": must be smaller than or equal to 5";
|
||||
return -1;
|
||||
}
|
||||
|
||||
config->dns.max_try = n;
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
|
@ -3442,6 +3478,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
|||
auto hostport =
|
||||
util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
|
||||
|
||||
if (!addr.dns) {
|
||||
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
|
||||
downstreamconf.family, resolve_flags) == -1) {
|
||||
LOG(FATAL) << "Resolving backend address failed: " << hostport;
|
||||
|
@ -3452,14 +3489,27 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
|||
LOG(INFO) << "Resolved backend address: " << hostport << " -> "
|
||||
<< util::to_numeric_addr(&addr.addr);
|
||||
}
|
||||
} else {
|
||||
LOG(INFO) << "Resolving backend address " << hostport
|
||||
<< " takes place dynamically";
|
||||
}
|
||||
}
|
||||
|
||||
if (g.affinity == AFFINITY_IP) {
|
||||
size_t idx = 0;
|
||||
for (auto &addr : g.addrs) {
|
||||
StringRef key;
|
||||
if (addr.dns) {
|
||||
if (addr.host_unix) {
|
||||
key = addr.host;
|
||||
} else {
|
||||
key = addr.hostport;
|
||||
}
|
||||
} else {
|
||||
auto p = reinterpret_cast<uint8_t *>(&addr.addr.su);
|
||||
rv = compute_affinity_hash(g.affinity_hash, idx,
|
||||
StringRef{p, addr.addr.len});
|
||||
key = StringRef{p, addr.addr.len};
|
||||
}
|
||||
rv = compute_affinity_hash(g.affinity_hash, idx, key);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -312,6 +312,11 @@ constexpr auto SHRPX_OPT_ECDH_CURVES = StringRef::from_lit("ecdh-curves");
|
|||
constexpr auto SHRPX_OPT_TLS_SCT_DIR = StringRef::from_lit("tls-sct-dir");
|
||||
constexpr auto SHRPX_OPT_BACKEND_CONNECT_TIMEOUT =
|
||||
StringRef::from_lit("backend-connect-timeout");
|
||||
constexpr auto SHRPX_OPT_DNS_CACHE_TIMEOUT =
|
||||
StringRef::from_lit("dns-cache-timeout");
|
||||
constexpr auto SHRPX_OPT_DNS_LOOKUP_TIMEOUT =
|
||||
StringRef::from_lit("dns-lookup-timeout");
|
||||
constexpr auto SHRPX_OPT_DNS_MAX_TRY = StringRef::from_lit("dns-max-try");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
|
@ -379,6 +384,7 @@ struct UpstreamAddr {
|
|||
};
|
||||
|
||||
struct DownstreamAddrConfig {
|
||||
// Resolved address if |dns| is false
|
||||
Address addr;
|
||||
// backend address. If |host_unix| is true, this is UNIX domain
|
||||
// socket path. This must be NULL terminated string.
|
||||
|
@ -397,6 +403,8 @@ struct DownstreamAddrConfig {
|
|||
// true if |host| contains UNIX domain socket path.
|
||||
bool host_unix;
|
||||
bool tls;
|
||||
// true if dynamic DNS is enabled
|
||||
bool dns;
|
||||
};
|
||||
|
||||
// Mapping hash to idx which is an index into
|
||||
|
@ -777,6 +785,16 @@ struct APIConfig {
|
|||
bool enabled;
|
||||
};
|
||||
|
||||
struct DNSConfig {
|
||||
struct {
|
||||
ev_tstamp cache;
|
||||
ev_tstamp lookup;
|
||||
} timeout;
|
||||
// The number of tries name resolver makes before abandoning
|
||||
// request.
|
||||
size_t max_try;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
Config()
|
||||
: balloc(4096, 4096),
|
||||
|
@ -787,6 +805,7 @@ struct Config {
|
|||
logging{},
|
||||
conn{},
|
||||
api{},
|
||||
dns{},
|
||||
num_worker{0},
|
||||
padding{0},
|
||||
rlimit_nofile{0},
|
||||
|
@ -815,6 +834,7 @@ struct Config {
|
|||
LoggingConfig logging;
|
||||
ConnectionConfig conn;
|
||||
APIConfig api;
|
||||
DNSConfig dns;
|
||||
StringRef pid_file;
|
||||
StringRef conf_path;
|
||||
StringRef user;
|
||||
|
@ -891,6 +911,9 @@ enum {
|
|||
SHRPX_OPTID_CONF,
|
||||
SHRPX_OPTID_DAEMON,
|
||||
SHRPX_OPTID_DH_PARAM_FILE,
|
||||
SHRPX_OPTID_DNS_CACHE_TIMEOUT,
|
||||
SHRPX_OPTID_DNS_LOOKUP_TIMEOUT,
|
||||
SHRPX_OPTID_DNS_MAX_TRY,
|
||||
SHRPX_OPTID_ECDH_CURVES,
|
||||
SHRPX_OPTID_ERROR_PAGE,
|
||||
SHRPX_OPTID_ERRORLOG_FILE,
|
||||
|
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2016 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "shrpx_dns_resolver.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "shrpx_log.h"
|
||||
#include "shrpx_connection.h"
|
||||
#include "shrpx_config.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
namespace {
|
||||
void sock_state_cb(void *data, int s, int read, int write) {
|
||||
auto resolv = static_cast<DNSResolver *>(data);
|
||||
|
||||
if (resolv->get_status(nullptr) != DNS_STATUS_RUNNING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (read) {
|
||||
resolv->start_rev(s);
|
||||
} else {
|
||||
resolv->stop_rev(s);
|
||||
}
|
||||
if (write) {
|
||||
resolv->start_wev(s);
|
||||
} else {
|
||||
resolv->stop_wev(s);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void host_cb(void *arg, int status, int timeouts, hostent *hostent) {
|
||||
auto resolv = static_cast<DNSResolver *>(arg);
|
||||
resolv->on_result(status, hostent);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void process_result(DNSResolver *resolv) {
|
||||
auto cb = resolv->get_complete_cb();
|
||||
if (!cb) {
|
||||
return;
|
||||
}
|
||||
Address result;
|
||||
auto status = resolv->get_status(&result);
|
||||
switch (status) {
|
||||
case DNS_STATUS_OK:
|
||||
case DNS_STATUS_ERROR:
|
||||
cb(status, &result);
|
||||
break;
|
||||
}
|
||||
// resolv may be deleted here.
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
auto resolv = static_cast<DNSResolver *>(w->data);
|
||||
resolv->on_read(w->fd);
|
||||
process_result(resolv);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
auto resolv = static_cast<DNSResolver *>(w->data);
|
||||
resolv->on_write(w->fd);
|
||||
process_result(resolv);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto resolv = static_cast<DNSResolver *>(w->data);
|
||||
resolv->on_timeout();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void stop_ev(struct ev_loop *loop,
|
||||
const std::vector<std::unique_ptr<ev_io>> &evs) {
|
||||
for (auto &w : evs) {
|
||||
ev_io_stop(loop, w.get());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DNSResolver::DNSResolver(struct ev_loop *loop)
|
||||
: loop_(loop),
|
||||
channel_(nullptr),
|
||||
family_(AF_UNSPEC),
|
||||
status_(DNS_STATUS_IDLE) {
|
||||
ev_timer_init(&timer_, timeoutcb, 0., 0.);
|
||||
timer_.data = this;
|
||||
}
|
||||
|
||||
DNSResolver::~DNSResolver() {
|
||||
if (channel_) {
|
||||
ares_destroy(channel_);
|
||||
}
|
||||
|
||||
stop_ev(loop_, revs_);
|
||||
stop_ev(loop_, wevs_);
|
||||
|
||||
ev_timer_stop(loop_, &timer_);
|
||||
}
|
||||
|
||||
int DNSResolver::resolve(const StringRef &name, int family) {
|
||||
if (status_ != DNS_STATUS_IDLE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Start resolving host " << name << " in IPv"
|
||||
<< (family == AF_INET ? "4" : "6");
|
||||
}
|
||||
|
||||
name_ = name;
|
||||
family_ = family;
|
||||
|
||||
int rv;
|
||||
|
||||
auto &dnsconf = get_config()->dns;
|
||||
|
||||
ares_options opts{};
|
||||
opts.sock_state_cb = sock_state_cb;
|
||||
opts.sock_state_cb_data = this;
|
||||
opts.timeout = static_cast<int>(dnsconf.timeout.lookup * 1000);
|
||||
opts.tries = dnsconf.max_try;
|
||||
|
||||
auto optmask = ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
|
||||
|
||||
ares_channel chan;
|
||||
rv = ares_init_options(&chan, &opts, optmask);
|
||||
if (rv != ARES_SUCCESS) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
|
||||
}
|
||||
status_ = DNS_STATUS_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel_ = chan;
|
||||
status_ = DNS_STATUS_RUNNING;
|
||||
|
||||
ares_gethostbyname(channel_, name_.c_str(), family_, host_cb, this);
|
||||
reset_timeout();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DNSResolver::on_read(int fd) { return handle_event(fd, ARES_SOCKET_BAD); }
|
||||
|
||||
int DNSResolver::on_write(int fd) { return handle_event(ARES_SOCKET_BAD, fd); }
|
||||
|
||||
int DNSResolver::on_timeout() {
|
||||
return handle_event(ARES_SOCKET_BAD, ARES_SOCKET_BAD);
|
||||
}
|
||||
|
||||
int DNSResolver::handle_event(int rfd, int wfd) {
|
||||
if (status_ == DNS_STATUS_IDLE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ares_process_fd(channel_, rfd, wfd);
|
||||
|
||||
switch (status_) {
|
||||
case DNS_STATUS_RUNNING: {
|
||||
reset_timeout();
|
||||
return 0;
|
||||
}
|
||||
case DNS_STATUS_OK:
|
||||
return 0;
|
||||
case DNS_STATUS_ERROR:
|
||||
return -1;
|
||||
default:
|
||||
// Unreachable
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void DNSResolver::reset_timeout() {
|
||||
if (status_ != DNS_STATUS_RUNNING) {
|
||||
return;
|
||||
}
|
||||
timeval tvout;
|
||||
auto tv = ares_timeout(channel_, nullptr, &tvout);
|
||||
if (tv == nullptr) {
|
||||
return;
|
||||
}
|
||||
timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000.;
|
||||
ev_timer_again(loop_, &timer_);
|
||||
}
|
||||
|
||||
int DNSResolver::get_status(Address *result) const {
|
||||
if (status_ != DNS_STATUS_OK) {
|
||||
return status_;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
memcpy(result, &result_, sizeof(result_));
|
||||
}
|
||||
|
||||
return status_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
|
||||
int fd, int event, IOCb cb, void *data) {
|
||||
for (auto &w : evs) {
|
||||
if (w->fd == fd) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto &w : evs) {
|
||||
if (w->fd == -1) {
|
||||
ev_io_set(w.get(), fd, event);
|
||||
ev_io_start(loop, w.get());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto w = make_unique<ev_io>();
|
||||
ev_io_init(w.get(), cb, fd, event);
|
||||
w->data = data;
|
||||
ev_io_start(loop, w.get());
|
||||
evs.emplace_back(std::move(w));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void stop_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
|
||||
int fd, int event) {
|
||||
for (auto &w : evs) {
|
||||
if (w->fd == fd) {
|
||||
ev_io_stop(loop, w.get());
|
||||
ev_io_set(w.get(), -1, event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void DNSResolver::start_rev(int fd) {
|
||||
start_ev(revs_, loop_, fd, EV_READ, readcb, this);
|
||||
}
|
||||
|
||||
void DNSResolver::stop_rev(int fd) { stop_ev(revs_, loop_, fd, EV_READ); }
|
||||
|
||||
void DNSResolver::start_wev(int fd) {
|
||||
start_ev(wevs_, loop_, fd, EV_WRITE, writecb, this);
|
||||
}
|
||||
|
||||
void DNSResolver::stop_wev(int fd) { stop_ev(wevs_, loop_, fd, EV_WRITE); }
|
||||
|
||||
void DNSResolver::on_result(int status, hostent *hostent) {
|
||||
stop_ev(loop_, revs_);
|
||||
stop_ev(loop_, wevs_);
|
||||
ev_timer_stop(loop_, &timer_);
|
||||
|
||||
if (status != ARES_SUCCESS) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup for " << name_
|
||||
<< " failed: " << ares_strerror(status);
|
||||
}
|
||||
status_ = DNS_STATUS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (hostent->h_addrtype) {
|
||||
case AF_INET:
|
||||
for (auto ap = hostent->h_addr_list; *ap; ++ap) {
|
||||
status_ = DNS_STATUS_OK;
|
||||
result_.len = sizeof(result_.su.in);
|
||||
result_.su.in = {};
|
||||
result_.su.in.sin_family = AF_INET;
|
||||
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
|
||||
result_.su.in.sin_len = sizeof(result_.su.in);
|
||||
#endif // HAVE_SOCKADDR_IN_SIN_LEN
|
||||
memcpy(&result_.su.in.sin_addr, *ap, sizeof(result_.su.in.sin_addr));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
for (auto ap = hostent->h_addr_list; *ap; ++ap) {
|
||||
status_ = DNS_STATUS_OK;
|
||||
result_.len = sizeof(result_.su.in6);
|
||||
result_.su.in6 = {};
|
||||
result_.su.in6.sin6_family = AF_INET6;
|
||||
#ifdef HAVE_SOCKADDR_IN6_SIN6_LEN
|
||||
result_.su.in6.sin6_len = sizeof(result_.su.in6);
|
||||
#endif // HAVE_SOCKADDR_IN6_SIN6_LEN
|
||||
memcpy(&result_.su.in6.sin6_addr, *ap, sizeof(result_.su.in6.sin6_addr));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (status_ == DNS_STATUS_OK) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
|
||||
<< util::numeric_name(&result_.su.sa, result_.len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
status_ = DNS_STATUS_ERROR;
|
||||
}
|
||||
|
||||
void DNSResolver::set_complete_cb(CompleteCb cb) {
|
||||
completeCb_ = std::move(cb);
|
||||
}
|
||||
|
||||
CompleteCb DNSResolver::get_complete_cb() const { return completeCb_; }
|
||||
|
||||
} // namespace shrpx
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2016 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SHRPX_DNS_RESOLVER_H
|
||||
#define SHRPX_DNS_RESOLVER_H
|
||||
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <ev.h>
|
||||
#include <ares.h>
|
||||
|
||||
#include "template.h"
|
||||
#include "network.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
enum DNSResolverStatus {
|
||||
// Resolver is in initial status
|
||||
DNS_STATUS_IDLE,
|
||||
// Resolver is currently resolving host name
|
||||
DNS_STATUS_RUNNING,
|
||||
// Resolver successfully resolved host name
|
||||
DNS_STATUS_OK,
|
||||
// Resolver failed to resolve host name
|
||||
DNS_STATUS_ERROR,
|
||||
};
|
||||
|
||||
// Callback function called when host name lookup is finished.
|
||||
// |status| is either DNS_STATUS_OK, or DNS_STATUS_ERROR. If |status|
|
||||
// is DNS_STATUS_OK, |result| points to the resolved address. Note
|
||||
// that port portion of |result| is undefined, and must be initialized
|
||||
// by application. This callback function is not called if name
|
||||
// lookup finishes in DNSResolver::resolve() completely. In this
|
||||
// case, application should call DNSResolver::get_status() to get
|
||||
// current status and result. In other words, callback is called if
|
||||
// get_status() returns DNS_STATUS_RUNNING.
|
||||
using CompleteCb = std::function<void(int status, const Address *result)>;
|
||||
|
||||
// DNSResolver is asynchronous name resolver, backed by c-ares
|
||||
// library.
|
||||
class DNSResolver {
|
||||
public:
|
||||
DNSResolver(struct ev_loop *loop);
|
||||
~DNSResolver();
|
||||
|
||||
// Starts resolving hostname |name|.
|
||||
int resolve(const StringRef &name, int family);
|
||||
// Returns status. If status_ is DNS_STATUS_SUCCESS && |result| is
|
||||
// not nullptr, |*result| is filled.
|
||||
int get_status(Address *result) const;
|
||||
// Sets callback function when name lookup finishes. The callback
|
||||
// function is called in a way that it can destroy this DNSResolver.
|
||||
void set_complete_cb(CompleteCb cb);
|
||||
CompleteCb get_complete_cb() const;
|
||||
|
||||
// Calls these functions when read/write event occurred respectively.
|
||||
int on_read(int fd);
|
||||
int on_write(int fd);
|
||||
int on_timeout();
|
||||
// Calls this function when DNS query finished.
|
||||
void on_result(int staus, hostent *hostent);
|
||||
void reset_timeout();
|
||||
|
||||
void start_rev(int fd);
|
||||
void stop_rev(int fd);
|
||||
void start_wev(int fd);
|
||||
void stop_wev(int fd);
|
||||
|
||||
private:
|
||||
int handle_event(int rfd, int wfd);
|
||||
|
||||
std::vector<std::unique_ptr<ev_io>> revs_, wevs_;
|
||||
Address result_;
|
||||
CompleteCb completeCb_;
|
||||
ev_timer timer_;
|
||||
StringRef name_;
|
||||
struct ev_loop *loop_;
|
||||
// ares_channel is pointer type
|
||||
ares_channel channel_;
|
||||
// AF_INET or AF_INET6. AF_INET for A record lookup, and AF_INET6
|
||||
// for AAAA record lookup.
|
||||
int family_;
|
||||
int status_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_DNS_RESOLVER_H
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2016 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "shrpx_dns_tracker.h"
|
||||
#include "shrpx_config.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
namespace {
|
||||
void gccb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
auto dns_tracker = static_cast<DNSTracker *>(w->data);
|
||||
dns_tracker->gc();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DNSTracker::DNSTracker(struct ev_loop *loop) : loop_(loop) {
|
||||
ev_timer_init(&gc_timer_, gccb, 0., 12_h);
|
||||
gc_timer_.data = this;
|
||||
}
|
||||
|
||||
DNSTracker::~DNSTracker() {
|
||||
ev_timer_stop(loop_, &gc_timer_);
|
||||
|
||||
for (auto &p : ents_) {
|
||||
auto &qlist = p.second.qlist;
|
||||
while (!qlist.empty()) {
|
||||
auto head = qlist.head;
|
||||
qlist.remove(head);
|
||||
head->status = DNS_STATUS_ERROR;
|
||||
head->in_qlist = false;
|
||||
// TODO Not sure we should call callback here, or it is even be
|
||||
// safe to do that.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
|
||||
ImmutableString host, int status,
|
||||
const Address *result) {
|
||||
auto &dnsconf = get_config()->dns;
|
||||
|
||||
auto ent = ResolverEntry{};
|
||||
ent.resolv = std::move(resolv);
|
||||
ent.host = std::move(host);
|
||||
ent.status = status;
|
||||
switch (status) {
|
||||
case DNS_STATUS_ERROR:
|
||||
case DNS_STATUS_OK:
|
||||
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
|
||||
break;
|
||||
}
|
||||
if (result) {
|
||||
ent.result = *result;
|
||||
}
|
||||
return ent;
|
||||
}
|
||||
|
||||
void DNSTracker::update_entry(ResolverEntry &ent,
|
||||
std::unique_ptr<DualDNSResolver> resolv,
|
||||
int status, const Address *result) {
|
||||
auto &dnsconf = get_config()->dns;
|
||||
|
||||
ent.resolv = std::move(resolv);
|
||||
ent.status = status;
|
||||
switch (status) {
|
||||
case DNS_STATUS_ERROR:
|
||||
case DNS_STATUS_OK:
|
||||
ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
|
||||
break;
|
||||
}
|
||||
if (result) {
|
||||
ent.result = *result;
|
||||
}
|
||||
}
|
||||
|
||||
int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
|
||||
int rv;
|
||||
|
||||
auto it = ents_.find(dnsq->host);
|
||||
|
||||
if (it == std::end(ents_)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "DNS entry not found for " << dnsq->host;
|
||||
}
|
||||
|
||||
auto resolv = make_unique<DualDNSResolver>(loop_);
|
||||
auto host_copy =
|
||||
ImmutableString{std::begin(dnsq->host), std::end(dnsq->host)};
|
||||
auto host = StringRef{host_copy};
|
||||
|
||||
rv = resolv->resolve(host);
|
||||
if (rv != 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << host;
|
||||
}
|
||||
|
||||
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
|
||||
DNS_STATUS_ERROR, nullptr));
|
||||
|
||||
start_gc_timer();
|
||||
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
|
||||
rv = resolv->get_status(result);
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR: {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << host;
|
||||
}
|
||||
|
||||
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
|
||||
DNS_STATUS_ERROR, nullptr));
|
||||
|
||||
start_gc_timer();
|
||||
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
case DNS_STATUS_OK: {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
|
||||
<< util::numeric_name(&result->su.sa, result->len);
|
||||
}
|
||||
|
||||
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
|
||||
DNS_STATUS_OK, result));
|
||||
|
||||
start_gc_timer();
|
||||
|
||||
return DNS_STATUS_OK;
|
||||
}
|
||||
case DNS_STATUS_RUNNING: {
|
||||
assert(rv == DNS_STATUS_RUNNING);
|
||||
|
||||
auto p = ents_.emplace(host,
|
||||
make_entry(std::move(resolv), std::move(host_copy),
|
||||
DNS_STATUS_RUNNING, nullptr));
|
||||
|
||||
start_gc_timer();
|
||||
|
||||
auto &ent = (*p.first).second;
|
||||
|
||||
add_to_qlist(ent, dnsq);
|
||||
|
||||
return DNS_STATUS_RUNNING;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
auto &ent = (*it).second;
|
||||
|
||||
if (ent.status != DNS_STATUS_RUNNING && ent.expiry < ev_now(loop_)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "DNS entry found for " << dnsq->host
|
||||
<< ", but it has been expired";
|
||||
}
|
||||
|
||||
auto resolv = make_unique<DualDNSResolver>(loop_);
|
||||
auto host = StringRef{ent.host};
|
||||
|
||||
rv = resolv->resolve(host);
|
||||
if (rv != 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << host;
|
||||
}
|
||||
|
||||
update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
|
||||
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
|
||||
rv = resolv->get_status(result);
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR: {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << host;
|
||||
}
|
||||
|
||||
update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
|
||||
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
case DNS_STATUS_OK: {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
|
||||
<< util::numeric_name(&result->su.sa, result->len);
|
||||
}
|
||||
|
||||
update_entry(ent, nullptr, DNS_STATUS_OK, result);
|
||||
|
||||
return DNS_STATUS_OK;
|
||||
}
|
||||
case DNS_STATUS_RUNNING: {
|
||||
update_entry(ent, std::move(resolv), DNS_STATUS_RUNNING, nullptr);
|
||||
add_to_qlist(ent, dnsq);
|
||||
|
||||
return DNS_STATUS_RUNNING;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
switch (ent.status) {
|
||||
case DNS_STATUS_RUNNING:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Waiting for name lookup complete for " << dnsq->host;
|
||||
}
|
||||
ent.qlist.append(dnsq);
|
||||
dnsq->in_qlist = true;
|
||||
return DNS_STATUS_RUNNING;
|
||||
case DNS_STATUS_ERROR:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup failed for " << dnsq->host << " (cached)";
|
||||
}
|
||||
return DNS_STATUS_ERROR;
|
||||
case DNS_STATUS_OK:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> "
|
||||
<< util::numeric_name(&ent.result.su.sa, ent.result.len);
|
||||
}
|
||||
if (result) {
|
||||
memcpy(result, &ent.result, sizeof(*result));
|
||||
}
|
||||
return DNS_STATUS_OK;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void DNSTracker::add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq) {
|
||||
auto loop = loop_;
|
||||
ent.resolv->set_complete_cb([&ent, loop](int status, const Address *result) {
|
||||
auto &qlist = ent.qlist;
|
||||
while (!qlist.empty()) {
|
||||
auto head = qlist.head;
|
||||
qlist.remove(head);
|
||||
head->status = status;
|
||||
head->in_qlist = false;
|
||||
auto cb = head->cb;
|
||||
cb(status, result);
|
||||
}
|
||||
|
||||
auto &dnsconf = get_config()->dns;
|
||||
|
||||
ent.resolv.reset();
|
||||
ent.status = status;
|
||||
ent.expiry = ev_now(loop) + dnsconf.timeout.cache;
|
||||
if (ent.status == DNS_STATUS_OK) {
|
||||
ent.result = *result;
|
||||
}
|
||||
});
|
||||
ent.qlist.append(dnsq);
|
||||
dnsq->in_qlist = true;
|
||||
}
|
||||
|
||||
void DNSTracker::cancel(DNSQuery *dnsq) {
|
||||
if (!dnsq->in_qlist) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = ents_.find(dnsq->host);
|
||||
if (it == std::end(ents_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &ent = (*it).second;
|
||||
ent.qlist.remove(dnsq);
|
||||
dnsq->in_qlist = false;
|
||||
}
|
||||
|
||||
void DNSTracker::start_gc_timer() {
|
||||
if (ev_is_active(&gc_timer_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ev_timer_again(loop_, &gc_timer_);
|
||||
}
|
||||
|
||||
void DNSTracker::gc() {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Starting removing expired DNS cache entries";
|
||||
}
|
||||
|
||||
auto now = ev_now(loop_);
|
||||
for (auto it = std::begin(ents_); it != std::end(ents_);) {
|
||||
auto &ent = (*it).second;
|
||||
if (ent.expiry >= now) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
it = ents_.erase(it);
|
||||
}
|
||||
|
||||
if (ents_.empty()) {
|
||||
ev_timer_stop(loop_, &gc_timer_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2016 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SHRPX_DNS_TRACKER_H
|
||||
#define SHRPX_DNS_TRACKER_H
|
||||
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "shrpx_dual_dns_resolver.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
struct DNSQuery {
|
||||
DNSQuery(StringRef host, CompleteCb cb)
|
||||
: host(std::move(host)),
|
||||
cb(std::move(cb)),
|
||||
dlnext(nullptr),
|
||||
dlprev(nullptr),
|
||||
status(DNS_STATUS_IDLE),
|
||||
in_qlist(false) {}
|
||||
|
||||
// Host name we lookup for.
|
||||
StringRef host;
|
||||
// Callback function called when name lookup finished. This
|
||||
// callback is not called if name lookup finishes within
|
||||
// DNSTracker::resolve().
|
||||
CompleteCb cb;
|
||||
DNSQuery *dlnext, *dlprev;
|
||||
int status;
|
||||
// true if this object is in linked list ResolverEntry::qlist.
|
||||
bool in_qlist;
|
||||
};
|
||||
|
||||
struct ResolverEntry {
|
||||
// Host name this entry lookups for.
|
||||
ImmutableString host;
|
||||
// DNS resolver. Only non-nullptr if status is DNS_STATUS_RUNNING.
|
||||
std::unique_ptr<DualDNSResolver> resolv;
|
||||
// DNSQuery interested in this name lookup result. The result is
|
||||
// notified to them all.
|
||||
DList<DNSQuery> qlist;
|
||||
// Use the same enum with DNSResolverStatus
|
||||
int status;
|
||||
// result and its expiry time
|
||||
Address result;
|
||||
// time point when cached result expires.
|
||||
ev_tstamp expiry;
|
||||
};
|
||||
|
||||
class DNSTracker {
|
||||
public:
|
||||
DNSTracker(struct ev_loop *loop);
|
||||
~DNSTracker();
|
||||
|
||||
// Lookups host name described in |dnsq|. If name lookup finishes
|
||||
// within this function (either it came from /etc/hosts, host name
|
||||
// is numeric, lookup result is cached, etc), it returns
|
||||
// DNS_STATUS_OK or DNS_STATUS_ERROR. If lookup is successful,
|
||||
// DNS_STATUS_OK is returned, and |result| is filled. If lookup
|
||||
// failed, DNS_STATUS_ERROR is returned. If name lookup is being
|
||||
// done background, it returns DNS_STATUS_RUNNING. Its completion
|
||||
// is notified by calling dnsq->cb.
|
||||
int resolve(Address *result, DNSQuery *dnsq);
|
||||
// Cancels name lookup requested by |dnsq|.
|
||||
void cancel(DNSQuery *dnsq);
|
||||
// Removes expired entries from ents_.
|
||||
void gc();
|
||||
// Starts GC timer.
|
||||
void start_gc_timer();
|
||||
|
||||
private:
|
||||
ResolverEntry make_entry(std::unique_ptr<DualDNSResolver> resolv,
|
||||
ImmutableString host, int status,
|
||||
const Address *result);
|
||||
|
||||
void update_entry(ResolverEntry &ent, std::unique_ptr<DualDNSResolver> resolv,
|
||||
int status, const Address *result);
|
||||
|
||||
void add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq);
|
||||
|
||||
std::map<StringRef, ResolverEntry> ents_;
|
||||
// Periodically iterates ents_, and removes expired entries to avoid
|
||||
// excessive use of memory. Since only backend API can potentially
|
||||
// increase memory consumption, interval could be very long.
|
||||
ev_timer gc_timer_;
|
||||
struct ev_loop *loop_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_DNS_TRACKER_H
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2016 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "shrpx_dual_dns_resolver.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
DualDNSResolver::DualDNSResolver(struct ev_loop *loop)
|
||||
: resolv4_(loop), resolv6_(loop) {
|
||||
auto cb = [this](int, const Address *) {
|
||||
int rv;
|
||||
Address result;
|
||||
|
||||
rv = this->get_status(&result);
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR:
|
||||
case DNS_STATUS_OK:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
auto cb = this->get_complete_cb();
|
||||
cb(rv, &result);
|
||||
};
|
||||
|
||||
resolv4_.set_complete_cb(cb);
|
||||
resolv6_.set_complete_cb(cb);
|
||||
}
|
||||
|
||||
int DualDNSResolver::resolve(const StringRef &host) {
|
||||
int rv4, rv6;
|
||||
rv4 = resolv4_.resolve(host, AF_INET);
|
||||
rv6 = resolv6_.resolve(host, AF_INET6);
|
||||
|
||||
if (rv4 != 0 && rv6 != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CompleteCb DualDNSResolver::get_complete_cb() const { return complete_cb_; }
|
||||
|
||||
void DualDNSResolver::set_complete_cb(CompleteCb cb) { complete_cb_ = cb; }
|
||||
|
||||
int DualDNSResolver::get_status(Address *result) const {
|
||||
int rv4, rv6;
|
||||
rv6 = resolv6_.get_status(result);
|
||||
if (rv6 == DNS_STATUS_OK) {
|
||||
return DNS_STATUS_OK;
|
||||
}
|
||||
rv4 = resolv4_.get_status(result);
|
||||
if (rv4 == DNS_STATUS_OK) {
|
||||
return DNS_STATUS_OK;
|
||||
}
|
||||
if (rv4 == DNS_STATUS_RUNNING || rv6 == DNS_STATUS_RUNNING) {
|
||||
return DNS_STATUS_RUNNING;
|
||||
}
|
||||
if (rv4 == DNS_STATUS_ERROR || rv6 == DNS_STATUS_ERROR) {
|
||||
return DNS_STATUS_ERROR;
|
||||
}
|
||||
return DNS_STATUS_IDLE;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2016 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SHRPX_DUAL_DNS_RESOLVER_H
|
||||
#define SHRPX_DUAL_DNS_RESOLVER_H
|
||||
|
||||
#include "shrpx.h"
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#include "shrpx_dns_resolver.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
// DualDNSResolver performs name resolution for both A and AAAA
|
||||
// records at the same time. The first successful return (or if we
|
||||
// have both successful results, prefer to AAAA) is chosen. This is
|
||||
// wrapper around 2 DNSResolver inside. resolve(), get_status(), and
|
||||
// how CompleteCb is called have the same semantics with DNSResolver.
|
||||
class DualDNSResolver {
|
||||
public:
|
||||
DualDNSResolver(struct ev_loop *loop);
|
||||
|
||||
// Resolves |host|. |host| must be NULL-terminated string.
|
||||
int resolve(const StringRef &host);
|
||||
CompleteCb get_complete_cb() const;
|
||||
void set_complete_cb(CompleteCb cb);
|
||||
int get_status(Address *result) const;
|
||||
|
||||
private:
|
||||
// For A record
|
||||
DNSResolver resolv4_;
|
||||
// For AAAA record
|
||||
DNSResolver resolv6_;
|
||||
CompleteCb complete_cb_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_DUAL_DNS_RESOLVER_H
|
|
@ -95,7 +95,7 @@ void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
|||
SSLOG(INFO, http2session) << "SETTINGS timeout";
|
||||
}
|
||||
|
||||
downstream_failure(http2session->get_addr());
|
||||
downstream_failure(http2session->get_addr(), http2session->get_raddr());
|
||||
|
||||
if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) {
|
||||
delete http2session;
|
||||
|
@ -202,6 +202,7 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
|||
group_(group),
|
||||
addr_(addr),
|
||||
session_(nullptr),
|
||||
raddr_(nullptr),
|
||||
state_(DISCONNECTED),
|
||||
connection_check_state_(CONNECTION_CHECK_NONE),
|
||||
freelist_zone_(FREELIST_ZONE_NONE) {
|
||||
|
@ -244,6 +245,11 @@ int Http2Session::disconnect(bool hard) {
|
|||
|
||||
wb_.reset();
|
||||
|
||||
if (dns_query_) {
|
||||
auto dns_tracker = worker_->get_dns_tracker();
|
||||
dns_tracker->cancel(dns_query_.get());
|
||||
}
|
||||
|
||||
conn_.rlimit.stopw();
|
||||
conn_.wlimit.stopw();
|
||||
|
||||
|
@ -302,12 +308,47 @@ int Http2Session::disconnect(bool hard) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int Http2Session::resolve_name() {
|
||||
int rv;
|
||||
|
||||
auto dns_query = make_unique<DNSQuery>(
|
||||
addr_->host, [this](int status, const Address *result) {
|
||||
int rv;
|
||||
|
||||
if (status == DNS_STATUS_OK) {
|
||||
*resolved_addr_ = *result;
|
||||
util::set_port(*this->resolved_addr_, this->addr_->port);
|
||||
}
|
||||
|
||||
rv = this->initiate_connection();
|
||||
if (rv != 0) {
|
||||
delete this;
|
||||
}
|
||||
});
|
||||
resolved_addr_ = make_unique<Address>();
|
||||
auto dns_tracker = worker_->get_dns_tracker();
|
||||
rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR:
|
||||
return -1;
|
||||
case DNS_STATUS_RUNNING:
|
||||
dns_query_ = std::move(dns_query);
|
||||
state_ = RESOLVING_NAME;
|
||||
return 0;
|
||||
case DNS_STATUS_OK:
|
||||
util::set_port(*resolved_addr_, addr_->port);
|
||||
return 0;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
int Http2Session::initiate_connection() {
|
||||
int rv = 0;
|
||||
|
||||
auto worker_blocker = worker_->get_connect_blocker();
|
||||
|
||||
if (state_ == DISCONNECTED) {
|
||||
if (state_ == DISCONNECTED || state_ == RESOLVING_NAME) {
|
||||
if (worker_blocker->blocked()) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
SSLOG(INFO, this)
|
||||
|
@ -350,6 +391,8 @@ int Http2Session::initiate_connection() {
|
|||
return -1;
|
||||
}
|
||||
|
||||
raddr_ = &proxy.addr;
|
||||
|
||||
worker_blocker->on_success();
|
||||
|
||||
ev_io_set(&conn_.rev, conn_.fd, EV_READ);
|
||||
|
@ -374,13 +417,17 @@ int Http2Session::initiate_connection() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED) {
|
||||
if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED ||
|
||||
state_ == RESOLVING_NAME) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
if (state_ != RESOLVING_NAME) {
|
||||
SSLOG(INFO, this) << "Connecting to downstream server";
|
||||
}
|
||||
}
|
||||
if (addr_->tls) {
|
||||
assert(ssl_ctx_);
|
||||
|
||||
if (state_ != RESOLVING_NAME) {
|
||||
auto ssl = ssl::create_ssl(ssl_ctx_);
|
||||
if (!ssl) {
|
||||
return -1;
|
||||
|
@ -405,18 +452,45 @@ int Http2Session::initiate_connection() {
|
|||
SSL_set_session(conn_.tls.ssl, tls_session);
|
||||
SSL_SESSION_free(tls_session);
|
||||
}
|
||||
}
|
||||
|
||||
if (state_ == DISCONNECTED) {
|
||||
if (addr_->dns) {
|
||||
rv = resolve_name();
|
||||
if (rv != 0) {
|
||||
downstream_failure(addr_, nullptr);
|
||||
return -1;
|
||||
}
|
||||
if (state_ == RESOLVING_NAME) {
|
||||
return 0;
|
||||
}
|
||||
raddr_ = resolved_addr_.get();
|
||||
} else {
|
||||
raddr_ = &addr_->addr;
|
||||
}
|
||||
}
|
||||
|
||||
if (state_ == RESOLVING_NAME) {
|
||||
if (dns_query_->status == DNS_STATUS_ERROR) {
|
||||
downstream_failure(addr_, nullptr);
|
||||
return -1;
|
||||
}
|
||||
assert(dns_query_->status == DNS_STATUS_OK);
|
||||
state_ = DISCONNECTED;
|
||||
dns_query_.reset();
|
||||
raddr_ = resolved_addr_.get();
|
||||
}
|
||||
|
||||
// If state_ == PROXY_CONNECTED, we has connected to the proxy
|
||||
// using conn_.fd and tunnel has been established.
|
||||
if (state_ == DISCONNECTED) {
|
||||
assert(conn_.fd == -1);
|
||||
|
||||
conn_.fd =
|
||||
util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
|
||||
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
|
||||
if (conn_.fd == -1) {
|
||||
auto error = errno;
|
||||
SSLOG(WARN, this)
|
||||
<< "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
|
||||
<< "socket() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
worker_blocker->on_failure();
|
||||
|
@ -427,15 +501,14 @@ int Http2Session::initiate_connection() {
|
|||
|
||||
rv = connect(conn_.fd,
|
||||
// TODO maybe not thread-safe?
|
||||
const_cast<sockaddr *>(&addr_->addr.su.sa),
|
||||
addr_->addr.len);
|
||||
const_cast<sockaddr *>(&raddr_->su.sa), raddr_->len);
|
||||
if (rv != 0 && errno != EINPROGRESS) {
|
||||
auto error = errno;
|
||||
SSLOG(WARN, this) << "connect() failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr)
|
||||
SSLOG(WARN, this)
|
||||
<< "connect() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -445,17 +518,44 @@ int Http2Session::initiate_connection() {
|
|||
|
||||
conn_.prepare_client_handshake();
|
||||
} else {
|
||||
if (state_ == DISCONNECTED) {
|
||||
// Without TLS and proxy.
|
||||
if (addr_->dns) {
|
||||
rv = resolve_name();
|
||||
if (rv != 0) {
|
||||
downstream_failure(addr_, nullptr);
|
||||
return -1;
|
||||
}
|
||||
if (state_ == RESOLVING_NAME) {
|
||||
return 0;
|
||||
}
|
||||
raddr_ = resolved_addr_.get();
|
||||
} else {
|
||||
raddr_ = &addr_->addr;
|
||||
}
|
||||
}
|
||||
|
||||
if (state_ == RESOLVING_NAME) {
|
||||
if (dns_query_->status == DNS_STATUS_ERROR) {
|
||||
downstream_failure(addr_, nullptr);
|
||||
return -1;
|
||||
}
|
||||
assert(dns_query_->status == DNS_STATUS_OK);
|
||||
state_ = DISCONNECTED;
|
||||
dns_query_.reset();
|
||||
raddr_ = resolved_addr_.get();
|
||||
}
|
||||
|
||||
if (state_ == DISCONNECTED) {
|
||||
// Without TLS and proxy.
|
||||
assert(conn_.fd == -1);
|
||||
|
||||
conn_.fd =
|
||||
util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
|
||||
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
|
||||
|
||||
if (conn_.fd == -1) {
|
||||
auto error = errno;
|
||||
SSLOG(WARN, this)
|
||||
<< "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
|
||||
<< "socket() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
worker_blocker->on_failure();
|
||||
|
@ -464,15 +564,15 @@ int Http2Session::initiate_connection() {
|
|||
|
||||
worker_blocker->on_success();
|
||||
|
||||
rv = connect(conn_.fd, const_cast<sockaddr *>(&addr_->addr.su.sa),
|
||||
addr_->addr.len);
|
||||
rv = connect(conn_.fd, const_cast<sockaddr *>(&raddr_->su.sa),
|
||||
raddr_->len);
|
||||
if (rv != 0 && errno != EINPROGRESS) {
|
||||
auto error = errno;
|
||||
SSLOG(WARN, this) << "connect() failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr)
|
||||
SSLOG(WARN, this)
|
||||
<< "connect() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1544,7 +1644,7 @@ int Http2Session::connection_made() {
|
|||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
|
||||
if (!next_proto) {
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1553,7 +1653,7 @@ int Http2Session::connection_made() {
|
|||
SSLOG(INFO, this) << "Negotiated next protocol: " << proto;
|
||||
}
|
||||
if (!util::check_h2_is_selected(proto)) {
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -1842,10 +1942,10 @@ int Http2Session::connected() {
|
|||
auto sock_error = util::get_socket_error(conn_.fd);
|
||||
if (sock_error != 0) {
|
||||
SSLOG(WARN, this) << "Backend connect failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr)
|
||||
<< util::to_numeric_addr(raddr_)
|
||||
<< ": errno=" << sock_error;
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -1955,7 +2055,7 @@ int Http2Session::tls_handshake() {
|
|||
}
|
||||
|
||||
if (rv < 0) {
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -1965,8 +2065,8 @@ int Http2Session::tls_handshake() {
|
|||
}
|
||||
|
||||
if (!get_config()->tls.insecure &&
|
||||
ssl::check_cert(conn_.tls.ssl, addr_) != 0) {
|
||||
downstream_failure(addr_);
|
||||
ssl::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -1974,8 +2074,8 @@ int Http2Session::tls_handshake() {
|
|||
if (!SSL_session_reused(conn_.tls.ssl)) {
|
||||
auto tls_session = SSL_get0_session(conn_.tls.ssl);
|
||||
if (tls_session) {
|
||||
ssl::try_cache_tls_session(addr_->tls_session_cache, addr_->addr,
|
||||
tls_session, ev_now(conn_.loop));
|
||||
ssl::try_cache_tls_session(addr_->tls_session_cache, *raddr_, tls_session,
|
||||
ev_now(conn_.loop));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2262,9 +2362,9 @@ void Http2Session::on_timeout() {
|
|||
}
|
||||
case CONNECTING: {
|
||||
SSLOG(WARN, this) << "Connect time out; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr);
|
||||
<< util::to_numeric_addr(raddr_);
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2284,4 +2384,6 @@ void Http2Session::check_retire() {
|
|||
signal_write();
|
||||
}
|
||||
|
||||
const Address *Http2Session::get_raddr() const { return raddr_; }
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -50,6 +50,7 @@ class Http2DownstreamConnection;
|
|||
class Worker;
|
||||
struct DownstreamAddrGroup;
|
||||
struct DownstreamAddr;
|
||||
struct DNSQuery;
|
||||
|
||||
struct StreamData {
|
||||
StreamData *dlnext, *dlprev;
|
||||
|
@ -81,6 +82,7 @@ public:
|
|||
// associated ClientHandlers will be deleted.
|
||||
int disconnect(bool hard = false);
|
||||
int initiate_connection();
|
||||
int resolve_name();
|
||||
|
||||
void add_downstream_connection(Http2DownstreamConnection *dconn);
|
||||
void remove_downstream_connection(Http2DownstreamConnection *dconn);
|
||||
|
@ -203,6 +205,9 @@ public:
|
|||
// shutdown the connection.
|
||||
void check_retire();
|
||||
|
||||
// Returns address used to connect to backend. Could be nullptr.
|
||||
const Address *get_raddr() const;
|
||||
|
||||
enum {
|
||||
// Disconnected
|
||||
DISCONNECTED,
|
||||
|
@ -218,6 +223,8 @@ public:
|
|||
CONNECTED,
|
||||
// Connection is started to fail
|
||||
CONNECT_FAILING,
|
||||
// Resolving host name
|
||||
RESOLVING_NAME,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -259,6 +266,13 @@ private:
|
|||
// Address of remote endpoint
|
||||
DownstreamAddr *addr_;
|
||||
nghttp2_session *session_;
|
||||
// Actual remote address used to contact backend. This is initially
|
||||
// nullptr, and may point to either &addr_->addr,
|
||||
// resolved_addr_.get(), or HTTP proxy's address structure.
|
||||
const Address *raddr_;
|
||||
// Resolved IP address if dns parameter is used
|
||||
std::unique_ptr<Address> resolved_addr_;
|
||||
std::unique_ptr<DNSQuery> dns_query_;
|
||||
int state_;
|
||||
int connection_check_state_;
|
||||
int freelist_zone_;
|
||||
|
|
|
@ -75,11 +75,12 @@ void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
|||
auto conn = static_cast<Connection *>(w->data);
|
||||
auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
|
||||
auto addr = dconn->get_addr();
|
||||
auto raddr = dconn->get_raddr();
|
||||
|
||||
DCLOG(WARN, dconn) << "Connect time out; addr="
|
||||
<< util::to_numeric_addr(&addr->addr);
|
||||
<< util::to_numeric_addr(raddr);
|
||||
|
||||
downstream_failure(addr);
|
||||
downstream_failure(addr, raddr);
|
||||
|
||||
auto downstream = dconn->get_downstream();
|
||||
auto upstream = downstream->get_upstream();
|
||||
|
@ -182,13 +183,34 @@ HttpDownstreamConnection::~HttpDownstreamConnection() {
|
|||
if (LOG_ENABLED(INFO)) {
|
||||
DCLOG(INFO, this) << "Deleted";
|
||||
}
|
||||
|
||||
if (dns_query_) {
|
||||
auto dns_tracker = worker_->get_dns_tracker();
|
||||
dns_tracker->cancel(dns_query_.get());
|
||||
}
|
||||
}
|
||||
|
||||
int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
||||
int rv;
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
|
||||
}
|
||||
|
||||
downstream_ = downstream;
|
||||
|
||||
rv = initiate_connection();
|
||||
if (rv != 0) {
|
||||
downstream_ = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpDownstreamConnection::initiate_connection() {
|
||||
int rv;
|
||||
|
||||
auto worker_blocker = worker_->get_connect_blocker();
|
||||
if (worker_blocker->blocked()) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -212,42 +234,136 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
|||
shared_addr->affinity == AFFINITY_NONE ? shared_addr->next : temp_idx;
|
||||
auto end = next_downstream;
|
||||
for (;;) {
|
||||
auto &addr = addrs[next_downstream];
|
||||
auto check_dns_result = dns_query_.get() != nullptr;
|
||||
|
||||
DownstreamAddr *addr;
|
||||
if (check_dns_result) {
|
||||
addr = addr_;
|
||||
addr_ = nullptr;
|
||||
assert(addr);
|
||||
assert(addr->dns);
|
||||
} else {
|
||||
assert(addr_ == nullptr);
|
||||
addr = &addrs[next_downstream];
|
||||
|
||||
if (++next_downstream >= addrs.size()) {
|
||||
next_downstream = 0;
|
||||
}
|
||||
|
||||
if (addr.proto != PROTO_HTTP1) {
|
||||
if (addr->proto != PROTO_HTTP1) {
|
||||
if (end == next_downstream) {
|
||||
return SHRPX_ERR_NETWORK;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto &connect_blocker = addr.connect_blocker;
|
||||
auto &connect_blocker = addr->connect_blocker;
|
||||
|
||||
if (connect_blocker->blocked()) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DCLOG(INFO, this) << "Backend server "
|
||||
<< util::to_numeric_addr(&addr.addr)
|
||||
<< " was not available temporarily";
|
||||
DCLOG(INFO, this) << "Backend server " << addr->host << ":"
|
||||
<< addr->port << " was not available temporarily";
|
||||
}
|
||||
|
||||
if (end == next_downstream) {
|
||||
if (check_dns_result) {
|
||||
dns_query_.reset();
|
||||
} else if (end == next_downstream) {
|
||||
return SHRPX_ERR_NETWORK;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
conn_.fd = util::create_nonblock_socket(addr.addr.su.storage.ss_family);
|
||||
Address *raddr;
|
||||
|
||||
if (addr->dns) {
|
||||
if (!check_dns_result) {
|
||||
auto dns_query = make_unique<DNSQuery>(
|
||||
addr->host, [this](int status, const Address *result) {
|
||||
int rv;
|
||||
|
||||
if (status == DNS_STATUS_OK) {
|
||||
*this->resolved_addr_ = *result;
|
||||
}
|
||||
|
||||
rv = this->initiate_connection();
|
||||
if (rv != 0) {
|
||||
// This callback destroys |this|.
|
||||
auto downstream = this->downstream_;
|
||||
auto upstream = downstream->get_upstream();
|
||||
auto handler = upstream->get_client_handler();
|
||||
|
||||
downstream->pop_downstream_connection();
|
||||
|
||||
auto ndconn = handler->get_downstream_connection(downstream);
|
||||
if (ndconn) {
|
||||
if (downstream->attach_downstream_connection(
|
||||
std::move(ndconn)) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||
|
||||
if (upstream->on_downstream_abort_request(downstream, 503) !=
|
||||
0) {
|
||||
delete handler;
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
auto dns_tracker = worker_->get_dns_tracker();
|
||||
|
||||
if (!resolved_addr_) {
|
||||
resolved_addr_ = make_unique<Address>();
|
||||
}
|
||||
rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR:
|
||||
downstream_failure(addr, nullptr);
|
||||
if (end == next_downstream) {
|
||||
return SHRPX_ERR_NETWORK;
|
||||
}
|
||||
continue;
|
||||
case DNS_STATUS_RUNNING:
|
||||
dns_query_ = std::move(dns_query);
|
||||
// Remember current addr
|
||||
addr_ = addr;
|
||||
return 0;
|
||||
case DNS_STATUS_OK:
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
} else {
|
||||
switch (dns_query_->status) {
|
||||
case DNS_STATUS_ERROR:
|
||||
dns_query_.reset();
|
||||
downstream_failure(addr, nullptr);
|
||||
continue;
|
||||
case DNS_STATUS_OK:
|
||||
dns_query_.reset();
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
raddr = resolved_addr_.get();
|
||||
util::set_port(*resolved_addr_, addr->port);
|
||||
} else {
|
||||
raddr = &addr->addr;
|
||||
}
|
||||
|
||||
conn_.fd = util::create_nonblock_socket(raddr->su.storage.ss_family);
|
||||
|
||||
if (conn_.fd == -1) {
|
||||
auto error = errno;
|
||||
DCLOG(WARN, this) << "socket() failed; addr="
|
||||
<< util::to_numeric_addr(&addr.addr)
|
||||
<< util::to_numeric_addr(raddr)
|
||||
<< ", errno=" << error;
|
||||
|
||||
worker_blocker->on_failure();
|
||||
|
@ -257,20 +373,19 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
|||
|
||||
worker_blocker->on_success();
|
||||
|
||||
int rv;
|
||||
rv = connect(conn_.fd, &addr.addr.su.sa, addr.addr.len);
|
||||
rv = connect(conn_.fd, &raddr->su.sa, raddr->len);
|
||||
if (rv != 0 && errno != EINPROGRESS) {
|
||||
auto error = errno;
|
||||
DCLOG(WARN, this) << "connect() failed; addr="
|
||||
<< util::to_numeric_addr(&addr.addr)
|
||||
<< util::to_numeric_addr(raddr)
|
||||
<< ", errno=" << error;
|
||||
|
||||
downstream_failure(&addr);
|
||||
downstream_failure(addr, raddr);
|
||||
|
||||
close(conn_.fd);
|
||||
conn_.fd = -1;
|
||||
|
||||
if (end == next_downstream) {
|
||||
if (!check_dns_result && end == next_downstream) {
|
||||
return SHRPX_ERR_NETWORK;
|
||||
}
|
||||
|
||||
|
@ -282,7 +397,8 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
|||
DCLOG(INFO, this) << "Connecting to downstream server";
|
||||
}
|
||||
|
||||
addr_ = &addr;
|
||||
addr_ = addr;
|
||||
raddr_ = raddr;
|
||||
|
||||
if (addr_->tls) {
|
||||
assert(ssl_ctx_);
|
||||
|
@ -334,8 +450,6 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
|||
ev_set_cb(&conn_.rev, readcb);
|
||||
}
|
||||
|
||||
downstream_ = downstream;
|
||||
|
||||
http_parser_init(&response_htp_, HTTP_RESPONSE);
|
||||
response_htp_.data = downstream_;
|
||||
|
||||
|
@ -1031,7 +1145,7 @@ int HttpDownstreamConnection::tls_handshake() {
|
|||
}
|
||||
|
||||
if (rv < 0) {
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -1041,8 +1155,8 @@ int HttpDownstreamConnection::tls_handshake() {
|
|||
}
|
||||
|
||||
if (!get_config()->tls.insecure &&
|
||||
ssl::check_cert(conn_.tls.ssl, addr_) != 0) {
|
||||
downstream_failure(addr_);
|
||||
ssl::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -1050,7 +1164,7 @@ int HttpDownstreamConnection::tls_handshake() {
|
|||
if (!SSL_session_reused(conn_.tls.ssl)) {
|
||||
auto session = SSL_get0_session(conn_.tls.ssl);
|
||||
if (session) {
|
||||
ssl::try_cache_tls_session(addr_->tls_session_cache, addr_->addr, session,
|
||||
ssl::try_cache_tls_session(addr_->tls_session_cache, *raddr_, session,
|
||||
ev_now(conn_.loop));
|
||||
}
|
||||
}
|
||||
|
@ -1215,10 +1329,10 @@ int HttpDownstreamConnection::connected() {
|
|||
conn_.wlimit.stopw();
|
||||
|
||||
DCLOG(WARN, this) << "Backend connect failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr)
|
||||
<< util::to_numeric_addr(raddr_)
|
||||
<< ": errno=" << sock_error;
|
||||
|
||||
downstream_failure(addr_);
|
||||
downstream_failure(addr_, raddr_);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -1282,4 +1396,6 @@ DownstreamAddr *HttpDownstreamConnection::get_addr() const { return addr_; }
|
|||
|
||||
bool HttpDownstreamConnection::poolable() const { return !group_->retired; }
|
||||
|
||||
const Address *HttpDownstreamConnection::get_raddr() const { return raddr_; }
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -39,6 +39,7 @@ class DownstreamConnectionPool;
|
|||
class Worker;
|
||||
struct DownstreamAddrGroup;
|
||||
struct DownstreamAddr;
|
||||
struct DNSQuery;
|
||||
|
||||
class HttpDownstreamConnection : public DownstreamConnection {
|
||||
public:
|
||||
|
@ -68,6 +69,8 @@ public:
|
|||
get_downstream_addr_group() const;
|
||||
virtual DownstreamAddr *get_addr() const;
|
||||
|
||||
int initiate_connection();
|
||||
|
||||
int read_clear();
|
||||
int write_clear();
|
||||
int read_tls();
|
||||
|
@ -80,6 +83,9 @@ public:
|
|||
void signal_write();
|
||||
int actual_signal_write();
|
||||
|
||||
// Returns address used to connect to backend. Could be nullptr.
|
||||
const Address *get_raddr() const;
|
||||
|
||||
int noop();
|
||||
|
||||
private:
|
||||
|
@ -92,6 +98,13 @@ private:
|
|||
std::shared_ptr<DownstreamAddrGroup> group_;
|
||||
// Address of remote endpoint
|
||||
DownstreamAddr *addr_;
|
||||
// Actual remote address used to contact backend. This is initially
|
||||
// nullptr, and may point to either &addr_->addr, or
|
||||
// resolved_addr_.get().
|
||||
const Address *raddr_;
|
||||
// Resolved IP address if dns parameter is used
|
||||
std::unique_ptr<Address> resolved_addr_;
|
||||
std::unique_ptr<DNSQuery> dns_query_;
|
||||
IOControl ioctrl_;
|
||||
http_parser response_htp_;
|
||||
ssize_t initial_addr_idx_;
|
||||
|
|
|
@ -114,6 +114,7 @@ LiveCheck::LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
|
|||
ssl_ctx_(ssl_ctx),
|
||||
addr_(addr),
|
||||
session_(nullptr),
|
||||
raddr_(nullptr),
|
||||
success_count_(0),
|
||||
fail_count_(0),
|
||||
settings_ack_received_(false),
|
||||
|
@ -134,6 +135,16 @@ LiveCheck::~LiveCheck() {
|
|||
}
|
||||
|
||||
void LiveCheck::disconnect() {
|
||||
if (dns_query_) {
|
||||
auto dns_tracker = worker_->get_dns_tracker();
|
||||
|
||||
dns_tracker->cancel(dns_query_.get());
|
||||
}
|
||||
|
||||
dns_query_.reset();
|
||||
// We can reuse resolved_addr_
|
||||
raddr_ = nullptr;
|
||||
|
||||
conn_.rlimit.stopw();
|
||||
conn_.wlimit.stopw();
|
||||
|
||||
|
@ -190,7 +201,7 @@ int LiveCheck::initiate_connection() {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (addr_->tls) {
|
||||
if (!dns_query_ && addr_->tls) {
|
||||
assert(ssl_ctx_);
|
||||
|
||||
auto ssl = ssl::create_ssl(ssl_ctx_);
|
||||
|
@ -212,20 +223,71 @@ int LiveCheck::initiate_connection() {
|
|||
conn_.set_ssl(ssl);
|
||||
}
|
||||
|
||||
conn_.fd = util::create_nonblock_socket(addr_->addr.su.storage.ss_family);
|
||||
if (addr_->dns) {
|
||||
if (!dns_query_) {
|
||||
auto dns_query = make_unique<DNSQuery>(
|
||||
addr_->host, [this](int status, const Address *result) {
|
||||
int rv;
|
||||
|
||||
if (status == DNS_STATUS_OK) {
|
||||
*this->resolved_addr_ = *result;
|
||||
}
|
||||
rv = this->initiate_connection();
|
||||
if (rv != 0) {
|
||||
this->on_failure();
|
||||
}
|
||||
});
|
||||
auto dns_tracker = worker_->get_dns_tracker();
|
||||
|
||||
if (!resolved_addr_) {
|
||||
resolved_addr_ = make_unique<Address>();
|
||||
}
|
||||
|
||||
rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
|
||||
switch (rv) {
|
||||
case DNS_STATUS_ERROR:
|
||||
return -1;
|
||||
case DNS_STATUS_RUNNING:
|
||||
dns_query_ = std::move(dns_query);
|
||||
return 0;
|
||||
case DNS_STATUS_OK:
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
} else {
|
||||
switch (dns_query_->status) {
|
||||
case DNS_STATUS_ERROR:
|
||||
dns_query_.reset();
|
||||
return -1;
|
||||
case DNS_STATUS_OK:
|
||||
dns_query_.reset();
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
util::set_port(*resolved_addr_, addr_->port);
|
||||
raddr_ = resolved_addr_.get();
|
||||
} else {
|
||||
raddr_ = &addr_->addr;
|
||||
}
|
||||
|
||||
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
|
||||
|
||||
if (conn_.fd == -1) {
|
||||
auto error = errno;
|
||||
LOG(WARN) << "socket() failed; addr=" << util::to_numeric_addr(&addr_->addr)
|
||||
LOG(WARN) << "socket() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = connect(conn_.fd, &addr_->addr.su.sa, addr_->addr.len);
|
||||
rv = connect(conn_.fd, &raddr_->su.sa, raddr_->len);
|
||||
if (rv != 0 && errno != EINPROGRESS) {
|
||||
auto error = errno;
|
||||
LOG(WARN) << "connect() failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr) << ", errno=" << error;
|
||||
LOG(WARN) << "connect() failed; addr=" << util::to_numeric_addr(raddr_)
|
||||
<< ", errno=" << error;
|
||||
|
||||
close(conn_.fd);
|
||||
conn_.fd = -1;
|
||||
|
@ -269,8 +331,7 @@ int LiveCheck::connected() {
|
|||
if (sock_error != 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Backend connect failed; addr="
|
||||
<< util::to_numeric_addr(&addr_->addr)
|
||||
<< ": errno=" << sock_error;
|
||||
<< util::to_numeric_addr(raddr_) << ": errno=" << sock_error;
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
@ -334,15 +395,15 @@ int LiveCheck::tls_handshake() {
|
|||
}
|
||||
|
||||
if (!get_config()->tls.insecure &&
|
||||
ssl::check_cert(conn_.tls.ssl, addr_) != 0) {
|
||||
ssl::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!SSL_session_reused(conn_.tls.ssl)) {
|
||||
auto tls_session = SSL_get0_session(conn_.tls.ssl);
|
||||
if (tls_session) {
|
||||
ssl::try_cache_tls_session(addr_->tls_session_cache, addr_->addr,
|
||||
tls_session, ev_now(conn_.loop));
|
||||
ssl::try_cache_tls_session(addr_->tls_session_cache, *raddr_, tls_session,
|
||||
ev_now(conn_.loop));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -601,7 +662,7 @@ void LiveCheck::on_failure() {
|
|||
++fail_count_;
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Liveness check for " << util::to_numeric_addr(&addr_->addr)
|
||||
LOG(INFO) << "Liveness check for " << addr_->host << ":" << addr_->port
|
||||
<< " failed " << fail_count_ << " time(s) in a row";
|
||||
}
|
||||
|
||||
|
@ -615,7 +676,7 @@ void LiveCheck::on_success() {
|
|||
fail_count_ = 0;
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Liveness check for " << util::to_numeric_addr(&addr_->addr)
|
||||
LOG(INFO) << "Liveness check for " << addr_->host << ":" << addr_->port
|
||||
<< " succeeded " << success_count_ << " time(s) in a row";
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace shrpx {
|
|||
|
||||
class Worker;
|
||||
struct DownstreamAddr;
|
||||
struct DNSQuery;
|
||||
|
||||
class LiveCheck {
|
||||
public:
|
||||
|
@ -102,6 +103,13 @@ private:
|
|||
// Address of remote endpoint
|
||||
DownstreamAddr *addr_;
|
||||
nghttp2_session *session_;
|
||||
// Actual remote address used to contact backend. This is initially
|
||||
// nullptr, and may point to either &addr_->addr, or
|
||||
// resolved_addr_.get().
|
||||
const Address *raddr_;
|
||||
// Resolved IP address if dns parameter is used
|
||||
std::unique_ptr<Address> resolved_addr_;
|
||||
std::unique_ptr<DNSQuery> dns_query_;
|
||||
// The number of successful connect attempt in a row.
|
||||
size_t success_count_;
|
||||
// The number of unsuccessful connect attempt in a row.
|
||||
|
|
|
@ -1171,10 +1171,10 @@ int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int check_cert(SSL *ssl, const DownstreamAddr *addr) {
|
||||
int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr) {
|
||||
auto hostname =
|
||||
addr->sni.empty() ? StringRef{addr->host} : StringRef{addr->sni};
|
||||
return check_cert(ssl, &addr->addr, hostname);
|
||||
return check_cert(ssl, raddr, hostname);
|
||||
}
|
||||
|
||||
CertLookupTree::CertLookupTree() {}
|
||||
|
|
|
@ -104,7 +104,10 @@ ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
|
|||
|
||||
// Check peer's certificate against given |address| and |host|.
|
||||
int check_cert(SSL *ssl, const Address *addr, const StringRef &host);
|
||||
int check_cert(SSL *ssl, const DownstreamAddr *addr);
|
||||
// Check peer's certificate against given host name described in
|
||||
// |addr| and numeric address in |raddr|. Note that |raddr| might not
|
||||
// point to &addr->addr.
|
||||
int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr);
|
||||
|
||||
struct WildcardRevPrefix {
|
||||
WildcardRevPrefix(const StringRef &prefix, size_t idx)
|
||||
|
|
|
@ -92,7 +92,7 @@ bool match_shared_downstream_addr(
|
|||
auto &b = rhs->addrs[i];
|
||||
if (a.host == b.host && a.port == b.port && a.host_unix == b.host_unix &&
|
||||
a.proto == b.proto && a.tls == b.tls && a.sni == b.sni &&
|
||||
a.fall == b.fall && a.rise == b.rise) {
|
||||
a.fall == b.fall && a.rise == b.rise && a.dns == b.dns) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
|||
std::shared_ptr<DownstreamConfig> downstreamconf)
|
||||
: randgen_(rd()),
|
||||
worker_stat_{},
|
||||
dns_tracker_(loop),
|
||||
loop_(loop),
|
||||
sv_ssl_ctx_(sv_ssl_ctx),
|
||||
cl_ssl_ctx_(cl_ssl_ctx),
|
||||
|
@ -209,6 +210,7 @@ void Worker::replace_downstream_config(
|
|||
dst_addr.sni = make_string_ref(shared_addr->balloc, src_addr.sni);
|
||||
dst_addr.fall = src_addr.fall;
|
||||
dst_addr.rise = src_addr.rise;
|
||||
dst_addr.dns = src_addr.dns;
|
||||
|
||||
auto shared_addr_ptr = shared_addr.get();
|
||||
|
||||
|
@ -490,6 +492,8 @@ ConnectionHandler *Worker::get_connection_handler() const {
|
|||
return conn_handler_;
|
||||
}
|
||||
|
||||
DNSTracker *Worker::get_dns_tracker() { return &dns_tracker_; }
|
||||
|
||||
namespace {
|
||||
size_t match_downstream_addr_group_host(
|
||||
const RouterConfig &routerconf, const StringRef &host,
|
||||
|
@ -635,7 +639,7 @@ size_t match_downstream_addr_group(
|
|||
catch_all, balloc);
|
||||
}
|
||||
|
||||
void downstream_failure(DownstreamAddr *addr) {
|
||||
void downstream_failure(DownstreamAddr *addr, const Address *raddr) {
|
||||
const auto &connect_blocker = addr->connect_blocker;
|
||||
|
||||
if (connect_blocker->in_offline()) {
|
||||
|
@ -651,8 +655,15 @@ void downstream_failure(DownstreamAddr *addr) {
|
|||
auto fail_count = connect_blocker->get_fail_count();
|
||||
|
||||
if (fail_count >= addr->fall) {
|
||||
LOG(WARN) << "Could not connect to " << util::to_numeric_addr(&addr->addr)
|
||||
<< " " << fail_count << " times in a row; considered as offline";
|
||||
if (raddr) {
|
||||
LOG(WARN) << "Could not connect to " << util::to_numeric_addr(raddr)
|
||||
<< " " << fail_count
|
||||
<< " times in a row; considered as offline";
|
||||
} else {
|
||||
LOG(WARN) << "Could not connect to " << addr->host << ":" << addr->port
|
||||
<< " " << fail_count
|
||||
<< " times in a row; considered as offline";
|
||||
}
|
||||
|
||||
connect_blocker->offline();
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "shrpx_ssl.h"
|
||||
#include "shrpx_live_check.h"
|
||||
#include "shrpx_connect_blocker.h"
|
||||
#include "shrpx_dns_tracker.h"
|
||||
#include "allocator.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
@ -112,6 +113,8 @@ struct DownstreamAddr {
|
|||
shrpx_proto proto;
|
||||
// true if TLS is used in this backend
|
||||
bool tls;
|
||||
// true if dynamic DNS is enabled
|
||||
bool dns;
|
||||
};
|
||||
|
||||
// Simplified weighted fair queuing. Actually we don't use queue here
|
||||
|
@ -263,6 +266,8 @@ public:
|
|||
|
||||
ConnectionHandler *get_connection_handler() const;
|
||||
|
||||
DNSTracker *get_dns_tracker();
|
||||
|
||||
private:
|
||||
#ifndef NOTHREADS
|
||||
std::future<void> fut_;
|
||||
|
@ -275,6 +280,7 @@ private:
|
|||
ev_timer proc_wev_timer_;
|
||||
MemchunkPool mcpool_;
|
||||
WorkerStat worker_stat_;
|
||||
DNSTracker dns_tracker_;
|
||||
|
||||
std::shared_ptr<DownstreamConfig> downstreamconf_;
|
||||
std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_;
|
||||
|
@ -314,7 +320,10 @@ size_t match_downstream_addr_group(
|
|||
const std::vector<std::shared_ptr<DownstreamAddrGroup>> &groups,
|
||||
size_t catch_all, BlockAllocator &balloc);
|
||||
|
||||
void downstream_failure(DownstreamAddr *addr);
|
||||
// Calls this function if connecting to backend failed. |raddr| is
|
||||
// the actual address used to connect to backend, and it could be
|
||||
// nullptr. This function may schedule live check.
|
||||
void downstream_failure(DownstreamAddr *addr, const Address *raddr);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
|
||||
#include <ev.h>
|
||||
|
||||
#include <ares.h>
|
||||
|
||||
#include "shrpx_config.h"
|
||||
#include "shrpx_connection_handler.h"
|
||||
#include "shrpx_log_config.h"
|
||||
|
@ -392,6 +394,7 @@ std::random_device rd;
|
|||
} // namespace
|
||||
|
||||
int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
||||
int rv;
|
||||
std::array<char, STRERROR_BUFSIZE> errbuf;
|
||||
(void)errbuf;
|
||||
|
||||
|
@ -400,6 +403,12 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
rv = ares_library_init(ARES_LIB_INIT_ALL);
|
||||
if (rv != 0) {
|
||||
LOG(FATAL) << "ares_library_init failed: " << ares_strerror(rv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto loop = EV_DEFAULT;
|
||||
|
||||
auto gen = std::mt19937(rd());
|
||||
|
@ -494,8 +503,6 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
|||
}
|
||||
}
|
||||
|
||||
int rv;
|
||||
|
||||
if (config->num_worker == 1) {
|
||||
rv = conn_handler.create_single_worker();
|
||||
if (rv != 0) {
|
||||
|
@ -575,6 +582,8 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
|
|||
}
|
||||
#endif // HAVE_NEVERBLEED
|
||||
|
||||
ares_library_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
11
src/util.cc
11
src/util.cc
|
@ -666,6 +666,17 @@ std::string to_numeric_addr(const Address *addr) {
|
|||
return s;
|
||||
}
|
||||
|
||||
void set_port(Address &addr, uint16_t port) {
|
||||
switch (addr.su.storage.ss_family) {
|
||||
case AF_INET:
|
||||
addr.su.in.sin_port = htons(port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr.su.in6.sin6_port = htons(port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int STDERR_COPY = -1;
|
||||
static int STDOUT_COPY = -1;
|
||||
|
||||
|
|
|
@ -478,6 +478,9 @@ std::string numeric_name(const struct sockaddr *sa, socklen_t salen);
|
|||
// IPv6 address, address is enclosed by square brackets ([]).
|
||||
std::string to_numeric_addr(const Address *addr);
|
||||
|
||||
// Sets |port| to |addr|.
|
||||
void set_port(Address &addr, uint16_t port);
|
||||
|
||||
// Makes internal copy of stderr (and possibly stdout in the future),
|
||||
// which is then used as pointer to /dev/stderr or /proc/self/fd/2
|
||||
void store_original_fds();
|
||||
|
|
Loading…
Reference in New Issue