1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
|
(* Ocsigen
* http://www.ocsigen.org
* Module revproxy.ml
* Copyright (C) 2007 Vincent Balat
* Laboratoire PPS - CNRS Universit Paris Diderot
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, with linking exception;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)
(** Reverse proxy for Ocsigen *)
(*
The reverse proxy is still experimental because it relies on the
experimental Ocsigen_http_client module.
TODO
- Change the policy for trusted servers for pipelining?
(see ocsigen_http_client.ml)
- add the ability to rewrite some headers from the config file
(for ex after a redirection, the new URL is wrong)
probably in another (filter) extension
- enhance pipelining
- HTTP/1.0
- ...
- Make possible to return for example (Ext_next 404) to allow
other extensions to take the request?
There is a problem if the body contains data (POST request) ...
this data has been sent and is lost ...
*)
open Lwt
open Ocsigen_extensions
open Simplexmlparser
exception Bad_answer_from_http_server
(*****************************************************************************)
(* The table of redirections for each virtual server *)
type redir =
{ regexp: Netstring_pcre.regexp;
full_url: Ocsigen_lib.yesnomaybe;
dest: string;
pipeline: bool;
keephost: bool}
(*****************************************************************************)
(* Finding redirections *)
(** The function that will generate the pages from the request. *)
let gen dir = function
| Ocsigen_extensions.Req_found _ ->
Lwt.return Ocsigen_extensions.Ext_do_nothing
| Ocsigen_extensions.Req_not_found (err, ri) ->
catch
(* Is it a redirection? *)
(fun () ->
Ocsigen_messages.debug2 "--Revproxy: Is it a redirection?";
let dest =
let ri = ri.request_info in
let fi full =
Ocsigen_extensions.find_redirection
dir.regexp
full
dir.dest
ri.ri_ssl
ri.ri_host
ri.ri_server_port
ri.ri_get_params_string
ri.ri_sub_path_string
ri.ri_full_path_string
in
match dir.full_url with
| Ocsigen_lib.Yes -> fi true
| Ocsigen_lib.No -> fi false
| Ocsigen_lib.Maybe ->
try fi false
with Ocsigen_extensions.Not_concerned -> fi true
in
let (https, host, port, uri) =
try
match Ocsigen_lib.parse_url dest with
| (Some https, Some host, port, uri, _, _, _, _) ->
let port = match port with
| None -> if https then 443 else 80
| Some p -> p
in
(https, host, port, uri)
| _ -> raise (Ocsigen_extensions.Error_in_config_file
("Revproxy : error in destination URL "^dest))
(*VVV catch only Neturl exceptions! *)
with e -> raise (Ocsigen_extensions.Error_in_config_file
("Revproxy : error in destination URL "^dest^" - "^
Printexc.to_string e))
in
Ocsigen_messages.debug
(fun () ->
"--Revproxy: YES! Redirection to "^
(if https then "https://" else "http://")^host^":"^
(string_of_int port)^uri);
Ocsigen_lib.get_inet_addr host >>= fun inet_addr ->
(* It is now safe to start next request.
We are sure that the request won't be taken in disorder.
=> We return.
*)
let host =
match
if dir.keephost
then match ri.request_info.ri_host with
| Some h -> Some h
| None -> None
else None
with
| Some h -> h
| None ->
if (not https && port=80) || (https && port=443)
then host
else host^":"^string_of_int port
in
let do_request =
let ri = ri.request_info in
if dir.pipeline then
Ocsigen_http_client.raw_request
~headers:ri.ri_http_frame.Ocsigen_http_frame.frame_header.Ocsigen_http_frame.Http_header.headers
~https
~port
~client:ri.ri_client
~keep_alive:true
~content:ri.ri_http_frame.Ocsigen_http_frame.frame_content
?content_length:ri.ri_content_length
~http_method:ri.ri_method
~host
~inet_addr
~uri ()
else
fun () ->
Ocsigen_http_client.basic_raw_request
~headers:ri.ri_http_frame.Ocsigen_http_frame.frame_header.Ocsigen_http_frame.Http_header.headers
~https
~port
~content:ri.ri_http_frame.Ocsigen_http_frame.frame_content
?content_length:ri.ri_content_length
~http_method:ri.ri_method
~host
~inet_addr
~uri ()
in
Lwt.return
(Ext_found
(fun () ->
do_request ()
>>= fun http_frame ->
let headers =
http_frame.Ocsigen_http_frame.frame_header.Ocsigen_http_frame.Http_header.headers
in
let code =
match
http_frame.Ocsigen_http_frame.frame_header.Ocsigen_http_frame.Http_header.mode
with
| Ocsigen_http_frame.Http_header.Answer code -> code
| _ -> raise Bad_answer_from_http_server
in
match http_frame.Ocsigen_http_frame.frame_content with
| None ->
let empty_result = Ocsigen_http_frame.empty_result () in
let length =
Ocsigen_headers.get_content_length http_frame
in
Lwt.return
{empty_result with
Ocsigen_http_frame.res_content_length = length;
res_headers= headers;
res_stop_stream = http_frame.Ocsigen_http_frame.frame_abort;
res_code= code;
}
| Some stream ->
let default_result =
Ocsigen_http_frame.default_result ()
in
let length =
Ocsigen_headers.get_content_length http_frame
in
Lwt.return
{default_result with
Ocsigen_http_frame.res_content_length = length;
res_stream = (stream, None);
res_stop_stream =
http_frame.Ocsigen_http_frame.frame_abort;
res_headers= headers;
res_code= code;
}
)
)
)
(function
| Not_concerned -> return (Ext_next err)
| e -> fail e)
(*****************************************************************************)
let parse_config = function
| Element ("revproxy", atts, []) ->
let rec parse_attrs ((r, f, d, pipeline, h) as res) = function
| [] -> res
| ("regexp", regexp)::l when r = None -> (* deprecated *)
parse_attrs
(Some (Netstring_pcre.regexp ("^"^regexp^"$")), Ocsigen_lib.Maybe,
d, pipeline, h)
l
| ("fullurl", regexp)::l when r = None ->
parse_attrs
(Some (Netstring_pcre.regexp ("^"^regexp^"$")), Ocsigen_lib.Yes,
d, pipeline, h)
l
| ("suburl", regexp)::l when r = None ->
parse_attrs
(Some (Netstring_pcre.regexp ("^"^regexp^"$")), Ocsigen_lib.No,
d, pipeline, h)
l
| ("dest", dest)::l when d = None ->
parse_attrs
(r, f, Some dest, pipeline, h)
l
| ("keephost", "keephost")::l ->
parse_attrs
(r, f, d, pipeline, true)
l
| ("nopipeline", "nopipeline")::l ->
parse_attrs
(r, f, d, false, h)
l
| (a, _) :: _ ->
badconfig "Wrong or duplicate attribute '%s' for <revproxy>" a
in
let dir =
match parse_attrs (None, Ocsigen_lib.Yes, None, true, false) atts with
| (None, _, _, _, _) ->
badconfig "Missing attribute 'regexp' for <revproxy>"
| (_, _, None, _, _) ->
badconfig "Missing attribute 'dest' for <revproxy>"
| (Some r, full, Some d, pipeline, h) ->
{
regexp=r;
full_url=full;
dest=d;
pipeline=pipeline;
keephost=h;
}
in
gen dir
| Element (t, _, _) -> raise (Bad_config_tag_for_extension t)
| _ -> raise (Error_in_config_file "(revproxy extension) Bad data")
(*****************************************************************************)
(** Registration of the extension *)
let () = register_extension
~name:"revproxy"
~fun_site:(fun _ _ _ _ -> parse_config)
~user_fun_site:(fun _ _ _ _ _ -> parse_config)
~respect_pipeline:true (* We ask ocsigen to respect pipeline order
when sending to extensions! *)
()
|