{"id":16776358,"url":"https://github.com/ameshkov/gocurl","last_synced_at":"2025-03-22T00:30:58.034Z","repository":{"id":194306745,"uuid":"690466933","full_name":"ameshkov/gocurl","owner":"ameshkov","description":"Simplified version of curl written in pure Go with additional features","archived":false,"fork":false,"pushed_at":"2025-03-21T08:56:45.000Z","size":179,"stargazers_count":41,"open_issues_count":12,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-21T09:33:11.842Z","etag":null,"topics":["curl","dns","ech","gocurl","golang","http","https","postquantum","websocket"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ameshkov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-12T08:51:26.000Z","updated_at":"2025-03-21T08:56:49.000Z","dependencies_parsed_at":"2024-04-03T09:51:11.056Z","dependency_job_id":"e495956c-c7f5-4937-ba0b-9d926a2bf444","html_url":"https://github.com/ameshkov/gocurl","commit_stats":null,"previous_names":["ameshkov/gocurl"],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ameshkov%2Fgocurl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ameshkov%2Fgocurl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ameshkov%2Fgocurl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ameshkov%2Fgocurl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ameshkov","download_url":"https://codeload.github.com/ameshkov/gocurl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244890102,"owners_count":20527030,"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":["curl","dns","ech","gocurl","golang","http","https","postquantum","websocket"],"created_at":"2024-10-13T07:09:43.399Z","updated_at":"2025-03-22T00:30:58.028Z","avatar_url":"https://github.com/ameshkov.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Go Report Card](https://goreportcard.com/badge/github.com/ameshkov/gocurl)](https://goreportcard.com/report/ameshkov/gocurl)\n[![Latest release](https://img.shields.io/github/release/ameshkov/gocurl/all.svg)](https://github.com/ameshkov/gocurl/releases)\n\n# gocurl\n\nSimplified version of [`curl`](https://curl.se/) written in Go.\n\n1. Supports a limited subset of curl options.\n2. Supports some flags that curl does\n   not. [Read more about the new stuff](#newstuff).\n\n* [Why in the world you need another curl?](#why)\n* [How to install gocurl?](#install)\n* [How to use gocurl?](#howtouse)\n* [New stuff](#newstuff)\n    * [Encrypted ClientHello](#ech)\n    * [Custom DNS servers](#dns)\n    * [Experimental flags](#exp)\n        * [Post-quantum cryptography](#pq)\n    * [WebSocket support](#websocket)\n* [All command-line arguments](#allcmdarguments)\n\n\u003ca id=\"why\"\u003e\u003c/a\u003e\n\n## Why in the world you need another curl?\n\nCurl is certainly awesome, but sometimes I need to have better control over\nwhat's happening on the inside and to be able to debug it. It seemed easier to\nme to implement the necessary parts of curl in Go.\n\nAlso, I'd like to be able to extend it with what fits my specific needs.\nUnfortunately, curl is a bit too huge for that now.\n\n\u003ca id=\"install\"\u003e\u003c/a\u003e\n\n## How to install gocurl?\n\n* Using homebrew:\n    ```shell\n    brew install ameshkov/tap/gocurl\n    ```\n* From source:\n    ```shell\n    go install github.com/ameshkov/gocurl@latest\n    ```\n* You can use [a Docker image][dockerimage]:\n    ```shell\n    docker run --rm ghcr.io/ameshkov/gocurl --help\n    ```\n* You can get a binary from the [releases page][releases].\n\n[dockerimage]: https://github.com/ameshkov/gocurl/pkgs/container/gocurl\n\n[releases]: https://github.com/ameshkov/gocurl/releases\n\n\u003ca id=\"howtouse\"\u003e\u003c/a\u003e\n\n## How to use gocurl?\n\nUse it the same way you use original curl.\n\n* `gocurl https://httpbin.agrd.workers.dev/get` make a `GET` request.\n* `gocurl -d \"test\" -v https://httpbin.agrd.workers.dev/post` make a `POST`\n  request with `test` data.\n* `gocurl -I https://httpbin.agrd.workers.dev/head` make a `HEAD` request.\n* `gocurl -I --insecure https://expired.badssl.com/` do not verify TLS\n  certificate.\n* `gocurl -I --http1.1 https://httpbin.agrd.workers.dev/head` force use\n  HTTP/1.1.\n* `gocurl -I --http2 https://httpbin.agrd.workers.dev/head` force use HTTP/2.\n* `gocurl -I --http3 https://httpbin.agrd.workers.dev/head` force use HTTP/3.\n* `gocurl -x socks5://user:pass@host:port https://httpbin.agrd.workers.dev/get`\n  use a proxy server.\n* `gocurl -I --tlsv1.3 https://tls-v1-2.badssl.com:1012/` force use TLS v1.3.\n* `gocurl -I --connect-to \"httpbin.agrd.workers.dev:443:172.67.152.85:443\"\n  https://httpbin.agrd.workers.dev/head` connect to the specified IP addresses.\n* `gocurl -I --resolve \"httpbin.agrd.workers.dev:443:172.67.152.85\"\n  https://httpbin.agrd.workers.dev/head` resolve the hostname to the specified\n  IP address. Note, that unlike `curl`, `gocurl` ignores port in this option.\n\n\u003ca id=\"newstuff\"\u003e\u003c/a\u003e\n\n### New stuff\n\nAlso, you can use some new stuff that is not supported by curl.\n\n* `gocurl --json-output https://httpbin.agrd.workers.dev/get` write output in\n  machine-readable format (JSON).\n* `gocurl --tls-split-hello 5:50 https://httpbin.agrd.workers.dev/get` split\n  TLS ClientHello in two parts and make a 50ms delay after sending the first\n  part.\n* `gocurl -v --ech https://crypto.cloudflare.com/cdn-cgi/trace` enables support\n  for ECH (Encrypted Client Hello) for the request. More on this [below](#ech).\n* `gocurl --dns-servers \"tls://dns.google\" https://httpbin.agrd.workers.dev/get`\n  uses custom DNS-over-TLS server to resolve hostnames. More on this\n  [below](#dns).\n* `gocurl --experiment pq https://pq.cloudflareresearch.com/` enables\n  post-quantum cryptography support for the request. More on this [below](#pq).\n* `gocurl wss://httpbin.agrd.workers.dev/ws` sends a WS upgrade request.\n* `gocurl -d \"test message\" wss://httpbin.agrd.workers.dev/ws` establishes a WS\n  connection, sends the first message through it and reads the response.\n\n\u003ca id=\"ech\"\u003e\u003c/a\u003e\n\n#### Encrypted ClientHello\n\nECH or Encrypted Client Hello is a new standard that allows completely\nencrypting TLS Client Hello. Currently, the RFC is in the [draft stage][echrfc],\nbut it is already supported by some big names [like Cloudflare][echcloudflare].\n`gocurl` supports ECH and provides several options to use it.\n\nThe simple option is just add `--ech` flag and see what happens:\n\n```shell\ngocurl -v --ech https://crypto.cloudflare.com/cdn-cgi/trace\n```\n\nIn this case, `gocurl` will try to discover the ECH configuration from DNS\nrecords and then use them to establish the connection.\n\nInstead of that, you may choose to supply your own configuration in the same\nbase64-encoded format as used by the SVCB record:\n\n```shell\n# Send a type=https query and find ech record there.\n% dig -t https crypto.cloudflare.com. +short\n1 . alpn=\"http/1.1,h2\" ipv4hint=162.159.137.85,162.159.138.85 ech=AEX+DQBBvgAgACARWS42g5NmDZo5pIpTWSwHzTwzdRKPdUW732QbyUeyDQAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:7::a29f:8955,2606:4700:7::a29f:8a55\n\n# You can now pass it to gocurl.\ngocurl -v \\\n  --echconfig \"AEX+DQBBvgAgACARWS42g5NmDZo5pIpTWSwHzTwzdRKPdUW732QbyUeyDQAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA=\" \\\n  https://crypto.cloudflare.com/cdn-cgi/trace\n```\n\n\u003e Interesting thing about ECH is that it may connect even if you use an expired\n\u003e configuration (see HelloRetryRequest in the RFC). It depends on both the\n\u003e server and the client implementation and does not work with Cloudflare at the\n\u003e moment.\n\nHere's what happens under the hood:\n\n1. `gocurl` resolves `crypto.cloudflare.com` IP address and connects to it.\n2. It sends TLS ClientHello (outer) with encrypted inner ClientHello to that IP\n   address. The ServerName field in the outer ClientHello is set to the one that\n   is encoded in the ECH configuration (in this example it will be\n   `cloudflare-ech.com`), and in the inner encrypted ClientHello it will be\n   set to `crypto.cloudflare.com`.\n\nYou may want to configure a specific \"client-facing\" server instead and the way\nto do that is to use `--connect-to`. Let's send a request to `cloudflare.com`\nand use `crypto.cloudflare.com` as a client-facing server for that.\n\n```shell\ngocurl -v \\\n  --connect-to \"cloudflare.com:443:crypto.cloudflare.com:443\" \\\n  --echconfig \"AEX+DQBBvgAgACARWS42g5NmDZo5pIpTWSwHzTwzdRKPdUW732QbyUeyDQAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA=\" \\\n  https://cloudflare.com/cdn-cgi/trace\n```\n\n\u003e For this command to work you may need to replace `--echconfig` with the\n\u003e current one discovered using DNS as was explained before.\n\nHere's what happens now:\n\n1. `gocurl` connects to `crypto.cloudflare.com` (client-facing relay).\n2. Sends a TLS ClientHello with `cloudflare-ech.com` in the Server Name\n   extension.\n3. Establishes a TLS connection with `cloudflare.com` using the inner encrypted\n   ClientHello.\n\n[echrfc]: https://datatracker.ietf.org/doc/draft-ietf-tls-esni/\n\n[echcloudflare]: https://blog.cloudflare.com/handshake-encryption-endgame-an-ech-update/\n\n\u003ca id=\"dns\"\u003e\u003c/a\u003e\n\n#### Custom DNS servers\n\n`gocurl` allows using custom DNS servers to resolve hostnames when making\nrequests. This can be achieved by using `--dns-servers` command-line argument.\n`curl` with `c-ares` also supports this command-line argument, but `gocurl`\nadds encrypted DNS support on top of it, it supports all popular DNS encryption\nprotocols: DNS-over-QUIC, DNS-over-HTTPS, DNS-over-TLS and DNSCrypt.\n\nYou can specify multiple DNS servers, in this case `gocurl` will attempt to use\nthem one by one until it receives a response or until all of them fail:\n\n```shell\ngocurl \\\n  --dns-servers \"tls://dns.adguard-dns.com,tls://dns.google\" \\\n  https://example.org/\n\n```\n\n* DNS-over-QUIC\n  ```shell\n  gocurl --dns-servers \"quic://dns.adguard-dns.com\" https://example.org/\n  ```\n\n* DNS-over-HTTPS\n  ```shell\n  gocurl --dns-servers \"https://dns.adguard-dns.com/dns-query\" https://example.org/\n  ```\n\n* DNS-over-TLS\n  ```shell\n  gocurl --dns-servers \"tls://dns.adguard-dns.com\" https://example.org/\n  ```\n\n* DNSCrypt\n  ```shell\n  gocurl \\\n      --dns-servers sdns://AQIAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20 \\\n      https://example.org/\n  ```\n\n\u003ca id=\"websocket\"\u003e\u003c/a\u003e\n\n#### WebSocket support\n\n`gocurl` provides some initial support for WebSocket protocol. It may be\nextended in the future, see the corresponding [Github issue][wsissue].\n\n* `gocurl wss://httpbin.agrd.workers.dev/ws` sends a WS upgrade request.\n* `gocurl -d \"test message\" wss://httpbin.agrd.workers.dev/ws` establishes a WS\n  connection, sends the first message through it and reads the response.\n\n[wsissue]: https://github.com/ameshkov/gocurl/issues/17\n\n\u003ca id=\"exp\"\u003e\u003c/a\u003e\n\n#### Experimental flags\n\nExperimental flags are added to `gocurl` whenever there's a feature that may be\ncompletely changed or removed in the future. Experiments can be enabled using\nthe `--experiment=\u003cname[:value]\u003e` argument where `name` is the experiment name\nand `value` is an optional string value (the need for it depends on the actual\nexperiment).\n\n\u003ca id=\"pq\"\u003e\u003c/a\u003e\n\n##### Post-quantum cryptography\n\nPost-quantum (PQ) cryptography has been designed to be secure against the\nthreat of quantum computers. You can learn more about it from Cloudflare's\n[blog post][postquantum]. `gocurl` supports it via the `--experiment=pq` flag.\n\nNote, that it is not available for `--http3` at the moment.\n\n```shell\ngocurl --experiment pq https://pq.cloudflareresearch.com/\n```\n\n[postquantum]: https://blog.cloudflare.com/post-quantum-for-all/\n\n\u003ca id=\"allcmdarguments\"\u003e\u003c/a\u003e\n\n## All command-line arguments\n\n```shell\nUsage:\n  gocurl [OPTIONS]\n\nApplication Options:\n      --url=\u003cURL\u003e                                           URL the request will be made to. Can be specified without any flags.\n  -X, --request=\u003cmethod\u003e                                    HTTP method. GET by default.\n  -d, --data=\u003cdata\u003e                                         Sends the specified data to the HTTP server using content type application/x-www-form-urlencoded.\n  -H, --header=                                             Extra header to include in the request. Can be specified multiple times.\n  -x, --proxy=[protocol://username:password@]host[:port]    Use the specified proxy. The proxy string can be specified with a protocol:// prefix.\n      --connect-to=\u003cHOST1:PORT1:HOST2:PORT2\u003e                For a request to the given HOST1:PORT1 pair, connect to HOST2:PORT2 instead. Can be specified\n                                                            multiple times.\n  -I, --head                                                Fetch the headers only.\n  -k, --insecure                                            Disables TLS verification of the connection.\n      --tlsv1.3                                             Forces gocurl to use TLS v1.3 or newer.\n      --tlsv1.2                                             Forces gocurl to use TLS v1.2 or newer.\n      --tls-max=\u003cVERSION\u003e                                   (TLS) VERSION defines maximum supported TLS version. Can be 1.2 or 1.3. The minimum acceptable\n                                                            version is set by tlsv1.2 or tlsv1.3.\n      --ciphers=\u003cspace-separated list of ciphers\u003e           Specifies which ciphers to use in the connection, see\n                                                            https://go.dev/src/crypto/tls/cipher_suites.go for the full list of available ciphers.\n      --tls-servername=\u003cHOSTNAME\u003e                           Specifies the server name that will be sent in TLS ClientHello\n      --http1.1                                             Forces gocurl to use HTTP v1.1.\n      --http2                                               Forces gocurl to use HTTP v2.\n      --http3                                               Forces gocurl to use HTTP v3.\n      --ech                                                 Enables ECH support for the request.\n      --echconfig=\u003cbase64-encoded data\u003e                     ECH configuration to use for this request. Implicitly enables --ech when specified.\n  -4, --ipv4                                                This option tells gocurl to use IPv4 addresses only when resolving host names.\n  -6, --ipv6                                                This option tells gocurl to use IPv6 addresses only when resolving host names.\n      --dns-servers=\u003cDNSADDR1,DNSADDR2\u003e                     DNS servers to use when making the request. Supports encrypted DNS: tls://, https://, quic://,\n                                                            sdns://\n      --resolve=\u003c[+]host:port:addr[,addr]...\u003e               Provide a custom address for a specific host. port is ignored by gocurl. '*' can be used instead of\n                                                            the host name. Can be specified multiple times.\n      --tls-split-hello=\u003cCHUNKSIZE:DELAY\u003e                   An option that allows splitting TLS ClientHello in two parts in order to avoid common DPI systems\n                                                            detecting TLS. CHUNKSIZE is the size of the first bytes before ClientHello is split, DELAY is delay\n                                                            in milliseconds before sending the second part.\n      --json-output                                         Makes gocurl write machine-readable output in JSON format.\n  -o, --output=\u003cfile\u003e                                       Defines where to write the received data. If not set, gocurl will write everything to stdout.\n      --experiment=\u003cname[:value]\u003e                           Allows enabling experimental options. See the documentation for available options. Can be specified\n                                                            multiple times.\n  -v, --verbose                                             Verbose output (optional).\n\nHelp Options:\n  -h, --help                                                Show this help message\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fameshkov%2Fgocurl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fameshkov%2Fgocurl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fameshkov%2Fgocurl/lists"}