diff --git a/.hgtags b/.hgtags
index ef68adcf4..517d2151b 100644
--- a/.hgtags
+++ b/.hgtags
@@ -465,3 +465,6 @@ a68ac0677f8553b1f84d357bc9da114731ab5f47 release-1.21.1
 bfbc52374adcbf2f9060afd62de940f6fab3bba5 release-1.21.2
 2217a9c1d0b86026f22700b3c089545db1964f55 release-1.21.3
 39be8a682c58308d9399cddd57e37f9fdb7bdf3e release-1.21.4
+d986378168fd4d70e0121cabac274c560cca9bdf release-1.21.5
+714eb4b2c09e712fb2572a2164ce2bf67638ccac release-1.21.6
+f669c9c2a617d80daf753e012265ab5290df0d9b release-1.22.0
diff --git a/auto/cc/clang b/auto/cc/clang
index a962ee234..8a15a049c 100644
--- a/auto/cc/clang
+++ b/auto/cc/clang
@@ -88,6 +88,9 @@ if [ "$NGX_SYSTEM" = "Darwin" ]; then
     CFLAGS="$CFLAGS -Wno-deprecated-declarations"
 fi
 
+# dont warn on vla-parameter
+CFLAGS="$CFLAGS -Wno-vla-parameter"
+
 # stop on warning
 CFLAGS="$CFLAGS -Werror"
 
diff --git a/auto/cc/gcc b/auto/cc/gcc
index a5c5c18fb..9fe64398f 100644
--- a/auto/cc/gcc
+++ b/auto/cc/gcc
@@ -164,6 +164,8 @@ case "$NGX_GCC_VER" in
     ;;
 esac
 
+# dont warn on vla-parameter
+CFLAGS="$CFLAGS -Wno-vla-parameter"
 
 # stop on warning
 CFLAGS="$CFLAGS -Werror"
diff --git a/auto/lib/pcre/conf b/auto/lib/pcre/conf
index 5e3960fea..20c1cafbe 100644
--- a/auto/lib/pcre/conf
+++ b/auto/lib/pcre/conf
@@ -4,87 +4,62 @@
 
 
 if [ $PCRE != NONE ]; then
-    CORE_INCS="$CORE_INCS $PCRE"
 
-    case "$NGX_CC_NAME" in
+    if [ -f $PCRE/src/pcre2.h.generic ]; then
 
-        msvc | owc | bcc)
-            have=NGX_PCRE . auto/have
-            have=PCRE_STATIC . auto/have
-            CORE_DEPS="$CORE_DEPS $PCRE/pcre.h"
-            LINK_DEPS="$LINK_DEPS $PCRE/pcre.lib"
-            CORE_LIBS="$CORE_LIBS $PCRE/pcre.lib"
-        ;;
+        PCRE_LIBRARY=PCRE2
 
-        icc)
-            have=NGX_PCRE . auto/have
-            CORE_DEPS="$CORE_DEPS $PCRE/pcre.h"
+        have=NGX_PCRE . auto/have
+        have=NGX_PCRE2 . auto/have
 
-            LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a"
+        if [ "$NGX_PLATFORM" = win32 ]; then
+            have=PCRE2_STATIC . auto/have
+        fi
 
-            echo $ngx_n "checking for PCRE library ...$ngx_c"
+        CORE_INCS="$CORE_INCS $PCRE/src/"
+        CORE_DEPS="$CORE_DEPS $PCRE/src/pcre2.h"
 
-            if [ -f $PCRE/pcre.h ]; then
-                ngx_pcre_ver=`grep PCRE_MAJOR $PCRE/pcre.h \
-                              | sed -e 's/^.*PCRE_MAJOR.* \(.*\)$/\1/'`
+        case "$NGX_CC_NAME" in
 
-            else if [ -f $PCRE/configure.in ]; then
-                ngx_pcre_ver=`grep PCRE_MAJOR= $PCRE/configure.in \
-                              | sed -e 's/^.*=\(.*\)$/\1/'`
+            msvc)
+                LINK_DEPS="$LINK_DEPS $PCRE/src/pcre2-8.lib"
+                CORE_LIBS="$CORE_LIBS $PCRE/src/pcre2-8.lib"
+            ;;
 
-            else
-                ngx_pcre_ver=`grep pcre_major, $PCRE/configure.ac \
-                              | sed -e 's/^.*pcre_major,.*\[\(.*\)\].*$/\1/'`
-            fi
-            fi
+            *)
+                LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre2-8.a"
+                CORE_LIBS="$CORE_LIBS $PCRE/.libs/libpcre2-8.a"
+            ;;
 
-            echo " $ngx_pcre_ver major version found"
-
-            # to allow -ipo optimization we link with the *.o but not library
-
-            case "$ngx_pcre_ver" in
-                4|5)
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre.o"
-                ;;
-
-                6)
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_chartables.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_compile.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_exec.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_fullinfo.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_globals.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_tables.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_try_flipped.o"
-                ;;
-
-                *)
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_chartables.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_compile.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_exec.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_fullinfo.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_globals.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_tables.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_try_flipped.o"
-                    CORE_LIBS="$CORE_LIBS $PCRE/pcre_newline.o"
-                ;;
-
-            esac
-        ;;
-
-        *)
-            have=NGX_PCRE . auto/have
+        esac
 
-            if [ "$NGX_PLATFORM" = win32 ]; then
-                have=PCRE_STATIC . auto/have
-            fi
+    else
 
-            CORE_DEPS="$CORE_DEPS $PCRE/pcre.h"
-            LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a"
-            CORE_LIBS="$CORE_LIBS $PCRE/.libs/libpcre.a"
-        ;;
+        PCRE_LIBRARY=PCRE
 
-    esac
+        have=NGX_PCRE . auto/have
 
+        if [ "$NGX_PLATFORM" = win32 ]; then
+            have=PCRE_STATIC . auto/have
+        fi
+
+        CORE_INCS="$CORE_INCS $PCRE"
+        CORE_DEPS="$CORE_DEPS $PCRE/pcre.h"
+
+        case "$NGX_CC_NAME" in
+
+            msvc | owc | bcc)
+                LINK_DEPS="$LINK_DEPS $PCRE/pcre.lib"
+                CORE_LIBS="$CORE_LIBS $PCRE/pcre.lib"
+            ;;
+
+            *)
+                LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a"
+                CORE_LIBS="$CORE_LIBS $PCRE/.libs/libpcre.a"
+            ;;
+
+        esac
+    fi
 
     if [ $PCRE_JIT = YES ]; then
         have=NGX_HAVE_PCRE_JIT . auto/have
@@ -94,8 +69,48 @@ if [ $PCRE != NONE ]; then
 else
 
     if [ "$NGX_PLATFORM" != win32 ]; then
-
         PCRE=NO
+    fi
+
+    if [ $PCRE = NO -a $PCRE2 != DISABLED ]; then
+
+        ngx_feature="PCRE2 library"
+        ngx_feature_name="NGX_PCRE2"
+        ngx_feature_run=no
+        ngx_feature_incs="#define PCRE2_CODE_UNIT_WIDTH 8
+                          #include <pcre2.h>"
+        ngx_feature_path=
+        ngx_feature_libs="-lpcre2-8"
+        ngx_feature_test="pcre2_code *re;
+                          re = pcre2_compile(NULL, 0, 0, NULL, NULL, NULL);
+                          if (re == NULL) return 1"
+        . auto/feature
+
+        if [ $ngx_found = no ]; then
+
+            # pcre2-config
+
+            ngx_pcre2_prefix=`pcre2-config --prefix 2>/dev/null`
+
+            if [ -n "$ngx_pcre2_prefix" ]; then
+                ngx_feature="PCRE2 library in $ngx_pcre2_prefix"
+                ngx_feature_path=`pcre2-config --cflags \
+                                  | sed -n -e 's/.*-I *\([^ ][^ ]*\).*/\1/p'`
+                ngx_feature_libs=`pcre2-config --libs8`
+                . auto/feature
+            fi
+        fi
+
+        if [ $ngx_found = yes ]; then
+            have=NGX_PCRE . auto/have
+            CORE_INCS="$CORE_INCS $ngx_feature_path"
+            CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+            PCRE=YES
+            PCRE_LIBRARY=PCRE2
+        fi
+    fi
+
+    if [ $PCRE = NO ]; then
 
         ngx_feature="PCRE library"
         ngx_feature_name="NGX_PCRE"
@@ -171,6 +186,7 @@ else
             CORE_INCS="$CORE_INCS $ngx_feature_path"
             CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
             PCRE=YES
+            PCRE_LIBRARY=PCRE
         fi
 
         if [ $PCRE = YES ]; then
diff --git a/auto/lib/pcre/make b/auto/lib/pcre/make
index 97c9f3ba0..839ef294b 100644
--- a/auto/lib/pcre/make
+++ b/auto/lib/pcre/make
@@ -3,36 +3,138 @@
 # Copyright (C) Nginx, Inc.
 
 
-case "$NGX_CC_NAME" in
+if [ $PCRE_LIBRARY = PCRE2 ]; then
+
+    # PCRE2
+
+    if [ $NGX_CC_NAME = msvc ]; then
+
+        # With PCRE2, it is not possible to compile all sources.
+        # Since list of source files changes between versions, we
+        # test files which might not be present.
+
+        ngx_pcre_srcs="pcre2_auto_possess.c \
+                       pcre2_chartables.c \
+                       pcre2_compile.c \
+                       pcre2_config.c \
+                       pcre2_context.c \
+                       pcre2_dfa_match.c \
+                       pcre2_error.c \
+                       pcre2_jit_compile.c \
+                       pcre2_maketables.c \
+                       pcre2_match.c \
+                       pcre2_match_data.c \
+                       pcre2_newline.c \
+                       pcre2_ord2utf.c \
+                       pcre2_pattern_info.c \
+                       pcre2_string_utils.c \
+                       pcre2_study.c \
+                       pcre2_substitute.c \
+                       pcre2_substring.c \
+                       pcre2_tables.c \
+                       pcre2_ucd.c \
+                       pcre2_valid_utf.c \
+                       pcre2_xclass.c"
+
+        ngx_pcre_test="pcre2_convert.c \
+                       pcre2_extuni.c \
+                       pcre2_find_bracket.c \
+                       pcre2_script_run.c \
+                       pcre2_serialize.c"
+
+        for ngx_src in $ngx_pcre_test
+        do
+            if [ -f $PCRE/src/$ngx_src ]; then
+                ngx_pcre_srcs="$ngx_pcre_srcs $ngx_src"
+            fi
+        done
+
+        ngx_pcre_objs=`echo $ngx_pcre_srcs \
+            | sed -e "s#\([^ ]*\.\)c#\1$ngx_objext#g"`
+
+        ngx_pcre_srcs=`echo $ngx_pcre_srcs \
+            | sed -e "s/  *\([^ ][^ ]*\)/$ngx_regex_cont\1/g"`
+        ngx_pcre_objs=`echo $ngx_pcre_objs \
+            | sed -e "s/  *\([^ ][^ ]*\)/$ngx_regex_cont\1/g"`
+
+        cat << END                                            >> $NGX_MAKEFILE
+
+PCRE_CFLAGS =	-O2 -Ob1 -Oi -Gs $LIBC $CPU_OPT
+PCRE_FLAGS =	-DHAVE_CONFIG_H -DPCRE2_STATIC -DPCRE2_CODE_UNIT_WIDTH=8 \\
+		-DHAVE_MEMMOVE
+
+PCRE_SRCS =	 $ngx_pcre_srcs
+PCRE_OBJS =	 $ngx_pcre_objs
+
+$PCRE/src/pcre2.h:
+	cd $PCRE/src \\
+	&& copy /y config.h.generic config.h \\
+	&& copy /y pcre2.h.generic pcre2.h \\
+	&& copy /y pcre2_chartables.c.dist pcre2_chartables.c
+
+$PCRE/src/pcre2-8.lib:	$PCRE/src/pcre2.h $NGX_MAKEFILE
+	cd $PCRE/src \\
+	&& cl -nologo -c \$(PCRE_CFLAGS) -I . \$(PCRE_FLAGS) \$(PCRE_SRCS) \\
+	&& link -lib -out:pcre2-8.lib -verbose:lib \$(PCRE_OBJS)
 
