Skip to content

CLI の HTTP クライアントである xh の基本的な使い方

以前に HTTPie のインストールと基本的な使い方 というメモを書きました。 HTTPie は CLI の HTTP / HTTPS クライアントです。 これを Rust で書き直したものが xh です。 xh は HTTPie とオプションがほぼ互換である為、HTTPie の利用経験があれば xh も利用出来るはずです。 HTTPie と xh の比較については How xh compares to HTTPie に以下の記載があります。

Advantages

  • Improved startup speed.
  • Available as a single statically linked binary that's easy to install and carry around.
  • HTTP/2 support.
  • Builtin translation to curl commands with the --curl flag.
  • Short, cheatsheet-style output from --help. (For longer output, pass help.)

Disadvantages

  • Not all of HTTPie's features are implemented. (#4)
  • No plugin system.
  • General immaturity. HTTPie is old and well-tested.
  • Worse documentation.

今回は xh の基本的な使い方をメモしておきます。

検証環境

対象 バージョン
Ubuntu 24.04.2 LTS
xh 0.24.0

インストール

Ubuntu へ xh をインストールするには下記などの方法があります。

  1. Rust Tools apt repo からインストールする
  2. シェルスクリプトでインストールする

Rust Tools apt repo からインストールする

Rust Tools apt repo の追加方法は Ubuntu に「Rust Tools apt repo」をインストールする にメモしてあります。

curl -fsSL https://apt.cli.rs/pubkey.asc | sudo tee -a /usr/share/keyrings/rust-tools.asc &&
curl -fsSL https://apt.cli.rs/rust-tools.list | sudo tee /etc/apt/sources.list.d/rust-tools.list &&
sudo apt update &&
sudo apt -y install xh

シェルスクリプトでインストールする

シェルスクリプトでインストールする場合は公式ページに記載されている通り、以下を実行します。

curl -sfL https://raw.githubusercontent.com/ducaale/xh/master/install.sh | sh

インストールされるファイル

xh を apt をインストールした場合、以下のファイル群がインストールされます。 バイナリとして /usr/bin/xh/usr/bin/xhs のふたつがインストールされます。

# dpkg -L xh | sort
/usr
/usr/bin
/usr/bin/xh
/usr/bin/xhs
/usr/share
/usr/share/bash-completion
/usr/share/bash-completion/completions
/usr/share/bash-completion/completions/xh
/usr/share/doc
/usr/share/doc/xh
/usr/share/doc/xh/NEWS.gz
/usr/share/doc/xh/README
/usr/share/doc/xh/copyright
/usr/share/fish
/usr/share/fish/vendor_completions.d
/usr/share/fish/vendor_completions.d/xh.fish
/usr/share/man
/usr/share/man/man1
/usr/share/man/man1/xh.1.gz
/usr/share/man/man1/xhs.1.gz
/usr/share/zsh
/usr/share/zsh/vendor-completions
/usr/share/zsh/vendor-completions/_xh

オプションを何も指定せずに実行する

オプションを何も指定せず、実行した場合は (HTTPS では無く) HTTP リクエストを実行します。

# xh httpbin.org/get
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 302
Content-Type: application/json
Date: Sun, 23 Feb 2025 00:53:56 GMT
Server: gunicorn/19.9.0

{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Host": "httpbin.org",
        "User-Agent": "xh/0.24.0",
        "X-Amzn-Trace-Id": "Root=1-67ba71a4-3ff5911004fb8f5648199362"
    },
    "origin": "192.0.2.1",
    "url": "http://httpbin.org/get"
}

ヘッダ・ボディを表示する

xh は指定が無い場合はレスポンス、具体的にはレスポンスヘッダとレスポンスボディのみ、表示します。 指定した部分だけを表示するには --print オプションに続けて文字列を指定します。

--print に指定する文字列 リクエスト/レスポンス ヘッダ/ボディ
H リクエスト ヘッダ
B リクエスト ボディ
h レスポンス ヘッダ
b レスポンス ボディ

