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.
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
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
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
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