-    msvc)
-        ngx_makefile=makefile.msvc
-        ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC"
-        ngx_pcre="PCRE=\"$PCRE\""
-    ;;
+END
+
+    else
+
+        cat << END                                            >> $NGX_MAKEFILE
 
-    owc)
-        ngx_makefile=makefile.owc
-        ngx_opt="CPU_OPT=\"$CPU_OPT\""
-        ngx_pcre=`echo PCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"`
-    ;;
+$PCRE/src/pcre2.h:	$PCRE/Makefile
+
+$PCRE/Makefile:	$NGX_MAKEFILE
+	cd $PCRE \\
+	&& if [ -f Makefile ]; then \$(MAKE) distclean; fi \\
+	&& CC="\$(CC)" CFLAGS="$PCRE_OPT" \\
+	./configure --disable-shared $PCRE_CONF_OPT
 
-    bcc)
-        ngx_makefile=makefile.bcc
-        ngx_opt="-DCPU_OPT=\"$CPU_OPT\""
-        ngx_pcre=`echo \-DPCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"`
-    ;;
+$PCRE/.libs/libpcre2-8.a:	$PCRE/Makefile
+	cd $PCRE \\
+	&& \$(MAKE) libpcre2-8.la
 
-    *)
-        ngx_makefile=
-    ;;
+END
 
-esac
+    fi
 
 
-if [ -n "$ngx_makefile" ]; then
+else
 
-    cat << END                                                >> $NGX_MAKEFILE
+    # PCRE
+
+    case "$NGX_CC_NAME" in
+
+        msvc)
+            ngx_makefile=makefile.msvc
+            ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC"
+            ngx_pcre="PCRE=\"$PCRE\""
+        ;;
+
+        owc)
+            ngx_makefile=makefile.owc
+            ngx_opt="CPU_OPT=\"$CPU_OPT\""
+            ngx_pcre=`echo PCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+        ;;
+
+        bcc)
+            ngx_makefile=makefile.bcc
+            ngx_opt="-DCPU_OPT=\"$CPU_OPT\""
+            ngx_pcre=`echo \-DPCRE=\"$PCRE\" \
+                | sed -e "s/\//$ngx_regex_dirsep/g"`
+        ;;
+
+        *)
+            ngx_makefile=
+        ;;
+
+    esac
+
+
+    if [ -n "$ngx_makefile" ]; then
+
+        cat << END                                            >> $NGX_MAKEFILE
 
 `echo "$PCRE/pcre.lib:	$PCRE/pcre.h $NGX_MAKEFILE"			\
 	| sed -e "s/\//$ngx_regex_dirsep/g"`
@@ -43,9 +145,9 @@ if [ -n "$ngx_makefile" ]; then
 
 END
 
-else
+    else
 
-    cat << END                                                >> $NGX_MAKEFILE
+        cat << END                                            >> $NGX_MAKEFILE
 
 $PCRE/pcre.h:	$PCRE/Makefile
 
@@ -61,4 +163,6 @@ $PCRE/.libs/libpcre.a:	$PCRE/Makefile
 
 END
 
+    fi
+
 fi
diff --git a/auto/modules b/auto/modules
index 22e1a9586..ff3d13be3 100644
--- a/auto/modules
+++ b/auto/modules
@@ -102,6 +102,11 @@ if [ $HTTP = YES ]; then
     fi
 
 
+    if [ $HTTP_V2 = YES ]; then
+        HTTP_SRCS="$HTTP_SRCS $HTTP_HUFF_SRCS"
+    fi
+
+
     # the module order is important
     #     ngx_http_static_module
     #     ngx_http_gzip_static_module
@@ -427,8 +432,6 @@ if [ $HTTP = YES ]; then
         ngx_module_srcs="src/http/v2/ngx_http_v2.c \
                          src/http/v2/ngx_http_v2_table.c \
                          src/http/v2/ngx_http_v2_encode.c \
-                         src/http/v2/ngx_http_v2_huff_decode.c \
-                         src/http/v2/ngx_http_v2_huff_encode.c \
                          src/http/v2/ngx_http_v2_module.c"
         ngx_module_libs=
         ngx_module_link=$HTTP_V2
diff --git a/auto/options b/auto/options
index bb3600557..03bd1411a 100644
--- a/auto/options
+++ b/auto/options
@@ -148,6 +148,7 @@ PCRE=NONE
 PCRE_OPT=
 PCRE_CONF_OPT=
 PCRE_JIT=YES
+PCRE2=YES
 
 USE_OPENSSL=NO
 OPENSSL="modules/quiche/quiche/deps/boringssl"
@@ -369,6 +370,7 @@ use the \"--without-mail_ssl_module\" option instead"
         --with-pcre=*)                   PCRE="$value"              ;;
         --with-pcre-opt=*)               PCRE_OPT="$value"          ;;
         --without-pcre-jit)              PCRE_JIT=NO                ;;
+        --without-pcre2)                 PCRE2=DISABLED             ;;
 
         --with-openssl=*)                OPENSSL="$value"           ;;
         --with-openssl-opt=*)            OPENSSL_OPT="$value"       ;;
@@ -591,6 +593,7 @@ cat << END
   --with-pcre=DIR                    set path to PCRE library sources
   --with-pcre-opt=OPTIONS            set additional build options for PCRE
   --without-pcre-jit                 don't build PCRE with JIT compilation support
+  --without-pcre2                    do not use PCRE2 library
 
   --with-zlib=DIR                    set path to zlib library sources
   --with-zlib-opt=OPTIONS            set additional build options for zlib
diff --git a/auto/os/freebsd b/auto/os/freebsd
index 937ca204e..870bac4b3 100644
--- a/auto/os/freebsd
+++ b/auto/os/freebsd
@@ -44,12 +44,10 @@ if [ $osreldate -gt 300007 ]; then
     CORE_SRCS="$CORE_SRCS $FREEBSD_SENDFILE_SRCS"
 fi
 
-if [ $NGX_FILE_AIO = YES ]; then
-    if [ $osreldate -gt 502103 ]; then
-        echo " + sendfile()'s SF_NODISKIO found"
+if [ $osreldate -gt 1100093 ]; then
+    echo " + sendfile()'s SF_NODISKIO found"
 
-        have=NGX_HAVE_AIO_SENDFILE . auto/have
-    fi
+    have=NGX_HAVE_SENDFILE_NODISKIO . auto/have
 fi
 
 # POSIX semaphores
diff --git a/auto/sources b/auto/sources
index 3dad11132..156f7979e 100644
--- a/auto/sources
+++ b/auto/sources
@@ -255,3 +255,6 @@ NGX_WIN32_RC="src/os/win32/nginx.rc"
 
 
 HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c
+
+HTTP_HUFF_SRCS="src/http/ngx_http_huff_decode.c
+                src/http/ngx_http_huff_encode.c"
diff --git a/auto/summary b/auto/summary
index 9aa776edf..b3c07eedc 100644
--- a/auto/summary
+++ b/auto/summary
@@ -16,9 +16,9 @@ if [ $USE_PCRE = DISABLED ]; then
 
 else
     case $PCRE in
-        YES)   echo "  + using system PCRE library" ;;
+        YES)   echo "  + using system $PCRE_LIBRARY library" ;;
         NONE)  echo "  + PCRE library is not used" ;;
-        *)     echo "  + using PCRE library: $PCRE" ;;
+        *)     echo "  + using $PCRE_LIBRARY library: $PCRE" ;;
     esac
 fi
 
diff --git a/contrib/vim/syntax/nginx.vim b/contrib/vim/syntax/nginx.vim
index 4907e2a54..6828cd3e5 100644
--- a/contrib/vim/syntax/nginx.vim
+++ b/contrib/vim/syntax/nginx.vim
@@ -5,6 +5,9 @@ if exists("b:current_syntax")
   finish
 end
 
+let s:save_cpo = &cpo
+set cpo&vim
+
 " general syntax
 
 if has("patch-7.4.1142")
@@ -152,6 +155,7 @@ syn keyword ngxDirective contained auth_jwt_header_set
 syn keyword ngxDirective contained auth_jwt_key_file
 syn keyword ngxDirective contained auth_jwt_key_request
 syn keyword ngxDirective contained auth_jwt_leeway
+syn keyword ngxDirective contained auth_jwt_require
 syn keyword ngxDirective contained auth_jwt_type
 syn keyword ngxDirective contained auth_request
 syn keyword ngxDirective contained auth_request_set
@@ -335,6 +339,10 @@ syn keyword ngxDirective contained ip_hash
 syn keyword ngxDirective contained js_access
 syn keyword ngxDirective contained js_body_filter
 syn keyword ngxDirective contained js_content
+syn keyword ngxDirective contained js_fetch_ciphers
+syn keyword ngxDirective contained js_fetch_protocols
+syn keyword ngxDirective contained js_fetch_trusted_certificate
+syn keyword ngxDirective contained js_fetch_verify_depth
 syn keyword ngxDirective contained js_filter
 syn keyword ngxDirective contained js_header_filter
 syn keyword ngxDirective contained js_import
@@ -402,6 +410,7 @@ syn keyword ngxDirective contained mp4_buffer_size
 syn keyword ngxDirective contained mp4_limit_rate
 syn keyword ngxDirective contained mp4_limit_rate_after
 syn keyword ngxDirective contained mp4_max_buffer_size
+syn keyword ngxDirective contained mp4_start_key_frame
 syn keyword ngxDirective contained msie_padding
 syn keyword ngxDirective contained msie_refresh
 syn keyword ngxDirective contained multi_accept
@@ -458,6 +467,7 @@ syn keyword ngxDirective contained proxy_cookie_flags
 syn keyword ngxDirective contained proxy_cookie_path
 syn keyword ngxDirective contained proxy_download_rate
 syn keyword ngxDirective contained proxy_force_ranges
+syn keyword ngxDirective contained proxy_half_close
 syn keyword ngxDirective contained proxy_headers_hash_bucket_size
 syn keyword ngxDirective contained proxy_headers_hash_max_size
 syn keyword ngxDirective contained proxy_hide_header
@@ -597,6 +607,7 @@ syn keyword ngxDirective contained ssi_min_file_chunk
 syn keyword ngxDirective contained ssi_silent_errors
 syn keyword ngxDirective contained ssi_types
 syn keyword ngxDirective contained ssi_value_length
+syn keyword ngxDirective contained ssl_alpn
 syn keyword ngxDirective contained ssl_buffer_size
 syn keyword ngxDirective contained ssl_certificate
 syn keyword ngxDirective contained ssl_certificate_key
@@ -788,11 +799,15 @@ syn keyword ngxDirectiveThirdParty contained auth_digest_user_file
 syn keyword ngxDirectiveThirdParty contained auth_gss
 syn keyword ngxDirectiveThirdParty contained auth_gss_allow_basic_fallback
 syn keyword ngxDirectiveThirdParty contained auth_gss_authorized_principal
