Blame


1 4b959a3a 2021-10-16 op I’d like to announce ‘com.omarpolo/gemini’: a new clojure library to make Gemini requests.
2 4b959a3a 2021-10-16 op
3 4b959a3a 2021-10-16 op => https://github.com/omar-polo/gemini com.omarpolo/gemini: A Clojure library to make Gemini requests
4 4b959a3a 2021-10-16 op
5 4b959a3a 2021-10-16 op It’s also available on clojars, so go download it! :D
6 4b959a3a 2021-10-16 op
7 4b959a3a 2021-10-16 op ## background
8 4b959a3a 2021-10-16 op
9 4b959a3a 2021-10-16 op I needed something to ping antenna when I publish things on this blog. The site is assembled using some clojure scripts (not the most idiomatic clojure you’d read, but it works) and I’m already shelling out to rsync for the upload. To ping antenna I wanted to do something natively.
10 4b959a3a 2021-10-16 op
11 4b959a3a 2021-10-16 op Gemini is a simple protocol, isn’t it? Writing something from scratch should be simple, right?
12 4b959a3a 2021-10-16 op
13 4b959a3a 2021-10-16 op ## the making of
14 4b959a3a 2021-10-16 op
15 4b959a3a 2021-10-16 op It wasn’t simple, at least for me. I kinda get sleepy when I have to dig into the Java stdlib to learn how to do things. And I don’t know how to do networking at all in java, so there was a lot that I needed to learn.
16 4b959a3a 2021-10-16 op
17 4b959a3a 2021-10-16 op I ended up writing a Java class, not because it’s required but because it’s easier. It exposes a really simple and barebone APIs to make Gemini requests and then wrote a more idiomatic (I hope) clojure wrapper around.
18 4b959a3a 2021-10-16 op
19 4b959a3a 2021-10-16 op Speaking of Java, disabling the certificate validation wasn’t exactly straightforward (you need to provide your own X509ExtendedTrustManager) and quite surprisingly it doesn’t do SNI by default.
20 4b959a3a 2021-10-16 op
21 4b959a3a 2021-10-16 op The magic spell to force a SSLSocket to do SNI is to
22 4b959a3a 2021-10-16 op
23 4b959a3a 2021-10-16 op ```
24 4b959a3a 2021-10-16 op var params = new SSLParameters();
25 4b959a3a 2021-10-16 op params.setServerNames(Collections.singletonList(new SNIHostName(host)))
26 4b959a3a 2021-10-16 op
27 4b959a3a 2021-10-16 op /* … */
28 4b959a3a 2021-10-16 op
29 4b959a3a 2021-10-16 op var socket = …;
30 4b959a3a 2021-10-16 op socket.setSSLParameters(params);
31 4b959a3a 2021-10-16 op ```
32 4b959a3a 2021-10-16 op
33 4b959a3a 2021-10-16 op I was able to contribute back the same trick to jgemini, a Java graphical Gemini browser.
34 4b959a3a 2021-10-16 op
35 4b959a3a 2021-10-16 op => https://github.com/kevinboone/jgemini jgemini: A Java-based graphical browser for the Gemini protocol
36 4b959a3a 2021-10-16 op
37 4b959a3a 2021-10-16 op
38 4b959a3a 2021-10-16 op ## The API
39 4b959a3a 2021-10-16 op
40 4b959a3a 2021-10-16 op The main function is ‘fetch’. It takes a map and return a map. So clojure-ish.
41 4b959a3a 2021-10-16 op
42 4b959a3a 2021-10-16 op The feature list is pretty short honestly:
43 4b959a3a 2021-10-16 op
44 4b959a3a 2021-10-16 op * can use a gemini server as proxy
45 4b959a3a 2021-10-16 op * can follow redirects
46 4b959a3a 2021-10-16 op
47 4b959a3a 2021-10-16 op and there’s a major drawback: the ‘close’ function must be called to clean things up. There’s a ‘with-request’ macro (similar to ‘with-open’) that should help.
48 4b959a3a 2021-10-16 op
49 4b959a3a 2021-10-16 op It’s easy to stream a request since I’m exporting the BufferedReader to clojure clients, you can just read from it. In combo with my gemtext library, it’s possible to stream text/gemini!
50 4b959a3a 2021-10-16 op
51 4b959a3a 2021-10-16 op => https://github.com/omar-polo/gemtext com.omarpolo/gemtext: text/gemini for clojure/clojurescript
52 4b959a3a 2021-10-16 op
53 4b959a3a 2021-10-16 op
54 4b959a3a 2021-10-16 op ## The future
55 4b959a3a 2021-10-16 op
56 4b959a3a 2021-10-16 op Gemini is simple, so there isn’t very much more to do. I planning to provide a function to control the certificates, so that one can implement TOFU on top of this library, but that’s that.
57 4b959a3a 2021-10-16 op
58 4b959a3a 2021-10-16 op I’m still not completely happy of the provided APIs, but they doesn’t seem too bad and I don’t have a clue on how to improve them. I’m open to suggestions thought ;)