HTTPie vs cURL (Part 1)

capricorn, rock, animal

Over the last decades cURL was the king in CLI HTTP client area. It has many features which is enough to test APIs. However, panta rei and world is changing constantly. Even is such scope where one tool has such a great adoption ratio new challengers occur. One of them is HTTPie. This is quite new tool which getting popular and popular. At a glance it's quite interesting choice. Let look deeply and compare both. It's time to fight. HTTPie vs cURL.

capricorn, rock, animal

The battlegroud

Before I start comparing tools, let me describe the environment.

OS

Both tools have been tested on Ubuntu

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"

httpie

This is the newest version

$ https --version
2.3.0

cURL

That version has been preinstalled on my os

$ curl -V
curl 7.58.0

Online Endpoints

A simple HTTP Request & Response Service

httpbin.org

http://httpbin.org/

Local Enspoints

Happily, httpbin is distributed as a docker image that's why if online service is not responding below one-liner does the job.

docker run -p 80:80 kennethreitz/httpbin

Output

Firstly let's look at output. It was a kind of surprise when I've realised that I'm not able to grep httpie output. It's weird but it's true. Please have a look:

httpie

$ http http://stackoverflow.com
HTTP/1.1 301 Moved Permanently
Accept-Ranges: bytes
Connection: keep-alive
Date: Mon, 01 Mar 2021 19:08:11 GMT
Set-Cookie: prov=d773721c-477a-11b0-4854-8c7582fdf528; domain=.stackoverflow.com; expires=Fri, 01-Jan-2055 00:00:00 GMT; path=/; HttpOnly
Transfer-Encoding: chunked
Vary: Fastly-SSL
Via: 1.1 varnish
X-Cache: MISS
X-Cache-Hits: 0
X-DNS-Prefetch-Control: off
X-Served-By: cache-fra19133-FRA
X-Timer: S1614625691.281767,VS0,VE92
cache-control: no-cache, no-store, must-revalidate
content-security-policy: upgrade-insecure-requests; frame-ancestors 'self' https://stackexchange.com
feature-policy: microphone 'none'; speaker 'none'
location: https://stackoverflow.com/
server: Microsoft-IIS/10.0
x-aspnet-duration-ms: 0
x-flags: AA
x-is-crawler: 1
x-providence-cookie: d773721c-477a-11b0-4854-8c7582fdf528
x-request-guid: 498dea5d-60c5-44b6-aef2-2bd3044655f0
$
$ http http://stackoverflow.com | grep Connection
$

Even though we can see it on the screen it is not possible to grep it. To overcome this small issue -h flag lands a hand.

$ http -h http://stackoverflow.com | grep Connection
Connection: keep-alive

It's a trifle but worth to note down.

cURL

In contrast, in old cURL you can grep by default although new problem appears - progress bar.

$ curl -I http://stackoverflow.com | grep Connection
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
Connection: keep-alive

However here as well we have few options resolve this issues. Firstly, you can redirect stderr to /dev/null

$ curl -I http://stackoverflow.com 2> /dev/null | grep Connection 
Connection: keep-alive

Secondly, you can switch to quiet mode as manual says:

-s, --silent
              Silent or quiet mode. Don't show progress meter or error messages.
$ curl -sI  http://stackoverflow.com | grep Connection
Connection: keep-alive

GET HTTPie vs cURL

Secondly, let's continue with HTTP methods as the most frequently use case

laptop, mockup, graphics tablet

httpie (HTTPie vs cURL)

For HTTPie this is a default method, so the schema is

$ http URL

And example

$ http http://httpbin.org/get
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 295
Content-Type: application/json
Date: Mon, 18 Jan 2021 21:51:17 GMT
Server: gunicorn/19.9.0

{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/2.3.0",
        "X-Amzn-Trace-Id": "Root=1-600602d5-3c1b6c212e56037958643a70"
    },
    "origin": "89.64.65.223",
    "url": "http://httpbin.org/get"
}

