Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,7 @@ will follow a redirection only for the second entry.
| <a href="#cookie-jar" id="cookie-jar"><code>-c, --cookie-jar &lt;FILE&gt;</code></a> | Write cookies to FILE after running the session.<br>The file will be written using the Netscape cookie file format.<br><br>Combined with [`-b, --cookie`](#cookie), you can simulate a cookie storage between successive Hurl runs.<br><br>This is a cli-only option.<br> |
| <a href="#curl" id="curl"><code>--curl &lt;FILE&gt;</code></a> | Export each request to a list of curl commands.<br><br>This is a cli-only option.<br> |
| <a href="#delay" id="delay"><code>--delay &lt;MILLISECONDS&gt;</code></a> | Sets delay before each request (aka sleep). The delay is not applied to requests that have been retried because of [`--retry`](#retry). See [`--retry-interval`](#retry-interval) to space retried requests.<br><br>You can specify time units in the delay expression. Set Hurl to use a delay of 2 seconds with `--delay 2s` or set it to 500 milliseconds with `--delay 500ms`. Supported time units: ms, s, m, h. No spaces allowed.<br> |
| <a href="#digest" id="digest"><code>--digest</code></a> | Tell Hurl to use HTTP Digest authentication<br> |
| <a href="#error-format" id="error-format"><code>--error-format &lt;FORMAT&gt;</code></a> | Control the format of error message (short by default or long)<br><br>This is a cli-only option.<br> |
| <a href="#file-root" id="file-root"><code>--file-root &lt;DIR&gt;</code></a> | Set root directory to import files in Hurl. This is used for files in multipart form data, request body and response output.<br>When it is not explicitly defined, files are relative to the Hurl file's directory.<br><br>This is a cli-only option.<br> |
| <a href="#from-entry" id="from-entry"><code>--from-entry &lt;ENTRY_NUMBER&gt;</code></a> | Execute Hurl file from ENTRY_NUMBER (starting at 1).<br><br>This is a cli-only option.<br> |
Expand Down
1 change: 1 addition & 0 deletions completions/_hurl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ _hurl() {
'(-c --cookie-jar)'{-c,--cookie-jar}'[Write cookies to FILE after running the session]: :_files' \
'--curl[Export each request to a list of curl commands]: :_files' \
'--delay[Sets delay before each request (aka sleep)]: :' \
'--digest[Tell Hurl to use HTTP Digest authentication]' \
'--error-format[Control the format of error messages]: :' \
'--file-root[Set root directory to import files \[default: input file directory\]]: :' \
'(-L --location)'{-L,--location}'[Follow redirects]' \
Expand Down
1 change: 1 addition & 0 deletions completions/_hurl.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Register-ArgumentCompleter -Native -CommandName 'hurl' -ScriptBlock {
[CompletionResult]::new('--cookie-jar', 'cookie-jar', [CompletionResultType]::ParameterName, 'Write cookies to FILE after running the session')
[CompletionResult]::new('--curl', 'curl', [CompletionResultType]::ParameterName, 'Export each request to a list of curl commands')
[CompletionResult]::new('--delay', 'delay', [CompletionResultType]::ParameterName, 'Sets delay before each request (aka sleep)')
[CompletionResult]::new('--digest', 'digest', [CompletionResultType]::ParameterName, 'Tell Hurl to use HTTP Digest authentication')
[CompletionResult]::new('--error-format', 'error-format', [CompletionResultType]::ParameterName, 'Control the format of error messages')
[CompletionResult]::new('--file-root', 'file-root', [CompletionResultType]::ParameterName, 'Set root directory to import files [default: input file directory]')
[CompletionResult]::new('--location', 'location', [CompletionResultType]::ParameterName, 'Follow redirects')
Expand Down
2 changes: 1 addition & 1 deletion completions/hurl.bash
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ _hurl()
cur="${COMP_WORDS[COMP_CWORD]}"

if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W '--aws-sigv4 --cacert --cert --key --color --compressed --connect-timeout --connect-to --continue-on-error --cookie --cookie-jar --curl --delay --error-format --file-root --location --location-trusted --from-entry --glob --header --http1.0 --http1.1 --http2 --http3 --ignore-asserts --include --insecure --interactive --ipv4 --ipv6 --jobs --json --limit-rate --max-filesize --max-redirs --max-time --negotiate --netrc --netrc-file --netrc-optional --no-color --no-output --no-pretty --noproxy --ntlm --output --parallel --path-as-is --pinnedpubkey --pretty --progress-bar --proxy --repeat --report-html --report-json --report-junit --report-tap --resolve --retry --retry-interval --secret --secrets-file --ssl-no-revoke --test --to-entry --unix-socket --user --user-agent --variable --variables-file --verbose --verbosity --very-verbose --help --version' -- "$cur"))
COMPREPLY=($(compgen -W '--aws-sigv4 --cacert --cert --key --color --compressed --connect-timeout --connect-to --continue-on-error --cookie --cookie-jar --curl --delay --digest --error-format --file-root --location --location-trusted --from-entry --glob --header --http1.0 --http1.1 --http2 --http3 --ignore-asserts --include --insecure --interactive --ipv4 --ipv6 --jobs --json --limit-rate --max-filesize --max-redirs --max-time --negotiate --netrc --netrc-file --netrc-optional --no-color --no-output --no-pretty --noproxy --ntlm --output --parallel --path-as-is --pinnedpubkey --pretty --progress-bar --proxy --repeat --report-html --report-json --report-junit --report-tap --resolve --retry --retry-interval --secret --secrets-file --ssl-no-revoke --test --to-entry --unix-socket --user --user-agent --variable --variables-file --verbose --verbosity --very-verbose --help --version' -- "$cur"))
return
fi
# Generate filenames by default
Expand Down
1 change: 1 addition & 0 deletions completions/hurl.fish
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ complete -c hurl -l cookie -d 'Read cookies from FILE'
complete -c hurl -l cookie-jar -d 'Write cookies to FILE after running the session'
complete -c hurl -l curl -d 'Export each request to a list of curl commands'
complete -c hurl -l delay -d 'Sets delay before each request (aka sleep)'
complete -c hurl -l digest -d 'Tell Hurl to use HTTP Digest authentication'
complete -c hurl -l error-format -d 'Control the format of error messages'
complete -c hurl -l file-root -d 'Set root directory to import files [default: input file directory]'
complete -c hurl -l location -d 'Follow redirects'
Expand Down
1 change: 1 addition & 0 deletions docs/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ will follow a redirection only for the second entry.
| <a href="#cookie-jar" id="cookie-jar"><code>-c, --cookie-jar &lt;FILE&gt;</code></a> | Write cookies to FILE after running the session.<br>The file will be written using the Netscape cookie file format.<br><br>Combined with [`-b, --cookie`](#cookie), you can simulate a cookie storage between successive Hurl runs.<br><br>This is a cli-only option.<br> |
| <a href="#curl" id="curl"><code>--curl &lt;FILE&gt;</code></a> | Export each request to a list of curl commands.<br><br>This is a cli-only option.<br> |
| <a href="#delay" id="delay"><code>--delay &lt;MILLISECONDS&gt;</code></a> | Sets delay before each request (aka sleep). The delay is not applied to requests that have been retried because of [`--retry`](#retry). See [`--retry-interval`](#retry-interval) to space retried requests.<br><br>You can specify time units in the delay expression. Set Hurl to use a delay of 2 seconds with `--delay 2s` or set it to 500 milliseconds with `--delay 500ms`. Supported time units: ms, s, m, h. No spaces allowed.<br> |
| <a href="#digest" id="digest"><code>--digest</code></a> | Tell Hurl to use HTTP Digest authentication<br> |
| <a href="#error-format" id="error-format"><code>--error-format &lt;FORMAT&gt;</code></a> | Control the format of error message (short by default or long)<br><br>This is a cli-only option.<br> |
| <a href="#file-root" id="file-root"><code>--file-root &lt;DIR&gt;</code></a> | Set root directory to import files in Hurl. This is used for files in multipart form data, request body and response output.<br>When it is not explicitly defined, files are relative to the Hurl file's directory.<br><br>This is a cli-only option.<br> |
| <a href="#from-entry" id="from-entry"><code>--from-entry &lt;ENTRY_NUMBER&gt;</code></a> | Execute Hurl file from ENTRY_NUMBER (starting at 1).<br><br>This is a cli-only option.<br> |
Expand Down
6 changes: 5 additions & 1 deletion docs/manual/hurl.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH hurl 1 "17 Dec 2025" "hurl 7.2.0-SNAPSHOT" " Hurl Manual"
.TH hurl 1 "21 Dec 2025" "hurl 7.2.0-SNAPSHOT" " Hurl Manual"
.SH NAME

hurl - run and test HTTP requests.
Expand Down Expand Up @@ -209,6 +209,10 @@ Sets delay before each request (aka sleep). The delay is not applied to requests

You can specify time units in the delay expression. Set Hurl to use a delay of 2 seconds with `--delay 2s` or set it to 500 milliseconds with `--delay 500ms`. Supported time units: ms, s, m, h. No spaces allowed.

.IP "--digest "

Tell Hurl to use HTTP Digest authentication

.IP "--error-format <FORMAT> "

Control the format of error message (short by default or long)
Expand Down
4 changes: 4 additions & 0 deletions docs/manual/hurl.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ Sets delay before each request (aka sleep). The delay is not applied to requests

You can specify time units in the delay expression. Set Hurl to use a delay of 2 seconds with `--delay 2s` or set it to 500 milliseconds with `--delay 500ms`. Supported time units: ms, s, m, h. No spaces allowed.

### --digest {#digest}

Tell Hurl to use HTTP Digest authentication

### --error-format <FORMAT> {#error-format}

Control the format of error message (short by default or long)
Expand Down
2 changes: 1 addition & 1 deletion docs/manual/hurlfmt.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH hurl 1 "17 Dec 2025" "hurl 7.2.0-SNAPSHOT" " Hurl Manual"
.TH hurl 1 "21 Dec 2025" "hurl 7.2.0-SNAPSHOT" " Hurl Manual"
.SH NAME

hurlfmt - format Hurl files
Expand Down
6 changes: 6 additions & 0 deletions docs/spec/options/hurl/digest.option
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: digest
long: digest
help: Tell Hurl to use HTTP Digest authentication
help_heading: HTTP options
---
Tell Hurl to use HTTP Digest authentication
2 changes: 1 addition & 1 deletion integration/hurl/tests_error_parser/invalid_option.err
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ error: Parsing option
--> tests_error_parser/invalid_option.hurl:3:1
|
3 | foo: true
| ^ the option name is not valid. Valid values are aws-sigv4, cacert, cert, compressed, connect-timeout, connect-to, delay, header, http1.0, http1.1, http2, http3, insecure, ipv4, ipv6, key, limit-rate, location, location-trusted, max-redirs, max-time, negotiate, netrc, netrc-file, netrc-optional, noproxy, ntlm, output, path-as-is, pinnedpubkey, proxy, repeat, resolve, retry, retry-interval, skip, unix-socket, user, variable, verbose, verbosity, very-verbose
| ^ the option name is not valid. Valid values are aws-sigv4, cacert, cert, compressed, connect-timeout, connect-to, delay, digest, header, http1.0, http1.1, http2, http3, insecure, ipv4, ipv6, key, limit-rate, location, location-trusted, max-redirs, max-time, negotiate, netrc, netrc-file, netrc-optional, noproxy, ntlm, output, path-as-is, pinnedpubkey, proxy, repeat, resolve, retry, retry-interval, skip, unix-socket, user, variable, verbose, verbosity, very-verbose
|

3 changes: 3 additions & 0 deletions integration/hurl/tests_ok/digest/digest.hurl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
GET http://localhost:8000/digest
HTTP 200

4 changes: 4 additions & 0 deletions integration/hurl/tests_ok/digest/digest.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Set-StrictMode -Version latest
$ErrorActionPreference = 'Stop'

hurl --digest -u "username:password" tests_ok/digest/digest.hurl
61 changes: 61 additions & 0 deletions integration/hurl/tests_ok/digest/digest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import re

from app import app
from flask import request

username = "username"
password = "password"
realm = "test-realm"
nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093"
opaque = "5ccc069c403ebaf9f0171e9517f40e41"


@app.route("/digest")
def digest_auth():
return get_auth_response()


def get_auth_response():
# Get the actual header that is returned by the client
actual_header = request.headers.get("Authorization", "")

if actual_header == "":
# This is the initial connection, need to return a 401 with Digest challenge
challenge = (
f'Digest realm="{realm}", nonce="{nonce}", opaque="{opaque}", qop="auth"'
)
response_headers = {"WWW-Authenticate": challenge}
status_code = 401
response = f"auth with '{username}':'{password}'"
elif actual_header.startswith("Digest "):
# Parse the Digest authorization header
# We just need to verify that the client sent a properly formatted Digest response
# We don't need to verify the actual hash is correct (that's curl's job)

# Check that required fields are present
auth_info = actual_header[7:] # Remove "Digest " prefix

required_fields = ["username", "realm", "nonce", "uri", "response"]
has_all_fields = all(f"{field}=" in auth_info for field in required_fields)

if has_all_fields:
# Verify the username matches
username_match = re.search(r'username="([^"]*)"', auth_info)
if username_match and username_match.group(1) == username:
response_headers = {}
status_code = 200
response = "authed"
else:
response_headers = {}
status_code = 401
response = "invalid username"
else:
response_headers = {}
status_code = 401
response = "missing required fields"
else:
response_headers = {}
status_code = 401
response = "invalid authorization header"

return response, status_code, response_headers
4 changes: 4 additions & 0 deletions integration/hurl/tests_ok/digest/digest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
set -Eeuo pipefail

hurl --digest -u "username:password" tests_ok/digest/digest.hurl
5 changes: 5 additions & 0 deletions integration/hurl/tests_ok/digest/digest_option.hurl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
GET http://localhost:8000/digest
[Options]
digest: true
user: username:password
HTTP 200
4 changes: 4 additions & 0 deletions integration/hurl/tests_ok/digest/digest_option.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Set-StrictMode -Version latest
$ErrorActionPreference = 'Stop'

hurl --verbose tests_ok/digest/digest_option.hurl
4 changes: 4 additions & 0 deletions integration/hurl/tests_ok/digest/digest_option.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
set -Eeuo pipefail

hurl --verbose tests_ok/digest/digest_option.hurl
2 changes: 2 additions & 0 deletions integration/hurl/tests_ok/help/help.out.pattern
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ HTTP options:
Maximum time allowed for connection [default: 300]
--connect-to <HOST1:PORT1:HOST2:PORT2>
For a request to the given HOST1:PORT1 pair, connect to HOST2:PORT2 instead
--digest
Tell Hurl to use HTTP Digest authentication
-H, --header <HEADER>
Pass custom header(s) to server
-0, --http1.0
Expand Down
Loading
Loading