Announcing Hurl 1.8.0

The Hurl team is happy to announce Hurl 1.8.0 Partying Face! .

Hurl is a command line tool powered by curl, that runs HTTP requests defined in a simple plain text format:

# Get home:
GET https://example.org

HTTP/1.1 200
[Captures]
csrf_token: xpath "string(//meta[@name='_csrf_token']/@content)"

# Do login!
POST https://example.org/login?user=toto&password=1234
X-CSRF-TOKEN: 

HTTP/1.1 302

Hurl can be used to get data like curl, or as a testing tool for JSON/XML apis and HTML content.

So, what’s new in 1.8.0?

Polling and Retry

You can now retry requests which asserts and captures have failed. This way, you can write polling scenarios or create robust tests even in flaky conditions. To activate retry, you can either use --retry option (every request of the run can be retried), or you can target a specific request with an [Options] section.

Let’s say we have an API to create a ressource. Once created, we want to poll this resource and wait until it reaches a certain state.

First we create a new resource with a POST request. We capture the id of the resource to be able to use it in the following requests:

# Create a new job
POST https://api.example.org/jobs

HTTP/* 201
[Captures]
job_id: jsonpath "$.id"
[Asserts]
jsonpath "$.state" == "RUNNING"

Then, we pull the resource with a GET request and check its state value. We’ve configured the GET request to be retried with an [Options] section. The JSONPath assert tests the value of state field. Because of the retry option, the GET request is going to be retried until state’s value is COMPLETED:

# ...

# Pull job status until it is completed
GET https://api.example.org/jobs/
[Options]
retry: 10

HTTP/* 200
[Asserts]
jsonpath "$.state" == "COMPLETED"

So, the full scenario looks like:

# Create a new job
POST https://api.example.org/jobs

HTTP/* 201
[Captures]
job_id: jsonpath "$.id"
[Asserts]
jsonpath "$.state" == "RUNNING"


# Pull job status until it is completed
GET https://api.example.org/
[Options]
retry: 10

HTTP/* 200
[Asserts]
jsonpath "$.state" == "COMPLETED"

Retry work on any asserts, whether they’re explicit (with an [Asserts] section), or implicit (the status code). For instance, the following snippet:

GET https://api.example.org/123456
[Options]
retry: 10
retry-interval: 4000
HTTP/* 200

will poll until https://api.example.org/123456 returns a 200 OK, with a 4 seconds delay between each retry.

Finally, one common need in shell script is to wait until a specific URL is ready and returns a 200 OK. This can be easily done now with Hurl:

$ echo -e 'GET https://example.org/health\nHTTP/* 200' | hurl --retry

URL Assert

The URL assert allows check on the final URL of a request: it’s particularly interesting when you tell Hurl to follow redirects (either with --location option or with an Options section):

# Check that HTTP is redirected to HTTPS:
GET http://example.org
[Options]
location: true

HTTP/* 200
[Asserts]
url == "https://example.org"

curl Debug Logs

In Hurl 1.7.0, we introduced --very-verbose option to output request and response bodies. Now, when very-verbose is used, Hurl displays debug logs from libcurl (prefixed with **), allowing displaying SSL certificates infos for instance:

$ echo 'HEAD https://hurl.dev' | hurl --very-verbose
* Options:
*     fail fast: true
*     follow redirect: false
*     insecure: false
*     max redirect: 50
*     retry: false
*     retry max count: 10
* ------------------------------------------------------------------------------
* Executing entry 1
*
* Cookie store:
*
* Request:
* HEAD https://hurl.dev
*
* Request can be run with the following curl command:
* curl 'https://hurl.dev' --head
*
**   Trying 145.239.78.213:443...
** Connected to hurl.dev (145.239.78.213) port 443 (#0)
** ALPN, offering h2
** ALPN, offering http/1.1
** successfully set certificate verify locations:
**  CAfile: /etc/ssl/cert.pem
**  CApath: none
** (304) (OUT), TLS handshake, Client hello (1):
** (304) (IN), TLS handshake, Server hello (2):
** TLSv1.2 (IN), TLS handshake, Certificate (11):
** TLSv1.2 (IN), TLS handshake, Server key exchange (12):
** TLSv1.2 (IN), TLS handshake, Server finished (14):
** TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
** TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
** TLSv1.2 (OUT), TLS handshake, Finished (20):
** TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
** TLSv1.2 (IN), TLS handshake, Finished (20):
** SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
** ALPN, server accepted to use h2
** Server certificate:
**  subject: CN=hurl.dev
**  start date: Sep 30 22:15:32 2022 GMT
**  expire date: Dec 29 22:15:31 2022 GMT
**  subjectAltName: host "hurl.dev" matched cert's "hurl.dev"
**  issuer: C=US; O=Let's Encrypt; CN=R3
**  SSL certificate verify ok.
** Using HTTP2, server supports multiplexing
** Connection state changed (HTTP/2 confirmed)
** Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
** Using Stream ID: 1 (easy handle 0x14c811800)
> HEAD / HTTP/2
> Host: hurl.dev
> accept: */*
> user-agent: hurl/1.8.0-SNAPSHOT
>
* Request body:
*
** Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
** Connection #0 to host hurl.dev left intact
* Response: (received 0 bytes in 110 ms)
*
< HTTP/2 200
< server: nginx/1.14.2
< date: Mon, 31 Oct 2022 13:12:41 GMT
< content-type: text/html
< content-length: 28370
< last-modified: Thu, 27 Oct 2022 14:15:30 GMT
< etag: "635a9282-6ed2"
< accept-ranges: bytes
<
* Response body:
*
*

That’s All

If you like Hurl, don’t hesitate to give us a star on GitHub or share it on Twitter!

We’ll be happy to hear from you, either for enhancement requests or for sharing your success story using Hurl!