As you can see, to the output go either response headers or response body. This is not typical for old-school folk. In addition, to send to the output only response body we have to use -b flag.

$ http -b http://httpbin.org/get
{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/2.3.0",
        "X-Amzn-Trace-Id": "Root=1-6006033c-758a0d4f0d1ae1fd0fdab1c0"
    },
    "origin": "89.64.65.223",
    "url": "http://httpbin.org/get"
}

cURL (HTTPie vs cURL)

Schema, is really simple

$ curl URL

Conversely, cURL prints only response body by default.

$ curl httpbin.org/get
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.58.0", 
    "X-Amzn-Trace-Id": "Root=1-600604b0-41d2f9c03b75d1a42e44ecaf"
  }, 
  "origin": "89.64.65.223", 
  "url": "http://httpbin.org/get"
}

To add response headers to the response body we have to add -i flag.

$ curl -i httpbin.org/get
HTTP/1.1 200 OK
Date: Mon, 01 Mar 2021 21:59:36 GMT
Content-Type: application/json
Content-Length: 253
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.58.0", 
    "X-Amzn-Trace-Id": "Root=1-603d63c8-777b08cf2ff4497c3d611f17"
  }, 
  "origin": "89.64.65.223", 
  "url": "http://httpbin.org/get"
}

After that, HTTP GET is quite simple and very similar in both cases but not equal.

POST HTTPie vs cURL

httpie

HTTPie has several ways to send POST request which is obviously good. For instance, key=value

$ http POST http://httpbin.org/post field1=value1
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 501
Content-Type: application/json
Date: Mon, 18 Jan 2021 22:06:28 GMT
Server: gunicorn/19.9.0

{
    "args": {},
    "data": "{"field1": "value1"}",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "application/json, */*;q=0.5",
        "Accept-Encoding": "gzip, deflate",
        "Content-Length": "20",
        "Content-Type": "application/json",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/2.3.0",
        "X-Amzn-Trace-Id": "Root=1-60060664-44c11f2c3888cf4660e0e7f3"
    },
    "json": {
        "field1": "value1"
    },
    "origin": "89.64.65.223",
    "url": "http://httpbin.org/post"
}

As you probably noticed we have Content-Type: application/json out-of-the-box which is kind of interesting cause in nowadays in most cases we are sending that type of data.

Another way is echo & pipe

$ echo '{"field1": "value1"}' | http POST httpbin.org/post
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 503
Content-Type: application/json
Date: Mon, 18 Jan 2021 22:09:40 GMT
Server: gunicorn/19.9.0

{
    "args": {},
    "data": "{"field1": "value1"}n",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "application/json, */*;q=0.5",
        "Accept-Encoding": "gzip, deflate",
        "Content-Length": "21",
        "Content-Type": "application/json",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/2.3.0",
        "X-Amzn-Trace-Id": "Root=1-60060724-18039522466e77f958a8a821"
    },
    "json": {
        "field1": "value1"
    },
    "origin": "89.64.65.223",
    "url": "http://httpbin.org/post"
}

more ways in next paragraphs.

cURL

When it comes to cURL the the basic schema is as follows:

$ curl -X POST URL -H "Content-Type: application/json" payload

end example

$ curl -X POST "http://httpbin.org/post" -H "Content-Type: application/json" -d '{"field1": "value1"}'
{
  "args": {}, 
  "data": "{"field1": "value1"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "20", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.58.0", 
    "X-Amzn-Trace-Id": "Root=1-600606d8-6498bd4a09ba060517f63ca5"
  }, 
  "json": {
    "field1": "value1"
  }, 
  "origin": "89.64.65.223", 
  "url": "http://httpbin.org/post"
}

And as you can see, like with GET, you only get response body. If you don't specify json headers the POST call won't be processed smoothly. Please have a look.

