Skip to content

Commit ef45ca7

Browse files
committed
Use cohttp as HTTP client for planet and CWN
The ocamlnet+gnutls combination is confused by servers that expose TLS1.3 resulting in errors like `GNUTLS_E_PUSH_ERROR`. This causes many feeds from planet to fail. This replaces it with cohttp using openssl as TLS client. Ocaml-tls is a possible alternative, but when used with cohttp it does not check server certificates, so it unsuitable for this task. A drawback is that cohttp does not have redirect logic, so it's necessary to implement it in here.
1 parent 5c81ff5 commit ef45ca7

File tree

6 files changed

+76
-27
lines changed

6 files changed

+76
-27
lines changed

.merlin

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ PKG uri
77
PKG omd
88
PKG opam2web
99
PKG cow
10+
PKG cohttp-lwt-unix
1011
S script
1112
B script

.travis.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ addons:
99
- ocaml-native-compilers
1010
- opam
1111
- aspcud
12-
- libgnutls28-dev
13-
- nettle-dev # for ocamlnet
1412

1513
script: ./.travis-ci.sh
1614
env:

Makefile.common

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ script/breadcrumb: script/breadcrumb.ml $(UTILS_DEP)
6969

7070
script/rss2html: script/rss2html.ml script/http.ml $(UTILS_DEP)
7171
cd script && \
72-
$(OCAMLOPT) -package netstring,nettls-gnutls,netclient,syndic \
73-
-linkpkg utils.cmx http.ml rss2html.ml -o ../"$@"
72+
$(OCAMLOPT) -package cohttp-lwt-unix,netstring,syndic \
73+
-thread -linkpkg utils.cmx http.ml rss2html.ml -o ../"$@"
7474

7575
script/weekly_news: script/weekly_news.ml script/http.ml $(UTILS_DEP)
7676
cd script && \
77-
$(OCAMLOPT) -package netstring,nettls-gnutls,netclient,syndic \
78-
-linkpkg utils.cmx http.ml weekly_news.ml -o ../"$@"
77+
$(OCAMLOPT) -package cohttp-lwt-unix,netstring,syndic \
78+
-thread -linkpkg utils.cmx http.ml weekly_news.ml -o ../"$@"
7979

8080
script/ocamltohtml:script/lexer.ml script/ocamltohtml.ml
8181
cd script && \
@@ -113,10 +113,10 @@ script/translations: script/translations.ml $(UTILS_DEP)
113113
utils.cmx translations.ml
114114

115115
script/%.cmx: script/%.ml script/%.cmi
116-
$(OCAMLOPT) -package netstring,nettls-gnutls,netclient,syndic \
116+
$(OCAMLOPT) -package netstring,syndic \
117117
-c $< -o $@
118118
script/%.cmi script/%.cmo: script/%.ml
119-
$(OCAMLC) -package netstring,nettls-gnutls,netclient,syndic -c $< -o $@
119+
$(OCAMLC) -package netstring,syndic -c $< -o $@
120120

