Hurl 3.0.0, run and test HTTP requests with plain text and curl

65 points jcamiel

I'm happy to announce the release of Hurl 3.0.0!

Hurl is an Open Source command line tool that allow you to run and test HTTP requests with plain text. You can use it to get datas or to test HTTP APIs (JSON / GraphQL / SOAP) in a CI/CD pipeline.

A basic sample:

GET https://example.org/api/tests/4567

HTTP 200
[Asserts]
header "x-foo" contains "bar"
certificate "Expire-Date" daysAfterNow > 15
jsonpath "$.status" == "RUNNING"    # Check the status code
jsonpath "$.tests" count == 25      # Check the number of items
jsonpath "$.id" matches /\\d{4}/     # Check the format of the id

Under the hood, Hurl uses curl with Rust bindings (thanks to the awesome curl-rust crate)

Documentation: https://hurl.dev

GitHub: https://github.com/Orange-OpenSource/hurl

In this new release, we have added the following new features:

  • Checking SSL Attributes (Expiration Date, Issuer, etc...)
  • Working with Date Values
  • Convert curl to Hurl
  • Simplify Hurl’s Rust APIs

Checking SSL Attributes (Expiration Date, Issuer, etc...)

In Hurl 3.0.0, we can now check various attributes of a SSL certificate:

# Check attributes of the SSL certificate            
GET https://example.org

HTTP 200
[Asserts]
certificate "Subject" == "CN=example.org"
certificate "Issuer" == "C=US, O=Let's Encrypt, CN=R3"
certificate "Expire-Date" daysAfterNow > 15
certificate "Serial-Number" matches /[\\da-f]+/

The following properties are available to check: Subject, Issuer, Start-Date, Expire-Date and Serial-Number. Using certificate asserts, you can simply create a cron job that will warn you if your certificate expires soon.

Working with Date Values

Introduced in Hurl 2.0.0, filters allow you to transform the data extracted from an HTTP response, whether in asserts or in captures:

GET https://example.org/api

HTTP 200
[Captures]
name: jsonpath "$user.id" replace /\\d/ "x"
[Asserts]
header "x-servers" split "," count == 2
header "x-servers" split "," nth 0 == "rec1"
header "x-servers" split "," nth 1 == "rec3"
jsonpath "$.books" count == 12

Now, we have filters to work on dates: daysAfterNow, daysBeforeNow, format and toDate. For instance, let’s say we have a JSON response like this:

{
  "published": "2023-01-23T18:25:43.511Z"
}

We can do the following:

  • extract the published string from the response => jsonpath "$.published"
  • transform this value to a date variable => toDate "%Y-%m-%dT%H:%M:%S%.fZ"
  • reformat this date to the day of the week => format "%A"

So, our Hurl test becomes:

GET https://example.org/books/123
HTTP 200
[Asserts]
jsonpath "$.published" == "2023-01-23T18:25:43.511Z"
jsonpath "$.published" toDate "%Y-%m-%dT%H:%M:%S%.fZ" format "%A" == "Monday"

ISO 8601 / RFC 3339 date and time format have shorthand format %+ so we can write:

GET https://example.org/books/123
HTTP 200
[Asserts]
jsonpath "$.published" == "2023-01-23T18:25:43.511Z"
jsonpath "$.published" toDate "%+" format "%A" == "Monday"

Some queries are natively returning date values: expiration date of SSL certificates or expiration date of cookies for instance.

Convert curl to Hurl

hurlfmt is our Swiss knife for working with Hurl formatted files. We use it to convert an Hurl file to a JSON file:

$ hurlfmt --out json test.hurl | jq
{
  "entries": [
    {
      "request": {
        "method": "GET",
        "url": "http://localhost:8000/test.json"
      },
      "response": {
        "status": 200,
...

With 3.0.0, we’ve added the ability to convert curl command lines to Hurl.

For instance, using hurlfmt:

$ hurlfmt --in curl curl.txt

We can convert this chain of curl commands:

curl http://localhost:8000/hello
curl http://localhost:8000/custom-headers -H 'Fruit:Raspberry' -H 'Fruit:Apple' -H 'Fruit:Banana' -H 'Fruit: Grape' -H 'Color:Green'
curl --header 'Content-Type: application/json' --data $'{"name": "Bob","password":"secret","age": 30}' 'http://localhost:8000/post-json'
curl --header 'Content-Type:' --data '@tests_ok/data.bin' 'http://localhost:8000/post-file'
curl --location 'http://localhost:8000/redirect-absolute'
curl -k https://localhost:8001/hello

to this Hurl file:

GET http://localhost:8000/hello

GET http://localhost:8000/custom-headers
Fruit: Raspberry
Fruit: Apple
Fruit: Banana
Fruit: Grape
Color: Green

POST http://localhost:8000/post-json
Content-Type: application/json
```
{"name": "Bob","password":"secret","age": 30}
```

POST http://localhost:8000/post-file
Content-Type:
file, tests_ok/data.bin;

GET http://localhost:8000/redirect-absolute
[Options]
location: true

GET https://localhost:8001/hello
[Options]
insecure: true

Simplify Hurl’s Rust APIs

Hurl purpose has always been to be an excellent command line application, whether you want to get datas from a website or you want to add integration tests in A CI/CD pipeline. Some people (to our surprise), are also using Hurl’s crate to integrate Hurl in there own program. With 3.0.0, we’ve given some attention to the APIs exposed by Hurl’s crate and, now, it’s even simpler to run an Hurl content and get result.

A minimal Rust sample using the run method:

// A simple Hurl sample
let content = r#"
GET http://localhost:8000/hello
HTTP 200
"#;

// Define runner options and logger
let options = RunnerOptionsBuilder::new()
    .follow_location(true)
    .verbosity(Some(Verbosity::Verbose))
    .build();
let logger = LoggerBuilder::new().build();

// Set variables
let mut variables = HashMap::default();
variables.insert("name".to_string(), Value::String("toto".to_string()));

// Run the Hurl sample
let result = runner::run(
    content,
    &options,
    &variables,
    &logger
);

To run a Hurl content, you have to provide the content as a string slice, a logger, some variables and runner options. Runner options and logger are created using the builder pattern: this way we can add more options without breaking binary compatibility.

That’s All

There are other improvements and bug fixes, you can check a complete list in our release note!