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