123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162(* Ocsigen
* http://www.ocsigen.org
* Module accesscontrol.ml
* Copyright (C) 2011 Pierre Chambart
*
* 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.
*)(** Handle Cross-Origin Resource Sharing (CORS) headers *)letsection=Lwt_log.Section.make"ocsigen:ext:cors"(*** MAIN FUNCTION ***)letdefault_frame()=Ocsigen_response.make(Cohttp.Response.make~status:`OK())typeconfig={methods:Cohttp.Code.methlistoption;(* None means: all method are accepted *)credentials:bool;max_age:intoption;exposed_headers:stringlist}exceptionRefusedletadd_headersconfigrresponse=matchOcsigen_request.headerrOcsigen_header.Name.originwith|None->Lwt.returnOcsigen_extensions.Ext_do_nothing|Someorigin->Lwt_log.ign_info_f~section"request with origin: %s"origin;letl=[Ocsigen_header.Name.access_control_allow_origin,origin]inletl=ifconfig.credentialsthen(Ocsigen_header.Name.access_control_allow_credentials,"true")::lelselinletl=matchOcsigen_request.headerrOcsigen_header.Name.access_control_request_methodwith|Somerequest_method->letmethods=matchconfig.methodswith|None->true|Somel->(tryList.mem(Cohttp.Code.method_of_stringrequest_method)lwith_->false)inifmethodsthen(Ocsigen_header.Name.access_control_allow_methods,request_method)::lelse(Lwt_log.ign_info~section"Method refused";raiseRefused)|None->linletl=matchOcsigen_request.headerrOcsigen_header.Name.access_control_request_headerswith|Somerequest_headers->(Ocsigen_header.Name.access_control_allow_headers,request_headers)::l|None->linletl=matchconfig.max_agewith|Somemax_age->(Ocsigen_header.Name.access_control_max_age,string_of_intmax_age)::l|None->linletl=matchconfig.exposed_headerswith|[]->l|exposed_headers->(Ocsigen_header.Name.access_control_expose_headers,String.concat", "exposed_headers)::linLwt.return(Ocsigen_extensions.Ext_found(fun()->Lwt.return@@Ocsigen_response.replace_headersresponsel))letmainconfig=function|Ocsigen_extensions.Req_not_found(_,{Ocsigen_extensions.request_info;_})->(matchOcsigen_request.methrequest_infowith|`OPTIONS->(Lwt_log.ign_info~section"OPTIONS request";tryadd_headersconfigrequest_info(default_frame())withRefused->Lwt_log.ign_info~section"Refused request";Lwt.returnOcsigen_extensions.Ext_do_nothing)|_->Lwt.returnOcsigen_extensions.Ext_do_nothing)|Ocsigen_extensions.Req_found({Ocsigen_extensions.request_info;_},response)->Lwt_log.ign_info~section"answered request";add_headersconfigrequest_inforesponse(* Register extension *)letcomma_space_regexp=Ocsigen_lib.Netstring_pcre.regexp"[[:blank:]\n]*,[[:blank:]\n]*"letparse_config___parse_funconfig_elem=letconfig=ref{methods=None;credentials=false;max_age=None;exposed_headers=[]}inOcsigen_extensions.(Configuration.process_element~in_tag:"host"~other_elements:(funt__->raise(Bad_config_tag_for_extensiont))~elements:[Configuration.element~name:"cors"~attributes:[Configuration.attribute~name:"credentials"(funs->lets=bool_of_stringsinconfig:={!configwithcredentials=s});Configuration.attribute~name:"max_age"(funs->lets=Some(int_of_strings)inconfig:={!configwithmax_age=s});Configuration.attribute~name:"exposed_headers"(funs->lets=Ocsigen_lib.Netstring_pcre.splitcomma_space_regexpsinconfig:={!configwithexposed_headers=s});Configuration.attribute~name:"methods"(funs->lets=Ocsigen_lib.Netstring_pcre.splitcomma_space_regexpsinlets=Some(List.mapCohttp.Code.method_of_strings)inconfig:={!configwithmethods=s})]()]config_elem);main!configlet()=Ocsigen_extensions.register~name:"CORS"~fun_site:(fun___->parse_config)()letrun?credentials?max_age?exposed_headers?methods()___=letcredentials=Ocsigen_lib.Option.get'falsecredentialsinletexposed_headers=Ocsigen_lib.Option.get'[]exposed_headersinmain{credentials;methods;max_age;exposed_headers}