+syn keyword ngxDirectiveThirdParty contained auth_gss_authorized_principal_regex
+syn keyword ngxDirectiveThirdParty contained auth_gss_constrained_delegation
+syn keyword ngxDirectiveThirdParty contained auth_gss_delegate_credentials
 syn keyword ngxDirectiveThirdParty contained auth_gss_force_realm
 syn keyword ngxDirectiveThirdParty contained auth_gss_format_full
 syn keyword ngxDirectiveThirdParty contained auth_gss_keytab
 syn keyword ngxDirectiveThirdParty contained auth_gss_map_to_local
 syn keyword ngxDirectiveThirdParty contained auth_gss_realm
+syn keyword ngxDirectiveThirdParty contained auth_gss_service_ccache
 syn keyword ngxDirectiveThirdParty contained auth_gss_service_name
 
 " LDAP Authentication
@@ -969,7 +984,6 @@ syn keyword ngxDirectiveThirdParty contained fancyindex_hide_parent_dir
 syn keyword ngxDirectiveThirdParty contained fancyindex_hide_symlinks
 syn keyword ngxDirectiveThirdParty contained fancyindex_ignore
 syn keyword ngxDirectiveThirdParty contained fancyindex_localtime
-syn keyword ngxDirectiveThirdParty contained fancyindex_name_length
 syn keyword ngxDirectiveThirdParty contained fancyindex_show_dotfiles
 syn keyword ngxDirectiveThirdParty contained fancyindex_show_path
 syn keyword ngxDirectiveThirdParty contained fancyindex_time_format
@@ -1059,7 +1073,9 @@ syn keyword ngxDirectiveThirdParty contained nchan_publisher_upstream_request
 syn keyword ngxDirectiveThirdParty contained nchan_pubsub
 syn keyword ngxDirectiveThirdParty contained nchan_pubsub_channel_id
 syn keyword ngxDirectiveThirdParty contained nchan_pubsub_location
+syn keyword ngxDirectiveThirdParty contained nchan_redis_cluster_check_interval
 syn keyword ngxDirectiveThirdParty contained nchan_redis_connect_timeout
+syn keyword ngxDirectiveThirdParty contained nchan_redis_discovered_ip_range_blacklist
 syn keyword ngxDirectiveThirdParty contained nchan_redis_fakesub_timer_interval
 syn keyword ngxDirectiveThirdParty contained nchan_redis_idle_channel_cache_timeout
 syn keyword ngxDirectiveThirdParty contained nchan_redis_namespace
@@ -1067,12 +1083,29 @@ syn keyword ngxDirectiveThirdParty contained nchan_redis_nostore_fastpublish
 syn keyword ngxDirectiveThirdParty contained nchan_redis_optimize_target
 syn keyword ngxDirectiveThirdParty contained nchan_redis_pass
 syn keyword ngxDirectiveThirdParty contained nchan_redis_pass_inheritable
+syn keyword ngxDirectiveThirdParty contained nchan_redis_password
 syn keyword ngxDirectiveThirdParty contained nchan_redis_ping_interval
 syn keyword ngxDirectiveThirdParty contained nchan_redis_publish_msgpacked_max_size
 syn keyword ngxDirectiveThirdParty contained nchan_redis_server
+syn keyword ngxDirectiveThirdParty contained nchan_redis_ssl
+syn keyword ngxDirectiveThirdParty contained nchan_redis_ssl_ciphers
+syn keyword ngxDirectiveThirdParty contained nchan_redis_ssl_client_certificate
+syn keyword ngxDirectiveThirdParty contained nchan_redis_ssl_client_certificate_key
+syn keyword ngxDirectiveThirdParty contained nchan_redis_ssl_server_name
+syn keyword ngxDirectiveThirdParty contained nchan_redis_ssl_trusted_certificate
+syn keyword ngxDirectiveThirdParty contained nchan_redis_ssl_trusted_certificate_path
+syn keyword ngxDirectiveThirdParty contained nchan_redis_ssl_verify_certificate
 syn keyword ngxDirectiveThirdParty contained nchan_redis_storage_mode
 syn keyword ngxDirectiveThirdParty contained nchan_redis_subscribe_weights
+syn keyword ngxDirectiveThirdParty contained nchan_redis_tls
+syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_ciphers
+syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_client_certificate
+syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_server_name
+syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_trusted_certificate
+syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_trusted_certificate_path
+syn keyword ngxDirectiveThirdParty contained nchan_redis_tls_verify_certificate
 syn keyword ngxDirectiveThirdParty contained nchan_redis_url
+syn keyword ngxDirectiveThirdParty contained nchan_redis_username
 syn keyword ngxDirectiveThirdParty contained nchan_redis_wait_after_connecting
 syn keyword ngxDirectiveThirdParty contained nchan_shared_memory_size
 syn keyword ngxDirectiveThirdParty contained nchan_storage_engine
@@ -1385,6 +1418,7 @@ syn keyword ngxDirectiveThirdParty contained lua_socket_read_timeout
 syn keyword ngxDirectiveThirdParty contained lua_socket_send_lowat
 syn keyword ngxDirectiveThirdParty contained lua_socket_send_timeout
 syn keyword ngxDirectiveThirdParty contained lua_ssl_ciphers
+syn keyword ngxDirectiveThirdParty contained lua_ssl_conf_command
 syn keyword ngxDirectiveThirdParty contained lua_ssl_crl
 syn keyword ngxDirectiveThirdParty contained lua_ssl_protocols
 syn keyword ngxDirectiveThirdParty contained lua_ssl_trusted_certificate
@@ -1392,6 +1426,7 @@ syn keyword ngxDirectiveThirdParty contained lua_ssl_verify_depth
 syn keyword ngxDirectiveThirdParty contained lua_thread_cache_max_entries
 syn keyword ngxDirectiveThirdParty contained lua_transform_underscores_in_response_headers
 syn keyword ngxDirectiveThirdParty contained lua_use_default_type
+syn keyword ngxDirectiveThirdParty contained lua_worker_thread_vm_pool_size
 syn keyword ngxDirectiveThirdParty contained rewrite_by_lua
 syn keyword ngxDirectiveThirdParty contained rewrite_by_lua_block
 syn keyword ngxDirectiveThirdParty contained rewrite_by_lua_file
@@ -1401,6 +1436,8 @@ syn keyword ngxDirectiveThirdParty contained set_by_lua_block
 syn keyword ngxDirectiveThirdParty contained set_by_lua_file
 syn keyword ngxDirectiveThirdParty contained ssl_certificate_by_lua_block
 syn keyword ngxDirectiveThirdParty contained ssl_certificate_by_lua_file
+syn keyword ngxDirectiveThirdParty contained ssl_client_hello_by_lua_block
+syn keyword ngxDirectiveThirdParty contained ssl_client_hello_by_lua_file
 syn keyword ngxDirectiveThirdParty contained ssl_session_fetch_by_lua_block
 syn keyword ngxDirectiveThirdParty contained ssl_session_fetch_by_lua_file
 syn keyword ngxDirectiveThirdParty contained ssl_session_store_by_lua_block
@@ -1719,15 +1756,18 @@ syn keyword ngxDirectiveThirdParty contained set_base32_alphabet
 syn keyword ngxDirectiveThirdParty contained set_base32_padding
 syn keyword ngxDirectiveThirdParty contained set_decode_base32
 syn keyword ngxDirectiveThirdParty contained set_decode_base64
+syn keyword ngxDirectiveThirdParty contained set_decode_base64url
 syn keyword ngxDirectiveThirdParty contained set_decode_hex
 syn keyword ngxDirectiveThirdParty contained set_encode_base32
 syn keyword ngxDirectiveThirdParty contained set_encode_base64
+syn keyword ngxDirectiveThirdParty contained set_encode_base64url
 syn keyword ngxDirectiveThirdParty contained set_encode_hex
 syn keyword ngxDirectiveThirdParty contained set_escape_uri
 syn keyword ngxDirectiveThirdParty contained set_formatted_gmt_time
 syn keyword ngxDirectiveThirdParty contained set_formatted_local_time
 syn keyword ngxDirectiveThirdParty contained set_hashed_upstream
 syn keyword ngxDirectiveThirdParty contained set_hmac_sha1
+syn keyword ngxDirectiveThirdParty contained set_hmac_sha256
 syn keyword ngxDirectiveThirdParty contained set_if_empty
 syn keyword ngxDirectiveThirdParty contained set_local_today
 syn keyword ngxDirectiveThirdParty contained set_misc_base32_padding
@@ -1849,6 +1889,7 @@ syn keyword ngxDirectiveThirdParty contained vod_notification_uri
 syn keyword ngxDirectiveThirdParty contained vod_open_file_thread_pool
 syn keyword ngxDirectiveThirdParty contained vod_output_buffer_pool
 syn keyword ngxDirectiveThirdParty contained vod_parse_hdlr_name
+syn keyword ngxDirectiveThirdParty contained vod_parse_udta_name
 syn keyword ngxDirectiveThirdParty contained vod_path_response_postfix
 syn keyword ngxDirectiveThirdParty contained vod_path_response_prefix
 syn keyword ngxDirectiveThirdParty contained vod_performance_counters
@@ -2447,4 +2488,7 @@ hi def link ngxDirectiveThirdPartyDeprecated Error
 hi def link ngxListenOptions Keyword
 hi def link ngxListenOptionsDeprecated Error
 
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
 let b:current_syntax = "nginx"