$ curl -X POST "http://httpbin.org/post" -d '{"field1": "value1"}'
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "{"field1": "value1"}": ""
  }, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "20", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.58.0", 
    "X-Amzn-Trace-Id": "Root=1-600605d5-758362926912420b4cc9051c"
  }, 
  "json": null, 
  "origin": "89.64.65.223", 
  "url": "http://httpbin.org/post"
}

DELETE HTTPie vs cURL

german shepherd, fully awake, pet

httpie

This paragraph will be bored cause is lack of fancy stuff.

$ http DELETE httpbin.org/delete
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 389
Content-Type: application/json
Date: Thu, 28 Jan 2021 08:21:21 GMT
Server: gunicorn/19.9.0

{
    "args": {},
    "data": "",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Content-Length": "0",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/2.3.0",
        "X-Amzn-Trace-Id": "Root=1-60127401-4bc3cfdb76d68c202af55aba"
    },
    "json": null,
    "origin": "89.64.65.223",
    "url": "http://httpbin.org/delete"
}

Standard with response headers bounded by default and ...

http -b DELETE httpbin.org/delete
{
    "args": {},
    "data": "",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Content-Length": "0",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/2.3.0",
        "X-Amzn-Trace-Id": "Root=1-6012754a-4b00584943727f9351aa36da"
    },
    "json": null,
    "origin": "89.64.65.223",
    "url": "http://httpbin.org/delete"
}

get only body.

cURL

Similarly, deleting using curl.

$ curl -XDELETE httpbin.org/delete
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.58.0", 
    "X-Amzn-Trace-Id": "Root=1-60127466-45ef2027733247e30e379074"
  }, 
  "json": null, 
  "origin": "89.64.65.223", 
  "url": "http://httpbin.org/delete"
}

To be able to show response headers you need o perform something like that:

$ curl -XDELETE -i httpbin.org/delete
HTTP/1.1 200 OK
Date: Thu, 28 Jan 2021 08:23:06 GMT
Content-Type: application/json
Content-Length: 319
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.58.0", 
    "X-Amzn-Trace-Id": "Root=1-6012746a-70ca51733607fd0c40b05079"
  }, 
  "json": null, 
  "origin": "89.64.65.223", 
  "url": "http://httpbin.org/delete"
}

PUT HTTPie vs cURL

httpie

Unsurprisingly, same story here. So general schema for PUT request is:

$ http PUT URL key=value

and example

$ http PUT httpbin.org/put name=John
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 491
Content-Type: application/json
Date: Mon, 01 Feb 2021 08:26:47 GMT
Server: gunicorn/19.9.0

{
    "args": {},
    "data": "{"name": "John"}",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "application/json, */*;q=0.5",
        "Accept-Encoding": "gzip, deflate",
        "Content-Length": "16",
        "Content-Type": "application/json",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/2.3.0",
        "X-Amzn-Trace-Id": "Root=1-6017bb47-0ca54c990178866d5847af8b"
    },
    "json": {
        "name": "John"
    },
    "origin": "89.64.75.24",
    "url": "http://httpbin.org/put"
}

cURL

General scheme is

$ curl -X PUT URL -H "accept: application/json" -d '<JSON>'

and of course - example

$ curl -X PUT "http://httpbin.org/put" -H "accept: application/json" -d '{"name": "John"}'
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "{"name": "John"}": ""
  }, 
  "headers": {
    "Accept": "application/json", 
    "Content-Length": "16", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.58.0", 
    "X-Amzn-Trace-Id": "Root=1-6017bb7c-4c3b3b95382e0ac37a4a4626"
  }, 
  "json": null, 
  "origin": "89.64.75.24", 
  "url": "http://httpbin.org/put"
}

As you noticed probably there are two main diffrences:

  • HTTPie sends JSON by default
  • HTTPie prints response's headers by default

Parts

Please continue reading

Part 2

Part 3

Leave a Reply

Your email address will not be published. Required fields are marked *