121121
TRASH += template/front_code_snippet.html opam_update_list \
122122
$(addprefix script/, generate_opam_update_list lang_of_filename translate \

ocamlorg.opam

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ depends: [
1111
"base-bytes" {build}
1212
"omd" {>= "1.2.1"}
1313
"ocamlnet" {>= "4.0.1"}
14-
"conf-gnutls"
14+
"cohttp-lwt-unix"
15+
"lwt_ssl"
1516
"mpp" {>= "0.3.1"}
1617
"uri" {>= "1.3.11"}
1718
"syndic" {>= "1.5.2"}

script/http.ml

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,72 @@
33
URL. *)
44

55
open Printf
6-
open Nethttp_client.Convenience
76

8-
let () =
9-
Nettls_gnutls.init();
10-
Nethttp_client.Convenience.configure_pipeline
11-
(fun p ->
12-
p#set_options { p#get_options with Nethttp_client.connection_timeout = 5. }
13-
)
7+
exception Error of string
8+
9+
let error fmt = Format.kasprintf (fun s -> raise (Error s)) fmt
10+
11+
let make_uri request_uri uri =
12+
let set_host uri =
13+
match Uri.host uri with
14+
| Some _ -> uri
15+
| None ->
16+
let request_host = Option.get (Uri.host request_uri) in
17+
Uri.with_host uri (Some request_host)
18+
in
19+
let set_scheme uri =
20+
match Uri.scheme uri with
21+
| Some _ -> uri
22+
| None ->
23+
let request_scheme = Option.get (Uri.scheme request_uri) in
24+
Uri.with_scheme uri (Some request_scheme)
25+
in
26+
Uri.of_string uri
27+
|> set_host
28+
|> set_scheme
29+
30+
let rec http_get_and_follow ~max_redirects uri =
31+
let open Lwt in
32+
Cohttp_lwt_unix.Client.get uri >>= follow_redirect ~max_redirects uri
1433

34+
and follow_redirect ~max_redirects request_uri (response, body) =
35+
let open Lwt in
36+
match Cohttp.Response.status response with
37+
| `OK -> Lwt.return (response, body)
38+
| `Resume_incomplete (* actually 308 Permanent Redirect *)
39+
| `Found
40+
| `Moved_permanently
41+
| `Temporary_redirect -> handle_redirect ~max_redirects request_uri response
42+
| `Not_found | `Gone -> error "Not found"
43+
| status -> error "Unhandled status: %s" (Cohttp.Code.string_of_status status)
44+
45+
and handle_redirect ~max_redirects request_uri response =
46+
if max_redirects <= 0 then
47+
error "Too many redirects"
48+
else
49+
let headers = Cohttp.Response.headers response in
50+
let location = Cohttp.Header.get headers "location" in
51+
match location with
52+
| None -> error "Redirection without Location header"
53+
| Some url ->
54+
let uri = make_uri request_uri url in
55+
http_get_and_follow uri ~max_redirects:(max_redirects - 1)
56+
57+
let http_get url =
58+
let max_redirects = 10 in
59+
let timeout = 5. in
60+
try
61+
Lwt_main.run (
62+
Lwt_unix.with_timeout timeout (fun () ->
63+
let open Lwt in
64+
let uri = Uri.of_string url in
65+
http_get_and_follow ~max_redirects uri >>= fun (_response, body) ->
66+
Cohttp_lwt.Body.to_string body)
67+
)
68+
with
69+
| Lwt_unix.Timeout -> error "Timeout"
70+
| Unix.Unix_error _ -> error "Unknown"
71+
| Ssl.Connection_error _ -> error "SSL connection error"
1572

1673
let age fn =
1774
let now = Unix.time () in (* in sec *)
@@ -46,7 +103,7 @@ let get ?(cache_secs=cache_secs) url =
46103
close_out fh;
47104
eprintf "(cached).\n%!";
48105
data
49-
with Nethttp_client.Http_protocol _ as e ->
106+
with Error _ as e ->
50107
if Sys.file_exists fn then get_from_cache()
51108
else (
52109
eprintf "FAILED!\n%!";

script/rss2html.ml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,18 +163,10 @@ let feed_of_url ~name url =
163163
with
164164
| Rss2.Error.Error _ ->
165165
broken_feed name url "Neither an Atom nor a RSS2 feed"
166-
| Nethttp_client.Http_protocol(Nethttp_client.Timeout s)
167-
| Nethttp_client.Http_protocol(Nethttp_client.Name_resolution_error s) ->
168-
broken_feed name url s
169-
| Nethttp_client.Http_protocol Nethttp_client.Too_many_redirections ->
170-
broken_feed name url "Too many redirections"
171-
| Nethttp_client.Http_protocol e ->
172-
broken_feed name url (Printexc.to_string e)
173-
| Nethttp_client.Http_error(err, _) ->
174-
let msg = Nethttp.(string_of_http_status (http_status_of_int err)) in
175-
broken_feed name url msg
176166
| Invalid_argument msg -> (* e.g. Syndic.Date.of_string *)
177167
broken_feed name url ("Invalid_argument: " ^ msg)
168+
| Http.Error s ->
169+
broken_feed name url ("HTTP error: " ^ s)
178170

179171
let planet_feeds =
180172
let add_feed acc line =

0 commit comments

Comments
 (0)