diff --git a/docs/text/LICENSE b/docs/text/LICENSE
index fd0c72d49..fdedcb746 100644
--- a/docs/text/LICENSE
+++ b/docs/text/LICENSE
@@ -1,6 +1,6 @@
 /* 
  * Copyright (C) 2002-2021 Igor Sysoev
- * Copyright (C) 2011-2021 Nginx, Inc.
+ * Copyright (C) 2011-2022 Nginx, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 0808b5c6e..3f86e4460 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,110 @@
 <change_log title="nginx">
 
 
+<changes ver="1.22.0" date="2022-05-24">
+
+<change>
+<para lang="ru">
+Стабильная ветка 1.22.x.
+</para>
+<para lang="en">
+1.22.x stable branch.
+</para>
+</change>
+
+</changes>
+
+
+<changes ver="1.21.6" date="2022-01-25">
+
+<change type="bugfix">
+<para lang="ru">
+при использование EPOLLEXCLUSIVE на Linux
+распределение клиентских соединений между рабочими процессами
+было неравномерным.
+</para>
+<para lang="en">
+when using EPOLLEXCLUSIVE on Linux
+client connections were unevenly distributed
+among worker processes.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+во время плавного завершения старых рабочих процессов
+nginx возвращал в ответах строку заголовка "Connection: keep-alive".
+</para>
+<para lang="en">
+nginx returned the "Connection: keep-alive" header line in responses
+during graceful shutdown of old worker processes.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в директиве ssl_session_ticket_key при использовании TLSv1.3.
+</para>
+<para lang="en">
+in the "ssl_session_ticket_key" when using TLSv1.3.
+</para>
+</change>
+
+</changes>
+
+
+<changes ver="1.21.5" date="2021-12-28">
+
+<change type="change">
+<para lang="ru">
+теперь nginx по умолчанию собирается с библиотекой PCRE2.
+</para>
+<para lang="en">
+now nginx is built with the PCRE2 library by default.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+теперь nginx всегда использует sendfile(SF_NODISKIO) на FreeBSD.
+</para>
+<para lang="en">
+now nginx always uses sendfile(SF_NODISKIO) on FreeBSD.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+поддержка sendfile(SF_NOCACHE) на FreeBSD.
+</para>
+<para lang="en">
+support for sendfile(SF_NOCACHE) on FreeBSD.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+переменная $ssl_curve.
+</para>
+<para lang="en">
+the $ssl_curve variable.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при использовании HTTP/2 без SSL вместе с директивами sendfile и aio
+соединения могли зависать.
+</para>
+<para lang="en">
+connections might hang
+when using HTTP/2 without SSL with the "sendfile" and "aio" directives.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.21.4" date="2021-11-02">
 
 <change type="change">
diff --git a/misc/GNUmakefile b/misc/GNUmakefile
index 36dd63836..8f839b477 100644
--- a/misc/GNUmakefile
+++ b/misc/GNUmakefile
@@ -6,9 +6,9 @@ TEMP =		tmp
 
 CC =		cl
 OBJS =		objs.msvc8
-OPENSSL =	openssl-1.1.1l
-ZLIB =		zlib-1.2.11
-PCRE =		pcre-8.44
+OPENSSL =	openssl-1.1.1o
+ZLIB =		zlib-1.2.12
+PCRE =		pcre2-10.39
 
 
 release: export
diff --git a/src/core/nginx.h b/src/core/nginx.h
index bc4af23af..329b603e1 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1021004
-#define NGINX_VERSION      "1.21.4"
+#define nginx_version      1022000
+#define NGINX_VERSION      "1.22.0"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h
index 4b665629c..fdcd0cddf 100644
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -90,9 +90,6 @@ struct ngx_output_chain_ctx_s {
 
 #if (NGX_HAVE_FILE_AIO || NGX_COMPAT)
     ngx_output_chain_aio_pt      aio_handler;
-#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
-    ssize_t                    (*aio_preload)(ngx_buf_t *file);
-#endif
 #endif
 
 #if (NGX_THREADS || NGX_COMPAT)
diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
index 88c672c39..c957feab1 100644
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -192,7 +192,7 @@ struct ngx_connection_s {
 
     unsigned            need_last_buf:1;
 
-#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
+#if (NGX_HAVE_SENDFILE_NODISKIO || NGX_COMPAT)
     unsigned            busy_count:2;
 #endif
 
diff --git a/src/core/ngx_module.h b/src/core/ngx_module.h
index 8cf3210e8..6fb455426 100644
--- a/src/core/ngx_module.h
+++ b/src/core/ngx_module.h
@@ -41,7 +41,7 @@
 #define NGX_MODULE_SIGNATURE_3   "0"
 #endif
 
-#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
+#if (NGX_HAVE_SENDFILE_NODISKIO || NGX_COMPAT)
 #define NGX_MODULE_SIGNATURE_4   "1"
 #else
 #define NGX_MODULE_SIGNATURE_4   "0"
diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c
index fd4603b19..857074253 100644
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -29,10 +29,6 @@
 
 static ngx_inline ngx_int_t
     ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
-#if (NGX_HAVE_AIO_SENDFILE)
-static ngx_int_t ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx,
-    ngx_file_t *file);
-#endif
 static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
     ngx_chain_t **chain, ngx_chain_t *in);
 static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
@@ -260,10 +256,6 @@ ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
     }
 #endif
 
-    if (buf->in_file && buf->file->directio) {
-        return 0;
-    }
-
     sendfile = ctx->sendfile;
 
 #if (NGX_SENDFILE_LIMIT)
@@ -272,6 +264,19 @@ ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
         sendfile = 0;
     }
 
+#endif
+
+#if !(NGX_HAVE_SENDFILE_NODISKIO)
+
+    /*
+     * With DIRECTIO, disable sendfile() unless sendfile(SF_NOCACHE)
+     * is available.
+     */
+
+    if (buf->in_file && buf->file->directio) {
+        sendfile = 0;
+    }
+
 #endif
 
     if (!sendfile) {
@@ -283,12 +288,6 @@ ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
         buf->in_file = 0;
     }
 
-#if (NGX_HAVE_AIO_SENDFILE)
-    if (ctx->aio_preload && buf->in_file) {
-        (void) ngx_output_chain_aio_setup(ctx, buf->file);
-    }
-#endif
-
     if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
         return 0;
     }
@@ -301,28 +300,6 @@ ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
 }
 
 
-#if (NGX_HAVE_AIO_SENDFILE)
-
-static ngx_int_t
-ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)
-{
-    ngx_event_aio_t  *aio;
-
-    if (file->aio == NULL && ngx_file_aio_init(file, ctx->pool) != NGX_OK) {
-        return NGX_ERROR;
-    }
-
-    aio = file->aio;
-
-    aio->data = ctx->filter_ctx;
-    aio->preload_handler = ctx->aio_preload;
-
-    return NGX_OK;
-}
-
-#endif
-
-
 static ngx_int_t
 ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
     ngx_chain_t *in)
diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c
index 52169f655..bebf3b6a8 100644
--- a/src/core/ngx_regex.c
+++ b/src/core/ngx_regex.c
@@ -10,15 +10,22 @@
 
 
 typedef struct {
-    ngx_flag_t  pcre_jit;
+    ngx_flag_t   pcre_jit;
+    ngx_list_t  *studies;
 } ngx_regex_conf_t;
 
 
+static ngx_inline void ngx_regex_malloc_init(ngx_pool_t *pool);
+static ngx_inline void ngx_regex_malloc_done(void);
+
+#if (NGX_PCRE2)
+static void * ngx_libc_cdecl ngx_regex_malloc(size_t size, void *data);
+static void ngx_libc_cdecl ngx_regex_free(void *p, void *data);
+#else
 static void * ngx_libc_cdecl ngx_regex_malloc(size_t size);
 static void ngx_libc_cdecl ngx_regex_free(void *p);
-#if (NGX_HAVE_PCRE_JIT)
-static void ngx_pcre_free_studies(void *data);
 #endif
+static void ngx_regex_cleanup(void *data);
 
 static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle);
 
@@ -65,31 +72,196 @@ ngx_module_t  ngx_regex_module = {
 };
 
 
-static ngx_pool_t  *ngx_pcre_pool;
-static ngx_list_t  *ngx_pcre_studies;
+static ngx_pool_t             *ngx_regex_pool;
+static ngx_list_t             *ngx_regex_studies;
+static ngx_uint_t              ngx_regex_direct_alloc;
+
+#if (NGX_PCRE2)
+static pcre2_compile_context  *ngx_regex_compile_context;
+static pcre2_match_data       *ngx_regex_match_data;
+static ngx_uint_t              ngx_regex_match_data_size;
+#endif
 
 
 void
 ngx_regex_init(void)
 {
+#if !(NGX_PCRE2)
     pcre_malloc = ngx_regex_malloc;
     pcre_free = ngx_regex_free;
+#endif
 }
 
 
 static ngx_inline void
 ngx_regex_malloc_init(ngx_pool_t *pool)
 {
-    ngx_pcre_pool = pool;
+    ngx_regex_pool = pool;
+    ngx_regex_direct_alloc = (pool == NULL) ? 1 : 0;
 }
 
 
 static ngx_inline void
 ngx_regex_malloc_done(void)
 {
-    ngx_pcre_pool = NULL;
+    ngx_regex_pool = NULL;
+    ngx_regex_direct_alloc = 0;
+}
+
+
+#if (NGX_PCRE2)
+
+ngx_int_t
+ngx_regex_compile(ngx_regex_compile_t *rc)
+{
+    int                     n, errcode;
+    char                   *p;
+    u_char                  errstr[128];
+    size_t                  erroff;
+    uint32_t                options;
+    pcre2_code             *re;
+    ngx_regex_elt_t        *elt;
+    pcre2_general_context  *gctx;
+    pcre2_compile_context  *cctx;
+
+    if (ngx_regex_compile_context == NULL) {
+        /*
+         * Allocate a compile context if not yet allocated.  This uses
+         * direct allocations from heap, so the result can be cached
+         * even at runtime.
+         */
+
+        ngx_regex_malloc_init(NULL);
+
+        gctx = pcre2_general_context_create(ngx_regex_malloc, ngx_regex_free,
+                                            NULL);
+        if (gctx == NULL) {
+            ngx_regex_malloc_done();
+            goto nomem;
+        }
+
+        cctx = pcre2_compile_context_create(gctx);
+        if (cctx == NULL) {
+            pcre2_general_context_free(gctx);
+            ngx_regex_malloc_done();
+            goto nomem;
+        }
+
+        ngx_regex_compile_context = cctx;
+
+        pcre2_general_context_free(gctx);
+        ngx_regex_malloc_done();
+    }
+
+    options = 0;
+
+    if (rc->options & NGX_REGEX_CASELESS) {
+        options |= PCRE2_CASELESS;
+    }
+
+    if (rc->options & NGX_REGEX_MULTILINE) {
+        options |= PCRE2_MULTILINE;
+    }
+
+    if (rc->options & ~(NGX_REGEX_CASELESS|NGX_REGEX_MULTILINE)) {
+        rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+                            "regex \"%V\" compilation failed: invalid options",
+                            &rc->pattern)
+                      - rc->err.data;
+        return NGX_ERROR;
+    }
+
+    ngx_regex_malloc_init(rc->pool);
+
+    re = pcre2_compile(rc->pattern.data, rc->pattern.len, options,
+                       &errcode, &erroff, ngx_regex_compile_context);
+
+    /* ensure that there is no current pool */
+    ngx_regex_malloc_done();
+
+    if (re == NULL) {
+        pcre2_get_error_message(errcode, errstr, 128);
+
+        if ((size_t) erroff == rc->pattern.len) {
+            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+                              "pcre2_compile() failed: %s in \"%V\"",
+                               errstr, &rc->pattern)
+                          - rc->err.data;
+
+        } else {
+            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+                              "pcre2_compile() failed: %s in \"%V\" at \"%s\"",
+                               errstr, &rc->pattern, rc->pattern.data + erroff)
+                          - rc->err.data;
+        }
+
+        return NGX_ERROR;
+    }
+
+    rc->regex = re;
+
+    /* do not study at runtime */
+
+    if (ngx_regex_studies != NULL) {
+        elt = ngx_list_push(ngx_regex_studies);
+        if (elt == NULL) {
+            goto nomem;
+        }
+
+        elt->regex = rc->regex;
+        elt->name = rc->pattern.data;
+    }
+
+    n = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &rc->captures);
+    if (n < 0) {
+        p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_CAPTURECOUNT) failed: %d";
+        goto failed;
+    }
+
+    if (rc->captures == 0) {
+        return NGX_OK;
+    }
+
+    n = pcre2_pattern_info(re, PCRE2_INFO_NAMECOUNT, &rc->named_captures);
+    if (n < 0) {
+        p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_NAMECOUNT) failed: %d";
+        goto failed;
+    }
+
+    if (rc->named_captures == 0) {
+        return NGX_OK;
+    }
+
+    n = pcre2_pattern_info(re, PCRE2_INFO_NAMEENTRYSIZE, &rc->name_size);
+    if (n < 0) {
+        p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_NAMEENTRYSIZE) failed: %d";
+        goto failed;
+    }
+
+    n = pcre2_pattern_info(re, PCRE2_INFO_NAMETABLE, &rc->names);
+    if (n < 0) {
+        p = "pcre2_pattern_info(\"%V\", PCRE2_INFO_NAMETABLE) failed: %d";
+        goto failed;
+    }
+
+    return NGX_OK;
+
+failed:
+
+    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
+                  - rc->err.data;
+    return NGX_ERROR;
+
+nomem:
+
+    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+                               "regex \"%V\" compilation failed: no memory",
+                               &rc->pattern)
+                  - rc->err.data;
+    return NGX_ERROR;
 }
 
