1{config, pkgs, lib, ...}:
2let
3 cfg = config.services.nghttpx;
4
5 # renderHost :: Either ServerOptions Path -> String
6 renderHost = server:
7 if builtins.isString server
8 then "unix://${server}"
9 else "${server.host},${builtins.toString server.port}";
10
11 # Filter out submodule parameters whose value is null or false or is
12 # the key _module.
13 #
14 # filterParams :: ParamsSubmodule -> ParamsSubmodule
15 filterParams = p:
16 lib.filterAttrs
17 (n: v: ("_module" != n) && (null != v) && (false != v))
18 (lib.optionalAttrs (null != p) p);
19
20 # renderBackend :: BackendSubmodule -> String
21 renderBackend = backend:
22 let
23 host = renderHost backend.server;
24 patterns = lib.concatStringsSep ":" backend.patterns;
25
26 # Render a set of backend parameters, this is somewhat
27 # complicated because nghttpx backend patterns can be entirely
28 # omitted and the params may be given as a mixed collection of
29 # 'key=val' pairs or atoms (e.g: 'proto=h2;tls')
30 params =
31 lib.mapAttrsToList
32 (n: v:
33 if builtins.isBool v
34 then n
35 else if builtins.isString v
36 then "${n}=${v}"
37 else "${n}=${builtins.toString v}")
38 (filterParams backend.params);
39
40 # NB: params are delimited by a ";" which is the same delimiter
41 # to separate the host;[pattern];[params] sections of a backend
42 sections =
43 builtins.filter (e: "" != e) ([
44 host
45 patterns
46 ]++params);
47 formattedSections = lib.concatStringsSep ";" sections;
48 in
49 "backend=${formattedSections}";
50
51 # renderFrontend :: FrontendSubmodule -> String
52 renderFrontend = frontend:
53 let
54 host = renderHost frontend.server;
55 params0 =
56 lib.mapAttrsToList
57 (n: v: if builtins.isBool v then n else v)
58 (filterParams frontend.params);
59
60 # NB: nghttpx doesn't accept "tls", you must omit "no-tls" for
61 # the default behavior of turning on TLS.
62 params1 = lib.remove "tls" params0;
63
64 sections = [ host] ++ params1;
65 formattedSections = lib.concatStringsSep ";" sections;
66 in
67 "frontend=${formattedSections}";
68
69 configurationFile = pkgs.writeText "nghttpx.conf" ''
70 ${lib.optionalString (null != cfg.tls) ("private-key-file="+cfg.tls.key)}
71 ${lib.optionalString (null != cfg.tls) ("certificate-file="+cfg.tls.crt)}
72
73 user=nghttpx
74
75 ${lib.concatMapStringsSep "\n" renderFrontend cfg.frontends}
76 ${lib.concatMapStringsSep "\n" renderBackend cfg.backends}
77
78 backlog=${builtins.toString cfg.backlog}
79 backend-address-family=${cfg.backend-address-family}
80
81 workers=${builtins.toString cfg.workers}
82 rlimit-nofile=${builtins.toString cfg.rlimit-nofile}
83
84 ${lib.optionalString cfg.single-thread "single-thread=yes"}
85 ${lib.optionalString cfg.single-process "single-process=yes"}
86
87 ${cfg.extraConfig}
88 '';
89in
90{ imports = [
91 ./nghttpx-options.nix
92 ];
93
94 config = lib.mkIf cfg.enable {
95
96 users.groups.nghttpx = { };
97 users.users.nghttpx = {
98 group = config.users.groups.nghttpx.name;
99 isSystemUser = true;
100 };
101
102
103 systemd.services = {
104 nghttpx = {
105 wantedBy = [ "multi-user.target" ];
106 after = [ "network.target" ];
107 script = ''
108 ${pkgs.nghttp2}/bin/nghttpx --conf=${configurationFile}
109 '';
110
111 serviceConfig = {
112 Restart = "on-failure";
113 RestartSec = 60;
114 };
115 };
116 };
117 };
118}