幾つかの実行例をメモしておきます。

レスポンスのみ、表示する (デフォルト)

これはデフォルトの動作です。 つまり、xh httpbin.org/getxh --print=hb httpbin.org/get は同じ振る舞いをします。

# xh --print=hb httpbin.org/get
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 302
Content-Type: application/json
Date: Sun, 23 Feb 2025 00:56:17 GMT
Server: gunicorn/19.9.0

{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Host": "httpbin.org",
        "User-Agent": "xh/0.24.0",
        "X-Amzn-Trace-Id": "Root=1-67ba7231-33ac10c856212b6278eea152"
    },
    "origin": "192.0.2.1",
    "url": "http://httpbin.org/get"
}

リクエストヘッダのみ、表示する

# xh --print=H httpbin.org/get
GET /get HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Host: httpbin.org
User-Agent: xh/0.24.0

レスポンスヘッダのみ、表示する

# xh --print=h httpbin.org/get
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 302
Content-Type: application/json
Date: Sun, 23 Feb 2025 00:55:21 GMT
Server: gunicorn/19.9.0

リクエストボディのみ、表示する

POST リクエストを実行する場合のデータ部分を表示することも出来ます。

# echo "[1, 2, 3]" | xh post --print=B httpbin.org/post
[
    1,
    2,
    3
]

レスポンスボディのみ、表示する

# xh --print=b httpbin.org/get
{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Host": "httpbin.org",
        "User-Agent": "xh/0.24.0",
        "X-Amzn-Trace-Id": "Root=1-67ba720a-12f37af63daea3183cc2741e"
    },
    "origin": "192.0.2.1",
    "url": "http://httpbin.org/get"
}

ヘッダのみ、表示する (リクエストヘッダ&レスポンスヘッダ)

# xh --print=Hh httpbin.org/get
GET /get HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Host: httpbin.org
User-Agent: xh/0.24.0

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 302
Content-Type: application/json
Date: Sun, 23 Feb 2025 00:56:00 GMT
Server: gunicorn/19.9.0

HTTPS アクセスする

xh は http://https:// といった指定が無い場合、デフォルトで (HTTPS リクエストでは無く) HTTP リクエストを発行します。

# xh --print=b httpbin.org/get
{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Host": "httpbin.org",
        "User-Agent": "xh/0.24.0",
        "X-Amzn-Trace-Id": "Root=1-67ba74c0-4382cc512eda1fa7150933a3"
    },
    "origin": "192.0.2.1",
    "url": "http://httpbin.org/get"
}

アクセス先が HTTPS の場合、xh https://example.com と実行するか、もしくは xhs example.com と実行することで HTTPS リクエストを発行出来ます。

# xhs --print=b httpbin.org/get
{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Host": "httpbin.org",
        "User-Agent": "xh/0.24.0",
        "X-Amzn-Trace-Id": "Root=1-67ba74c5-4c0d1a0f5075367f2257ff5d"
    },
    "origin": "192.0.2.1",
    "url": "https://httpbin.org/get"
}

結果を保存する

結果を保存するには -d (--download) と -o (--output) オプションを指定します。

# xh -d httpbin.org/json -o res.json
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 429
Content-Type: application/json
Date: Sun, 23 Feb 2025 01:08:18 GMT
Server: gunicorn/19.9.0

Downloading 429 B to "res.json"
Done. 429 B in 0.00027s (1.54 MiB/s)
# cat res.json
{
  "slideshow": {
    "author": "Yours Truly",
    "date": "date of publication",
    "slides": [
      {
        "title": "Wake up to WonderWidgets!",
        "type": "all"
      },
      {
        "items": [
          "Why <em>WonderWidgets</em> are great",
          "Who <em>buys</em> WonderWidgets"
        ],
        "title": "Overview",
        "type": "all"
      }
    ],
    "title": "Sample Slide Show"
  }
}