+#else
 
 ngx_int_t
 ngx_regex_compile(ngx_regex_compile_t *rc)
@@ -98,11 +270,30 @@ ngx_regex_compile(ngx_regex_compile_t *rc)
     char             *p;
     pcre             *re;
     const char       *errstr;
+    ngx_uint_t        options;
     ngx_regex_elt_t  *elt;
 
+    options = 0;
+
+    if (rc->options & NGX_REGEX_CASELESS) {
+        options |= PCRE_CASELESS;
+    }
+
+    if (rc->options & NGX_REGEX_MULTILINE) {
+        options |= PCRE_MULTILINE;
+    }
+
+    if (rc->options & ~(NGX_REGEX_CASELESS|NGX_REGEX_MULTILINE)) {
+        rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+                            "regex \"%V\" compilation failed: invalid options",
+                            &rc->pattern)
+                      - rc->err.data;
+        return NGX_ERROR;
+    }
+
     ngx_regex_malloc_init(rc->pool);
 
-    re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
+    re = pcre_compile((const char *) rc->pattern.data, (int) options,
                       &errstr, &erroff, NULL);
 
     /* ensure that there is no current pool */
@@ -113,13 +304,13 @@ ngx_regex_compile(ngx_regex_compile_t *rc)
            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                               "pcre_compile() failed: %s in \"%V\"",
                                errstr, &rc->pattern)
-                      - rc->err.data;
+                         - rc->err.data;
 
         } else {
            rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                               "pcre_compile() failed: %s in \"%V\" at \"%s\"",
                                errstr, &rc->pattern, rc->pattern.data + erroff)
-                      - rc->err.data;
+                         - rc->err.data;
         }
 
         return NGX_ERROR;
@@ -134,8 +325,8 @@ ngx_regex_compile(ngx_regex_compile_t *rc)
 
     /* do not study at runtime */
 
-    if (ngx_pcre_studies != NULL) {
-        elt = ngx_list_push(ngx_pcre_studies);
+    if (ngx_regex_studies != NULL) {
+        elt = ngx_list_push(ngx_regex_studies);
         if (elt == NULL) {
             goto nomem;
         }
@@ -193,6 +384,83 @@ ngx_regex_compile(ngx_regex_compile_t *rc)
     return NGX_ERROR;
 }
 
+#endif
+
+
+#if (NGX_PCRE2)
+
+ngx_int_t
+ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_uint_t size)
+{
+    size_t      *ov;
+    ngx_int_t    rc;
+    ngx_uint_t   n, i;
+
+    /*
+     * The pcre2_match() function might allocate memory for backtracking
+     * frames, typical allocations are from 40k and above.  So the allocator
+     * is configured to do direct allocations from heap during matching.
+     */
+
+    ngx_regex_malloc_init(NULL);
+
+    if (ngx_regex_match_data == NULL
+        || size > ngx_regex_match_data_size)
+    {
+        /*
+         * Allocate a match data if not yet allocated or smaller than
+         * needed.
+         */
+
+        if (ngx_regex_match_data) {
+            pcre2_match_data_free(ngx_regex_match_data);
+        }
+
+        ngx_regex_match_data_size = size;
+        ngx_regex_match_data = pcre2_match_data_create(size / 3, NULL);
+
+        if (ngx_regex_match_data == NULL) {
+            rc = PCRE2_ERROR_NOMEMORY;
+            goto failed;
+        }
+    }
+
+    rc = pcre2_match(re, s->data, s->len, 0, 0, ngx_regex_match_data, NULL);
+
+    if (rc < 0) {
+        goto failed;
+    }
+
+    n = pcre2_get_ovector_count(ngx_regex_match_data);
+    ov = pcre2_get_ovector_pointer(ngx_regex_match_data);
+
+    if (n > size / 3) {
+        n = size / 3;
+    }
+
+    for (i = 0; i < n; i++) {
+        captures[i * 2] = ov[i * 2];
+        captures[i * 2 + 1] = ov[i * 2 + 1];
+    }
+
+failed:
+
+    ngx_regex_malloc_done();
+
+    return rc;
+}
+
+#else
+
+ngx_int_t
+ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_uint_t size)
+{
+    return pcre_exec(re->code, re->extra, (const char *) s->data, s->len,
+                     0, 0, captures, size);
+}
+
+#endif
+
 
 ngx_int_t
 ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log)
@@ -227,14 +495,40 @@ ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log)
 }
 
 
+#if (NGX_PCRE2)
+
 static void * ngx_libc_cdecl
-ngx_regex_malloc(size_t size)
+ngx_regex_malloc(size_t size, void *data)
 {
-    ngx_pool_t      *pool;
-    pool = ngx_pcre_pool;
+    if (ngx_regex_pool) {
+        return ngx_palloc(ngx_regex_pool, size);
+    }
 
-    if (pool) {
-        return ngx_palloc(pool, size);
+    if (ngx_regex_direct_alloc) {
+        return ngx_alloc(size, ngx_cycle->log);
+    }
+
+    return NULL;
+}
+
+
+static void ngx_libc_cdecl
+ngx_regex_free(void *p, void *data)
+{
+    if (ngx_regex_direct_alloc) {
+        ngx_free(p);
+    }
+
+    return;
+}
+
+#else
+
+static void * ngx_libc_cdecl
+ngx_regex_malloc(size_t size)
+{
+    if (ngx_regex_pool) {
+        return ngx_palloc(ngx_regex_pool, size);
     }
 
     return NULL;
@@ -247,19 +541,20 @@ ngx_regex_free(void *p)
     return;
 }
 
+#endif
 
-#if (NGX_HAVE_PCRE_JIT)
 
 static void
-ngx_pcre_free_studies(void *data)
+ngx_regex_cleanup(void *data)
 {
-    ngx_list_t *studies = data;
+#if (NGX_PCRE2 || NGX_HAVE_PCRE_JIT)
+    ngx_regex_conf_t *rcf = data;
 
     ngx_uint_t        i;
     ngx_list_part_t  *part;
     ngx_regex_elt_t  *elts;
 
-    part = &studies->part;
+    part = &rcf->studies->part;
     elts = part->elts;
 
     for (i = 0; /* void */ ; i++) {
@@ -274,56 +569,83 @@ ngx_pcre_free_studies(void *data)
             i = 0;
         }
 
+        /*
+         * The PCRE JIT compiler uses mmap for its executable codes, so we
+         * have to explicitly call the pcre_free_study() function to free
+         * this memory.  In PCRE2, we call the pcre2_code_free() function
+         * for the same reason.
+         */
+
+#if (NGX_PCRE2)
+        pcre2_code_free(elts[i].regex);
+#else
         if (elts[i].regex->extra != NULL) {
             pcre_free_study(elts[i].regex->extra);
         }
+#endif
+    }
+#endif
+
+    /*
+     * On configuration parsing errors ngx_regex_module_init() will not
+     * be called.  Make sure ngx_regex_studies is properly cleared anyway.
+     */
+
+    ngx_regex_studies = NULL;
+
+#if (NGX_PCRE2)
+
+    /*
+     * Free compile context and match data.  If needed at runtime by
+     * the new cycle, these will be re-allocated.
+     */
+
+    if (ngx_regex_compile_context) {
+        pcre2_compile_context_free(ngx_regex_compile_context);
+        ngx_regex_compile_context = NULL;
+    }
+
+    if (ngx_regex_match_data) {
+        pcre2_match_data_free(ngx_regex_match_data);
+        ngx_regex_match_data = NULL;
+        ngx_regex_match_data_size = 0;
     }
-}
 
 #endif
+}
 
 
 static ngx_int_t
 ngx_regex_module_init(ngx_cycle_t *cycle)
 {
-    int               opt;
-    const char       *errstr;
-    ngx_uint_t        i;
-    ngx_list_part_t  *part;
-    ngx_regex_elt_t  *elts;
+    int                opt;
+#if !(NGX_PCRE2)
+    const char        *errstr;
+#endif
+    ngx_uint_t         i;
+    ngx_list_part_t   *part;
+    ngx_regex_elt_t   *elts;
+    ngx_regex_conf_t  *rcf;
 
     opt = 0;
 
-#if (NGX_HAVE_PCRE_JIT)
-    {
-    ngx_regex_conf_t    *rcf;
-    ngx_pool_cleanup_t  *cln;
-
     rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module);
 
+#if (NGX_PCRE2 || NGX_HAVE_PCRE_JIT)
+
     if (rcf->pcre_jit) {
+#if (NGX_PCRE2)
+        opt = 1;
+#else
         opt = PCRE_STUDY_JIT_COMPILE;
-
-        /*
-         * The PCRE JIT compiler uses mmap for its executable codes, so we
-         * have to explicitly call the pcre_free_study() function to free
-         * this memory.
-         */
-
-        cln = ngx_pool_cleanup_add(cycle->pool, 0);
-        if (cln == NULL) {
-            return NGX_ERROR;
-        }
-
-        cln->handler = ngx_pcre_free_studies;
-        cln->data = ngx_pcre_studies;
-    }
+#endif
     }
+
 #endif
 
     ngx_regex_malloc_init(cycle->pool);
 
-    part = &ngx_pcre_studies->part;
+    part = &rcf->studies->part;
     elts = part->elts;
 
     for (i = 0; /* void */ ; i++) {
@@ -338,6 +660,23 @@ ngx_regex_module_init(ngx_cycle_t *cycle)
             i = 0;
         }
 
+#if (NGX_PCRE2)
+
+        if (opt) {
+            int  n;
+
+            n = pcre2_jit_compile(elts[i].regex, PCRE2_JIT_COMPLETE);
+
+            if (n != 0) {
+                ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
+                              "pcre2_jit_compile() failed: %d in \"%s\", "
+                              "ignored",
+                              n, elts[i].name);
+            }
+        }
+
+#else
+
         elts[i].regex->extra = pcre_study(elts[i].regex->code, opt, &errstr);
 
         if (errstr != NULL) {
@@ -360,12 +699,16 @@ ngx_regex_module_init(ngx_cycle_t *cycle)
                               elts[i].name);
             }
         }
+#endif
 #endif
     }
 
     ngx_regex_malloc_done();
 
-    ngx_pcre_studies = NULL;
+    ngx_regex_studies = NULL;
+#if (NGX_PCRE2)
+    ngx_regex_compile_context = NULL;
+#endif
 
     return NGX_OK;
 }
@@ -374,7 +717,8 @@ ngx_regex_module_init(ngx_cycle_t *cycle)
 static void *
 ngx_regex_create_conf(ngx_cycle_t *cycle)
 {
-    ngx_regex_conf_t  *rcf;
+    ngx_regex_conf_t    *rcf;
+    ngx_pool_cleanup_t  *cln;
 
     rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t));
     if (rcf == NULL) {
@@ -383,11 +727,21 @@ ngx_regex_create_conf(ngx_cycle_t *cycle)
 
     rcf->pcre_jit = NGX_CONF_UNSET;
 
-    ngx_pcre_studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t));
-    if (ngx_pcre_studies == NULL) {
+    cln = ngx_pool_cleanup_add(cycle->pool, 0);
+    if (cln == NULL) {
         return NULL;
     }
 
+    cln->handler = ngx_regex_cleanup;
+    cln->data = rcf;
+
+    rcf->studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t));
+    if (rcf->studies == NULL) {
+        return NULL;
+    }
+
+    ngx_regex_studies = rcf->studies;
+
     return rcf;
 }
 
@@ -412,7 +766,21 @@ ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data)
         return NGX_CONF_OK;
     }
 
