Hello everybody:

I'm experimenting a bit with plaintext HTTP/2 backends (for gRPC 😢) and
encountering some weird issues. My goal is to be able to support etcd,
which multiplexes a normal good API on HTTP/1.1 with a gRPC API on HTTP/2
on the same plaintext port. I'm doing my testing on HAproxy 2.2.0 so that I
can get the PRI upgrade support.

In order to pass through HTTP/2 to the backend, it seems like I need "proto
h2" on the `server` line? Otherwise I see that the frontend connection is
HTTP/2 and the backend connection is HTTP/1.1 (verified with tcpdump on the
load balancer and the backend), which does not work for the gRPC use case.
Is this expected? One would think that the h2-upgrade functionality would
upgrade both the frontend *and* the backend connection. And, of course,
when "proto h2" is on the server line, HTTP/1.1 requests fail entirely.

As an aside: when "proto h2" is on the server line, is there any way to do
a healthcheck with HTTP/1.1? It is... very annoying to construct a gRPC
healthcheck message using the http-check send language, but the -vvv output
indicates that there's no way to pass a non-h2 proto.

I have no inclination to provision thousands of TLS certificates for all
these internal use-cases and would love to just get HTTP/2 working over
plaintext, but it kind of seems like even under HAproxy 2.2 this is still
not really functional.

Initial config looks something like the following:

frontend sys-etcd_fe
    mode http
    option http-buffer-request
    option httplog
    option log-separate-errors
    timeout client 9s
    timeout client-fin 12s
    bind ::1:26462
    default_backend sys-etcd_be

backend sys-etcd_be
    mode http
    option forwardfor
    option redispatch
    retries 3
    timeout connect 4s
    timeout server 90s
    timeout tunnel 3600s
    server host1 10.132.15.129:26462 weight 16

This works fine for HTTP/1.1 but cannot be used to transit gRPC traffic
because it doesn't support plaintext HTTP/2. Even if the client connects
with HTTP/2 (e.g., with --http2 in curl, or with a gRPC client), that only
affects the frontend connection, not the backend connection.

The following config passes HTTP/2 but does not work for HTTP/1.1 at all:

frontend sys-etcd_fe
    mode http
    option http-buffer-request
    option httplog
    option log-separate-errors
    timeout client 9s
    timeout client-fin 12s
    bind ::1:26462
    default_backend sys-etcd_be

backend sys-etcd_be
    mode http
    option forwardfor
    option redispatch
    retries 3
    http-check connect proto h2
    http-check send meth POST uri /etcdserverpb.Maintenance/Status body
"00000" ver HTTP/2 hdr Host etcd hdr Content-Type application/grpc
    http-check expect status 200
    timeout connect 4s
    timeout server 90s
    timeout tunnel 3600s
    server host1 10.132.15.129:26462 weight 16 proto h2 check


This is the best config I've got so far, which feels like it shouldn't
work, but does:


frontend sys-etcd_fe
    mode http
    option http-buffer-request
    option httplog
    option log-separate-errors
    timeout client 9s
    timeout client-fin 12s
    bind ::1:26462
    use_backend sys-etcd_be_h2 if { fc_http_major eq 2 }
    default_backend sys-etcd_be_h1

backend sys-etc_be_h1
    mode http
    option forwardfor
    option redispatch
    option httpchk /health
    retries 3
    timeout connect 4s
    timeout server 90s
    timeout tunnel 3600s
    server host1 10.132.15.129:26462 weight 16 check

backend sys-etcd_be_h2
    mode http
    option forwardfor
    option redispatch
    retries 3
    http-check connect proto h2
    http-check send meth POST uri /etcdserverpb.Maintenance/Status body
"00000" ver HTTP/2 hdr Host etcd hdr Content-Type application/grpc
    http-check expect status 200
    timeout connect 4s
    timeout server 90s
    timeout tunnel 3600s
    server host1 10.132.15.129:26462 weight 16 proto h2 check

HAproxy version output:


$ /usr/sbin/haproxy  -vvv
HA-Proxy version 2.2.0 2020/07/07 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2
2025.
Known bugs: http://www.haproxy.org/bugs/bugs-2.2.0.html
Running on: Linux 5.4.15-1ep.el6.x86_64 #1 SMP Mon Jan 27 17:13:12 UTC 2020
x86_64
Build options :
  TARGET  = linux-glibc
  CPU     = generic
  CC      = gcc
  CFLAGS  = -m64 -march=x86-64 -O2 -g -Wall -Wextra
-Wdeclaration-after-statement -fwrapv -Wno-unused-label -Wno-sign-compare
-Wno-unused-parameter -Wno-clobbered -Wno-missing-field-initializers
-Wtype-limits
  OPTIONS = USE_PCRE=1 USE_OPENSSL=1 USE_LUA=1 USE_ZLIB=1 USE_TFO=1 USE_NS=

Feature list : +EPOLL -KQUEUE +NETFILTER +PCRE -PCRE_JIT -PCRE2 -PCRE2_JIT
+POLL -PRIVATE_CACHE +THREAD -PTHREAD_PSHARED +BACKTRACE -STATIC_PCRE
-STATIC_PCRE2 +TPROXY +LINUX_TPROXY +LINUX_SPLICE +LIBCRYPT +CRYPT_H
+GETADDRINFO +OPENSSL +LUA +FUTEX +ACCEPT4 +ZLIB -SLZ +CPU_AFFINITY +TFO
-NS +DL +RT -DEVICEATLAS -51DEGREES -WURFL -SYSTEMD -OBSOLETE_LINKER +PRCTL
+THREAD_DUMP -EVPORTS

Default settings :
  bufsize = 16384, maxrewrite = 1024, maxpollevents = 200

Built with multi-threading support (MAX_THREADS=64, default=2).
Built with OpenSSL version : OpenSSL 1.0.2u  20 Dec 2019
Running on OpenSSL version : OpenSSL 1.0.2u  20 Dec 2019
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports : SSLv3 TLSv1.0 TLSv1.1 TLSv1.2
Built with Lua version : Lua 5.3.5
Built with zlib version : 1.2.3
Running on zlib version : 1.2.3
Compression algorithms supported : identity("identity"),
deflate("deflate"), raw-deflate("deflate"), gzip("gzip")
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT
IP_FREEBIND
Built with PCRE version : 7.8 2008-09-05
Running on PCRE version : 7.8 2008-09-05
PCRE library supports JIT : no (USE_PCRE_JIT not set)
Encrypted password support via crypt(3): yes
Built with gcc compiler version 4.4.7 20120313 (Red Hat 4.4.7-23)

Available polling systems :
      epoll : pref=300,  test result OK
       poll : pref=200,  test result OK
     select : pref=150,  test result OK
Total: 3 (3 usable), will use epoll.

Available multiplexer protocols :
(protocols marked as <default> cannot be specified using 'proto' keyword)
            fcgi : mode=HTTP       side=BE        mux=FCGI
       <default> : mode=HTTP       side=FE|BE     mux=H1
              h2 : mode=HTTP       side=FE|BE     mux=H2
       <default> : mode=TCP        side=FE|BE     mux=PASS

Available services : none

Available filters :
[SPOE] spoe
[COMP] compression
[TRACE] trace
[CACHE] cache
[FCGI] fcgi-app
-- 
James Brown
Engineer

Reply via email to