リクエストヘッダを追加する

リクエストヘッダを追加するには HEADER:VALUE のように指定します。

# xh --print=H httpbin.org/get X-AAA:aaa X-BBB:bbb X-CCC:ccc
GET /get HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Host: httpbin.org
User-Agent: xh/0.24.0
X-Aaa: aaa
X-Bbb: bbb
X-Ccc: ccc

但し、リクエストアイテム部分を URL より前に指定するとエラーになるので注意が必要です。

# xh --print=H X-AAA:aaa httpbin.org/get
error: Invalid value for '[REQUEST_ITEM]...': "httpbin.org/get"

Usage: xh [OPTIONS] <[METHOD] URL> [REQUEST_ITEM]...

For more information, try '--help'.

User-Agent を書き換える

リクエストヘッダの追加とほぼ同様の手順ですが、User-Agent を書き換えるには user-agent:VALUE のように指定します。

# xh --print=H httpbin.org/get user-agent:foobar
GET /get HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Host: httpbin.org
User-Agent: foobar

サーバ証明書の警告を無視する

サーバ証明書に問題がある場合、デフォルトではアクセスすることが出来ません。

# xh https://192.0.2.100
xh: error: error sending request for url (https://192.0.2.100/)

Caused by:
    0: client error (Connect)
    1: invalid peer certificate: UnknownIssuer

サーバ証明書の警告を無視してアクセスを続行するには --verify no を指定します。

# xh --verify no https://192.0.2.100
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: Keep-Alive
Content-Length: 10
Content-Type: text/html
Date: Sun, 23 Feb 2025 01:22:49 GMT
Etag: "a-62ec50fa12960"
Keep-Alive: timeout=5, max=100
Last-Modified: Sun, 23 Feb 2025 01:22:45 GMT
Server: Apache/2.4.58 (Ubuntu)

It works!

Proxy 経由でアクセスする

Proxy 経由でアクセスする場合は --proxy PROTOCOL:PROXY_URL オプションを指定します。 下記の実行例では --proxy http:http:// と記載されており、一見誤っているように見えますが PROTOCOL:PROXY_URL と指定する必要がある為、これは正しい指定です。

# xh --proxy http:http://192.0.2.200:8080 http://httpbin.org/get
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Cache-Status: unknown;detail=no-cache
Connection: keep-alive
Content-Length: 351
Content-Type: application/json
Date: Sun, 23 Feb 2025 01:29:55 GMT
Server: gunicorn/19.9.0
Via: 1.1 unknown (squid)

{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Cache-Control": "max-age=259200",
        "Host": "httpbin.org",
        "User-Agent": "xh/0.24.0",
        "X-Amzn-Trace-Id": "Root=1-67ba7a13-53e282720676bfaf6be2fe47"
    },
    "origin": "unknown, 192.0.2.1",
    "url": "http://httpbin.org/get"
}

設定ファイルでデフォルト値を指定する

xh を実行した時のデフォルト値は予め .config/xh/config.json ファイルに JSON 形式で指定しておくことが出来ます。 例えば以下のように指定します。

.config/xh/config.json
{ "default_options": ["--print=Hh", "--verify=no"] }

この設定を行うことにより「リクエスト・レスポンスのヘッダのみを表示し、サーバ証明書の検証を行わない」という振る舞いをします。

# xh httpbin.org/get
GET /get HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Host: httpbin.org
User-Agent: xh/0.24.0

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 302
Content-Type: application/json
Date: Sun, 23 Feb 2025 03:28:17 GMT
Server: gunicorn/19.9.0

但し、現時点では以下のように KEY:VALUE を指定するとエラーになってしまうようです。

{ "default_options": ["--print=Hh", "foo:bar"] }

参考

xh のヘルプ

# xh --help
xh is a friendly and fast tool for sending HTTP requests

Usage: xh [OPTIONS] <[METHOD] URL> [REQUEST_ITEM]...

