packages feed

restman-0.7.5.1: README.md

[![pipeline status](https://gitlab.com/krakrjak/restman/badges/master/pipeline.svg)](https://gitlab.com/krakrjak/restman/-/commits/master)
[![Latest Release](https://gitlab.com/krakrjak/restman/-/badges/release.svg)](https://gitlab.com/krakrjak/restman/-/releases)
[![License: BSD2](https://img.shields.io/badge/license-BSD2-blue.svg)](LICENSE)

# RESTMan

**RESTMan** is a keyboard-driven TUI HTTP client for the terminal. Compose and
send HTTP(S) requests interactively — or script one-shot requests from the
command line — without leaving your shell.

[[_TOC_]]

---

## Features

- **All HTTP methods** — GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT,
  PATCH, or any custom verb
- **Interactive TUI** — full-screen [Brick](https://hackage.haskell.org/package/brick)-based
  terminal UI
- **Oneshot mode** — `--oneshot URI` prints the response body to stdout and
  exits (scriptable like `curl`)
- **Quick Request mode** — pass a URI on the command line to pre-populate the
  TUI and fire the request immediately
- **Syntax-highlighted responses** — MIME-type-aware, pygments colour theme via
  [Skylighting](https://hackage.haskell.org/package/skylighting)
- **Bi-directional scroll** — arrow keys, Vi/Vim keys (`hjkl`), Page Up/Down,
  and mouse wheel
- **Custom headers** — add, edit, enable, or disable individual headers in a
  table with horizontal scroll
- **Default headers toggle** — view and toggle the wreq default headers
  (`Accept-Encoding`, `User-Agent`, …)
- **Request payloads** — provide a body inline (`--payload`) or from a file
  (`--payload-file`)
- **TLS / HTTPS** — via `http-client-tls`; 30-second timeout guards against
  captive portals and slow proxies
- **Shell completions** — HTTP method names auto-complete via
  `optparse-applicative`

---

## TUI Preview

```
   Header(s)           Payload              Session           Help
================================================================================
 ╭── Verb ──╮ ╭─────────────────── URL to Query? ──────────────────────────╮
 │  GET    ▼│ │ https://api.example.com/v1/items                           │
 ╰──────────╯ ╰────────────────────────────────────────────────────────────╯
 ╭──────────────────────────────────────────────────────────────────────────╮
 │ [X] Use Default Headers                                                  │
 │  ┌──────────────────────────────────────────────────────────────────┐   │
 │  │ Accept-Encoding: gzip, deflate, br                               │   │
 │  │ User-Agent: Haskell wreq-0.5.4.1/0.0.0.0                        │   │
 │  └──────────────────────────────────────────────────────────────────┘   │
 ╰──────────────────────────────────────────────────────────────────────────╯
 ╭─ Active ──╮╭────────────── Name ─────────────╮╭──────── Value ───────────╮
 │    [X]    ││ Authorization                   ││ Bearer my-token           │
 │    [ ]    ││ X-Request-ID                    ││ deadbeef-cafe             │
 ╰───────────╯╰─────────────────────────────────╯╰───────────────────────────╯
 [+ Add Custom Header]
 ╭────────────────────────── Response Body ──────────────────────────────╮▲
 │ {                                                                     │ │
 │   "items": [                                                          │▓│
 │     { "id": 1, "name": "widget" }                                    │ │
 │   ]                                                                   │▼│
 ╰───────────────────────────────────────────────────────────────────────╯◄▓►
```

---

## Platform Support

| Platform | Status |
|---|---|
| Linux | ✓ Fully supported |
| macOS — Intel | ✓ Supported |
| macOS — Apple Silicon (M1 / M2 / M3) | ✓ Supported (requires `llvm`, see below) |
| Windows via WSL2 + Debian | ✓ Supported — recommended Windows path |
| Windows native | ⚠ Experimental — supported since [Brick 2.0](https://hackage.haskell.org/package/brick) / [vty 6.0](https://hackage.haskell.org/package/vty) via [`vty-windows`](https://hackage.haskell.org/package/vty-windows), but builds have been unreliable; see [Known Limitations](#known-limitations) |

---

## Prerequisites

| Tool | Version | Notes |
|---|---|---|
| [GHC](https://www.haskell.org/ghc/) | 9.10.x | Managed automatically by Stack |
| [Stack](https://docs.haskellstack.org/) | any recent | Primary build tool |
| [llvm](https://llvm.org/) | — | **macOS Apple Silicon only** — `brew install llvm` |

Stack will download and configure the correct GHC version (via Stackage
**LTS 24.38**) on first run.

---

## Quick Start

```sh
# 1. Clone the repository
git clone https://gitlab.com/krakrjak/restman.git
cd restman

# 2. Set up the compiler (once — downloads GHC 9.10.x if needed)
stack setup

# 3. Build RESTMan and all dependencies
stack build

# 4. Launch the TUI
stack exec restman
```

> **First build** resolves and compiles all Haskell dependencies, which can
> take several minutes.

---

## Installation

To install the `restman` binary to `~/.local/bin`:

```sh
stack install
```

Add `~/.local/bin` to your `PATH` so you can run `restman` directly:

```sh
# Add to ~/.bashrc, ~/.zshrc, etc.
export PATH="$HOME/.local/bin:$PATH"
```

RESTMan is also listed on [Hackage](https://hackage.haskell.org/package/restman)
as a package reference.

---

## Usage

### Modes of Operation

| Mode | How to invoke | Description |
|---|---|---|
| **Interactive TUI** | `restman` | Full-screen TUI — compose and send requests interactively |
| **Quick Request** | `restman URI` | TUI launches with URI pre-filled; request fires immediately |
| **Oneshot** | `restman --oneshot URI` | Prints response body to stdout and exits — no TUI |

### Command-line Reference

```
Usage: restman [--help] [-m|--method METHOD]
                   [--no-default-headers | --default-headers]
                   [-h|--custom-header NAME_VALUE]
                   [--payload PAYLOAD | --payload-file PATHNAME]
                   [--oneshot] [URI]
```

| Flag | Short | Default | Description |
|---|---|---|---|
| `--help` | | | Show help and exit |
| `--method METHOD` | `-m` | `GET` | HTTP method / verb |
| `--no-default-headers` | | off | Send **only** custom headers (suppress wreq defaults) |
| `--default-headers` | | on | Append custom headers after wreq defaults |
| `--custom-header NAME_VALUE` | `-h` | — | Add a `Name:Value` header (repeatable) |
| `--payload PAYLOAD` | | — | Inline request body (UTF-8) |
| `--payload-file PATHNAME` | | — | Load request body from a file |
| `URI` | | — | Target URL (positional, optional) |
| `--oneshot` | | off | Non-interactive one-shot mode (requires `URI`) |

`--custom-header` format: `Name:Value` — the first colon separates the header
name from its value. The flag may be repeated to supply multiple headers.

### Examples

```sh
# Open the TUI (no arguments)
restman

# Open the TUI pre-loaded with a URL (request fires immediately)
restman https://httpbin.org/get

# One-shot GET — print the response body to stdout
restman --oneshot https://httpbin.org/get

# One-shot POST with a JSON payload
restman --oneshot -m POST \
  -h 'Content-Type:application/json' \
  --payload '{"key":"value"}' \
  https://httpbin.org/post

# PUT with a binary payload file, suppressing default headers
restman -m PUT \
  --no-default-headers \
  -h 'Content-Type:application/octet-stream' \
  --payload-file ./data.bin \
  https://example.com/upload
```

---

## TUI Keybindings

### Global

| Key | Action |
|---|---|
| `Esc` / `Ctrl+Q` | Quit RESTMan |
| `Tab` | Move focus to the next widget |
| `Shift+Tab` | Move focus to the previous widget |
| `Enter` (URL editor focused) | Send the HTTP request |

### Method Selector Popup

| Key | Action |
|---|---|
| `↓` (on Method field) | Open the method selector popup |
| `↑` / `↓` | Navigate the method list |
| `Enter` | Confirm selection and close the popup |
| `Esc` | Close the popup without changing the method |

### Custom Headers

| Key | Action |
|---|---|
| `Space` on `[+ Add Custom Header]` | Add a new empty header row |
| `Space` on a header checkbox | Enable / disable that header |
| `Tab` into a Name or Value cell | Begin editing the cell |
| `←` / `→` | Scroll cell content horizontally |

### Default Headers Toggle

| Key | Action |
|---|---|
| `Tab` to `[X] Use Default Headers` | Move focus to the toggle |
| `Space` | Toggle default headers on / off |

### Response Viewport

| Key | Action |
|---|---|
| `↓` / `j` | Scroll down one line |
| `↑` / `k` | Scroll up one line |
| `→` / `l` | Scroll right one column |
| `←` / `h` | Scroll left one column |
| `Page Down` | Scroll down one page |
| `Page Up` | Scroll up one page |
| Mouse scroll | Scroll three lines up / down |

---

## Building from Source

### Linux / Debian (and Windows WSL2)

Install the required system packages:

```sh
apt install haskell-stack libtinfo-dev libz1g-dev
```

Upgrade to the latest Stack release, then set up the compiler and build:

```sh
stack upgrade          # may prompt for sudo
stack setup            # downloads GHC 9.10.x
stack build
```

### macOS — Intel

```sh
stack setup
stack build
```

### macOS — Apple Silicon (M1 / M2 / M3)

Install [ghcup](https://www.haskell.org/ghcup/) and LLVM:

```sh
brew install llvm
```

Export the LLVM paths (add these to your shell profile):

```sh
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"
export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"
```

Then build with the `aarch64` target:

```sh
stack --arch aarch64 --system-ghc build
```

### Windows

**WSL2 (recommended):** Use WSL2 with a Debian distribution and follow the
Linux / Debian steps above.

**Windows native (experimental):** Native Windows support was introduced in
vty 6.0 / Brick 2.0 via
[`vty-windows`](https://hackage.haskell.org/package/vty-windows), which
RESTMan picks up transitively through Brick. However, `vty-windows` has a
tightly pinned `Win32` dependency and build failures have been reported.
If you want to attempt a native build, install
[GHCup](https://www.haskell.org/ghcup/) for Windows, then:

```sh
stack setup
stack build
```

Please report any success or failure in the
[issue tracker](https://gitlab.com/krakrjak/restman/-/issues).

---

## Running Tests

```sh
stack test
```

The test suite uses [Tasty](https://hackage.haskell.org/package/tasty) with
HUnit unit tests, Hedgehog property tests, and an in-process HTTP echo server
for integration tests.

---

## Known Limitations

- **Windows native (experimental)** — native Windows support was added in
  [vty 6.0](https://hackage.haskell.org/package/vty) and
  [Brick 2.0](https://hackage.haskell.org/package/brick) via the
  [`vty-windows`](https://hackage.haskell.org/package/vty-windows) backend.
  However, `vty-windows` has a tightly pinned `Win32` dependency and has had
  reported build failures. WSL2 + Debian remains the most reliable Windows
  path until `vty-windows` stabilises.
- **8 KB header line limit** — `http-client` enforces a hard 8 KB limit per
  response header line. Sites with very large headers (e.g. `slashdot.org`'s
  `Content-Security-Policy`) will produce an `OverlongHeaders` exception.
- **Captive portals** — the 30-second response timeout guards against infinite
  hangs, but portal authentication pages are not navigable from the TUI.

---

## Contributing

Bug reports and feature requests:
[gitlab.com/krakrjak/restman/issues](https://gitlab.com/krakrjak/restman/-/issues)

Before submitting a merge request, please ensure:

- `stack build` passes with no warnings
- `stack test` is green
- Code passes `hlint` and is formatted with `stylish-haskell`
- [pre-commit](https://pre-commit.com/) hooks are installed
  (`pre-commit install`)

---

## License

[BSD 2-Clause](LICENSE) — © Zac Slade, Boyd Stephen Smith Jr.