-#if (NGX_HAVE_PCRE_JIT)
+#if (NGX_PCRE2)
+    {
+    int       r;
+    uint32_t  jit;
+
+    jit = 0;
+    r = pcre2_config(PCRE2_CONFIG_JIT, &jit);
+
+    if (r != 0 || jit != 1) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "PCRE2 library does not support JIT");
+        *fp = 0;
+    }
+    }
+#elif (NGX_HAVE_PCRE_JIT)
     {
     int  jit, r;
 
diff --git a/src/core/ngx_regex.h b/src/core/ngx_regex.h
index 680486c81..182373a22 100644
--- a/src/core/ngx_regex.h
+++ b/src/core/ngx_regex.h
@@ -12,24 +12,38 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 
-#include <pcre.h>
 
+#if (NGX_PCRE2)
+
+#define PCRE2_CODE_UNIT_WIDTH  8
+#include <pcre2.h>
+
+#define NGX_REGEX_NO_MATCHED   PCRE2_ERROR_NOMATCH   /* -1 */
 
-#define NGX_REGEX_NO_MATCHED  PCRE_ERROR_NOMATCH   /* -1 */
+typedef pcre2_code  ngx_regex_t;
 
-#define NGX_REGEX_CASELESS    PCRE_CASELESS
+#else
+
+#include <pcre.h>
 
+#define NGX_REGEX_NO_MATCHED   PCRE_ERROR_NOMATCH    /* -1 */
 
 typedef struct {
     pcre        *code;
     pcre_extra  *extra;
 } ngx_regex_t;
 
+#endif
+
+
+#define NGX_REGEX_CASELESS     0x00000001
+#define NGX_REGEX_MULTILINE    0x00000002
+
 
 typedef struct {
     ngx_str_t     pattern;
     ngx_pool_t   *pool;
-    ngx_int_t     options;
+    ngx_uint_t    options;
 
     ngx_regex_t  *regex;
     int           captures;
@@ -49,10 +63,14 @@ typedef struct {
 void ngx_regex_init(void);
 ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc);
 
-#define ngx_regex_exec(re, s, captures, size)                                \
-    pcre_exec(re->code, re->extra, (const char *) (s)->data, (s)->len, 0, 0, \
-              captures, size)
-#define ngx_regex_exec_n      "pcre_exec()"
+ngx_int_t ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures,
+    ngx_uint_t size);
+
+#if (NGX_PCRE2)
+#define ngx_regex_exec_n       "pcre2_match()"
+#else
+#define ngx_regex_exec_n       "pcre_exec()"
+#endif
 
 ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log);
 
diff --git a/src/core/ngx_rwlock.c b/src/core/ngx_rwlock.c
index ed2b0f810..e7da8a8ec 100644
--- a/src/core/ngx_rwlock.c
+++ b/src/core/ngx_rwlock.c
@@ -89,22 +89,10 @@ ngx_rwlock_rlock(ngx_atomic_t *lock)
 void
 ngx_rwlock_unlock(ngx_atomic_t *lock)
 {
-    ngx_atomic_uint_t  readers;
-
-    readers = *lock;
-
-    if (readers == NGX_RWLOCK_WLOCK) {
+    if (*lock == NGX_RWLOCK_WLOCK) {
         (void) ngx_atomic_cmp_set(lock, NGX_RWLOCK_WLOCK, 0);
-        return;
-    }
-
-    for ( ;; ) {
-
-        if (ngx_atomic_cmp_set(lock, readers, readers - 1)) {
-            return;
-        }
-
-        readers = *lock;
+    } else {
+        (void) ngx_atomic_fetch_add(lock, -1);
     }
 }
 
diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c
index 0d187ca33..47229b507 100644
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -55,6 +55,7 @@ ngx_uint_t            ngx_accept_events;
 ngx_uint_t            ngx_accept_mutex_held;
 ngx_msec_t            ngx_accept_mutex_delay;
 ngx_int_t             ngx_accept_disabled;
+ngx_uint_t            ngx_use_exclusive_accept;
 
 
 #if (NGX_STAT_STUB)
@@ -644,6 +645,8 @@ ngx_event_process_init(ngx_cycle_t *cycle)
 
 #endif
 
+    ngx_use_exclusive_accept = 0;
+
     ngx_queue_init(&ngx_posted_accept_events);
     ngx_queue_init(&ngx_posted_next_events);
     ngx_queue_init(&ngx_posted_events);
@@ -889,6 +892,8 @@ ngx_event_process_init(ngx_cycle_t *cycle)
         if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)
             && ccf->worker_processes > 1)
         {
+            ngx_use_exclusive_accept = 1;
+
             if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
                 == NGX_ERROR)
             {
diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h
index eb17346b2..f2bd071ed 100644
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -147,10 +147,6 @@ struct ngx_event_aio_s {
 
     ngx_fd_t                   fd;
 
-#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
-    ssize_t                  (*preload_handler)(ngx_buf_t *file);
-#endif
-
 #if (NGX_HAVE_EVENTFD)
     int64_t                    res;
 #endif
@@ -468,6 +464,7 @@ extern ngx_uint_t             ngx_accept_events;
 extern ngx_uint_t             ngx_accept_mutex_held;
 extern ngx_msec_t             ngx_accept_mutex_delay;
 extern ngx_int_t              ngx_accept_disabled;
+extern ngx_uint_t             ngx_use_exclusive_accept;
 
 
 #if (NGX_STAT_STUB)
diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c
index b05666c76..27038799d 100644
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -11,6 +11,9 @@
 
 
 static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all);
+#if (NGX_HAVE_EPOLLEXCLUSIVE)
+static void ngx_reorder_accept_events(ngx_listening_t *ls);
+#endif
 static void ngx_close_accepted_connection(ngx_connection_t *c);
 
 
@@ -314,6 +317,10 @@ ngx_event_accept(ngx_event_t *ev)
         }
 
     } while (ev->available);
+
+#if (NGX_HAVE_EPOLLEXCLUSIVE)
+    ngx_reorder_accept_events(ls);
+#endif
 }
 
 
@@ -420,6 +427,57 @@ ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all)
 }
 
 
+#if (NGX_HAVE_EPOLLEXCLUSIVE)
+
+static void
+ngx_reorder_accept_events(ngx_listening_t *ls)
+{
+    ngx_connection_t  *c;
+
+    /*
+     * Linux with EPOLLEXCLUSIVE usually notifies only the process which
+     * was first to add the listening socket to the epoll instance.  As
+     * a result most of the connections are handled by the first worker
+     * process.  To fix this, we re-add the socket periodically, so other
+     * workers will get a chance to accept connections.
+     */
+
+    if (!ngx_use_exclusive_accept) {
+        return;
+    }
+
+#if (NGX_HAVE_REUSEPORT)
+
+    if (ls->reuseport) {
+        return;
+    }
+
+#endif
+
+    c = ls->connection;
+
+    if (c->requests++ % 16 != 0
+        && ngx_accept_disabled <= 0)
+    {
+        return;
+    }
+
+    if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)
+        == NGX_ERROR)
+    {
+        return;
+    }
+
+    if (ngx_add_event(c->read, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
+        == NGX_ERROR)
+    {
+        return;
+    }
+}
+
+#endif
+
+
 static void
 ngx_close_accepted_connection(ngx_connection_t *c)
 {
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 393ec9d57..1789e5d3a 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1383,6 +1383,9 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
     if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) {
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                       "SSL_CTX_set0_tmp_dh_pkey(\%s\") failed", file->data);
+#if (OPENSSL_VERSION_NUMBER >= 0x3000001fL)
+        EVP_PKEY_free(dh);
+#endif
         BIO_free(bio);
         return NGX_ERROR;
     }
@@ -2981,7 +2984,7 @@ ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
 {
 #ifdef BIO_get_ktls_send
 
-    int        sslerr;
+    int        sslerr, flags;
     ssize_t    n;
     ngx_err_t  err;
 
@@ -2993,8 +2996,20 @@ ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
 
     ngx_set_errno(0);
 
+#if (NGX_HAVE_SENDFILE_NODISKIO)
+
+    flags = (c->busy_count <= 2) ? SF_NODISKIO : 0;
+
+    if (file->file->directio) {
+        flags |= SF_NOCACHE;
+    }
+
+#else
+    flags = 0;
+#endif
+
     n = SSL_sendfile(c->ssl->connection, file->file->fd, file->file_pos,
-                     size, 0);
+                     size, flags);
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_sendfile: %d", n);
 
@@ -3013,6 +3028,10 @@ ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
             ngx_post_event(c->read, &ngx_posted_events);
         }
 
+#if (NGX_HAVE_SENDFILE_NODISKIO)
+        c->busy_count = 0;
+#endif
+
         c->sent += n;
 
         return n;
@@ -3077,6 +3096,23 @@ ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
             ngx_post_event(c->read, &ngx_posted_events);
         }
 
+#if (NGX_HAVE_SENDFILE_NODISKIO)
+
+        if (ngx_errno == EBUSY) {
+            c->busy_count++;
+
+            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                           "SSL_sendfile() busy, count:%d", c->busy_count);
+
+            if (c->write->posted) {
+                ngx_delete_posted_event(c->write);
+            }
+
+            ngx_post_event(c->write, &ngx_posted_next_events);
+        }
+
+#endif
+
         c->write->ready = 0;
         return NGX_AGAIN;
     }
@@ -4454,7 +4490,21 @@ ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
             return -1;
         }
 
-        return (i == 0) ? 1 : 2 /* renew */;
+        /* renew if TLSv1.3 */
+
+#ifdef TLS1_3_VERSION
+        if (SSL_version(ssl_conn) == TLS1_3_VERSION) {
+            return 2;
+        }
+#endif
+
+        /* renew if non-default key */
+
+        if (i != 0) {
+            return 2;
+        }
+
+        return 1;
     }
 }
 
@@ -4772,6 +4822,42 @@ ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
 }
 
 