Arguments:
  <[METHOD] URL>     The request URL, preceded by an optional HTTP method
  [REQUEST_ITEM]...  Optional key-value pairs to be included in the request.

Options:
  -j, --json                             (default) Serialize data items from the command line as a JSON object
  -f, --form                             Serialize data items from the command line as form fields
      --multipart                        Like --form, but force a multipart/form-data request even without files
      --raw <RAW>                        Pass raw request data without extra processing
      --pretty <STYLE>                   Controls output processing [possible values: all, colors, format, none]
      --format-options <FORMAT_OPTIONS>  Set output formatting options
  -s, --style <THEME>                    Output coloring style [possible values: auto, solarized, monokai, fruity]
      --response-charset <ENCODING>      Override the response encoding for terminal display purposes
      --response-mime <MIME_TYPE>        Override the response mime type for coloring and formatting for the terminal
  -p, --print <FORMAT>                   String specifying what the output should contain
  -h, --headers                          Print only the response headers. Shortcut for --print=h
  -b, --body                             Print only the response body. Shortcut for --print=b
  -m, --meta                             Print only the response metadata. Shortcut for --print=m
  -v, --verbose...                       Print the whole request as well as the response
      --debug                            Print full error stack traces and debug log messages
      --all                              Show any intermediary requests/responses while following redirects with --follow
  -P, --history-print <FORMAT>           The same as --print but applies only to intermediary requests/responses
  -q, --quiet...                         Do not print to stdout or stderr
  -S, --stream                           Always stream the response body
  -x, --compress...                      Content compressed (encoded) with Deflate algorithm
  -o, --output <FILE>                    Save output to FILE instead of stdout
  -d, --download                         Download the body to a file instead of printing it
  -c, --continue                         Resume an interrupted download. Requires --download and --output
      --session <FILE>                   Create, or reuse and update a session
      --session-read-only <FILE>         Create or read a session without updating it form the request/response exchange
  -A, --auth-type <AUTH_TYPE>            Specify the auth mechanism [possible values: basic, bearer, digest]
  -a, --auth <USER[:PASS] | TOKEN>       Authenticate as USER with PASS (-A basic|digest) or with TOKEN (-A bearer)
      --ignore-netrc                     Do not use credentials from .netrc
      --offline                          Construct HTTP requests without sending them anywhere
      --check-status                     (default) Exit with an error status code if the server replies with an error
  -F, --follow                           Do follow redirects
      --max-redirects <NUM>              Number of redirects to follow. Only respected if --follow is used
      --timeout <SEC>                    Connection timeout of the request
      --proxy <PROTOCOL:URL>             Use a proxy for a protocol. For example: --proxy https:http://proxy.host:8080
      --verify <VERIFY>                  If "no", skip SSL verification. If a file path, use it as a CA bundle
      --cert <FILE>                      Use a client side certificate for SSL
      --cert-key <FILE>                  A private key file to use with --cert
      --ssl <VERSION>                    Force a particular TLS version [possible values: auto, tls1, tls1.1, tls1.2, tls1.3]
      --https                            Make HTTPS requests if not specified in the URL
      --http-version <VERSION>           HTTP version to use [possible values: 1.0, 1.1, 2, 2-prior-knowledge]
      --resolve <HOST:ADDRESS>           Override DNS resolution for specific domain to a custom IP
      --interface <NAME>                 Bind to a network interface or local IP address
  -4, --ipv4                             Resolve hostname to ipv4 addresses only
  -6, --ipv6                             Resolve hostname to ipv6 addresses only
  -I, --ignore-stdin                     Do not attempt to read stdin
      --curl                             Print a translation to a curl command
      --curl-long                        Use the long versions of curl's flags
      --generate <KIND>                  Generate shell completions or man pages
      --help                             Print help
  -V, --version                          Print version

Each option can be reset with a --no-OPTION argument.

Run "xh help" for more complete documentation.