123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220(* Ocsigen
* http://www.ocsigen.org
* Module revproxy.ml
* Copyright (C) 2007 Vincent Balat
*
* 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. *)openLwt.Infixletsection=Lwt_log.Section.make"ocsigen:ext:revproxy"exceptionBad_answer_from_http_server(** The table of redirections for each virtual server *)typeredir={regexp:Pcre.regexp;full_url:Ocsigen_lib.yesnomaybe;dest:string;pipeline:bool;keephost:bool}(** Generate the pages from the request *)letgendir=function|Ocsigen_extensions.Req_found_->Lwt.returnOcsigen_extensions.Ext_do_nothing|Ocsigen_extensions.Req_not_found(err,{Ocsigen_extensions.request_info;_})->Lwt.catch(* Is it a redirection? *)(fun()->Lwt_log.ign_info~section"Is it a redirection?";letdest=letfifull=Ocsigen_extensions.find_redirectiondir.regexpfulldir.destrequest_infoinmatchdir.full_urlwith|Ocsigen_lib.Yes->fitrue|Ocsigen_lib.No->fifalse|Ocsigen_lib.Maybe->tryfifalsewithOcsigen_extensions.Not_concerned->fitrueinlet(https,host,port,path)=try(* FIXME: we do not seem to handle GET
parameters. Why? *)matchOcsigen_lib.Url.parsedestwith|(Somehttps,Somehost,port,path,_,_,_)->letport=matchportwith|None->ifhttpsthen443else80|Somep->pin(https,host,port,path)|_->raise(Ocsigen_extensions.Error_in_config_file("Revproxy : error in destination URL "^dest))(*VVV catch only URL-related exceptions? *)withe->raise(Ocsigen_extensions.Error_in_config_file("Revproxy : error in destination URL "^dest^" - "^Printexc.to_stringe))inLwt_log.ign_info_f~section"YES! Redirection to http%s://%s:%d/%s"(ifhttpsthen"s"else"")hostportpath;Ocsigen_lib.Ip_address.get_inet_addrhost>>=fun_inet_addr->(* It is now safe to start processing next request.
We are sure that the request won't be taken in disorder,
so we return. *)letdo_request()=letheaders=leth=Cohttp.Request.headers(Ocsigen_request.to_cohttprequest_info)inleth=Ocsigen_request.versionrequest_info|>Cohttp.Code.string_of_version|>Cohttp.Header.replacehOcsigen_header.Name.(to_stringx_forwarded_proto)inleth=letforward=letaddress=Unix.string_of_inet_addr(Ocsigen_request.addressrequest_info)inString.concat", "(Ocsigen_request.remote_iprequest_info::Ocsigen_request.forward_iprequest_info@[address])inCohttp.Header.replacehOcsigen_header.Name.(to_stringx_forwarded_for)forwardinCohttp.Header.removehOcsigen_header.Name.(to_stringhost)anduri=letscheme=ifOcsigen_request.sslrequest_infothen"https"else"http"andhost=matchifdir.keephostthenOcsigen_request.hostrequest_infoelseNonewith|Somehost->host|None->hostinUri.make~scheme~host~port~path()andbody=Ocsigen_request.bodyrequest_infoandmeth=Ocsigen_request.methrequest_infoinCohttp_lwt_unix.Client.call~headers~bodymethuriinLwt.return@@Ocsigen_extensions.Ext_found(fun()->do_request()>|=Ocsigen_response.of_cohttp))(function|Ocsigen_extensions.Not_concerned->Lwt.return(Ocsigen_extensions.Ext_nexterr)|e->Lwt.faile)letparse_configconfig_elem=letregexp=refNoneinletfull_url=refOcsigen_lib.Yesinletdest=refNoneinletpipeline=reftrueinletkeephost=reffalseinOcsigen_extensions.(Configuration.process_element~in_tag:"host"~other_elements:(funt__->raise(Bad_config_tag_for_extensiont))~elements:[Configuration.element~name:"revproxy"~attributes:[Configuration.attribute~name:"regexp"(funs->regexp:=Somes;full_url:=Ocsigen_lib.Yes);Configuration.attribute~name:"fullurl"(funs->regexp:=Somes;full_url:=Ocsigen_lib.Yes);Configuration.attribute~name:"suburl"(funs->regexp:=Somes;full_url:=Ocsigen_lib.No);Configuration.attribute~name:"dest"(funs->dest:=Somes);Configuration.attribute~name:"keephost"(function"keephost"->keephost:=true|_->());Configuration.attribute~name:"nopipeline"(function"nopipeline"->pipeline:=false|_->());]()]config_elem);match!regexp,!full_url,!dest,!pipeline,!keephostwith|(None,_,_,_,_)->Ocsigen_extensions.badconfig"Missing attribute 'regexp' for <revproxy>"|(_,_,None,_,_)->Ocsigen_extensions.badconfig"Missing attribute 'dest' for <revproxy>"|(Someregexp,full_url,Somedest,pipeline,keephost)->gen{regexp=Ocsigen_lib.Netstring_pcre.regexp("^"^regexp^"$");full_url;dest;pipeline;keephost;}let()=Ocsigen_extensions.register~name:"revproxy"~fun_site:(fun______->parse_config)~respect_pipeline:true(* We ask ocsigen to respect pipeline order
when sending to extensions! *)()