+ngx_int_t
+ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+#ifdef SSL_get_negotiated_group
+
+    int  nid;
+
+    nid = SSL_get_negotiated_group(c->ssl->connection);
+
+    if (nid != NID_undef) {
+
+        if ((nid & TLSEXT_nid_unknown) == 0) {
+            s->len = ngx_strlen(OBJ_nid2sn(nid));
+            s->data = (u_char *) OBJ_nid2sn(nid);
+            return NGX_OK;
+        }
+
+        s->len = sizeof("0x0000") - 1;
+
+        s->data = ngx_pnalloc(pool, s->len);
+        if (s->data == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_sprintf(s->data, "0x%04xd", nid & 0xffff);
+
+        return NGX_OK;
+    }
+
+#endif
+
+    s->len = 0;
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
 {
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 80ee48d5f..d46ccde3a 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -269,6 +269,8 @@ ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
+ngx_int_t ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
 ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index 6842b7c6e..864fc4fda 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -3180,10 +3180,10 @@ ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
             ctx->field_rest -= size;
 
             if (ctx->field_huffman) {
-                if (ngx_http_v2_huff_decode(&ctx->field_state, p, size,
-                                            &ctx->field_end,
-                                            ctx->field_rest == 0,
-                                            r->connection->log)
+                if (ngx_http_huff_decode(&ctx->field_state, p, size,
+                                         &ctx->field_end,
+                                         ctx->field_rest == 0,
+                                         r->connection->log)
                     != NGX_OK)
                 {
                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -3289,10 +3289,10 @@ ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
             ctx->field_rest -= size;
 
             if (ctx->field_huffman) {
-                if (ngx_http_v2_huff_decode(&ctx->field_state, p, size,
-                                            &ctx->field_end,
-                                            ctx->field_rest == 0,
-                                            r->connection->log)
+                if (ngx_http_huff_decode(&ctx->field_state, p, size,
+                                         &ctx->field_end,
+                                         ctx->field_rest == 0,
+                                         r->connection->log)
                     != NGX_OK)
                 {
                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index e94e11b01..fb9761ed4 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -384,6 +384,9 @@ static ngx_http_variable_t  ngx_http_ssl_vars[] = {
     { ngx_string("ssl_ciphers"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_ciphers, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_curve"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_curve, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
     { ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
index 444f93536..7bb881ec2 100644
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -171,6 +171,14 @@ ngx_uint_t  ngx_http_degraded(ngx_http_request_t *);
 #endif
 
 
+#if (NGX_HTTP_V2)
+ngx_int_t ngx_http_huff_decode(u_char *state, u_char *src, size_t len,
+    u_char **dst, ngx_uint_t last, ngx_log_t *log);
+size_t ngx_http_huff_encode(u_char *src, size_t len, u_char *dst,
+    ngx_uint_t lower);
+#endif
+
+
 extern ngx_module_t  ngx_http_module;
 
 extern ngx_str_t  ngx_http_html_default_types[];
diff --git a/src/http/ngx_http_copy_filter_module.c b/src/http/ngx_http_copy_filter_module.c
index c8ad5daee..bd3028bc7 100644
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -19,10 +19,6 @@ typedef struct {
 static void ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx,
     ngx_file_t *file);
 static void ngx_http_copy_aio_event_handler(ngx_event_t *ev);
-#if (NGX_HAVE_AIO_SENDFILE)
-static ssize_t ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file);
-static void ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev);
-#endif
 #endif
 #if (NGX_THREADS)
 static ngx_int_t ngx_http_copy_thread_handler(ngx_thread_task_t *task,
@@ -128,9 +124,6 @@ ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
 #if (NGX_HAVE_FILE_AIO)
         if (ngx_file_aio && clcf->aio == NGX_HTTP_AIO_ON) {
             ctx->aio_handler = ngx_http_copy_aio_handler;
-#if (NGX_HAVE_AIO_SENDFILE)
-            ctx->aio_preload = ngx_http_copy_aio_sendfile_preload;
-#endif
         }
 #endif
 
@@ -207,53 +200,6 @@ ngx_http_copy_aio_event_handler(ngx_event_t *ev)
     ngx_http_run_posted_requests(c);
 }
 
-
-#if (NGX_HAVE_AIO_SENDFILE)
-
-static ssize_t
-ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file)
-{
-    ssize_t                  n;
-    static u_char            buf[1];
-    ngx_event_aio_t         *aio;
-    ngx_http_request_t      *r;
-    ngx_output_chain_ctx_t  *ctx;
-
-    n = ngx_file_aio_read(file->file, buf, 1, file->file_pos, NULL);
-
-    if (n == NGX_AGAIN) {
-        aio = file->file->aio;
-        aio->handler = ngx_http_copy_aio_sendfile_event_handler;
-
-        r = aio->data;
-        r->main->blocked++;
-        r->aio = 1;
-
-        ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
-        ctx->aio = 1;
-    }
-
-    return n;
-}
-
-
-static void
-ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)
-{
-    ngx_event_aio_t     *aio;
-    ngx_http_request_t  *r;
-
-    aio = ev->data;
-    r = aio->data;
-
-    r->main->blocked--;
-    r->aio = 0;
-    ev->complete = 0;
-
-    r->connection->write->handler(r->connection->write);
-}
-
-#endif
 #endif
 
 
@@ -263,6 +209,7 @@ static ngx_int_t
 ngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
 {
     ngx_str_t                  name;
+    ngx_connection_t          *c;
     ngx_thread_pool_t         *tp;
     ngx_http_request_t        *r;
     ngx_output_chain_ctx_t    *ctx;
@@ -270,6 +217,27 @@ ngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
 
     r = file->thread_ctx;
 
+    if (r->aio) {
+        /*
+         * tolerate sendfile() calls if another operation is already
+         * running; this can happen due to subrequests, multiple calls
+         * of the next body filter from a filter, or in HTTP/2 due to
+         * a write event on the main connection
+         */
+
+        c = r->connection;
+
+#if (NGX_HTTP_V2)
+        if (r->stream) {
+            c = r->stream->connection->connection;
+        }
+#endif
+
+        if (task == c->sendfile_task) {
+            return NGX_OK;
+        }
+    }
+
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     tp = clcf->thread_pool;
 
@@ -323,6 +291,20 @@ ngx_http_copy_thread_event_handler(ngx_event_t *ev)
     r->main->blocked--;
     r->aio = 0;
 
+#if (NGX_HTTP_V2)
+
+    if (r->stream) {
+        /*
+         * for HTTP/2, update write event to make sure processing will
+         * reach the main connection to handle sendfile() in threads
+         */
+
+        c->write->ready = 1;
+        c->write->active = 0;
+    }
+
+#endif
+
     if (r->done) {
         /*
          * trigger connection event handler if the subrequest was
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index c4ff42044..07c6cc4d4 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -4575,19 +4575,6 @@ ngx_http_core_set_aio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 #endif
     }
 
-#if (NGX_HAVE_AIO_SENDFILE)
-
-    if (ngx_strcmp(value[1].data, "sendfile") == 0) {
-        clcf->aio = NGX_HTTP_AIO_ON;
-
-        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                           "the \"sendfile\" parameter of "
-                           "the \"aio\" directive is deprecated");
-        return NGX_CONF_OK;
-    }
-
-#endif
-
     if (ngx_strncmp(value[1].data, "threads", 7) == 0
         && (value[1].len == 7 || value[1].data[7] == '='))
     {
diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c
index 9b8940590..76f6e9629 100644
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -197,6 +197,10 @@ ngx_http_header_filter(ngx_http_request_t *r)
         }
     }
 
+    if (r->keepalive && (ngx_terminate || ngx_exiting)) {
+        r->keepalive = 0;
+    }
+
     len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1
           /* the end of the header */
           + sizeof(CRLF) - 1;
diff --git a/src/http/v2/ngx_http_v2_huff_decode.c b/src/http/ngx_http_huff_decode.c
similarity index 99%
rename from src/http/v2/ngx_http_v2_huff_decode.c
rename to src/http/ngx_http_huff_decode.c
index 49ca576f7..14b7b7896 100644
--- a/src/http/v2/ngx_http_v2_huff_decode.c
+++ b/src/http/ngx_http_huff_decode.c
@@ -15,14 +15,14 @@ typedef struct {
     u_char  emit;
     u_char  sym;
     u_char  ending;
-} ngx_http_v2_huff_decode_code_t;
+} ngx_http_huff_decode_code_t;
 
 
-static ngx_inline ngx_int_t ngx_http_v2_huff_decode_bits(u_char *state,
+static ngx_inline ngx_int_t ngx_http_huff_decode_bits(u_char *state,
     u_char *ending, ngx_uint_t bits, u_char **dst);
 
 
-static ngx_http_v2_huff_decode_code_t  ngx_http_v2_huff_decode_codes[256][16] =
+static ngx_http_huff_decode_code_t  ngx_http_huff_decode_codes[256][16] =
 {
     /* 0 */
     {
@@ -2640,7 +2640,7 @@ static ngx_http_v2_huff_decode_code_t  ngx_http_v2_huff_decode_codes[256][16] =
 
 
 ngx_int_t
-ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
+ngx_http_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
     ngx_uint_t last, ngx_log_t *log)
 {
     u_char  *end, ch, ending;
@@ -2653,7 +2653,7 @@ ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
     while (src != end) {
         ch = *src++;
 
-        if (ngx_http_v2_huff_decode_bits(state, &ending, ch >> 4, dst)
+        if (ngx_http_huff_decode_bits(state, &ending, ch >> 4, dst)
             != NGX_OK)
         {
             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
@@ -2663,7 +2663,7 @@ ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
             return NGX_ERROR;
         }
 
-        if (ngx_http_v2_huff_decode_bits(state, &ending, ch & 0xf, dst)
+        if (ngx_http_huff_decode_bits(state, &ending, ch & 0xf, dst)
             != NGX_OK)
         {
             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
@@ -2692,12 +2692,12 @@ ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
 
 
 static ngx_inline ngx_int_t
-ngx_http_v2_huff_decode_bits(u_char *state, u_char *ending, ngx_uint_t bits,
+ngx_http_huff_decode_bits(u_char *state, u_char *ending, ngx_uint_t bits,
     u_char **dst)
 {
-    ngx_http_v2_huff_decode_code_t  code;
+    ngx_http_huff_decode_code_t  code;
 
-    code = ngx_http_v2_huff_decode_codes[*state][bits];
+    code = ngx_http_huff_decode_codes[*state][bits];
 
     if (code.next == *state) {
         return NGX_ERROR;
diff --git a/src/http/v2/ngx_http_v2_huff_encode.c b/src/http/ngx_http_huff_encode.c
similarity index 93%
rename from src/http/v2/ngx_http_v2_huff_encode.c
rename to src/http/ngx_http_huff_encode.c
index 3f822cd0b..c03b153da 100644
--- a/src/http/v2/ngx_http_v2_huff_encode.c
+++ b/src/http/ngx_http_huff_encode.c
@@ -14,10 +14,10 @@
 typedef struct {
     uint32_t  code;
     uint32_t  len;
-} ngx_http_v2_huff_encode_code_t;
+} ngx_http_huff_encode_code_t;
 
 
-static ngx_http_v2_huff_encode_code_t  ngx_http_v2_huff_encode_table[256] =
+static ngx_http_huff_encode_code_t  ngx_http_huff_encode_table[256] =
 {
     {0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28},
     {0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28},
@@ -87,7 +87,7 @@ static ngx_http_v2_huff_encode_code_t  ngx_http_v2_huff_encode_table[256] =
 
 
 /* same as above, but embeds lowercase transformation */
-static ngx_http_v2_huff_encode_code_t  ngx_http_v2_huff_encode_table_lc[256] =
+static ngx_http_huff_encode_code_t  ngx_http_huff_encode_table_lc[256] =
 {
     {0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28},
     {0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28},
@@ -161,10 +161,10 @@ static ngx_http_v2_huff_encode_code_t  ngx_http_v2_huff_encode_table_lc[256] =
 #if (NGX_HAVE_LITTLE_ENDIAN)
 
 #if (NGX_HAVE_GCC_BSWAP64)
-#define ngx_http_v2_huff_encode_buf(dst, buf)                                 \
+#define ngx_http_huff_encode_buf(dst, buf)                                    \
     (*(uint64_t *) (dst) = __builtin_bswap64(buf))
 #else
-#define ngx_http_v2_huff_encode_buf(dst, buf)                                 \
+#define ngx_http_huff_encode_buf(dst, buf)                                    \
     ((dst)[0] = (u_char) ((buf) >> 56),                                       \
      (dst)[1] = (u_char) ((buf) >> 48),                                       \
      (dst)[2] = (u_char) ((buf) >> 40),                                       \
@@ -176,28 +176,28 @@ static ngx_http_v2_huff_encode_code_t  ngx_http_v2_huff_encode_table_lc[256] =
 #endif
 
 #else /* !NGX_HAVE_LITTLE_ENDIAN */
-#define ngx_http_v2_huff_encode_buf(dst, buf)                                 \
+#define ngx_http_huff_encode_buf(dst, buf)                                    \
     (*(uint64_t *) (dst) = (buf))
 #endif
 
 #else /* NGX_PTR_SIZE == 4 */
 
-#define ngx_http_v2_huff_encode_buf(dst, buf)                                 \
+#define ngx_http_huff_encode_buf(dst, buf)                                    \
     (*(uint32_t *) (dst) = htonl(buf))
 
 #endif
 
 
 size_t
-ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower)
+ngx_http_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower)
 {
-    u_char                          *end;
-    size_t                           hlen;
-    ngx_uint_t                       buf, pending, code;
-    ngx_http_v2_huff_encode_code_t  *table, *next;
+    u_char                       *end;
+    size_t                        hlen;
+    ngx_uint_t                    buf, pending, code;
+    ngx_http_huff_encode_code_t  *table, *next;
 
-    table = lower ? ngx_http_v2_huff_encode_table_lc
-                  : ngx_http_v2_huff_encode_table;
+    table = lower ? ngx_http_huff_encode_table_lc
+                  : ngx_http_huff_encode_table;
     hlen = 0;
     buf = 0;
     pending = 0;
@@ -224,7 +224,7 @@ ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower)
 
         buf |= code >> pending;
 
-        ngx_http_v2_huff_encode_buf(&dst[hlen], buf);
+        ngx_http_huff_encode_buf(&dst[hlen], buf);
 
         hlen += sizeof(buf);
 
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 84f4acc1c..fd8728c28 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -3860,6 +3860,7 @@ ngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
 {
     ngx_str_t                  name;
     ngx_event_pipe_t          *p;
+    ngx_connection_t          *c;
     ngx_thread_pool_t         *tp;
     ngx_http_request_t        *r;
     ngx_http_core_loc_conf_t  *clcf;
@@ -3867,6 +3868,27 @@ ngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
     r = file->thread_ctx;
     p = r->upstream->pipe;
 
+    if (r->aio) {
+        /*
+         * tolerate sendfile() calls if another operation is already
+         * running; this can happen due to subrequests, multiple calls
+         * of the next body filter from a filter, or in HTTP/2 due to
+         * a write event on the main connection
+         */
+
+        c = r->connection;
+
+#if (NGX_HTTP_V2)
+        if (r->stream) {
+            c = r->stream->connection->connection;
+        }
+#endif
+
+        if (task == c->sendfile_task) {
+            return NGX_OK;
+        }
+    }
+
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     tp = clcf->thread_pool;
 
@@ -3918,6 +3940,20 @@ ngx_http_upstream_thread_event_handler(ngx_event_t *ev)
     r->main->blocked--;
     r->aio = 0;
 
+#if (NGX_HTTP_V2)
+
+    if (r->stream) {
+        /*
+         * for HTTP/2, update write event to make sure processing will
+         * reach the main connection to handle sendfile() in threads
+         */
+
+        c->write->ready = 1;
+        c->write->active = 0;
+    }
+
+#endif
+
     if (r->done) {
         /*
          * trigger connection event handler if the subrequest was
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index fe6416b1d..e11f17acf 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -1602,10 +1602,10 @@ ngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c, u_char *pos,
     h2c->state.length -= size;
     h2c->state.field_rest -= size;
 
-    if (ngx_http_v2_huff_decode(&h2c->state.field_state, pos, size,
-                                &h2c->state.field_end,
-                                h2c->state.field_rest == 0,
-                                h2c->connection->log)
+    if (ngx_http_huff_decode(&h2c->state.field_state, pos, size,
+                             &h2c->state.field_end,
+                             h2c->state.field_rest == 0,
+                             h2c->connection->log)
         != NGX_OK)
     {
         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
index 517a48856..49bc31952 100644
--- a/src/http/v2/ngx_http_v2.h
+++ b/src/http/v2/ngx_http_v2.h
@@ -368,12 +368,6 @@ ngx_int_t ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
 ngx_int_t ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size);
 
 
-ngx_int_t ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len,
-    u_char **dst, ngx_uint_t last, ngx_log_t *log);
-size_t ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst,
-    ngx_uint_t lower);
-
-
 #define ngx_http_v2_prefix(bits)  ((1 << (bits)) - 1)
 
 
diff --git a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c
index d1fb7217a..1961c0734 100644
--- a/src/http/v2/ngx_http_v2_encode.c
+++ b/src/http/v2/ngx_http_v2_encode.c
@@ -20,7 +20,7 @@ ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp,
 {
     size_t  hlen;
 
-    hlen = ngx_http_v2_huff_encode(src, len, tmp, lower);
+    hlen = ngx_http_huff_encode(src, len, tmp, lower);
 
     if (hlen > 0) {
         *dst = NGX_HTTP_V2_ENCODE_HUFF;
diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c
index 90993b6d4..77cf03cf9 100644
--- a/src/http/v2/ngx_http_v2_filter_module.c
+++ b/src/http/v2/ngx_http_v2_filter_module.c
@@ -1407,6 +1407,9 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
     size = 0;
 #endif
 
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+                   "http2 send chain: %p", in);
+
     while (in) {
         size = ngx_buf_size(in->buf);
 
@@ -1425,12 +1428,8 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
             return NGX_CHAIN_ERROR;
         }
 
-        if (stream->queued) {
-            fc->write->active = 1;
-            fc->write->ready = 0;
-
-        } else {
-            fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
+        if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
+            return NGX_CHAIN_ERROR;
         }
 
         return NULL;
@@ -1439,9 +1438,16 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
     h2c = stream->connection;
 
     if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
-        fc->write->active = 1;
-        fc->write->ready = 0;
-        return in;
+
+        if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
+            return NGX_CHAIN_ERROR;
+        }
+
+        if (ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
+            fc->write->active = 1;
+            fc->write->ready = 0;
+            return in;
+        }
     }
 
     if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
@@ -1784,6 +1790,11 @@ ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
 static ngx_inline ngx_int_t
 ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
 {
+    if (stream->queued == 0) {
+        fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
+        return NGX_OK;
+    }
+
     stream->blocked = 1;
 
     if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
diff --git a/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/os/unix/ngx_freebsd_sendfile_chain.c
index 3d415bd2c..5c6a830d8 100644
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -32,23 +32,22 @@
 ngx_chain_t *
 ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
 {
-    int               rc, flags;
-    off_t             send, prev_send, sent;
-    size_t            file_size;
-    ssize_t           n;
-    ngx_uint_t        eintr, eagain;
-    ngx_err_t         err;
-    ngx_buf_t        *file;
-    ngx_event_t      *wev;
-    ngx_chain_t      *cl;
-    ngx_iovec_t       header, trailer;
-    struct sf_hdtr    hdtr;
-    struct iovec      headers[NGX_IOVS_PREALLOCATE];
-    struct iovec      trailers[NGX_IOVS_PREALLOCATE];
-#if (NGX_HAVE_AIO_SENDFILE)
-    ngx_uint_t        ebusy;
-    ngx_event_aio_t  *aio;
+    int              rc, flags;
+    off_t            send, prev_send, sent;
+    size_t           file_size;
+    ssize_t          n;
+    ngx_err_t        err;
+    ngx_buf_t       *file;
+    ngx_uint_t       eintr, eagain;
+#if (NGX_HAVE_SENDFILE_NODISKIO)
+    ngx_uint_t       ebusy;
 #endif
+    ngx_event_t     *wev;
+    ngx_chain_t     *cl;
+    ngx_iovec_t      header, trailer;
+    struct sf_hdtr   hdtr;
+    struct iovec     headers[NGX_IOVS_PREALLOCATE];
+    struct iovec     trailers[NGX_IOVS_PREALLOCATE];
 
     wev = c->write;
 
@@ -77,11 +76,6 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
     eagain = 0;
     flags = 0;
 
-#if (NGX_HAVE_AIO_SENDFILE && NGX_SUPPRESS_WARN)
-    aio = NULL;
-    file = NULL;
-#endif
-
     header.iovs = headers;
     header.nalloc = NGX_IOVS_PREALLOCATE;
 
@@ -90,7 +84,7 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
 
     for ( ;; ) {
         eintr = 0;
-#if (NGX_HAVE_AIO_SENDFILE)
+#if (NGX_HAVE_SENDFILE_NODISKIO)
         ebusy = 0;
 #endif
         prev_send = send;
@@ -179,9 +173,14 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
 
             sent = 0;
 
-#if (NGX_HAVE_AIO_SENDFILE)
-            aio = file->file->aio;
-            flags = (aio && aio->preload_handler) ? SF_NODISKIO : 0;
+#if (NGX_HAVE_SENDFILE_NODISKIO)
+
+            flags = (c->busy_count <= 2) ? SF_NODISKIO : 0;
+
+            if (file->file->directio) {
+                flags |= SF_NOCACHE;
+            }
+
 #endif
 
             rc = sendfile(file->file->fd, c->fd, file->file_pos,
@@ -199,7 +198,7 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
                     eintr = 1;
                     break;
 
-#if (NGX_HAVE_AIO_SENDFILE)
+#if (NGX_HAVE_SENDFILE_NODISKIO)
                 case NGX_EBUSY:
                     ebusy = 1;
                     break;
@@ -252,54 +251,30 @@ ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
 
         in = ngx_chain_update_sent(in, sent);
 
-#if (NGX_HAVE_AIO_SENDFILE)
+#if (NGX_HAVE_SENDFILE_NODISKIO)
 
         if (ebusy) {
-            if (aio->event.active) {
-                /*
-                 * tolerate duplicate calls; they can happen due to subrequests
-                 * or multiple calls of the next body filter from a filter
-                 */
-
-                if (sent) {
-                    c->busy_count = 0;
-                }
-
-                return in;
-            }
-
             if (sent == 0) {
                 c->busy_count++;
 
-                if (c->busy_count > 2) {
-                    ngx_log_error(NGX_LOG_ALERT, c->log, 0,
-                                  "sendfile(%V) returned busy again",
-                                  &file->file->name);
-
-                    c->busy_count = 0;
-                    aio->preload_handler = NULL;
-
-                    send = prev_send;
-                    continue;
-                }
+                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                               "sendfile() busy, count:%d", c->busy_count);
 
             } else {
                 c->busy_count = 0;
             }
 
-            n = aio->preload_handler(file);
-
-            if (n > 0) {
-                send = prev_send + sent;
-                continue;
+            if (wev->posted) {
+                ngx_delete_posted_event(wev);
             }
 
+            ngx_post_event(wev, &ngx_posted_next_events);
+
+            wev->ready = 0;
             return in;
         }
 
-        if (flags == SF_NODISKIO) {
-            c->busy_count = 0;
-        }
+        c->busy_count = 0;
 
 #endif
 
diff --git a/src/os/unix/ngx_linux_sendfile_chain.c b/src/os/unix/ngx_linux_sendfile_chain.c
index 91e7f1d93..101d91a9a 100644
--- a/src/os/unix/ngx_linux_sendfile_chain.c
+++ b/src/os/unix/ngx_linux_sendfile_chain.c
@@ -379,15 +379,6 @@ ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size)
         return ctx->sent;
     }
 
-    if (task->event.active && ctx->file == file) {
-        /*
-         * tolerate duplicate calls; they can happen due to subrequests
-         * or multiple calls of the next body filter from a filter
-         */
-
-        return NGX_DONE;
-    }
-
     ctx->file = file;
     ctx->socket = c->fd;
     ctx->size = size;
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index 530fe8b3d..c5308322a 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -269,6 +269,9 @@ static ngx_stream_variable_t  ngx_stream_ssl_vars[] = {
     { ngx_string("ssl_ciphers"), NULL, ngx_stream_ssl_variable,
       (uintptr_t) ngx_ssl_get_ciphers, NGX_STREAM_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_curve"), NULL, ngx_stream_ssl_variable,
+      (uintptr_t) ngx_ssl_get_curve, NGX_STREAM_VAR_CHANGEABLE, 0 },
+
     { ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable,
       (uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 },