Skip to content

Instantly share code, notes, and snippets.

@fffonion
Last active January 14, 2024 23:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fffonion/8bcfd8faa46078f5e0ac0d32645d2f27 to your computer and use it in GitHub Desktop.
Save fffonion/8bcfd8faa46078f5e0ac0d32645d2f27 to your computer and use it in GitHub Desktop.
Patch nginx configurations to add quic listeners and use new http2 directive.
#!/usr/bin/env python3
import os
import re
import sys
import argparse
def modify(path, args):
print("DO: ", path)
lines = []
with open(path, "r") as f:
http2 = False
need_http3 = set()
h3_port = None
ident = ""
for l in f.readlines():
ll = l.strip()
if ll.startswith("listen"):
if not ident:
ident = re.findall("^(\s+)", l)[0]
p = re.findall("listen\s*([^\s]+)", ll)[0]
if "quic" in l:
if p in need_http3:
need_http3.remove(p)
elif "ssl" in l:
need_http3.add(p)
if ("quic" in l or "ssl" in l) and not "unix:" in p and not h3_port:
port = re.findall("(?:^|:)(\d+)$", p.split()[0])[0]
h3_port = port
if "http2" in l:
http2 = True
l = l.replace(" http2", "")
print("~ " + l.strip())
elif ll.startswith("http2 on"):
http2 = False
elif ll:
if http2 and not args.noh2:
lines.append(ident + "http2 on;\n")
print("+ http2 on;")
suffix = ""
if os.path.basename(path) == "default":
suffix = " default_server reuseport";
if not args.noh3l:
for h in need_http3:
lines.append(ident + "listen " + h + " quic" + suffix + ";\n")
print("+ listen " + h + " quic" + suffix + ";")
if h3_port and not args.noh3a:
lines.append(ident + "add_header Alt-Svc 'h3=\":%s\"; ma=86400';\n" % port)
print("add_header Alt-Svc 'h3=\":%s\"; ma=86400';\n" % port)
http2 = False
need_http3 = set()
h3_port = None
if ("$http_host" in l or "ngx.var.http_host" in l) and not args.nohost:
l = l.replace("$http_host", "$host")
l = l.replace("ngx.var.http_host", "ngx.var.host")
print("~ " + l.strip())
lines.append(l)
if not args.force:
return
with open(path, "w") as f:
f.write("".join(lines))
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--force', action='store_true', default=False, help="actually write to files")
parser.add_argument("path", help="directory containing all confs (not recursive)", default="/etc/nginx/sites-enabled")
parser.add_argument('--no-h3-listen', dest="noh3l", action='store_true', default=False, help="don't add new quic listen directives")
parser.add_argument('--no-h3-alt-svc', dest="noh3a", action='store_true', default=False, help="don't add new quic add_header directives")
parser.add_argument('--no-h2-conversion', dest="noh2", action='store_true', default=False, help="don't convert new http2 directives")
parser.add_argument('--no-host-conversion', dest="nohost", action='store_true', default=False, help="don't convert $http_host to $host")
args = parser.parse_args()
if args.force:
print("not DRY")
for path in os.listdir(args.path):
if path.startswith("."):
continue
modify(os.path.join(args.path, path), args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment