The Hurl team is thrilled to announce Hurl 4.2.0 !
Hurl is a command line tool powered by curl, that runs HTTP requests defined in a simple plain text format:
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
Hurl HTTP engine is powered by curl, one of the most reliable HTTP libraries, available in millions of softwares, devices (TVs, cars, printers etc..), and is even used on Mars. In details, Hurl is written in Rust, offloading the HTTP layer to libcurl. We like this design a lot because Hurl benefits directly from curl’s power and capabilities “for free”, adding some nice asserts and captures possibilities to chain and test HTTP requests.
Started with Hurl 4.2.0, Hurl supports now HTTP/3! Running a Hurl file with HTTP/3 can be done on the command line,
with [--http3
] option:
$ hurl --http3 test.hurl
Like curl, there are also [--http2
], [--http1.1
] and [--http1.0
] options to force a certain version of HTTP. With
options sections, we can specify an HTTP version per request, in the same Hurl file:
GET https://myserver.com
[Options]
http3: true
HTTP/3 200
GET https://myserver.com
[Options]
http2: true
HTTP/2 200
To use HTTP/3, the underlying libcurl used by Hurl must expose HTTP/3 features. Simply run hurl --version
to check
which libcurl features are supported:
$ hurl --version
hurl 4.2.0 (x86_64-apple-darwin23.0) libcurl/8.1.2 (SecureTransport) LibreSSL/3.3.6 zlib/1.2.12 nghttp2/1.55.1
Features (libcurl): alt-svc AsynchDNS HSTS HTTP2 HTTP3 IPv6 Largefile libz NTLM NTLM_WB SPNEGO SSL UnixSockets
Features (built-in): brotli
If installed on macOS with Homebrew, Hurl uses the system libcurl that does not support HTTP/3 yet. To use HTTP/3 on macOS, you can:
- compile your own version of libcurl to support HTTP/3. Cloudflare has a simple Homebrew formula to build libcurl with HTTP/3
- link Hurl with this updated libcurl
- Enjoy!
There are very few HTTP clients that support HTTP/3, so we’re very happy with this new feature and look forward for feedbacks!
Thanks to @humphd, a recurring provider of new features, Hurl can now be installed through conda-forge, a community-led packet manager for Conda:
$ conda install -c conda-forge hurl
conda-forge Hurl installation supports macOS, Linux, Windows, on x86 and ARM 64 bits architectures so we’ve got you covered!
What’s very interesting with conda-forge is that other packet managers use it as a distribution source. For instance, pixi, a powerful and fast package management tool, uses the existing conda ecosystem, so Hurl can be installed with pixi:
$ pixi init hello-world
$ cd hello-world
$ pixi add hurl
There are, of course, a lot of other ways to install Hurl. If your preferred platform is not yet supported, drop-us an issue on GitHub!
Hurl can be used as a testing tool, but it can be also simply used to get HTTP response when you’ve to chain multiple dependant requests (download a resource behind a login for instance). In a Hurl file, you can chain multiple requests, passing data from one to another:
# Get home:
GET https://example.org
HTTP 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: {{csrf_token}}
HTTP 302
# Get our precisous resource:
GET https://example.org/protected
HTTP 200
When run, Hurl outputs the last response body on the standard output (in this case the response of
GET https://example.org/protected
). Like curl, this response can be saved to a file with [--output
] option:
$ hurl --output /tmp/response.json api.hurl
With options sections, we can now save to file each individual response of any request.
Let’s imagine we have a cat API that returns a JSON response for a given cat. If we want to save multiple cat images on disk, we can write this Hurl file:
--output
]--output
]Our Hurl file will look like this:
# Get our first cat resource and capture its image
GET https://catapi.com/cats/1
HTTP 200
[Captures]
img: jsonpath "$.url"
# Download its image and save it to disk
GET {{img}}
[Options]
output: cat1.jpg
HTTP 200
# Do the same with the second cat:
GET https://catapi.com/cats/2
HTTP 200
[Captures]
img: jsonpath "$.url"
# Download its image and save it to disk
GET {{img}}
[Options]
output: cat2.jpg
HTTP 200
Data can be captured and/or tested from HTTP responses using JSONPath, XPath, regex etc... Filters allow us to
even refine the extracted data. With the [new jsonpath
filter], we’re able to chain XPath or regex queries with a
JSONPath filter:
For instance, given this HTML file:
<!DOCTYPE html>
<html>
<body>
<p id="user"></p>
<script>
var s = '{"first_name" : "Sammy", "last_name" : "Shark", "location" : "Ocean"}';
var obj = JSON.parse(s);
document.getElementById("user").innerHTML =
"Name: " + obj.first_name + " " + obj.last_name + "<br>" +
"Location: " + obj.location;
</script>
</body>
</html>
We want to extract the string {"first_name" : "Sammy", "last_name" : "Shark", "location" : "Ocean"}
and analyse it as
a JSON:
regex
query], we extract this string regex /var s = '(.*)';/
jsonpath
filter], we test the data jsonpath "$.first_name" == "Sammy"
So our Hurl file will be:
GET https://example.com/test.html
HTTP 200
[Asserts]
regex /var s = '(.*)';/ jsonpath "$.first_name" == "Sammy"
regex /var s = '(.*)';/ jsonpath "$.last_name" == "Shark"
regex /var s = '(.*)';/ jsonpath "$.location" == "Ocean"
Or with an intermediate capture:
GET https://example.com/test.html
HTTP 200
[Captures]
s: regex /var s = '(.*)';/
[Asserts]
variable "s" jsonpath "$.first_name" == "Sammy"
variable "s" jsonpath "$.last_name" == "Shark"
variable "s" jsonpath "$.location" == "Ocean"
Check out all the available filters to get the data you want from the HTTP responses.
Finally, in Hurl 4.2.0, more curl options have been implemented [--ipv4
/ --ipv6
], [--unix-socket
] and
[--location-trusted
].
--ipv4
/ --ipv6
In a shell:
$ hurl --ipv6 foo.hurl
Or for a specific request in a Hurl file:
GET https://foo.com
[Options]
ipv6: true
HTTP 200
GET https://foo.com
[Options]
ipv4: true
HTTP 200
--unix-socket
In a shell:
$ hurl --unix-socket pid bar.hurl
Or for a specific request in a Hurl file:
GET https://bar.com
[Options]
unix-socket: "pid"
HTTP 200
--location-trusted
In a shell:
$ hurl --location-trusted baz.hurl
Or for a specific request in a Hurl file:
GET https://baz.com
[Options]
location-trusted: true
HTTP 200
All the options can, of course, be combined on a specific request:
GET https://baz.com
[Options]
ipv6: true
location-trusted: true
skip: false
HTTP 200
There are other improvements with Hurl 4.2.0 (dark mode for HTML report, skip option request, etc...) and also a lot of bug fixes, you can check the complete list of enhancements and bug fixes in our release note.
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!
]]>The Hurl team is happy to announce Hurl 4.1.0 !
Hurl is a command line tool powered by curl, that runs HTTP requests defined in a simple plain text format:
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
What’s new in this release:
--connect-to
and --resolve
per Request OptionWe’ve added a new test report: TAP, the Test Anything Protocol. TAP is a simple text-based interface between testing modules in a test harness. With HTML report and JUnit report, Hurl supports now TAP report.
Let’s say we run some tests. We can use --report-tap REPORT-FILE
option to set the report TAP file. If the report file
exists, results will be appended to it.
$ hurl --test --report-tap report.txt *.hurl
add-favorite.hurl: Running [1/6]
add-favorite.hurl: Success (7 request(s) in 5516 ms)
basic.hurl: Running [2/6]
basic.hurl: Success (7 request(s) in 1537 ms)
csrf.hurl: Running [3/6]
error: Assert status code
--> csrf.hurl:3:6
|
3 | HTTP 301
| ^^^ actual value is <200>
|
csrf.hurl: Failure (2 request(s) in 5527 ms)
login.hurl: Running [4/6]
login.hurl: Success (3 request(s) in 3091 ms)
perf.hurl: Running [5/6]
perf.hurl: Success (4 request(s) in 1317 ms)
security.hurl: Running [6/6]
security.hurl: Success (5 request(s) in 2278 ms)
write tap report report.txt
--------------------------------------------------------------------------------
Executed files: 6
Succeeded files: 5 (83.3%)
Failed files: 1 (16.7%)
Duration: 19304 ms
Then, we can see what our TAP report looks like:
$ cat report.txt
1..6
ok 1 - add-favorite.hurl
ok 2 - basic.hurl
not ok 3 - csrf.hurl
ok 4 - login.hurl
ok 5 - perf.hurl
ok 6 - security.hurl
Simple and neat! TAP has wide support across many language and there are many tools that can convert TAP to other formats, so it’s a nice addition to Hurl!
With the new --delay
option, you can add a delay between requests:
$ hurl --delay 2000 --test *.hurl
This command add a 2 seconds delay between each request. As with a lot of Hurl command line options, you
can specify a delay for a single request, with an [Options]
section,
without impacting other requests:
GET https://foo.com/a
HTTP 200
# This next request will be runned 5s after the
# first one
GET https://foo.com/b
[Options]
delay: 5000
HTTP 200
Speaking of [Options]
sections, --connect-to
and --resolve
can now be specified per request:
GET https://foo.com/a
[Options]
connect-to: foo.com:80:localhost:8000
HTTP 200
# --resolve option allow to us custom address for a specific host and port pair.
GET http://foo.com:8000/resolve
[Options]
resolve: foo.com:8000:127.0.0.1
HTTP 200
As of Hurl 4.1.0, the [Options]
section supports:
GET https://example.org
# An options section, each option is optional and applied only to this request...
[Options]
aws-sigv4: aws:amz:sts # generate AWS SigV4 Authorization header
cacert: /etc/cert.pem # a custom certificate file
compressed: true # request a compressed response
insecure: true # allows insecure SSL connections and transfers
location: true # follow redirection for this request
max-redirs: 10 # maximum number of redirections
path-as-is: true # tell curl to not handle sequences of /../ or /./ in the given URL path
variable: country=Italy # define variable country
variable: planet=Earth # define variable planet
verbose: true # allow verbose output
very-verbose: true # allow more verbose output
If you need an Hurl command line option (which make sense for a single request) to be on this list, don’t hesitate to fill an issue!
Every interaction with Amazon S3 is either authenticated or anonymous. Authenticating to AWS
is done through AWS Signature Version 4. With --aws-sigv4
,
you can use AWS Signature Version 4 to authenticate your requests
$ hurl --user someAccessKeyId:someSecretKey \
--aws-sigv4 aws:amz:eu-central-1:foo \
file.hurl
And of course, --aws-sigv4
can be specified for a single request:
GET https://foo.execute-api.us-east-1.amazonas.com/dev/bafe12
[Options]
aws-sigv4: aws:amz:eu-central-1:foo
HTTP 200
Hurl can be installed as a native binary on a large number of platforms. We also provide a Docker image. Since 4.1.0, Hurl Docker’s image is a multi-arch build: along x86 architectures, the image supports now ARM 64 bits targets such as Raspberry Pis, AWS A1 instances or even ARM’s Apple computers.
$ docker run -v /tmp/:/tmp/ ghcr.io/orange-opensource/hurl:4.1.0 --test example.hurl
example.hurl: Running [1/1]
example.hurl: Success (1 request(s) in 190 ms)
--------------------------------------------------------------------------------
Executed files: 1
Succeeded files: 1 (100.0%)
Failed files: 0 (0.0%)
Duration: 193 ms
Changes that require a particular attention:
--fail-at-end
option to
--continue-on-error
as the latter is more
understandable--path-as-is
option name (instead of --path_as_is
)There are other improvements and bug fixes, you can check a complete list in our release note. If you like Hurl, don’t hesitate to give us a star on GitHub or share it on Twitter / X!
We’ll be happy to hear from you, either for enhancement requests or for sharing your success story using Hurl!
]]>The Hurl team is happy to announce Hurl 4.0.0 !
Hurl is a command line tool powered by curl, that runs HTTP requests defined in a simple plain text format:
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
What’s new in this release:
We’ve improved Hurl HTML report. The HTML report is pure HTML, without any JavaScript and with inlined CSS, so it’s should be easy to integrate in your favorite CI/CD solution (like GitLab CI/CD or GitHub Actions for instance). Now, each run produces:
libcurl
and you can find an explanation of each
indicator in the documentationThe timings used to construct the requests timeline are also exposed through --json
option. --json
gives you a structured
view of a Hurl run with errors, asserts, certificates, captures, cookies and so timings. You can even use it to produce your own report!
What’s interesting with rich visualisation is it can reveal hidden or not obvious things. For instance, you can have this kind of gaps on some runs:
After analysis, the gap between requests in this sample test is caused by a huge numbers of assertions on the HTTP response. We have, as of Hurl 4.0.0, a naive
approach of asserts computation: each asserts of the same response is independent, and we parse and recompute every assert from scratch.
Until we see these edge cases, we were very proud of Hurl speed (due to the combination of libcurl
and
Rust). Now, we know that we have to improve assert performance for the next release !
When you’ve error in some test, the analysis can be difficult because you don’t have a lot of information apart of the expected values:
$ hurl --test test.hurl
test.hurl: Running [1/1]
error: Assert failure
--> test.hurl:4:0
|
4 | header "Control-Security-Policy" contains "default-src 'self'"
| actual: none
| expected: contains string <default-src 'self'>
|
test.hurl: Failure (1 request(s) in 128 ms)
--------------------------------------------------------------------------------
Executed files: 1
Succeeded files: 0 (0.0%)
Failed files: 1 (100.0%)
Duration: 130 ms
With the new --error-format
option, you can opt in for a longer error description. In this mode, the response header and the response
body are automatically logged:
$ hurl --error-format long --test test.hurl
test.hurl: Running [1/1]
HTTP/2 200
date: Thu, 29 Jun 2023 16:06:58 GMT
content-type: text/html
content-length: 58941
last-modified: Thu, 29 Jun 2023 14:37:22 GMT
etag: "649d9722-e63d"
strict-transport-security: max-age=31536000; includeSubDomains
content-security-policy: default-src 'self'; script-src 'self' 'unsafe-eval' 'wasm-unsafe-eval'
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
accept-ranges: bytes
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="apple-touch-icon" href="/assets/img/hurl-icon-120.png" />
...
...
...
</body>
</html>
error: Assert failure
--> test.hurl:4:0
|
4 | header "Control-Security-Policy" contains "default-src 'self'"
| actual: none
| expected: contains string <default-src 'self'>
|
test.hurl: Failure (1 request(s) in 146 ms)
--------------------------------------------------------------------------------
Executed files: 1
Succeeded files: 0 (0.0%)
Failed files: 1 (100.0%)
Duration: 148 ms
In this example, we can see that there is actually a Content-Security-Policy
whereas we’re querying a Control-Security-Policy
header. The bug is now really simple to solve because the response headers and body are logged.
This option is really useful in CI/CD where you want to have all the available context to debug your session, without re-running your tests. Beware that, as the body response is logged, the log can be really long.
Textual asserts in Hurl work by automatically decoding the response body bytes, based on the Content-Type
response header.
That way, if we have a Latin 1 encoded HTML or an UFT-8 encoded HTML we can write the same assert without any encoding concern:
# UTF-8 encoded document:
GET https://example.org/charset/utf8
HTTP 200
Content-Type: text/html; charset=utf-8
[Asserts]
body == "<p>café</p>"
# Latin1 encoded document:
GET https://example.org/charset/latin1
HTTP 200
Content-Type: text/html; charset=latin1
[Asserts]
body == "<p>café</p>"
To decode a response from bytes to text, Hurl uses charset
hint from Content-Type
response header. But sometimes the Content-Type
response header doesn’t specify any encoding. Or the encoding is indicated inside the HTML document through <meta>
tag:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=gb2312'>
</head>
<body>你好世界</body>
</html>
In this case, a decode
filter can now be used to explicitly decodes bytes to text and do checks:
GET https://example.com/hello_gb231
HTTP 200
[Asserts]
header "Content-Type" == "text/html"
bytes contains hex,c4e3bac3cac0bde7; # 你好世界 encoded in GB2312
bytes decode "gb2312" xpath "string(//body)" == "你好世界"
As hinted in the previous Hurl snippet, you can now evaluate XPath expression on response part with a xpath
filter.
In Hurl 4.0.0, we’ve slightly changed the evaluation of JSONPath query. There is no proper specifications for JSONPath. The de-facto one, that Hurl tries to follow as closely as possible, is still https://goessner.net/articles/JsonPath/. There are a few edge cases for which several implementations differ. For instance, standard JSONPath always returns a collection, which most of the time is not meaningful, and harder to test. Some implementations (such as the Java library https://github.com/json-path/JsonPath) also distinguish between node value (definite path) and collection (indefinite path).
Basically, in Hurl 4.0.0, the only selectors returning a value are:
$.store.book[2]
)$.store.bicycle.color/$.store.bicycle['color']
)Other selectors, that use filters (for instance ?(@.price >= 10)
or $[*].id
) will return a collection. You can then use
nth
filter to extract a value from this collection.
GET https://example.com/books
HTTP 200
[Asserts]
jsonpath "$.store.book[0].title" == "Dune"
jsonpath "$.store.book[*].title" nth 0 == "Dune"
Hurl 4.0.0 supports now any custom HTTP method. The only constraint is to write the method in uppercase. You can right-away
experiment the incoming new QUERY
method:
QUERY https://example.org/contacts
Content-Type: example/query
Accept: text/csv
HTTP 200
Content-Type: text/csv
```
surname, givenname, email
Smith, John, john.smith@example.org
Jones, Sally, sally.jones@example.com
Dubois, Camille, camille.dubois@example.net
```
There are other improvements and bug fixes, you can check a complete list in our release note. 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!
]]>The Hurl team is happy to announce Hurl 3.0.0!
Hurl is a command line tool powered by curl, that runs HTTP requests defined in a simple plain text format:
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
What’s new in this release:
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.
If you want more information on the SSL layer, you can also use --very-verbose
option that will output curl debug logs,
including SSL informations:
$ echo 'HEAD https://hurl.dev' | hurl --very-verbose
* Options:
* fail fast: true
...
*
* 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):
...
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:
published
string from the response => jsonpath "$.published"
toDate "%Y-%m-%dT%H:%M:%S%.fZ"
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:
GET https://example.org
HTTP 200
[Asserts]
cookie "LSID[Expires]" format "%a, %d %b %Y %H:%M:%S" == "Thu, 13 Jan 2078 22:23:01"
certificate "Expire-Date" daysAfterNow > 15
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,
...
This can be useful if you want to convert your big Hurl tests suite to a solution that uses a different format.
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
Another use case is to convert a request played in your favorite browser to Hurl format and replay it with Hurl. Using Firefox, in the Developer Tools / Network tab, you can export an HTTP request to a curl command:
Then using hurlfmt
, you will be able to convert this request to Hurl. We’re very happy to have
implemented this feature and we’re looking forward to feedback!
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 (a library in the Rust world) 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 Rust builder pattern: this way we can add more options without breaking binary compatibility (thanks @robjtede).
There are other improvements and bug fixes, you can check a complete list in our release note. 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!
]]>
Christmas has been busy for the Hurl team! We’re happy to announce Hurl 2.0.0, with a lot of new features and bug fixes. If you don’t know Hurl, it’s a command line tool powered by curl that runs HTTP requests defined in a simple text format:
# Create a new cat:
POST https://example.org/api/cats
{
"name": "Kitty",
"color": "black"
}
HTTP 201
[Captures]
cat-id: jsonpath "$.id"
# Get our new cat:
GET https://example.org/api/cats/{{cat-id}}
HTTP 200
[Asserts]
jsonpath "$.lives" == 9
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 2.0.0?
Hurl can now be used to send GraphQL query request very easily. The syntax is natural and use ```
body with a graphql
hint.
For instance, using GitHub GraphQL APIs, we can check the number of stars of a project:
POST https://api.github.com/graphql
Authorization: bearer {{token}}
```graphql
{
repository(owner: "Orange-OpenSource", name: "hurl") {
stargazerCount
}
}
```
HTTP 200
And this Hurl file can be run like this:
$ hurl --variable token=FOO github.hurl | jq
{
"data": {
"repository": {
"stargazerCount": 3578
}
}
}
It’s a very “Markdowny” syntax which re-use existing Hurl multiline body.
GraphQL queries, of course, can use GraphQL variables:
POST https://api.starwars.com/graphql
```graphql
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
variables {
"episode": "JEDI"
}
```
HTTP 200
And, as any multiline body, GraphQL queries can be templatized with Hurl variables!
POST https://api.starwars.com/graphql
```graphql
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
variables {
"episode": {{episode}}
}
```
HTTP 200
As GraphQL APIs return JSON, we can add asserts on the HTTP response:
POST https://api.starwars.com/graphql
```graphql
{
human(id: "1000") {
name
height
}
}
```
HTTP 200
[Asserts]
jsonpath "$.data.human.name" == "Luke Skywalker"
jsonpath "$.data.human.height" == 1.72
Sometimes you want to process response body before capturing data or adding tests.
Let’s say we have an endpoint that returns an HTTP header x-servers
containing a joined list of servers (for instance rec1.org,rec3.org
).
You want to test the value of this header: number of server, value of server 1 etc... You can now use filters
to process the response headers and add clean asserts:
GET https://example.org/api
HTTP 200
[Asserts]
header "x-servers" split "," count == 2
header "x-servers" split "," nth 0 == "rec1.org"
header "x-servers" split "," nth 1 == "rec3.org"
jsonpath "$.books" count == 12
In this sample, header "x-servers"
extracts the HTTP header from the response. Then, we have a chain of two filters
split "," nth 0
. The first filter split
turns the header to a list, and the second filter, nth
, returns an element of the list.
Then we have our final test with == "rec1.org"
.
Filters can also be applied on captures:
GET https://example.org/api
HTTP 200
[Captures]
name: jsonpath "$user.id" replace /\d/ "x" # Make our user anonymous
Let’s take a final example, with a live existing service that returns a CSV of stock exchanges https://csvbase.com/meripaterson/stock-exchanges
First, we split our response body by lines and save it in a variable lines
.
GET https://csvbase.com/meripaterson/stock-exchanges
HTTP 200
[Captures]
rows: body split "\n"
Then, we create two other variables australia
and algeria
that are row 7 and 18.
GET https://csvbase.com/meripaterson/stock-exchanges
HTTP 200
[Captures]
rows: body split "\n"
australia: variable "rows" nth 7 split ","
algeria: variable "rows" nth 18 split ","
Now that we have our row 7 and 18, we can add assert on specifics columns:
GET https://csvbase.com/meripaterson/stock-exchanges
HTTP 200
[Captures]
lines: body split "\n"
australia: variable "lines" nth 7 split ","
algeria: variable "lines" nth 18 split ","
[Asserts]
variable "australia" nth 1 == "Australia & Oceania"
variable "australia" nth 2 == "Australia"
variable "algeria" nth 1 == "Africa"
variable "algeria" nth 2 == "Algeria"
Et voilà 🎉!
Filters can be used to transform the HTTP response data easily. We have included a first round of filters count
,
htmlEscape
, htmlUnescape
, nth
, regex
, replace
, split
, toInt
, urlDecode
and urlEncode
. Don’t hesitate to give us some
feedbacks on this nice new feature!
Under the hood, Hurl uses curl for HTTP transfers. We have implemented the following curl options: -E/--cert
, --connect-to
,
--key
, --resolve
and --ssl-no-revoke
. For instance, given this Hurl file hello.hurl
:
GET http://foo.com/hello
HTTP 200
`Hello World!`
GET http://bar.com/hello
HTTP 200
`Hello World!`
GET http://baz.com/hello
HTTP 200
`Hello World!`
You can force the resolution of foo.com
, bar.com
and baz.com
to localhost
with --connect-to
:
$ hurl --connect-to foo.com:80:localhost:8000 \
--connect-to bar.com:80:localhost:8000 \
--connect-to baz.com:80:localhost:8000 \
hello.hurl
Astute readers have already spot some minor syntax changes. Instead of HTTP/*
for wildcard HTTP version test, you can simply
write HTTP
now (the first syntax still work but is deprecated)
# New Syntax
GET https://foo.com
HTTP 200
# Deprecated syntax
GET https://foo.com
HTTP/* 200
If you want to add precise tests on HTTP version, you can still use HTTP/1.0
, HTTP/1.1
and HTTP/2
:
GET https://foo.com
HTTP/2 200
GET https://bar.com
HTTP/1.1 200
GET https://baz.com
HTTP/1.0 200
Thanks to @jmoore34 for this idea, it’s obvious now you see it!
Finally, plain text body have been tweaked, to allow the use of a language hint like graphql
or json
:
Post a CSV:
POST https://example.com/csv
```
line1,red,1
line2,green,2
line3,green,3
```
Use a GraphQL query
POST https://example.com/graphql
```graphql
{
human {
name
}
}
```
Post a JSON body
POST https://example.com/json
```json
{
"name": "toto",
"age": 18
}
```
Post a JSON body without ```
(this shorter syntax is prefered over the previous one)
POST https://example.com/json
{
"name": "toto",
"age": 18
}
And finally, post a single string without newline:
POST https://example.com/csv
`Hello world!`
You may notice that the syntax takes huge inspiration from Markdown and that’s very intentional!
There are other improvements and bug fixes, you can check a complete list in our release note. 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!
]]>The Hurl team is happy to announce Hurl 1.8.0 🥳! .
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?
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
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"
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:
*
*
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!
]]>The Hurl team is happy to announce a new version of Hurl, 1.7.0.
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 an integration testing tool for JSON/XML HTTP apis / HTML content.
So, what’s new in 1.7.0?
We’ve improved -v/--verbose
option:
--very-verbose
option to output request and response body for each entry of your Hurl fileFirst, we’ve added more color to the debug output.
In 1.6.1, a verbose output of Hurl looks like:
$ echo 'GET https://google.fr' | hurl --verbose
* fail fast: true
* insecure: false
* follow redirect: false
* max redirect: 50
* ------------------------------------------------------------------------------
* executing entry 1
*
* Cookie store:
*
* Request
* GET https://google.fr
*
* request can be run with the following curl command:
* curl 'https://google.fr'
*
> GET / HTTP/2
> Host: google.fr
> accept: */*
> user-agent: hurl/1.6.1
>
< HTTP/2 301
< location: https://www.google.fr/
< content-type: text/html; charset=UTF-8
< date: Thu, 18 Aug 2022 09:55:23 GMT
< expires: Thu, 18 Aug 2022 09:55:23 GMT
< cache-control: private, max-age=2592000
< server: gws
< content-length: 219
< x-xss-protection: 0
< x-frame-options: SAMEORIGIN
< set-cookie: CONSENT=PENDING+677; expires=Sat, 17-Aug-2024 09:55:23 GMT; path=/; domain=.google.fr; Secure
< p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
<
*
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="https://www.google.fr/">here</A>.
</BODY></HTML>
In 1.7.0, headers are highlighted and requests and responses are more visible:
$ echo 'GET https://google.fr' | hurl --verbose
* Options:
* fail fast: true
* insecure: false
* follow redirect: false
* max redirect: 50
* ------------------------------------------------------------------------------
* Executing entry 1
*
* Cookie store:
*
* Request:
* GET https://google.fr
*
* Request can be run with the following curl command:
* curl 'https://google.fr'
*
> GET / HTTP/2
> Host: google.fr
> accept: */*
> user-agent: hurl/1.7.0-snapshot
>
* Response: (received 219 bytes in 111 ms)
*
< HTTP/2 301
< location: https://www.google.fr/
< content-type: text/html; charset=UTF-8
< date: Thu, 18 Aug 2022 09:56:40 GMT
< expires: Thu, 18 Aug 2022 09:56:40 GMT
< cache-control: private, max-age=2592000
< server: gws
< content-length: 219
< x-xss-protection: 0
< x-frame-options: SAMEORIGIN
< set-cookie: CONSENT=PENDING+308; expires=Sat, 17-Aug-2024 09:56:40 GMT; path=/; domain=.google.fr; Secure
< p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
<
*
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="https://www.google.fr/">here</A>.
</BODY></HTML>
Error on asserts are also colored now:
error: Assert status code
--> /tmp/test.hurl:2:8
|
2 | HTTP/* 200
| ^^^ actual value is <301>
|
Colors can be forced with --color
, or deactivated with --no-color
, and Hurl supports now NO_COLOR
environnement
variables (see http://no-color.org).
Secondly, intermediary request and response bodies can be outputted in debug with --very-verbose
option. By default,
the last HTTP body response is outputted on standard output (like curl). With --verbose
option, request and response
headers are also displayed on standard error, and with --very-verbose
option, request and response bodies are finally also
displayed on standard error. If your Hurl file has a lot of entries, debug logs can be pretty large, but you can mitigate it
with the brand new [Options]
section that we’re going to present now.
Options such as --location
, --verbose
, --insecure
can be used at the command line and applied to every
request of an Hurl file. An [Options]
section can be used to apply option to only one request (without passing options
to the command line), while other requests are unaffected.
GET https://example.org
# An options section, each option is optional and applied only to this request...
[Options]
cacert: /etc/cert.pem # a custom certificate file
compressed: true # request a compressed response
insecure: true # allows insecure SSL connections and transfers
location: true # follow redirection for this request
max-redirs: 10 # maximum number of redirections
verbose: true # allow verbose output
very-verbose: true # allow more verbose output
So, given this Hurl file
GET https://google.fr
HTTP/* 301
GET https://google.fr
[Options]
location: true
HTTP/* 200
GET https://google.fr
HTTP/* 301
The second entry will follow location (and so we can test the status code to be 200 instead of 301).
You can use it to logs a specific entry:
# ... previous entries
GET https://api.example.org
[Options]
very-verbose: true
HTTP/* 200
# ... next entries
And only the debug logs of the specific entry will be displayed on standard error.
Started since 1.6.1, Hurl is available on npm, and can be easily integrated in various JavaScript projects. Hurl on npm is a thin JavaScript wrapper around the native binary.
To install it, just run:
$ npm install --save-dev @orangeopensource/hurl
And then edit your package.json
:
{
"name": "sample-app",
"scripts": {
"test": "hurl --test --glob test/*.hurl",
...
},
...
Now you can run your integration tests with Hurl:
$ npm test
test/bar.hurl: Running [1/3]
test/bar.hurl: Success (5 request(s) in 136 ms)
test/baz.hurl: Running [2/3]
error: Assert failure
--> test/baz.hurl:6:0
|
6 | xpath "string(//title)" == "Something"
| actual: string <301 Moved>
| expected: string <Something>
|
test/baz.hurl: Failure (4 request(s) in 62 ms)
test/foo.hurl: Running [3/3]
test/foo.hurl: Success (10 request(s) in 527 ms)
--------------------------------------------------------------------------------
Executed files: 3
Succeeded files: 2 (66.7%)
Failed files: 1 (33.3%)
Duration: 766 ms
JSON and XML are first class citizens in Hurl with JSONPath assert and XPath assert. For XPath, we now support asserts with namespaces:
<?xml version="1.0"?>
<!-- both namespace prefixes are available throughout -->
<bk:book xmlns:bk='urn:loc.gov:books'
xmlns:isbn='urn:ISBN:0-395-36341-6'>
<bk:title>Cheaper by the Dozen</bk:title>
<isbn:number>1568491379</isbn:number>
</bk:book>
Can be tested with the following Hurl file:
GET http://localhost:8000/assert-xpath
HTTP/1.0 200
[Asserts]
xpath "string(//bk:book/bk:title)" == "Cheaper by the Dozen"
xpath "string(//*[name()='bk:book']/*[name()='bk:title'])" == "Cheaper by the Dozen"
xpath "string(//*[local-name()='book']/*[local-name()='title'])" == "Cheaper by the Dozen"
xpath "string(//bk:book/isbn:number)" == "1568491379"
xpath "string(//*[name()='bk:book']/*[name()='isbn:number'])" == "1568491379"
xpath "string(//*[local-name()='book']/*[local-name()='number'])" == "1568491379"
For convenience, the first default namespace can be used with _
.
This sample:
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
circle:hover {fill-opacity:0.9;}
</style>
<g style="fill-opacity:0.7;">
<circle cx="6.5cm" cy="2cm" r="100" style="fill:red; stroke:black; stroke-width:0.1cm" transform="translate(0,50)" />
<circle cx="6.5cm" cy="2cm" r="100" style="fill:blue; stroke:black; stroke-width:0.1cm" transform="translate(70,150)" />
<circle cx="6.5cm" cy="2cm" r="100" style="fill:green; stroke:black; stroke-width:0.1cm" transform="translate(-70,150)"/>
</g>
</svg>
Can be tested with the following Hurl file:
GET http://localhost:8000/assert-xpath-svg
HTTP/1.0 200
[Asserts]
xpath "//_:svg/_:g/_:circle" count == 3
xpath "//*[local-name()='svg']/*[local-name()='g']/*[local-name()='circle']" count == 3
xpath "//*[name()='svg']/*[name()='g']/*[name()='circle']" count == 3
Under the hood, we’ve improved our code and Hurl should be quicker than ever. We’ve completely rewritten our grammar to be more correct and support Hurl future evolutions.
There are other changes and bug fixes in the Hurl 1.7.0 release: check out the release note!
And, finally, a big thanks to all our contributors!
]]>The Hurl team is happy to announce a new version of Hurl, 1.6.0.
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
So, what’s new in 1.6.0?
Before 1.6.0, basic authentication could be achieved by computing and sending an Authorization header:
# Authorization header value can be computed with:
# `echo -n 'bob:secret' | base64`
GET https://example.org/protected
Authorization: Basic Ym9iOnNlY3JldA==
In 1.6.0, we’ve introduced a basic authentication section to pass username/password explicitly, without base64 encoding:
GET https://example.org/protected
[BasicAuth]
bob: secret
Basic authentication allows per request authentication. If you want to add
basic authentication to all the request of a Hurl file you could use -u/--user
option.
matches
predicates values are regex. Before 1.6.0, metacharacters in pattern
values should be escaped (like \d
etc...):
GET https://sample.org/hello
HTTP/1.0 200
[Asserts]
jsonpath "$.date" matches "^\\d{4}-\\d{2}-\\d{2}$"
jsonpath "$.name" matches "Hello [a-zA-Z]+!"
In 1.6.0, we’ve added regex literal for matches
:
GET https://sample.org/hello
HTTP/1.0 200
[Asserts]
jsonpath "$.date" matches /^\d{4}-\d{2}-\d{2}$/
jsonpath "$.name" matches /Hello [a-zA-Z]+!/
The new asserts are much more readable and easier to write.
Basic authentication section and regex literal has been suggested to us by David Humphrey so thanks again David for your ideas!
In interactive mode, Hurl plays each request and pauses between each entry, allowing to debug a session step by step. We’ve improved the interactive mode to display the next request to be played:
...
< Last-Modified: Fri, 11 Feb 2022 13:28:20 GMT
< Connection: keep-alive
< ETag: "62066474-6f02"
< Accept-Ranges: bytes
<
*
interactive mode
next request:
GET https://hurl.dev/docs/manual.html
Press Q (Quit) or C (Continue)
Under the hood, we’ve improved our code and Hurl should be quicker than ever. There are other changes and bug fixes in the Hurl 1.6.0 release: check out the release note!
If you like Hurl, don’t hesitate to give us a star!
And, finally, a big thanks to all our contributors!
]]>The Hurl team is happy to announce a new version of Hurl, 1.5.0. Hurl is a command line tool that runs HTTP requests defined in a simple plain text format.
So, what’s new in 1.5.0?
Hurl relies upon curl for the HTTP engine. We’ve improved the version output
--version/-h
to provide informations about which libcurl Hurl is linked with:
$ hurl --version
hurl 1.5.0 libcurl/7.54.0 LibreSSL/2.6.5 zlib/1.2.11
Now tests can be launched with --glob
option. This is particularly useful on
Windows where it is less easy to grep
and find
your test:
$ hurl --glob "tests/**/*.hurl"
This command will run tests/project1/test1.hurl
, tests/project1/test2.hurl
and
tests/project2/test3.hurl
. Note that to avoid your shell accidentally expanding glob patterns
before Hurl handles them, you should use single quotes or double quotes around each pattern.
Before 1.5.0, to inject variables in a Hurl file you can
use command line option, with --variable
:
$ hurl --variable host=example.org --variable user=jc test.hurl
use an input file, with --variables-file
:
Given file.env
defining each variable on a new line:
host=example.org
user=jc
Variables are injected with file.env
:
$ hurl --variables-files file.env test.hurl
Starting in 1.5.0, environment variables can be used to inject variables:
$ echo HURL_host=example.org
$ echo HURL_user=jc
$ hurl test.hurl
Each HURL_foo
environment variables will resolve as a foo
variable inside
the Hurl file.
Test report can be exported in JUnit format:
$ hurl --test --report-junit /tmp/result.xml *.hurl
To be consistent, we’ve also rename --html
options to --report-html
. Of course,
one can combine the two kind of reports:
$ hurl --test --report-junit /tmp/result.xml --report-html /tmp/report/ *.hurl
There are other changes and bug fixes in the Hurl 1.5.0 release: check out the release note!
And, finally, a big thanks to our contributors!
]]>