Skip to content

taylorwood/clojurl

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
src
 
 
 
 
 
 
 
 
 
 

Clojurl

An example HTTP/S client CLI using Clojure and GraalVM native image.

Generated with clj.native-cli template. Uses deps.edn and clj.native-image.

Prerequisites

  • GraalVM 1.0.0-RC9 or higher
  • Clojure

GraalVM 1.0.0-RC7 added HTTPS as a supported protocol, and this is a brief walkthrough for using it in a Clojure project with GraalVM Community Edition for macOS.

Enable HTTPS protocol support with native-image options --enable-https or --enable-url-protocols=https.

Earlier versions of GraalVM

The following steps are only necessary with GraalVM 19.2.1 and earlier:

  1. Configure path to libsunec.dylib on macOS (or libsunec.do on Linux)

    This shared object comes with the GraalVM distribution and can be found in $GRAALVM_HOME/jre/lib/. GraalVM uses System.loadLibrary to load it at run-time whenever it's first used. The file must either be in the current working directory, or in a path specified in Java system property java.library.path.

    I set the Java system property at run-time, before first HTTPS attempt:

    (System/setProperty "java.library.path"
                        (str (System/getenv "GRAALVM_HOME") "/jre/lib"))

    See this and this for more information on HTTPS support in GraalVM and native images. If you're distributing a native image, you'll need to include libsunec. If it's in the same directory as your image you don't need to set java.library.path.

    You'll see a warning at run-time if this hasn't been properly configured:

    WARNING: The sunec native library could not be loaded.
    
  2. Use more complete certificate store

    Some versions of GraalVM may have a smaller set of CA certificates. You can workaround this by replacing GraalVM's cacerts. I renamed the file and replaced it with a symbolic link to cacerts from the JRE that comes with macOS Mojave:

    $ mv $GRAALVM_HOME/jre/lib/security/cacerts $GRAALVM_HOME/jre/lib/security/cacerts.bak
    $ ln -s $(/usr/libexec/java_home)/jre/lib/security/cacerts $GRAALVM_HOME/jre/lib/security/cacerts

    If you don't do this, you might see errors like this when attempting HTTPS connections:

    Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    8<------------------------
    Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    8<------------------------
    Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    

Usage

Compile the program with GraalVM native-image:

$ clojure -A:native-image

Print CLI options:

$ ./clojurl -h
  -u, --uri URI             URI of request
  -H, --header HEADER       Request header(s)
  -d, --data DATA           Request data
  -m, --method METHOD  GET  Request method e.g. GET, POST, etc.
  -o, --output FORMAT  edn  Output format e.g. edn, hickory
  -v, --verbose             Print verbose info
  -h, --help                Print this message

Responses can be printed in EDN or Hickory format.

Make a request and print response to stdout:

$ ./clojurl -u https://postman-echo.com/get
  {:headers
   {"content-encoding" "gzip",
    "content-type" "application/json; charset=utf-8",
    "date" "Fri, 05 Oct 2018 03:56:49 GMT",
    "etag" "W/\"10b-EZIoyNoyzUvEaPxY+kzMOEgaNh0\"",
    "server" "nginx",
    "vary" "Accept-Encoding",
    "content-length" "194",
    "connection" "keep-alive"},
   :status 200,
   :body
   "{\"args\":{},\"headers\":{\"host\":\"postman-echo.com\",\"accept\":\"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\",\"accept-encoding\":\"gzip, deflate\",\"user-agent\":\"Java/1.8.0_172\",\"x-forwarded-port\":\"443\",\"x-forwarded-proto\":\"https\"},\"url\":\"https://postman-echo.com/get\"}"}
$ ./clojurl -H Accept=application/json -H X-Session-Id=1234 -H Content-Type=application/json \
     -u https://postman-echo.com/post \
     -m post -d "{'foo':true}"
  {:headers
   {"content-encoding" "gzip",
    "content-type" "application/json; charset=utf-8",
    "date" "Fri, 05 Oct 2018 03:57:06 GMT",
    "etag" "W/\"16d-FiL2opG823uS6YyXMHVrz5k+/Vk\"",
    "server" "nginx",
    "set-cookie"
    "sails.sid=s%3Af-U0lE-XKYPefMu_II_Sggg1HGVI4LlY.lbh1ZWAEX58lBuDVpo2vRZ%2FPAo1AHllJPSPsJ01RFvc; Path=/; HttpOnly",
    "vary" "Accept-Encoding",
    "content-length" "237",
    "connection" "keep-alive"},
   :status 200,
   :body
   "{\"args\":{},\"data\":\"{'foo':true}\",\"files\":{},\"form\":{},\"headers\":{\"host\":\"postman-echo.com\",\"content-length\":\"12\",\"accept\":\"application/json\",\"accept-encoding\":\"gzip, deflate\",\"content-type\":\"application/json\",\"user-agent\":\"Java/1.8.0_172\",\"x-session-id\":\"1234\",\"x-forwarded-port\":\"443\",\"x-forwarded-proto\":\"https\"},\"json\":null,\"url\":\"https://postman-echo.com/post\"}"}

As a proof-of-concept for using Clojure 1.9 + clojure.spec.alpha + Expound with GraalVM native-image, the CLI options are validated using specs and invalid options can be explained using Expound:

$ ./clojurl -u https://postman-echo.com/get -o foo --verbose
Invalid option(s)
-- Spec failed --------------------

  {:headers ...,
   :method ...,
   :output-fn nil,
              ^^^
   :url ...,
   :verbose? ...}

should satisfy

  ifn?

-- Relevant specs -------

:clojurl/output-fn:
  clojure.core/ifn?
:clojurl/options:
  (clojure.spec.alpha/keys
   :req-un
   [:clojurl/url :clojurl/output-fn]
   :opt-un
   [:clojurl/method :clojurl/headers :clojurl/body])

-------------------------
Detected 1 error

About

An example Clojure CLI HTTP/S client using GraalVM native image

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published