mod_rewrite and ProxyPass
[hcoop/domtool2.git] / src / plugins / apache.sml
... / ...
CommitLineData
1(* HCoop Domtool (http://hcoop.sourceforge.net/)
2 * Copyright (c) 2006, Adam Chlipala
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *)
18
19(* Apache HTTPD handling *)
20
21structure Apache :> APACHE = struct
22
23open Ast
24
25val _ = Env.type_one "proxy_port"
26 Env.int
27 (fn n => n >= 1024)
28
29val _ = Env.type_one "rewrite_arg"
30 Env.string
31 (CharVector.all Char.isAlphaNum)
32
33val dl = ErrorMsg.dummyLoc
34
35val _ = Main.registerDefault ("WebNodes",
36 (TList (TBase "node", dl), dl),
37 (fn () => (EList (map (fn s => (EString s, dl)) Config.Apache.webNodes), dl)))
38
39val _ = Main.registerDefault ("SSL",
40 (TBase "bool", dl),
41 (fn () => (EVar "false", dl)))
42
43val _ = Main.registerDefault ("User",
44 (TBase "your_user", dl),
45 (fn () => (EString (Domain.getUser ()), dl)))
46
47val _ = Main.registerDefault ("Group",
48 (TBase "your_group", dl),
49 (fn () => (EString (Domain.getUser ()), dl)))
50
51val _ = Main.registerDefault ("DocumentRoot",
52 (TBase "your_path", dl),
53 (fn () => (EString ("/home/" ^ Domain.getUser () ^ "/public_html"), dl)))
54
55val _ = Main.registerDefault ("ServerAdmin",
56 (TBase "email", dl),
57 (fn () => (EString (Domain.getUser () ^ "@" ^ Config.defaultDomain), dl)))
58
59
60val redirect_code = fn (EVar "temp", _) => SOME "temp"
61 | (EVar "permanent", _) => SOME "permanent"
62 | (EVar "seeother", _) => SOME "seeother"
63 | (EVar "redir300", _) => SOME "300"
64 | (EVar "redir301", _) => SOME "301"
65 | (EVar "redir302", _) => SOME "302"
66 | (EVar "redir303", _) => SOME "303"
67 | (EVar "redir304", _) => SOME "304"
68 | (EVar "redir305", _) => SOME "305"
69 | (EVar "redir307", _) => SOME "307"
70 | _ => NONE
71
72val flag = fn (EVar "redirect", _) => SOME "R"
73 | (EVar "forbidden", _) => SOME "F"
74 | (EVar "gone", _) => SOME "G"
75 | (EVar "last", _) => SOME "L"
76 | (EVar "chain", _) => SOME "C"
77 | (EVar "nosubreq", _) => SOME "NS"
78 | (EVar "nocase", _) => SOME "NC"
79 | (EVar "qsappend", _) => SOME "QSA"
80 | (EVar "noescape", _) => SOME "NE"
81 | (EVar "passthrough", _) => SOME "PT"
82 | (EApp ((EVar "mimeType", _), e), _) =>
83 Option.map (fn s => "T=" ^ s) (Env.string e)
84 | (EApp ((EVar "redirectWith", _), e), _) =>
85 Option.map (fn s => "R=" ^ s) (redirect_code e)
86 | (EApp ((EVar "skip", _), e), _) =>
87 Option.map (fn n => "S=" ^ Int.toString n) (Env.int e)
88 | (EApp ((EApp ((EVar "env", _), e1), _), e2), _) =>
89 (case Env.string e1 of
90 NONE => NONE
91 | SOME s1 => Option.map (fn s2 => "E=" ^ s1 ^ ":" ^ s2)
92 (Env.string e2))
93
94 | _ => NONE
95
96
97val vhostsChanged = ref false
98
99val () = Slave.registerPreHandler
100 (fn () => vhostsChanged := false)
101
102val () = Slave.registerFileHandler (fn fs =>
103 let
104 val spl = OS.Path.splitDirFile (#file fs)
105 in
106 if String.isSuffix ".vhost" (#file spl)
107 orelse String.isSuffix ".vhost_ssl" (#file spl) then
108 (vhostsChanged := true;
109 case #action fs of
110 Slave.Delete =>
111 ignore (OS.Process.system (Config.rm
112 ^ " -rf "
113 ^ Config.Apache.confDir
114 ^ "/"
115 ^ #file spl))
116 | _ =>
117 ignore (OS.Process.system (Config.cp
118 ^ " "
119 ^ #file fs
120 ^ " "
121 ^ Config.Apache.confDir
122 ^ "/"
123 ^ #file spl)))
124 else
125 ()
126 end)
127
128val () = Slave.registerPostHandler
129 (fn () =>
130 (if !vhostsChanged then
131 Slave.shellF ([Config.Apache.reload],
132 fn cl => "Error reloading Apache with " ^ cl)
133 else
134 ()))
135
136val vhostFiles : TextIO.outstream list ref = ref []
137fun write s = app (fn file => TextIO.output (file, s)) (!vhostFiles)
138
139val rewriteEnabled = ref false
140
141val () = Env.containerV_one "vhost"
142 ("host", Env.string)
143 (fn (env, host) =>
144 let
145 val nodes = Env.env (Env.list Env.string) (env, "WebNodes")
146
147 val ssl = Env.env Env.bool (env, "SSL")
148 val user = Env.env Env.string (env, "User")
149 val group = Env.env Env.string (env, "Group")
150 val docroot = Env.env Env.string (env, "DocumentRoot")
151 val sadmin = Env.env Env.string (env, "ServerAdmin")
152
153 val fullHost = host ^ "." ^ Domain.currentDomain ()
154 val confFile = fullHost ^ (if ssl then ".vhost_ssl" else ".vhost")
155 in
156 rewriteEnabled := false;
157 vhostFiles := map (fn node =>
158 let
159 val file = Domain.domainFile {node = node,
160 name = confFile}
161 in
162 TextIO.output (file, "<VirtualHost ");
163 TextIO.output (file, Domain.nodeIp node);
164 TextIO.output (file, ":");
165 TextIO.output (file, if ssl then
166 "443"
167 else
168 "80");
169 TextIO.output (file, ">\n");
170 file
171 end)
172 nodes;
173 write "\tSuexecUserGroup ";
174 write user;
175 write " ";
176 write group;
177 write "\n\tDocumentRoot ";
178 write docroot;
179 write "\n\tServerAdmin ";
180 write sadmin;
181 write "\n"
182 end,
183 fn () => (write "</VirtualHost>\n";
184 app TextIO.closeOut (!vhostFiles)))
185
186fun checkRewrite () =
187 if !rewriteEnabled then
188 ()
189 else
190 (write "\tRewriteEngine on\n";
191 rewriteEnabled := true)
192
193val () = Env.action_three "localProxyRewrite"
194 ("from", Env.string, "to", Env.string, "port", Env.int)
195 (fn (from, to, port) =>
196 (checkRewrite ();
197 write "\tRewriteRule\t";
198 write from;
199 write "\thttp://localhost:";
200 write (Int.toString port);
201 write "/";
202 write to;
203 write " [P]\n"))
204
205val () = Env.action_three "localProxyPass"
206 ("from", Env.string, "to", Env.string, "port", Env.int)
207 (fn (from, to, port) =>
208 let
209 val to =
210 case to of
211 "" => "/"
212 | _ => if String.sub (to, 0) = #"/" then
213 to
214 else
215 "/" ^ to
216 in
217 write "\tProxyPass\t";
218 write from;
219 write "\thttp://localhost:";
220 write (Int.toString port);
221 write to;
222 write "\n"
223 end)
224
225val () = Env.action_three "localProxyPassReverse"
226 ("from", Env.string, "to", Env.string, "port", Env.int)
227 (fn (from, to, port) =>
228 let
229 val to =
230 case to of
231 "" => "/"
232 | _ => if String.sub (to, 0) = #"/" then
233 to
234 else
235 "/" ^ to
236 in
237 write "\tProxyPassReverse\t";
238 write from;
239 write "\thttp://localhost:";
240 write (Int.toString port);
241 write to;
242 write "\n"
243 end)
244
245val () = Env.action_three "rewriteRule"
246 ("from", Env.string, "to", Env.string, "flags", Env.list flag)
247 (fn (from, to, flags) =>
248 (checkRewrite ();
249 write "\tRewriteRule\t";
250 write from;
251 write "\t";
252 write to;
253 case flags of
254 [] => ()
255 | flag::rest => (write " [";
256 write flag;
257 app (fn flag => (write ",";
258 write flag)) rest;
259 write "]");
260 write "\n"))
261
262end