-
Notifications
You must be signed in to change notification settings - Fork 1
/
http_request.ml
130 lines (113 loc) · 4.47 KB
/
http_request.ml
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
(*pp camlp4o -I `ocamlfind query lwt.syntax` pa_lwt.cmo *)
(*
OCaml HTTP - do it yourself (fully OCaml) HTTP daemon
Copyright (C) <2002-2005> Stefano Zacchiroli <[email protected]>
Copyright (C) <2009> Anil Madhavapeddy <[email protected]>
Copyright (C) <2009> David Sheets <[email protected]>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as
published by the Free Software Foundation, version 2.
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 Library General Public License for more details.
You should have received a copy of the GNU Library 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
*)
open Printf
open Lwt
open Http_common
open Http_types
let debug_dump_request path params =
debug_print ("request path = " ^ path);
debug_print (
sprintf"request params = %s"
(String.concat ";"
(List.map (fun (h,v) -> String.concat "=" [h;v]) params)))
let auth_sep_RE = Pcre.regexp ":"
let basic_auth_RE = Pcre.regexp "^Basic\\s+"
type request = {
r_msg: Http_message.message;
r_params: (string, string) Hashtbl.t;
r_get_params: (string * string) list;
r_post_params: (string * string) list;
r_meth: meth;
r_uri: string;
r_version: version;
r_path: string;
}
let init_request ~clisockaddr ~srvsockaddr ic =
lwt (meth, uri, version) = Http_parser.parse_request_fst_line ic in
let uri_str = Neturl.string_of_url uri in
let path = Http_parser.parse_path uri in
let query_get_params = Http_parser.parse_query_get_params uri in
lwt headers = Http_parser.parse_headers ic in
let headers = List.map (fun (h,v) -> (String.lowercase h, v)) headers in
lwt body = (if meth = `POST then begin
let limit = try Some
(Int64.of_string (List.assoc "content-length" headers))
with Not_found -> None in
match limit with
|None -> Lwt_io.read ic >|= (fun s -> [`String s])
|Some count -> return [`Inchan (count, ic)]
end
else (* TODO empty body for methods other than POST, is ok? *)
return [`String ""]) in
lwt query_post_params =
match meth with
| `POST -> begin
try
let ct = List.assoc "content-type" headers in
if ct = "application/x-www-form-urlencoded" then
(Http_message.string_of_body body) >|= Http_parser.split_query_params
else return []
with Not_found -> return []
end
| _ -> return []
in
let params = query_post_params @ query_get_params in (* prefers POST params *)
let _ = debug_dump_request path params in
let msg = Http_message.init ~body ~headers ~version ~clisockaddr ~srvsockaddr in
let params_tbl =
let tbl = Hashtbl.create (List.length params) in
List.iter (fun (n,v) -> Hashtbl.add tbl n v) params;
tbl in
return { r_msg=msg; r_params=params_tbl; r_get_params = query_get_params;
r_post_params = query_post_params; r_uri=uri_str; r_meth=meth;
r_version=version; r_path=path }
let meth r = r.r_meth
let uri r = r.r_uri
let path r = r.r_path
let body r = Http_message.body r.r_msg
let header r ~name = Http_message.header r.r_msg ~name
let param ?meth ?default r name =
try
(match meth with
| None -> Hashtbl.find r.r_params name
| Some `GET -> List.assoc name r.r_get_params
| Some `POST -> List.assoc name r.r_post_params)
with Not_found ->
(match default with
| None -> raise (Param_not_found name)
| Some value -> value)
let param_all ?meth r name =
(match (meth: meth option) with
| None -> List.rev (Hashtbl.find_all r.r_params name)
| Some `DELETE
| Some `HEAD
| Some `GET -> Http_misc.list_assoc_all name r.r_get_params
| Some `POST -> Http_misc.list_assoc_all name r.r_post_params)
let params r = r.r_params
let params_get r = r.r_get_params
let params_post r = r.r_post_params
let authorization r =
match Http_message.header r.r_msg ~name:"authorization" with
| [] -> None
| h :: _ ->
let credentials = Netencoding.Base64.decode (Pcre.replace ~rex:basic_auth_RE h) in
debug_print ("HTTP Basic auth credentials: " ^ credentials);
(match Pcre.split ~rex:auth_sep_RE credentials with
| [username; password] -> Some (`Basic (username, password))
| l -> None)