caddy: `abort` directive returns http/500 on http/3

I think the easiest way to show what I mean by that is with an example:

localhost {
	abort
}
❯ curl -kI https://localhost
curl: (92) HTTP/2 stream 0 was not closed cleanly: INTERNAL_ERROR (err 2)

The connection via http/2 gets closed immediately, which is expected when using the abort directive. Excerpt from the docs (permalink):

Prevents any response to the client by immediately aborting the HTTP handler chain and closing the connection. Any concurrent, active HTTP streams on the same connection are interrupted.

The same config on http/3 however, yields a 500 Internal Server Error

❯ curl -kI https://localhost --http3
HTTP/3 500 
server: Caddy
alt-svc: h3=":443"; ma=2592000

Running Caddy with debug doesn’t return any meaningful insights this time, but QUIC_GO_LOG_LEVEL=INFO (as documented in https://github.com/lucas-clemente/quic-go/wiki/Logging) does:

2022/10/06 19:15:03.320	INFO	server HEAD localhost/
2022/10/06 19:15:03.320	INFO	server http: panic serving: net/http: abort Handler
goroutine 68 [running]:
github.com/lucas-clemente/quic-go/http3.(*Server).handleRequest.func2.1()
	github.com/lucas-clemente/quic-go@v0.28.2-0.20220813150001-9957668d4301/http3/server.go:583 +0x6f
panic({0x17e0de0, 0xc00007cbb0})
	runtime/panic.go:838 +0x207
github.com/caddyserver/caddy/v2/modules/caddyhttp.StaticResponse.ServeHTTP({{0x0, 0x0}, 0x0, {0x0, 0x0}, 0x0, 0x1}, {0x1dcc2e0?, 0xc00058ea00?}, 0xc00065c200, ...)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/staticresp.go:183 +0xa3d
github.com/caddyserver/caddy/v2/modules/caddyhttp.wrapMiddleware.func1.1({0x1dcc2e0?, 0xc00058ea00?}, 0x1dc27a0?)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/routes.go:290 +0x42
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x1dc27a0?, {0x1dcc2e0?, 0xc00058ea00?}, 0x0?)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyhttp.go:58 +0x2f
github.com/caddyserver/caddy/v2/modules/caddyhttp.wrapRoute.func1.1({0x1dcc2e0, 0xc00058ea00}, 0xc00065c200)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/routes.go:259 +0x3a8
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0xc0003f8000?, {0x1dcc2e0?, 0xc00058ea00?}, 0x1dc27a0?)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyhttp.go:58 +0x2f
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*Subroute).ServeHTTP(0xc00062b920, {0x1dcc2e0, 0xc00058ea00}, 0x178b301?, {0x1dc27a0, 0x1b55740})
	github.com/caddyserver/caddy/v2/modules/caddyhttp/subroute.go:74 +0x6d
github.com/caddyserver/caddy/v2/modules/caddyhttp.wrapMiddleware.func1.1({0x1dcc2e0?, 0xc00058ea00?}, 0x1dc27a0?)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/routes.go:290 +0x42
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x1dc27a0?, {0x1dcc2e0?, 0xc00058ea00?}, 0x7dc5ed?)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyhttp.go:58 +0x2f
github.com/caddyserver/caddy/v2/modules/caddyhttp.wrapRoute.func1.1({0x1dcc2e0, 0xc00058ea00}, 0xc00065c200)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/routes.go:259 +0x3a8
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0xc0005e6400?, {0x1dcc2e0?, 0xc00058ea00?}, 0x1995920?)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyhttp.go:58 +0x2f
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*Server).enforcementHandler(0x0?, {0x1dcc2e0?, 0xc00058ea00?}, 0xc000622ea0?, {0x1dc27a0?, 0xc00062b9a0?})
	github.com/caddyserver/caddy/v2/modules/caddyhttp/server.go:370 +0x252
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*Server).wrapPrimaryRoute.func1({0x1dcc2e0?, 0xc00058ea00?}, 0x4c7297?)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/server.go:346 +0x3b
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0xc0001f0750?, {0x1dcc2e0?, 0xc00058ea00?}, 0xc00065c200?)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyhttp.go:58 +0x2f
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*Server).ServeHTTP(0xc000345680, {0x1dcc2e0, 0xc00058ea00}, 0xc00065c200)
	github.com/caddyserver/caddy/v2/modules/caddyhttp/server.go:282 +0xb46
github.com/lucas-clemente/quic-go/http3.(*Server).handleRequest.func2(0x1dcf398?, 0xc0005bf080?, {0x1dbcbe0?, 0xc000345680?}, 0x1930460?, 0xc0005bf0b0?)
	github.com/lucas-clemente/quic-go@v0.28.2-0.20220813150001-9957668d4301/http3/server.go:588 +0x7d
github.com/lucas-clemente/quic-go/http3.(*Server).handleRequest(0xc000134a50, {0x1ddb720?, 0xc0004d8600}, {0x7f32a148c288, 0xc000352000}, 0x0?, 0xc000116810)
	github.com/lucas-clemente/quic-go@v0.28.2-0.20220813150001-9957668d4301/http3/server.go:589 +0xbd0
github.com/lucas-clemente/quic-go/http3.(*Server).handleConn.func1()
	github.com/lucas-clemente/quic-go@v0.28.2-0.20220813150001-9957668d4301/http3/server.go:432 +0xfd
created by github.com/lucas-clemente/quic-go/http3.(*Server).handleConn
	github.com/lucas-clemente/quic-go@v0.28.2-0.20220813150001-9957668d4301/http3/server.go:431 +0x1d7
2022/10/06 19:15:03.320	INFO	server Responding with 500
2022/10/06 19:15:03.321	INFO	server Peer closed connection with error: NO_ERROR
2022/10/06 19:15:03.321	INFO	server Connection 66dbb88190861fcb30249402ddbf06a8bd8165f3 closed.

The relevant code snippets seem to be: https://github.com/caddyserver/caddy/blob/498f32bab96ba00e1a7d04c6bc8b367c97d8a335/modules/caddyhttp/staticresp.go#L181-L184 and https://github.com/lucas-clemente/quic-go/blob/424a66389c01d10678bfb980cfe6faa8524b42b6/http3/server.go#L575-L595


My curl version, though browsers will run into that too and the curl version doesn’t really matter:

❯ curl --version
curl 7.85.0 (x86_64-pc-linux-gnu) libcurl/7.85.0 OpenSSL/3.0.5 zlib/1.2.12 brotli/1.0.9 zstd/1.5.2 libidn2/2.3.2 libssh2/1.10.0 nghttp2/1.49.0 ngtcp2/0.9.0 nghttp3/0.7.0
Release-Date: 2022-08-31
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp 
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTP3 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd

A slightly modified version for plain http

http://http1.localhost {
	abort
}

yields

❯ curl -I http://http1.localhost
curl: (52) Empty reply from server

As far as I am aware, this has been the case since essentially ever. I went as far back as v2.4.6, which might not seem that far. But I only stopped because my curl version dropped support for the old quic-draft versions. I use the abort directive for my wildcard domains (just like the example here) and only noticed it when I did a little typo, to which my browser greeted me with a blank http/500 page instead of the usual and expected connection reset.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 23 (20 by maintainers)

Most upvoted comments

https://github.com/lucas-clemente/quic-go/issues/3575 was merged. Thank you @shade34321 for your contribution. This issue will be resolved with the next quic-go update (once we cut a release containing this fix).

I fixed it in the pr, which also fixes some of the difference between stdlib and http3.

Fixed in the upstream.

That seems like a really simple fix. A PR would be appreciated 😃