#HTTPS client and server

The bundled spork-https gives an HTTPS client and server on top of jsec's TLS streams. chirp adds a small overlay to the client; the server is upstream's.

#Client requests

(import spork-https :as https)

(def res (https/get "https://example.com/"))
(print (res :status))
(print (string/slice (res :body) 0 15))

The response is a table with :status, :headers and :body. https/post, put, patch, delete, head, request (method first) and download exist as well. Options go after the url: :body, :headers, :max-redirects, :tls-opts, :stream-opts.

#Default User-Agent

Some services, GitHub's API among them, reject requests without a User-Agent. chirp sends User-Agent: chirp/<version> when the request has none. A User-Agent you set, in any letter case, is sent unchanged:

(https/get "https://api.github.com/repos/roobie/chirp"
           :headers {"User-Agent" "my-tool"})

#The :ca option

Upstream documents :tls-opts {:ca "ca.pem"} as a path to a CA file, but jsec's client ignores it and falls back to the system store. chirp copies :ca into :security {:ca-file ...}, which jsec does read, for both :tls-opts and :stream-opts:

(https/get "https://internal.example/" :tls-opts {:ca "internal-ca.pem"})

#Trusting one certificate

:trusted-cert takes the certificate as PEM data, not a path. It suits a self-signed server:

(https/get "https://localhost:8443/" :tls-opts {:trusted-cert (slurp "server.pem")})

#Windows certificates

Windows has no CA bundle file for chirp to find, so HTTPS there needs SSL_CERT_FILE set to a PEM bundle, for example the curl CA bundle or the ca-bundle.crt that ships with Git for Windows:

$env:SSL_CERT_FILE = "C:\Program Files\Git\ucrt64\etc\ssl\certs\ca-bundle.crt"

#Minimal HTTPS server

(import spork-https :as https)
(import jsec/cert)

(defn main [&]
  (def certs (cert/generate-self-signed-cert
               {:common-name "localhost" :organization "demo"
                :country "US" :days-valid 1 :bits 2048}))
  (def server
    (https/server (fn [req]
                    {:status 200
                     :headers {"Content-Type" "text/plain"}
                     :body (string "hello " (req :path))})
                  (certs :cert) (certs :key) "localhost" "8443"))
  (print "listening on https://localhost:8443/")
  # The server runs on the event loop; keep main alive.
  (forever (ev/sleep 3600)))

The handler receives the request table and returns a response table. The port is a string. cert and key are PEM strings or file paths. (:close server) stops it.