Giter VIP home page Giter VIP logo

xh's Introduction

xh

Version info Packaging status

xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance.

asciicast

Installation

via cURL (Linux & macOS)

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

via Powershell (Windows)

iwr -useb https://raw.githubusercontent.com/ducaale/xh/master/install.ps1 | iex

via a package manager

OS Method Command
Any Cargo* cargo install xh --locked
Any Huber huber install xh
Android (Termux) pkg pkg install xh
Android (Magisk/KernelSU) MMRL** mmrl install xhhttp
Alpine Linux apk*** apk add xh
Arch Linux Pacman pacman -S xh
Debian & Ubuntu Apt**** sudo apt install xh
FreeBSD FreshPorts pkg install xh
NetBSD pkgsrc pkgin install xh
Linux & macOS Nixpkgs nix-env -iA nixpkgs.xh
Linux & macOS Homebrew brew install xh
macOS MacPorts sudo port install xh
Windows Scoop scoop install xh
Windows Chocolatey choco install xh
Windows Winget winget add ducaale.xh

* Make sure that you have Rust 1.74 or later installed

** You will need to install the MMRL CLI

*** The xh package is available in Edge and will be in v3.17+. It is built with native-tls only.

**** You will need to add the apt repository from https://apt.cli.rs/

via pre-built binaries

The release page contains prebuilt binaries for Linux, macOS and Windows.

Usage

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
      --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
  -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
      --help                             Print help
  -V, --version                          Print version

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

Run xh help for more detailed information.

Request Items

xh uses HTTPie's request-item syntax to set headers, request body, query string, etc.

  • =/:= for setting the request body's JSON or form fields (= for strings and := for other JSON types).
  • == for adding query strings.
  • @ for including files in multipart requests e.g [email protected] or [email protected];type=image/jpeg;filename=goodbye.jpg.
  • : for adding or removing headers e.g connection:keep-alive or connection:.
  • ; for including headers with empty values e.g header-without-value;.

An @ prefix can be used to read a value from a file. For example: x-api-key:@api-key.txt.

The request body can also be read from standard input, or from a file using @filename.

To construct a complex JSON object, a JSON path can be used as a key e.g app[container][0][id]=090-5. For more information on this syntax, refer to https://httpie.io/docs/cli/nested-json.

Shorthand form for URLs

Similar to HTTPie, specifying the scheme portion of the request URL is optional, and a leading colon works as shorthand for localhost. :8000 is equivalent to localhost:8000, and :/path is equivalent to localhost/path.

URLs can have a leading :// which allows quickly converting a URL into a valid xh or HTTPie command. For example http://httpbin.org/json becomes http ://httpbin.org/json.

xh http://localhost:3000/users # resolves to http://localhost:3000/users
xh localhost:3000/users        # resolves to http://localhost:3000/users
xh :3000/users                 # resolves to http://localhost:3000/users
xh :/users                     # resolves to http://localhost:80/users
xh example.com                 # resolves to http://example.com
xh ://example.com              # resolves to http://example.com

Making HTTPS requests by default

xh will default to HTTPS scheme if the binary name is one of xhs, https, or xhttps. If you have installed xh via a package manager, both xh and xhs should be available by default. Otherwise, you need to create one like this:

cd /path/to/xh && ln -s ./xh ./xhs
xh httpbin.org/get  # resolves to http://httpbin.org/get
xhs httpbin.org/get # resolves to https://httpbin.org/get

Strict compatibility mode

If xh is invoked as http or https (by renaming the binary), or if the XH_HTTPIE_COMPAT_MODE environment variable is set, it will run in HTTPie compatibility mode. The only current difference is that --check-status is not enabled by default.

Examples

# Send a GET request
xh httpbin.org/json

# Send a POST request with body {"name": "ahmed", "age": 24}
xh httpbin.org/post name=ahmed age:=24

# Send a GET request with querystring id=5&sort=true
xh get httpbin.org/json id==5 sort==true

# Send a GET request and include a header named x-api-key with value 12345
xh get httpbin.org/json x-api-key:12345

# Send a POST request with body read from stdin.
echo "[1, 2, 3]" | xh post httpbin.org/post

