{"id":13628213,"url":"https://github.com/taylorwood/clojurl","last_synced_at":"2025-10-18T06:19:24.075Z","repository":{"id":76165093,"uuid":"151534217","full_name":"taylorwood/clojurl","owner":"taylorwood","description":"An example Clojure CLI HTTP/S client using GraalVM native image","archived":false,"fork":false,"pushed_at":"2019-11-21T05:15:21.000Z","size":17,"stargazers_count":65,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-14T04:18:32.374Z","etag":null,"topics":["cli","clojure","graalvm","https","native-image"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/taylorwood.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-10-04T07:26:21.000Z","updated_at":"2024-05-31T07:51:15.000Z","dependencies_parsed_at":"2024-01-12T18:38:30.183Z","dependency_job_id":"307218c5-2255-4ead-b5e5-db15229133b1","html_url":"https://github.com/taylorwood/clojurl","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taylorwood%2Fclojurl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taylorwood%2Fclojurl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taylorwood%2Fclojurl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taylorwood%2Fclojurl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/taylorwood","download_url":"https://codeload.github.com/taylorwood/clojurl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254485042,"owners_count":22078767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","clojure","graalvm","https","native-image"],"created_at":"2024-08-01T22:00:48.522Z","updated_at":"2025-10-18T06:19:23.978Z","avatar_url":"https://github.com/taylorwood.png","language":"Clojure","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"readme":"# Clojurl\n\nAn example HTTP/S client CLI using Clojure and GraalVM native image.\n\nGenerated with [clj.native-cli template](https://github.com/taylorwood/clj.native-cli).\nUses deps.edn and [clj.native-image](https://github.com/taylorwood/clj.native-image).\n\n## Prerequisites\n\n- GraalVM 1.0.0-RC9 or higher\n- Clojure\n\nGraalVM 1.0.0-RC7 added HTTPS as a supported protocol, and this is a brief walkthrough\nfor using it in a Clojure project with GraalVM Community Edition for macOS.\n\nEnable HTTPS protocol support with `native-image` options `--enable-https` or `--enable-url-protocols=https`.\n\n#### Earlier versions of GraalVM\n\nThe following steps are only necessary with GraalVM 19.2.1 and earlier:\n\n1. Configure path to `libsunec.dylib` on macOS (or `libsunec.do` on Linux)\n\n   This shared object comes with the GraalVM distribution and can be found in\n   `$GRAALVM_HOME/jre/lib/`. GraalVM uses `System.loadLibrary` to load it at run-time\n   whenever it's first used. The file must either be in the current working directory,\n   or in a path specified in Java system property `java.library.path`.\n\n   I set the Java system property at run-time, before first HTTPS attempt:\n   ```clojure\n   (System/setProperty \"java.library.path\"\n                       (str (System/getenv \"GRAALVM_HOME\") \"/jre/lib\"))\n   ```\n\n   See [this](https://github.com/oracle/graal/blob/master/substratevm/JCA-SECURITY-SERVICES.md#native-implementations)\n   and [this](https://github.com/oracle/graal/blob/master/substratevm/URL-PROTOCOLS.md#https-support)\n   for more information on HTTPS support in GraalVM and native images. If you're distributing\n   a native image, you'll need to include libsunec. If it's in the same directory as your image\n   you don't need to set `java.library.path`.\n\n   You'll see a [warning](https://github.com/oracle/graal/blob/e3ef4f3f741d171a83c2dd2a0390dbede6b2c62d/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java#L204)\n   at run-time if this hasn't been properly configured:\n   ```\n   WARNING: The sunec native library could not be loaded.\n   ```\n1. Use more complete certificate store\n\n   Some versions of GraalVM may have a smaller set of CA certificates. You can workaround this\n   by replacing GraalVM's `cacerts`. I renamed the file and replaced it with a symbolic link\n   to `cacerts` from the JRE that comes with macOS Mojave:\n   ```bash\n   $ mv $GRAALVM_HOME/jre/lib/security/cacerts $GRAALVM_HOME/jre/lib/security/cacerts.bak\n   $ ln -s $(/usr/libexec/java_home)/jre/lib/security/cacerts $GRAALVM_HOME/jre/lib/security/cacerts\n   ```\n\n   If you don't do this, you might see errors like this when attempting HTTPS connections:\n   ```\n   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\n   8\u003c------------------------\n   Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target\n   8\u003c------------------------\n   Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target\n   ```\n\n## Usage\n\nCompile the program with GraalVM `native-image`:\n```bash\n$ clojure -A:native-image\n```\n\nPrint CLI options:\n```\n$ ./clojurl -h\n  -u, --uri URI             URI of request\n  -H, --header HEADER       Request header(s)\n  -d, --data DATA           Request data\n  -m, --method METHOD  GET  Request method e.g. GET, POST, etc.\n  -o, --output FORMAT  edn  Output format e.g. edn, hickory\n  -v, --verbose             Print verbose info\n  -h, --help                Print this message\n```\nResponses can be printed in EDN or Hickory format.\n\nMake a request and print response to stdout:\n```\n$ ./clojurl -u https://postman-echo.com/get\n  {:headers\n   {\"content-encoding\" \"gzip\",\n    \"content-type\" \"application/json; charset=utf-8\",\n    \"date\" \"Fri, 05 Oct 2018 03:56:49 GMT\",\n    \"etag\" \"W/\\\"10b-EZIoyNoyzUvEaPxY+kzMOEgaNh0\\\"\",\n    \"server\" \"nginx\",\n    \"vary\" \"Accept-Encoding\",\n    \"content-length\" \"194\",\n    \"connection\" \"keep-alive\"},\n   :status 200,\n   :body\n   \"{\\\"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\\\"}\"}\n```\n```\n$ ./clojurl -H Accept=application/json -H X-Session-Id=1234 -H Content-Type=application/json \\\n     -u https://postman-echo.com/post \\\n     -m post -d \"{'foo':true}\"\n  {:headers\n   {\"content-encoding\" \"gzip\",\n    \"content-type\" \"application/json; charset=utf-8\",\n    \"date\" \"Fri, 05 Oct 2018 03:57:06 GMT\",\n    \"etag\" \"W/\\\"16d-FiL2opG823uS6YyXMHVrz5k+/Vk\\\"\",\n    \"server\" \"nginx\",\n    \"set-cookie\"\n    \"sails.sid=s%3Af-U0lE-XKYPefMu_II_Sggg1HGVI4LlY.lbh1ZWAEX58lBuDVpo2vRZ%2FPAo1AHllJPSPsJ01RFvc; Path=/; HttpOnly\",\n    \"vary\" \"Accept-Encoding\",\n    \"content-length\" \"237\",\n    \"connection\" \"keep-alive\"},\n   :status 200,\n   :body\n   \"{\\\"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\\\"}\"}\n```\n\nAs a proof-of-concept for using Clojure 1.9 + clojure.spec.alpha + Expound with GraalVM native-image,\nthe CLI options are validated using specs and invalid options can be explained using Expound:\n```\n$ ./clojurl -u https://postman-echo.com/get -o foo --verbose\nInvalid option(s)\n-- Spec failed --------------------\n\n  {:headers ...,\n   :method ...,\n   :output-fn nil,\n              ^^^\n   :url ...,\n   :verbose? ...}\n\nshould satisfy\n\n  ifn?\n\n-- Relevant specs -------\n\n:clojurl/output-fn:\n  clojure.core/ifn?\n:clojurl/options:\n  (clojure.spec.alpha/keys\n   :req-un\n   [:clojurl/url :clojurl/output-fn]\n   :opt-un\n   [:clojurl/method :clojurl/headers :clojurl/body])\n\n-------------------------\nDetected 1 error\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaylorwood%2Fclojurl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftaylorwood%2Fclojurl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaylorwood%2Fclojurl/lists"}