source: code/trunk/http/http.ml@ 2

Last change on this file since 2 was 2, checked in by fox, 3 years ago

Samhain 21

Converter

  • type selection
  • subdir conversion
  • htm extension

Gemini

  • index.gmi
  • topics and latest
  • gmi.atom feed

Add pull (http(s)) operation

  • peers.pub.conf and peers.priv.conf

HTML5 format & fixes by Novaburst
Phony target (thanks Gergely)

May

Basic unit renamed from Note to Text.
New modular text-parser, internal to Logarion, for generic notation parsing. The default input format is now a much plainer text.
Logarion created texts have part of the UUID in filename.
Logarion's index re-written in Messagepack format. Removed indices command. They are generated during convert.

File size: 5.8 KB
RevLine 
[2]1let http_body fn uri =
2 let open Lwt in
3 let open Cohttp_lwt_unix in
4 Client.get uri >>= fun (headers, body) ->
5 body |> Cohttp_lwt.Body.to_string >|= fun body -> fn (headers, body)
6
7let response (headers, body) =
8 let open Cohttp in
9 match Header.get (headers |> Response.headers) "content-type" with
10 | Some "application/msgpack" | Some "application/octet-stream"
11 | Some "text/plain" | Some "text/plain; charset=utf-8" -> Ok body
12 | Some x -> Error ("Invalid content-type: " ^ x)
13 | None -> Ok body
14
15let http_apply fn uri = Lwt_main.run (http_body fn uri)
16
17module S = Set.Make(String)
18
19(*let is_selected sl =*)
20(* let check str a b c = Option.(fold ~none:(is_none b && is_none c) ~some:(fun x -> x = str) a) in*)
21(* function*)
22(* | `Author s -> check s sl.authors sl.topics*)
23(* | `Topic s -> check s sl.topics sl.authors*)
24
25(* TODO: parse using Header_pack *)
26
27let sub_id text = Logarion.(String.sub (text.Text.uuid |> Id.to_string) 0 8)
28let fname dir text = dir ^ sub_id text ^ ".txt"
29
30let newer time id dir =
31 match Logarion.File_store.to_text @@ Filename.concat dir (String.sub (Logarion.Id.to_string id) 0 8 ^ ".txt") with
32 | Error x -> prerr_endline x; true
33 | Ok txt -> time > (Logarion.(Header_pack.date (Date.listing txt.date)))
34 | exception (Sys_error _) -> true
35
36let pull_text url dir id =
37 let path = Uri.path url in
38 let u = Uri.with_path url (path ^ "/" ^ String.sub (Logarion.Id.to_string id) 0 8 ^ ".txt") in
39 match http_apply response u with
40 | Error msg -> prerr_endline @@ " Failed " ^ Uri.to_string u ^ " " ^ msg
41 | Ok txt ->
42 match Logarion.Text.of_string txt with
43 | Error s -> prerr_endline s
44 | Ok text ->
45 let file = open_out_gen [Open_creat; Open_trunc; Open_wronly] 0o640 (fname dir text) in
46 output_string file txt; close_out file
47
48(*TODO: adapt Archive predication function to work with free sets*)
49let parse_index _is_selected fn url p =
50 let open Logarion.Header_pack in
51 let dir = "peers/" ^ match Uri.host url with
52 None -> "local/" ^ Uri.path url | Some s -> s ^ Uri.path url ^ "/" in
53 Printf.printf "%s => %s\n" p.info.name dir;
54 (match Msgpck.to_list p.peers with [] -> () | ps ->
55 print_string " peers: ";
56 List.iter (fun x -> print_string (" " ^ Msgpck.to_string x)) ps;
57 print_newline ());
58 match Msgpck.to_list p.texts with
59 | [] -> print_endline ", has empty index"
60 | texts ->
61 match Bos.OS.Dir.create ~mode:0o740 (Fpath.v dir) with
62 | Error (`msg s) -> prerr_endline ("Error making domain dir:" ^ s);
63 | _ ->
64 let numof_texts = string_of_int @@ List.length texts in
65 let text_num_len = String.length numof_texts in
66 let of_pck i x =
67 Printf.printf "\r%*d/%s %!" text_num_len (i+1) numof_texts;
68 match x with
69 | Msgpck.List (id::time::title::_authors::_topics) ->
70 (match Logarion.Id.of_bytes Msgpck.(to_bytes id) with
71 | None -> prerr_endline ("Invalid id for " ^ Msgpck.to_string title)
72 | Some id ->
73 let t = match time with Msgpck.Int i -> Int32.of_int i | Msgpck.Uint32 i -> i | x -> Msgpck.to_uint32 x in
74 if newer t id dir then fn url dir id)
75 | _ -> prerr_endline ("Invalid record structure") in
76 List.iteri of_pck texts;
77 print_newline ()
78
79let pull_index url _authors _topics =
80 let index_url = Uri.(with_path url (path url ^ "/index.pck")) in
81 match http_apply response index_url with
82 | Error msg -> prerr_endline @@ "Failed index request for " ^ Uri.to_string index_url ^ " " ^ msg
83 | Ok body ->
84 let _i, pack = Msgpck.StringBuf.read body in
85(* let predicates =*)
86(* A.predicate A.authored authors_opt*)
87(* @ A.predicate A.topics topics_opt*)
88(* in*)
89 let is_selected text = List.fold_left (fun a e -> a && e text) true [](*predicates*) in
90 match Logarion.Header_pack.unpack pack with None -> ()
91 | Some headers -> parse_index is_selected pull_text url headers
92
93module Msg = struct
94 type t = Ptime.t * string
95 let compare (x0,y0) (x1,y1) = match Ptime.compare x1 x0 with 0 -> String.compare y0 y1 | c -> c
96end
97
98module MsgSet = Set.Make(Msg)
99
100let pull_msgs url _authors _topics = match http_apply response url with
101 | Error msg -> prerr_endline @@ "Failed index request for " ^ Uri.(to_string url) ^ " " ^ msg
102 | Ok body ->
103 let rec fold_msgs s a fn =
104 let t, msg = Scanf.bscanf s "%s %s@\n" (fun t m -> t, m) in
105 if t <> "" then fold_msgs s (fn a t msg) fn else a
106 in
107 let s = Scanf.Scanning.from_string body in
108 let msgs = MsgSet.empty in
109 let date_string t = Ptime.to_date t |>
110 fun (y, m, d) -> Printf.sprintf "%04d-%02d-%02d" y m d in
111 let msgs = fold_msgs s msgs
112 (fun msgs t m -> match Ptime.of_rfc3339 t with
113 | Ok (v,_,_) -> let open MsgSet in
114 let msgs = if cardinal msgs > 1 then remove (max_elt msgs) msgs else msgs in
115 add (v,m) msgs
116 | _ -> msgs) in
117 print_endline ("\n┌────=[ " ^ Uri.to_string url);
118 MsgSet.iter
119 (fun (t,m) -> print_endline
120 ("│ " ^ date_string t ^ "\n│ " ^ m ^ "\n└─────────")) msgs
121
122let pull_url url = match Uri.of_string url with
123 | x when x = Uri.empty -> (fun _ _ -> ())
124 | x when Uri.scheme x = Some "msg+http" -> pull_msgs Uri.(with_scheme x (Some "http"))
125 | x when Uri.scheme x = Some "msg+https"-> pull_msgs Uri.(with_scheme x (Some "https"))
126 | x -> pull_index x
127
128let pull_list auths topics =
129 let pull peer_url () = pull_url peer_url auths topics in
130 let open Logarion.Peers in
131 fold_file pull () public_fname;
132 fold_file pull () private_fname
133
134let pull = function "" -> pull_list | x -> pull_url x
135
136open Cmdliner
137
138let pull_term =
139 let authors = Arg.(value & opt (some string) None & info ["a"; "authors"] ~docv:"AUTHORS" ~doc:"select authors") in
140 let topics = Arg.(value & opt (some string) None & info ["t"; "topics"] ~docv:"TOPICS" ~doc:"select topics") in
141 let url = Arg.(value & pos 0 string "" & info [] ~docv:"URL" ~doc:"HTTP URL of Logarion") in
142 Term.(const pull $ url $ authors $ topics),
143 Term.info "pull" ~doc:"pull listed texts" ~man:[ `S "DESCRIPTION"; `P "Pull texts from archive at address"]
Note: See TracBrowser for help on using the repository browser.