# Send a PUT request and pipe the result to less
xh put httpbin.org/put id:=49 age:=25 | less

# Download and save to res.json
xh -d httpbin.org/json -o res.json

# Make a request with a custom user agent
xh httpbin.org/get user-agent:foobar

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.

Similar or related Projects

  • curlie - frontend to cURL that adds the ease of use of httpie
  • httpie-go - httpie-like HTTP client written in Go
  • curl2httpie - convert command arguments between cURL and HTTPie

xh's People

Contributors

austinbutler avatar blyxxyz avatar bnyro avatar chrisk-0 avatar dergoogler avatar dtrodrigues avatar ducaale avatar dwink avatar henno avatar idanski avatar innobead avatar jayvdb avatar jgoday avatar jihchi avatar jirutka avatar lispyclouds avatar myhro avatar nitsky avatar orhun avatar otaconix avatar plombard avatar porglezomp avatar quarticcat avatar samuelmarks avatar sanpii avatar sorairolake avatar till--h avatar wezm avatar zicklag avatar zuisong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xh's Issues

HTTPie feature parity checklist

  • Support =@ and :=@ as seperators in request data.
  • Support ==@ and :@ for reading query string and header values from a file (#288)
  • Support specifying file type in multipart requests i.e key@file_name;type=file_type.
  • Support --response-charset and --response-mime flags. (#184)
  • Support custom boundary in multipart requests (currently not supported in reqwest).
  • Support unsetting headers + empty headers.
  • Support reading request body from stdin.
  • Support --raw flag (#202)
  • Support reading request data from a file.
  • Support piping response body to stdout (including binary).
  • Support unbuffered streamed responses.
  • Support Download mode.
  • Support resuming interrupted downloads
  • Support Sessions. (#125)
  • Support HEAD HTTP method.
  • Support --all flag for printing intermediary requests/responses. (#137)
  • Support --history-print. (#137)
  • Support the --timeout flag.
  • Support default options (#165)
  • Support the --path-as-is flag
  • Decode responses compressed in deflate format. (#158)
  • Support --compress flag.
  • Support --chunked flag.
  • Support -vv/--meta flags. (#240)
  • Add Monokai theme. (#157)
  • Add Fruity theme. (#206)
  • Support Digest authentication. (#176)
  • (undecided) Warn the user when there is no incoming data after a certain time passed on stdin.

PROTOCOL_ERROR - Thread 'main' panicked at 'called `Result::unwrap()`

Hello,

  1. I tried a simple GET on https:// google.com. Internally ht got panicked and threw the below error.
➜  ~ ht get https://google.com
HTTP/2.0 400 Bad Request
content-length: 1555
content-type: text/html; charset=UTF-8
date: Sun, 07 Feb 2021 01:19:14 GMT
referrer-policy: no-referrer

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: reqwest::Error { kind: Body, source: hyper::Error(Body, Error { kind: Proto(PROTOCOL_ERROR) }) }', src/printer.rs:208:79
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
  1. When tried with just http://google.com ht errored out with too many redirects
➜  ~ ht get http://google.com
Error: reqwest::Error { kind: Redirect, url: Url { scheme: "http", host: Some(Domain("www.google.com")), port: None, path: "/", query: None, fragment: None }, source: TooManyRedirects }

Environment
MacOS - 10.15

Rename this project

I was made aware [1] the name Yahc might be confused with a rust community member who goes by the name Yaahc. Therefore I am planning to rename this project using one of the following:

  • ht: This would have been the best pick, but it is already being used by a similar project https://github.com/mark-burnett/ht
  • rq: This name is being used by a popular python job queue library. Although it is possible to append -rs at the end to avoid a name clash.
  • htyr: Not sure what it means but for some reason I like it.

[1] https://www.reddit.com/r/rust/comments/k0aika/yahc_yet_another_httpie_clone_in_rust/gdgza01?utm_source=share&utm_medium=web2x&context=3

Broken pipe panic when piping output

Thanks for this, it's dramatically faster than the original when pretty printing big JSON responses!

When piping to head or less I'm seeing a broken pipe-related panic when the pager exits. Seems likely to be related to this? rust-lang/rust#46016

Reproducing it requires a response of sufficient length:

22:02:25 ~ > RUST_BACKTRACE=1 ht get 'http://httpbin.org/stream/150' | head
{"url": "http://httpbin.org/stream/150", "args": {}, "headers": {"Host": "httpbin.org", "X-Amzn-Trace-Id": "Root=1-601e06c4-02a347b5507c64fe408cf5b6", "Accept-Encoding": "gzip, deflate", "Accept": "*/*"}, "origin": "104.162.100.223", "id": 0}

{"url": "http://httpbin.org/stream/150", "args": {}, "headers": {"Host": "httpbin.org", "X-Amzn-Trace-Id": "Root=1-601e06c4-02a347b5507c64fe408cf5b6", "Accept-Encoding": "gzip, deflate", "Accept": "*/*"}, "origin": "104.162.100.223", "id": 1}

{"url": "http://httpbin.org/stream/150", "args": {}, "headers": {"Host": "httpbin.org", "X-Amzn-Trace-Id": "Root=1-601e06c4-02a347b5507c64fe408cf5b6", "Accept-Encoding": "gzip, deflate", "Accept": "*/*"}, "origin": "104.162.100.223", "id": 2}

{"url": "http://httpbin.org/stream/150", "args": {}, "headers": {"Host": "httpbin.org", "X-Amzn-Trace-Id": "Root=1-601e06c4-02a347b5507c64fe408cf5b6", "Accept-Encoding": "gzip, deflate", "Accept": "*/*"}, "origin": "104.162.100.223", "id": 3}

{"url": "http://httpbin.org/stream/150", "args": {}, "headers": {"Host": "httpbin.org", "X-Amzn-Trace-Id": "Root=1-601e06c4-02a347b5507c64fe408cf5b6", "Accept-Encoding": "gzip, deflate", "Accept": "*/*"}, "origin": "104.162.100.223", "id": 4}

thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)', library/std/src/io/stdio.rs:895:9
stack backtrace:
   0: rust_begin_unwind
             at /build/rustc-71Ymwo/rustc-1.47.0+dfsg1+llvm/library/std/src/panicking.rs:475
   1: std::panicking::begin_panic_fmt
             at /build/rustc-71Ymwo/rustc-1.47.0+dfsg1+llvm/library/std/src/panicking.rs:429
   2: std::io::stdio::print_to
             at /build/rustc-71Ymwo/rustc-1.47.0+dfsg1+llvm/library/std/src/io/stdio.rs:895
   3: std::io::stdio::_print
             at /build/rustc-71Ymwo/rustc-1.47.0+dfsg1+llvm/library/std/src/io/stdio.rs:907
   4: ht::buffer::Buffer::write
   5: ht::main::{{closure}}
   6: std::thread::local::LocalKey<T>::with
   7: tokio::runtime::enter::Enter::block_on
   8: tokio::runtime::thread_pool::ThreadPool::block_on
   9: tokio::runtime::Runtime::block_on
  10: ht::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Installed from cargo, rustc 1.47.0
Ubuntu 20.10 / Linux 5.8.0-41-generic

query parameter values are not escaped properly

Hi,
I'm passing a query parameter of the form geometry=POINT(1 2) to a request :

ht post ".../api/...doStuff?exporttype=legacy&geometry=POINT(1 2)" <<< "[]"

please note the request is within double quotes

Using ht version 3.5.0, my app receives POINT(1 as the value of parameter geometry

[Feature] One-line binary install improvement

I have a plethora of great statically linked binary installs in Linux (Ubuntu). These include ripgrep, exa, bat, fd etc. None shines as well as chezmoi when it comes to the one-line installer.

I recommend to implement a one-line binary installer to follow that of chezmoi. See this documentation: https://github.com/twpayne/chezmoi/blob/master/docs/INSTALL.md#one-line-binary-install

In effect, what it does, is to download the latest statically linked binary for your operating system and architecture into ./bin
This means I can put myself in whatever dir I want and just call the installer and it will be installed into the bin path of my choosing. Be that ~/bin or .local/bin or whatever I prefer.

Better redirect response handling

In httpie, redirect outputs are much clearer, and also don't cause the program to return an error status:

$ http google.com
HTTP/1.1 301 Moved Permanently
Cache-Control: public, max-age=2592000
Content-Length: 219
Content-Type: text/html; charset=UTF-8
Date: Sun, 07 Feb 2021 08:12:48 GMT
Expires: Tue, 09 Mar 2021 08:12:48 GMT
Location: http://www.google.com/
Server: gws
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0

<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="http://www.google.com/">here</A>.
</BODY></HTML>
$ echo $?
0

On ht, on the other hand, this is not the case:

$ ht -v google.com
GET / HTTP/1.1
accept: */*
accept-encoding: gzip, deflate
connection: keep-alive
host: google.com

Error: reqwest::Error { kind: Redirect, url: Url { scheme: "http", host: Some(Domain("www.google.com")), port: None, path: "/", query: None, fragment: None }, source: TooManyRedirects }
$ echo $?
1

Httpie's handling of this case is obviously better in terms of output readability, but IMO also with regards to program success - someone wanting to GET a resource either wants to succeed on a redirect response or follow the redirect optionally (similar to curl --fail behavior)

Allow omission of the HTTP method

HTTPie allows omitting HTTP methods in which case it will default to GET if the body is empty or to POST otherwise.

http :8080/users          # will be translated to `http get :8080/users`
http :8080/users name=ali # will be translated to `http post :8080/users name=ali`

This should be possible when clap-rs/clap#2122 gets addressed.

"--no-" variants of flags

HTTPie lets you negate flags, e.g. --no-check-status cancels out --check-status. While argparse has optional support for that, HTTPie does it by manually processing arguments argparse didn't recognize (plus a few hardcoded cases).

clap doesn't support this, but there's an issue for it (clap-rs/clap#815). It also has an escape hatch for looking at unknown arguments, but I don't know exactly how powerful it is, and I'm worried it would degrade the usual error message.

A downside of HTTPie's current approach (and probably of the escape hatch approach for clap) is that the order of the flags doesn't matter. Normally the last flag counts, so --form --json works like --json and --json --form like --form, but --no-check-status always overrides --check-status, even if it comes first. But that's acceptable, since the typical usage would be to add --no-check-status after an alias that includes --check-status, and the opposite wouldn't come up because there's little reason to use --no-check-status in an alias.

If this is added to clap it might take a long time before it's usable in xh. It'd land in version 3.0.0, and then after that we might have to wait for structopt to support it (or maybe switch to clap_derive).

"Stateful session" support

This is a broad, open-ended question. When I'm using a tool like ht with an API that requires authentication and several headers, I find myself struggling to manage that state. My best answer is I set environment variables that I use in the URL for the authentication, frequently go back in the shell history to edit URLs etc. If there is a cookie needed it's even more complicated. Same goes for setting non-standard headers.

GUI tools like Insomnia allow managing this in projects which reduces this pain, but requires using a GUI for a task that's (in my experience) a better fit for the terminal.

So here is my question. Is there a reasonable way to implement some kind support for stateful sessions in ht, that's perhaps applied and mutated automatically when found in the current dir? (a bit like a browser). For example:

# .ht.session
base_url: "https://my-test-api.com/api
headers:
  Content-Type: "application/json"
  X-My-Specific-Header: "That value"

auth:
  token: "Bearer d2h5IHdvdWxkIHlvdSB0YWtlIHRoZSB0aW1lIHRvIGRlY29kZSB0aGlz"

and then used like:

$ ht get /v2/user
Using .ht.session
... dump user json ... 

The above a very raw demonstration that hopefully communicates the idea - not the concrete proposal itself.

There is of course a fine balance between explicitness and ease of use. Hiding too much information can lead to surprising behavior. But I still feel there could be a reasonable middle ground. Any thoughts?

Allow forcing HTTP version

Hey, I really like this tool and use it quite a bit for debugging. As such, I sometimes need to specifically request a certain version of HTTP from a server. Could you add a mode to force the HTTP version? Curl has --http1.0, --http1.1, --http2, --http3 and I think that makes sense.

Please consider adding those flags.

Download fails with "broken pipe" error

When downloading this particular file xh tends to fail.

❯ xh --download --output lid.176.bin https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin
HTTP/2.0 200 OK
accept-ranges: bytes
cf-cache-status: DYNAMIC
cf-ray: 647ae6814e4ae80d-LAX
cf-request-id: 09c0ae64d00000e80d05a72000000001
content-length: 131266198
content-type: application/octet-stream
date: Thu, 29 Apr 2021 19:22:40 GMT
etag: "be9a581239b678aafe80d1d4a46d86d8-16"
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
last-modified: Fri, 18 Jan 2019 15:01:14 GMT
server: cloudflare
set-cookie: __cfduid=dfd5b87e416587032a11e8c6f286918f91619724160; expires=Sat, 29-May-21 19:22:40 GMT; path=/; domain=.fbaipublicfiles.com; HttpOnly; SameSite=Lax
x-amz-id-2: umZpKluJeIllqq/c6zhTmEKE2/dYK0mJ7QBI6xtRpEMj1sko6eU1W2m970vyu07+/Ih7QglfQPA=
x-amz-request-id: C4P9T3MK474MWB9J
x-amz-server-side-encryption: AES256
x-amz-version-id: i0bvGpDpJiHbCgE2pdfmiT6e8rhcCJdD

Downloading 125.19MB to "lid.176.bin"
Error: request or response body error: error reading a body from connection: broken pipe

Caused by:
    0: error reading a body from connection: broken pipe
    1: broken pipe

On my macOS laptop it seems most consistently to fail. It also intermittently fails on two Arch Linux machines. Both curl and httpie consistently download the file without issue.

Downloading is slow

Hey, cool tool. :)

I noticed that downloading is pretty slow as compared to wget. For instance, on downloading a random large file, using ht -d I get 7-13MB/s on repeated tests while using wget I get a good 60MB/s (my full connection speed). Any idea?

Sorry I can't be more specific but literally all I do is a multi-run test of a 2-5GB-sized file between wget and ht -d.

Librarification

It would be nice to expose a library interface, to enable projects to build on top of xh along the lines of http-prompt. It would also help with #88.

This could be a pretty big change. We'd have to decide what to expose and commit to not breaking compatibility. Refactoring would become harder the more we expose.

Some concrete steps would be:

  • Move most things out of main
  • Change instances of pub to pub(crate)
  • Add top-level re-exports
  • (Maybe) add #[non_exhaustive] to certain public enums
  • (Maybe) use custom error types instead of anyhow

It could help to have a consuming project that lives in the same repository (in a workspace).

At first glance, we might not have to expose that much, see how http-prompt calls it. But I haven't looked at other projects, and a richer API might be a lot more convenient.

Breaking the public interface after 1.0 would mean incrementing the major version number. I can think of a few approaches:

  • Get this in place before a 1.0 release and be very careful to provide something we can remain compatible with.
  • Be willing to increment the major version number even when nothing breaks for normal users of the tool.
  • Move most of the code into a new package (e.g. xh_core) that can increment its major version freely, while the xh package becomes a wrapper around it with more conservative versioning.

Download progress indicators are always colored

We should disable the coloring if NO_COLOR is set. We might also want to control it via the --pretty flag but that might be a bit tricky since that flag only takes into account stdout output while progress indicators use stderr for their output.

An interesting thing to note is that progress indicator colors do not suffer from issue #98.

Replace --default-scheme

The only useful use of --default-scheme is --default-scheme=https (http is the default, other schemes don't work). Maybe it should be replaced by a --https flag?

We could keep --default-scheme as a deprecated hidden flag.

Includes docs/xh.1, README.md, LICENSE.md in the release artifacts

I have looked at how bat release artifact is structured (ripgrep should have a similar structure)

$ wget https://github.com/sharkdp/bat/releases/download/v0.17.1/bat-v0.17.1-x86_64-apple-darwin.tar.gz
$ tar xvzf bat-v0.17.1-x86_64-apple-darwin.tar.gz
$ tree bat-v0.17.1-x86_64-apple-darwin
bat-v0.17.1-x86_64-apple-darwin
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── autocomplete
│   ├── bat.fish
│   └── bat.zsh
├── bat
└── bat.1

The main difference they have with xh is:

  • The release version is part of the downloaded tar's filename.
  • the tar includes README.md, LICENSE.md, autocompletion, and a manpage file
  • The tar contains a folder to avoid polluting the user's filesystem with random files from the release.

Apart from updating .github\workflows\CI.yaml, we would need to update places where xh is published i.e Homebrew, Scoop, etc.

ht doesn't provide content-length header

It seem that ht does not provide content-length header in http POST. It will result in errors. Examples are listed below.

❯ ht post pb.mgt.moe -f [email protected] --verbose
POST / HTTP/1.1
accept: */*
accept-encoding: gzip, deflate
connection: keep-alive
content-type: multipart/form-data; boundary=88ddf3abad0728f1-290fb9c86111357f-23e2e46e8a5ed38e-0582e206d0c93f3d
host: pb.mgt.moe

+--------------------------------------------+
| NOTE: multipart data not shown in terminal |
+--------------------------------------------+

HTTP/1.1 411 Length Required
connection: keep-alive
content-length: 35
content-type: text/plain; charset=utf-8
date: Sun, 07 Feb 2021 01:46:53 GMT
keep-alive: timeout=4
proxy-connection: keep-alive
server: Caddy

A content-length header is required⏎

Same function with curl

curl:

❯ cat ./test.cxx | curl -F c=@- https://pb.mgt.moe -v
* Uses proxy env variable https_proxy == 'http://127.0.0.1:1080'
*   Trying 127.0.0.1:1080...
* Connected to 127.0.0.1 (127.0.0.1) port 1080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to pb.mgt.moe:443
> CONNECT pb.mgt.moe:443 HTTP/1.1
> Host: pb.mgt.moe:443
> User-Agent: curl/7.75.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=pb.mgt.moe
*  start date: Dec 31 05:40:41 2020 GMT
*  expire date: Mar 31 05:40:41 2021 GMT
*  subjectAltName: host "pb.mgt.moe" matched cert's "pb.mgt.moe"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* 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 0x55b9e951e640)
> POST / HTTP/2
> Host: pb.mgt.moe
> user-agent: curl/7.75.0
> accept: */*
> content-length: 248
> content-type: multipart/form-data; boundary=------------------------a3d95940aa84134e
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* We are completely uploaded and fine
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< content-type: text/plain; charset=utf-8
< date: Sun, 07 Feb 2021 01:48:33 GMT
< server: Caddy
< content-length: 221
<
date: 2021-02-07 01:48:33.179267152 UTC
digest: a0efb408dd0568dafacbb17f6cdef5551b70647a3fbeda995e00fb61a818751d
short: 7bl3
size: 98
url: http://pb.mgt.moe/7bl3
status: existed
uuid: 202dc5a8-26a1-438a-815c-d322c4907507
* Connection #0 to host 127.0.0.1 left intact

400 bad request on get

curl gives the expected response, whilst ht does not.

$ curl -i http://chaz6.com
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sat, 06 Feb 2021 12:42:53 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: https://chaz6.com/

<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>

$ ht get http://chaz6.com
HTTP/2.0 400 Bad Request
content-length: 150
content-type: text/html
date: Sat, 06 Feb 2021 12:41:40 GMT
server: nginx

<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx</center>
</body>
</html>

Either remove or increase async

ht uses async, but it uses it inconsistently and I'm not sure that it needs it.

Is it likely that it will start doing multiple requests at a time in the future? If so, then blocking APIs like std::fs should be avoided. If not, then maybe reqwest's blocking API would be easier to use.

Option to convert into curl command (possibly from as well!)

I have always preferred httpie but as I have to often run same command on remote servers/docker for debugging. Sometimes I also pass these commands to colleagues, who might have liking for different tool (curl being most popular choice). It happens the other way too when I get curl commands.

I'm sure this would resonant with people who access multiple systems throughout the day.

It would be a great addition if this tool could have an option to print equivalent curl command and exit without making request. Even greater if it could do the reverse too.

And thanks for rewriting httpie in rust.

Maybe use reqwest default-tls?

Just tried running ht https://www.google.com, and got an error

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: reqwest::Error { kind: Body, source: hyper::Error(Body, Error { kind: Proto(PROTOCOL_ERROR) }) }'

Also tried on another website and got

 Custom { kind: InvalidData, error: PeerMisbehavedError("received unadvertised sig scheme RSA_PKCS1_SHA1") }

After recompiling with reqwests's default feature "deafult-tls", both websites worked fine.

Allow multiline field value for JSON request body

I don't know if it's supported by httpie, but I think it's a valid use case. Basically when requesting with JSON body, one do it like this:

ht -j -v put https://httpbin.org/put SingleLine=my_string
PUT /put HTTP/1.1
accept: application/json, */*
accept-encoding: gzip, deflate
connection: keep-alive
content-length: 26
content-type: application/json
host: httpbin.org

{
    "SingleLine": "my_string"
}

HTTP/2.0 200 OK
access-control-allow-credentials: true
access-control-allow-origin: *
content-length: 474
content-type: application/json
date: Tue, 09 Feb 2021 06:53:46 GMT
server: gunicorn/19.9.0

{
    "args": {},
    "data": "{\"SingleLine\":\"my_string\"}",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "application/json, */*",
        "Accept-Encoding": "gzip, deflate",
        "Content-Length": "26",
        "Content-Type": "application/json",
        "Host": "httpbin.org",
        "X-Amzn-Trace-Id": "Root=1-6022317a-<removed>"
    },
    "json": {
        "SingleLine": "my_string"
    },
    "origin": "<removed>",
    "url": "https://httpbin.org/put"
}

That worked flawlessly, and I really like the way JSON body was constructed by using separated key-value pairs, it's much easy to write, and easier to type without typo.

But when one of the values contains multiple lines, ht is not happy:

ht -j -v put https://httpbin.org/put SingleLine=my_string MultiLine=my\nstring
error: Invalid value for '<REQUEST_ITEM>...': error: "MultiLine=my\nstring" is not a valid value

This happens with ht 0.5.0 which is the latest version. I know for httpie you can pipe raw JSON data into it so any form of data is supported, but simple key-value construction is just so much better. Will ht support this?

Support escaping request item seperators

For example, if someone provides foo\==bar, it should be parsed as DataField("foo=", "bar") and not as UrlParam("foo", "bar"). Our current regex could be modified to support that through negative lookbehind or via some other trick but that comes with some drawbacks:

  1. AFAIK, there is no way to discard the escape character. For example, if a regex like this was used ^(.+?)(?<!(\\))(==|:=|=|@|:)((?s).+)$ on foo\==bar, it would produce ("foo\=", "=", "bar") instead of ("foo=", "=", "bar").
  2. Some of the advanced features needed might not be supported in the regex crate we're currently using e.g lookaround.

I've been lately playing with https://github.com/Geal/nom to see how easy it is to support nested JSON syntax. Maybe that could help us here?

See https://httpie.io/docs#escaping-rules

Update the tagline for this project

We would like to remove the HTTPie clone part in our tagline or reword it. I propose that we use something similar to the one used inside src/cli.rs.

Friendly and fast tool for sending HTTP requests

These are the places that would need updating after we agree on what tagline to use:

  • The Github's about section
  • Any reference to it in our code including Cargo.toml and generate.sh.
  • The desc value in Homebrew
  • The description value in Scoop
  • The description value in Arch Linux's package
  • The description value in NO_COLOR

panic on get

Ups?!

 % ht -V
ht 0.3.5

% RUST_BACKTRACE=full ht get https://google.de
HTTP/2.0 400 Bad Request
content-length: 1555
content-type: text/html; charset=UTF-8
date: Sat, 06 Feb 2021 01:51:40 GMT
referrer-policy: no-referrer

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: reqwest::Error { kind: Body, source: hyper::Error(Body, Error { kind: Proto(PROTOCOL_ERROR) }) }', src/printer.rs:208:79
stack backtrace:
   0:        0x10cc13104 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::ha0848bb2602b5d05
   1:        0x10cc31b20 - core::fmt::write::h9f3ccac2ef682b93
   2:        0x10cc0cc86 - std::io::Write::write_fmt::h0a47673aab280496
   3:        0x10cc14ee9 - std::panicking::default_hook::{{closure}}::h850c6aaf5e80c2f5
   4:        0x10cc14bad - std::panicking::default_hook::h037801299da6e1c6
   5:        0x10cc1556b - std::panicking::rust_panic_with_hook::h76436d4cf7a368ac
   6:        0x10cc15095 - std::panicking::begin_panic_handler::{{closure}}::h516c76d70abf04f6
   7:        0x10cc13578 - std::sys_common::backtrace::__rust_end_short_backtrace::h653290b4f930faed
   8:        0x10cc14ffa - _rust_begin_unwind
   9:        0x10cc44d5f - core::panicking::panic_fmt::hde9134dd19c9a74f
  10:        0x10cc44c65 - core::result::unwrap_failed::hcef4aa8b48b8f381
  11:        0x10c85639e - ht::main::{{closure}}::he3ac16d2a9991dec
  12:        0x10c8410cf - std::thread::local::LocalKey<T>::with::he4e6385d82693aed
  13:        0x10c868df7 - tokio::runtime::enter::Enter::block_on::ha21fbfb709a85010
  14:        0x10c87d6bb - tokio::runtime::thread_pool::ThreadPool::block_on::h85096403de5f2e48
  15:        0x10c834583 - tokio::runtime::Runtime::block_on::hcb3032661041b561
  16:        0x10c869b94 - ht::main::h1c0bf400bc026e2c
  17:        0x10c878dea - std::sys_common::backtrace::__rust_begin_short_backtrace::h23c3e2aace5f6064
  18:        0x10c879371 - std::rt::lang_start::{{closure}}::h61625dd6a2f250f3
  19:        0x10cc158e4 - std::rt::lang_start_internal::h36ccce6e8a047133
  20:        0x10c869c79 - _main

changelog

I love this tool! thanks for working on this!
every time I see a release I am happy but then its super hard to figure out what actually changed.
do you consider starting a changelog?

Suggestion: Set Accept header

When supplying a JSON body to a request it might be a good idea to set the the HTTP Accept header (to application/json) so that well behaved web-servers will respond with errors in JSON instead of HTML.

Alternately there could be a method to manually set the Accept header like the -H option to curl.

Allow for optionally using OpenSSL

Hey, I think it'd be neat if the user could choose between using rustls or OpenSSL. For instance, I do sometimes have the requirement to use either one or the other and if xh supported that it'd be pretty good. I'm not sure whether it'd be a compile-time switch or a runtime one as linking to OpenSSL at all times would decrease the nice portability of xh which I do value.

Perhaps allow the user to add OpenSSL support during compile time? Then distro packagers could choose to ship a xh supporting both versions while you still keep building the static builds with only rustls.

I imagine building this would enable a flag --native-tls which would then internally switch TLS calls to the linked OpenSSL.

Use cases:

  • Sometimes I want it to use my system certificate store instead of the shipped certs.
  • Sometimes rustls introduces weird behavior or performes badly (for instance #135)
  • Sometimes I want to make sure a given server I poke is compliant in more ways than one.

What do you think?

Generate man pages for xh

Clap-rs by convention uses -h for short documentation and --help for the detailed form. Unfortunately, the -h flag has now been taken by --header which means there is no way for users to access the condensed form of xh docs.

Proposed solution:

  1. use --help to display the short-form of the documentation.
  2. generate man pages for viewing the long-form of the documentation. This would be in a way a replacement for HTTPie's online documentation.

Some helpful references:

Document why this project exists

It would be great to add a note to the readme to explain why this project exists and why someone might want to choose it over HTTPie. This could also include any points where xh deliberately differs from HTTPie in behaviour/syntax.

Even if the answer is "because I felt like making it" (which is a perfectly valid reason!), I still think it would be valuable to have it written down.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.