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.
Show Response and Request Headers - HTTPie vs cURL
Now is time to dive deeper.
httpie (HTTPie vs cURL)
Such things are really important during the API testing or troubleshooting that's why when I've noticed it in HTTPie
I was really surprised and happy. Ease and simplicity in this fields is remarkable.
Scheme
Output Options:
--print WHAT, -p WHAT
String specifying what the output should contain:
'H' request headers
'h' response headers
Example
$ http --print=Hh DELETE httpbin.org/delete
DELETE /delete HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 0
Host: httpbin.org
User-Agent: HTTPie/2.3.0
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:29:03 GMT
Server: gunicorn/19.9.0
Pretty cool, isn't it?
cURL
The good news is that it is feasible although requires some grepping skills 🙂 Firstly, we have to add 3 flags:
-v
: to get headers-s
: to hide progres bar-o
: to write body to/dev/null
-v, --verbose
Makes curl verbose during the operation. Useful for debugging and seeing what's going on "under the hood". A line starting with '>' means "header data" sent by curl, '<' means "header data" received by curl that is hidden in normal cases, and a line starting with '*' means additional info provided by curl.
-s, --silent
Silent or quiet mode. Don't show progress meter or error messages. Makes Curl mute. It will still output the data you ask for, potentially even to the terminal/stdout unless you redirect it.
-o, --output <file>
Write output to <file> instead of stdout.
After that, rest is grepping and cutting the output. Example below
$ curl -vso /dev/null -XDELETE httpbin.org/delete 2>&1 | grep -E "(>|<)" | cut -c 3-
DELETE /delete HTTP/1.1
Host: httpbin.org
User-Agent: curl/7.58.0
Accept: */*
HTTP/1.1 200 OK
Date: Wed, 03 Mar 2021 21:58:09 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
Here HTTPie is better.
Show body of request and response - HTTPie vs cURL
Yes, after headers the time has come for body, however it will be a little bit tricky. Let's show the body of the request! It's not common.
httpie
httpie
is well prepared. There is a flag -print=Bb
which does the job
$ http --print=Bb PUT httpbin.org/put name=John
{
"name": "John"
}
{
"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-6017bc01-1e2edc604e5b7217462be769"
},
"json": {
"name": "John"
},
"origin": "1.2.3.4",
"url": "http://httpbin.org/put"
}
cURL
On the other hand, in case of cURL this is much more difficult
$ curl -X PUT "http://httpbin.org/put" -s -H "accept: application/json" -d '{"name": "John"}' --trace-ascii /dev/stdout | grep -v ==
=> Send header, 160 bytes (0xa0)
0000: PUT /put HTTP/1.1
0013: Host: httpbin.org
0026: User-Agent: curl/7.58.0
003f: accept: application/json
0059: Content-Length: 16
006d: Content-Type: application/x-www-form-urlencoded
009e:
=> Send data, 16 bytes (0x10)
0000: {"name": "John"}
<= Recv header, 17 bytes (0x11)
0000: HTTP/1.1 200 OK
<= Recv header, 37 bytes (0x25)
0000: Date: Thu, 04 Mar 2021 21:06:25 GMT
<= Recv header, 32 bytes (0x20)
0000: Content-Type: application/json
<= Recv header, 21 bytes (0x15)
0000: Content-Length: 450
<= Recv header, 24 bytes (0x18)
0000: Connection: keep-alive
<= Recv header, 25 bytes (0x19)
0000: Server: gunicorn/19.9.0
<= Recv header, 32 bytes (0x20)
0000: Access-Control-Allow-Origin: *
<= Recv header, 40 bytes (0x28)
0000: Access-Control-Allow-Credentials: true
<= Recv header, 2 bytes (0x2)
0000:
<= Recv data, 450 bytes (0x1c2)
0000: {. "args": {}, . "data": "", . "files": {}, . "form": {.
0040: "{\"name\": \"John\"}": "". }, . "headers": {. "Accept": "a
0080: pplication/json", . "Content-Length": "16", . "Content-Typ
00c0: e": "application/x-www-form-urlencoded", . "Host": "httpbin.o
0100: rg", . "User-Agent": "curl/7.58.0", . "X-Amzn-Trace-Id": "
0140: Root=1-60414bd1-3600535b32dec9125c6c2f62". }, . "json": null,
0180: . "origin": "89.64.65.223", . "url": "http://httpbin.org/put".
01c0: }.
{
"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-60414bd1-3600535b32dec9125c6c2f62"
},
"json": null,
"origin": "1.2.3.4",
"url": "http://httpbin.org/put"
}
An output isn't perfect but for troubleshooting is enough.
Download a file - HTTPie vs cURL
httpie
The -d
flag switches HTTPie
to the download mode, behaviour reminds the wget
- response headers goes to stderr
and a progress bar is visible
$ http --download https://github.com/httpie/httpie/archive/master.tar.gz
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://render.githubusercontent.com
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox
Content-Type: application/x-gzip
Date: Sun, 07 Mar 2021 19:41:34 GMT
ETag: "c5ba4b7d336d769c6235115ba5cf2106043faf8d1ce122ab21a57e92c43bd1eb"
Strict-Transport-Security: max-age=31536000
Transfer-Encoding: chunked
Vary: Authorization,Accept-Encoding
X-Content-Type-Options: nosniff
X-Frame-Options: deny
X-GitHub-Request-Id: 103E:90E6:1F4283:2B2F84:60452C6E
X-XSS-Protection: 1; mode=block
content-disposition: attachment; filename=httpie-master.tar.gz
Downloading to "httpie-master.tar.gz"
Done. 1.69 MB in 0.23530s (7.19 MB/s)
Apart from just downloading, the tool provides: setting filename, piping and measuring the download.
Piping
$ http -d https://github.com/httpie/httpie/archive/master.tar.gz | tar zxf -
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://render.githubusercontent.com
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox
Content-Type: application/x-gzip
Date: Sun, 07 Mar 2021 20:26:56 GMT
ETag: "c5ba4b7d336d769c6235115ba5cf2106043faf8d1ce122ab21a57e92c43bd1eb"
Strict-Transport-Security: max-age=31536000
Transfer-Encoding: chunked
Vary: Authorization,Accept-Encoding
X-Content-Type-Options: nosniff
X-Frame-Options: deny
X-GitHub-Request-Id: 0FD4:7761:860C1:F4E8C:6045370F
X-XSS-Protection: 1; mode=block
content-disposition: attachment; filename=httpie-master.tar.gz
Downloading to "<stdout>"
Done. 1.69 MB in 0.78814s (2.15 MB/s)
$ ls
httpie-master
and resuming ...
$ http -dco master.tar.gz https://github.com/httpie/httpie/archive/master.tar.gz
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://render.githubusercontent.com
Content-Length: 1774119
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox
Content-Type: application/x-gzip
Date: Sun, 07 Mar 2021 20:54:04 GMT
ETag: "c5ba4b7d336d769c6235115ba5cf2106043faf8d1ce122ab21a57e92c43bd1eb"
Strict-Transport-Security: max-age=31536000
Vary: Authorization,Accept-Encoding
X-Content-Type-Options: nosniff
X-Frame-Options: deny
X-GitHub-Request-Id: 0FC4:E8B0:1BBB7:984A4:60453D6C
X-XSS-Protection: 1; mode=block
content-disposition: attachment; filename=httpie-master.tar.gz
Downloading 1.69 MB to "master.tar.gz"
Done. 1.69 MB in 0.93541s (1.81 MB/s)
cURL
Similarly, here the same story. Of course, we can download the files! Moreover you can specify the output file name and resume interrupted downloading 🙂 Here some examples
$ curl -L --output master.tar.gz https://github.com/httpie/httpie/archive/master.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 121 100 121 0 0 806 0 --:--:-- --:--:-- --:--:-- 806
100 1732k 0 1732k 0 0 2267k 0 --:--:-- --:--:-- --:--:-- 10.0M
Resume is more tricky as documenation describes
-C, --continue-at <offset>
Continue/Resume a previous file transfer at the given offset. The given offset is the exact number of bytes that will be skipped, counting from the beginning of the source file before it is transferred to the destination. If used with uploads, the FTP server command SIZE will not be used by curl.
Use "-C -" to tell curl to automatically find out where/how to resume the transfer. It then uses the given output/input files to figure that out.
So, we need run following command
$ curl -C - URL
User agent - HTTPie vs cURL
Commonly used setting of User Agent (name of browser) is quite simple in both cases
httpie
$ http --print=HB PUT httpbin.org/put User-Agent:NEW_USER_AGENT name=John
PUT /put HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 16
Content-Type: application/json
Host: httpbin.org
User-Agent: NEW_USER_AGENT
{
"name": "John"
}
cURL
$ curl -A NEW_AGENT -X GET "http://httpbin.org/get"
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "NEW_AGENT",
"X-Amzn-Trace-Id": "Root=1-6017c6da-66f491f95d5cd7e607139a18"
},
"origin": "1.2.3.4",
"url": "http://httpbin.org/get"
}
Here is a draw.
Referer
Setting of the referer you can set as simple as user agent
httpie
$ http PUT httpbin.org/put Referer:https://google.com name=John
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 529
Content-Type: application/json
Date: Mon, 01 Feb 2021 09:37:39 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",
"Referer": "https://google.com",
"User-Agent": "HTTPie/2.3.0",
"X-Amzn-Trace-Id": "Root=1-6017cbe3-118c895827ab0d353ba1f877"
},
"json": {
"name": "John"
},
"origin": "1.2.3.4",
"url": "http://httpbin.org/put"
}
cURL
$ curl -e https://google.com -X GET "http://httpbin.org/get"
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"Referer": "https://google.com",
"User-Agent": "curl/7.58.0",
"X-Amzn-Trace-Id": "Root=1-6017cc4b-77b56d1570a2b86369021d5f"
},
"origin": "1.2.3.4",
"url": "http://httpbin.org/get"
}
Nothing to add.
Upload file - HTTPie vs cURL
Uploading file is not easy
httpie
For HTTPie this is just simple POST operation with parameters but we have to use @
to simulate uploading. And remember just @
cause =@
will upload content of the file as a text field.
$ http -f POST httpbin.org/post img@file.png
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 3568
Content-Type: application/json
Date: Tue, 11 May 2021 04:30:13 GMT
Server: gunicorn/19.9.0
{
"args": {},
"data": "",
"files": {
"img": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAC6CAIAAACBawdGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAiGSURBVHhe7djtjuUmEITh3E0uM5c+QdMr1FtgjDGNv95H9SNjoH0kU4q0//wACEPBgEAUDAhEwYBAFAwIRMGAQBQMCETBgEAUDAhEwYBAFAwIRMGAQBQMCETBgEAUDAhEwYBAFAwIRMGAQBQMCETBgEAUDAhEwYBAFAwIRMGAQBQMCETBgEAUDAhEwYBA8QX7798//wF8T3DBUrtygO+JLJhvlw/wGQv/D+YDfMOSf+SQduUAb7ekYEbaZQFebWHBEmlXDvBSawtmpF0W4I2uKJiRglmAd7muYIm0ywK8yKUFS6RdOcArXF0wI+2yAM93j4Il0i4L8HC3KZiRgqUAT3azgiVSMAvwTPcrmJGCpQAPdNeCJVKwHOA5blywRKrlAzzBvQuWSK98gNu7fcGMVMsHuLGHFCyTdlmAu3pawRJpV86tyG/zwZc8sGBGbm3O5eT37Aav9tiCJXJTc64iP+No8EZPLpiRa2pZT37AmRwiZ8vgUs8vmJFbZVlDXpqzRbZtpUF2dgZXeEvBjFwpSxx5Uc4hclaSyfPhYK13FSyR+5QznczPGSNDQoOFXlewRO6Tzywy1tJJTl0bBHtjwYzcpJzzZGBKDzly22Cq9xbMyO3JGTY2R07NipDVk8EMby+YkatjGTAwQY4cyvkJOWdG4YRvFCyRS2M56uhx2b+bgSPDMfKwHRz3mYIZuTGWTj2nZM9uBo7MjZDVnqDpYwVL5H5YerT3+9XPBoXvFczIzbA0tHfKalC2yLabBL++WrBELoRli2yLywCZcMN82IcLlsg9yBGyuiCd5NTN80nfLpiRe2DxZGlZ2mTzQDxZOprOCd9DwX7JPcjJ5PnKVMkeSc+elAbZ2ZlMnku+hII5cg8sW8pt8sSnhxyRZPLc0iA7JZ3kVCMl2ZDzDRTsb3IJckqyoZoxMiRna6mTnJL0kCNbqZI9OW9HwWrkEliErErOk4HVDJAJkk5yqkyV7PF5Lwq2QW5ATibPJVPITMkYGbKVHnJEskW2STJ5Xs0TULAm+aKWxlLOSTKtzDCZ004nOZXTIDtP5sYo2B75lpat5zlnyKgyZ1Tn+IdlesgRyy7ZfzK3RMH6yLdsZ5jM2cqw9hxZlbTJ5pwq2TMx90PBusm3bGSMDLFUl4Z1DvHbylTJnhxPlg6lSvZYboaCHSSf85IMOzTEby4jZNVnd4NPe+eWzm1XoGDHyedcnzFjQ+SUJJOH/s+elGRDzpbObctRsOPkWy7OsDND/Nnd7O7vJwdzqnr2LEfBjpMPuSYnzZomc8q0t42RIZaq3Q3LUbDj/FfcSo+BI8PmvshP688ZMspS2t2wHAU7rvyE/onPFtmWEi3iXX7mbqaQmRbRWLoCBTtu6xP65zkl2ZASLe51MrmdKWSmxWssXYGCHdf4fn4px5OllAUiXudnbkW2TeTHWryt51egYMe1v59f9akuLTD9jTKwP3PJ8JSs+vAiFOw4//3KTyirjawx8Y1+VJndPdNtza8+vAgFG9L+hH61mmWmvFeGlBGymhOhOr/68CIUbMjuJ/Qbyqxx/qUyoUyD7MyZqzq5+vAiFGyI/4TVrygbqol25nVytpoeciRnlurM6sOLULBRu1/Rb9hKnDMvkrNl+snBMmdsjao+vAgFG9XzFf2eRqY7M1/OSo6S46HJqg8vQsFG+a+49SFlT0r1oWWio5Nl/1YGyHH/5/SY6sPrULATer7l1h55bpni0EzZ3MiY6gT/cFay6sPrULAT/Le0CFlN8WTJclLnNNm2m2G7Q/yGgYj26hUo2DntLyqrKSXZYBnWniOrPTlp4qgei1/XgYKd5j+qZeu5pSQbLAPaE2R1N1NMH9i2+HUdKNgM/rtaqg9zqmSPpZOcShGyWk2Q0Pl5eJl7oGCTyNeVlBuqZI9ll+xPEbJaTZDQV/jhktugYPPIN87Jtp57sienSvZYMnnus0zcS/3kMrdBwaaSz2zxZCmlSvZYhKx2ZrG4V/vJkjuhYLPJx7Z4spRSJXtyGku7WS/u7XGTp6JgMfznz8nk+YJcJeg3BI0NQMHC+EuQk8nziNxB0O8JGhuAgoXxl0Cyu+FM7iPuhwWNDUDBwvhLUE3ntv7cSuhvi5s8GwULI5fA/+lzlBzPuZXo3xY6fCoKFsZfArsH8sSnhxzZyh3E/SSZnHJvFCxS9R74hz4NsrMnF4r7JTI55fYoWKTGbZClHE+WJP17Vgr6ATLW8gQULFj7QvjV/pRkg89iEa/2M3MegoIFa1wLWerJLtlvWWb6e2VgznNQsHhyOcZyiJxNWWPiG/0on6ehYPHkiozlKDmeEm3W62SOzwNRsCXkopzJIXI2Jc75t/gJkseiYAvJpUlpkJ2SfnIwZyKZnHKUHPd5OAp2b3LbJJ3kVM4UMjPlKDme8woU7Ank5pXZJft9zpBRKYfI2ZwXoWCPIhexzC7ZbxkgEyz95GDO61CwB5JLWU2bbE7pJKd8esgRn5eiYI8lF7SRKtljqZI9ZXrIEcl7UbDnk8u6OD3kiM/bUbAXkbsbmk5yKuczKNgbyW2em35y0PIxFOwD5IoP5Cg5nvM9FAyO9MGnk5zK+SoKhhqph88W2ebzYRQM26QnA/k8CoY90pnO4BcFQwcpTztwKBiOkC6Vwd8oGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggGBKBgQiIIBgSgYEIiCAYEoGBCIggFhfn7+B8kgougj0FROAAAAAElFTkSuQmCC"
},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "2457",
"Content-Type": "multipart/form-data; boundary=3068a0c706d841a2b12475ff3abc0e7d",
"Host": "httpbin.org",
"User-Agent": "HTTPie/2.3.0",
"X-Amzn-Trace-Id": "Root=1-609a0855-66a917cc726c48973c6ec823"
},
"json": null,
"origin": "1.2.3.4",
"url": "http://httpbin.org/post"
}
When you add =@
for binary data such error appears
$ http -f POST httpbin.org/post img=@file.png
usage: http [--json] [--form] [--multipart] [--boundary BOUNDARY] [--compress]
[--pretty {all,colors,format,none}] [--style STYLE] [--unsorted]
[--sorted] [--format-options FORMAT_OPTIONS] [--print WHAT]
[--headers] [--body] [--verbose] [--all] [--history-print WHAT]
[--stream] [--output FILE] [--download] [--continue] [--quiet]
[--session SESSION_NAME_OR_PATH | --session-read-only SESSION_NAME_OR_PATH]
[--auth USER[:PASS]] [--auth-type {basic,digest}] [--ignore-netrc]
[--offline] [--proxy PROTOCOL:PROXY_URL] [--follow]
[--max-redirects MAX_REDIRECTS] [--max-headers MAX_HEADERS]
[--timeout SECONDS] [--check-status] [--path-as-is] [--chunked]
[--verify VERIFY] [--ssl {ssl2.3,tls1,tls1.1,tls1.2}]
[--ciphers CIPHERS] [--cert CERT] [--cert-key CERT_KEY]
[--ignore-stdin] [--help] [--version] [--traceback]
[--default-scheme DEFAULT_SCHEME] [--debug]
[METHOD] URL [REQUEST_ITEM [REQUEST_ITEM ...]]
http: error: "img=@file.png": cannot embed the content of "file.png", not a UTF8 or ASCII-encoded text file
cURL
cURL has dedicated flag for uploading -T
Schema:
curl -T file_to_upload URL
Follow redirects
Redirecting policy is similar in both tools. By default HTTPie
redirects are not followed.
httpie
Default you get message (You should be redirected automatically to target
)
$ http http://localhost/redirect/3
HTTP/1.1 302 FOUND
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 247
Content-Type: text/html; charset=utf-8
Date: Mon, 01 Feb 2021 20:05:30 GMT
Location: /relative-redirect/2
Server: gunicorn/19.9.0
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/relative-redirect/2">/relative-redirect/2</a>. If not click the link.
with redirection (flog --follow
)
$ http --follow http://localhost/redirect/3
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 254
Content-Type: application/json
Date: Mon, 01 Feb 2021 20:06:42 GMT
Server: gunicorn/19.9.0
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
"Host": "localhost",
"User-Agent": "HTTPie/2.3.0"
},
"origin": "172.17.0.1",
"url": "http://localhost/get"
}
cURL
Default behaviour is exactly the same
$ curl -X GET "http://localhost/redirect/3" -H "accept: text/html"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/relative-redirect/2">/relative-redirect/2</a>. If not click the link.
with redirection (flag -L
)
$ curl -X GET -L "http://localhost/redirect/3" -H "accept: text/html"
{
"args": {},
"headers": {
"Accept": "text/html",
"Host": "localhost",
"User-Agent": "curl/7.58.0"
},
"origin": "172.17.0.1",
"url": "http://localhost/get"
}
Set Custom Headers
How to set custom header? Both tools allow it.
httpie
Schema
Header:Value
And example:
$ http --print=H http://localhost/headers User-Agent:dolega.dev/1.0 'Cookie:var1=yes;var2=no'
GET /headers HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: var1=yes;var2=no
Host: localhost
User-Agent: dolega.dev/1.0
cURL
Only example
$ curl -X GET "http://localhost/headers" -H "accept: application/json" -H "User-Agent: dolega.dev/1.0" -H "Cookie: var1=yes;var2=no"
{
"headers": {
"Accept": "application/json",
"Cookie": "var1=yes;var2=no",
"Host": "localhost",
"User-Agent": "dolega.dev/1.0"
}
}
As you can see httpie
syntax is a little bit better (more readable) but this is not big thing.
JSON data
Now is a time to look closely to JSON and how these tools deal with it.
httpie
HTTPie can create non-string fields which is pretty cool. Below example without this feature
$ http PUT httpbin.org/put name=Mark age=29 isOnline=false protocols='["http", "https"]'
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 671
Content-Type: application/json
Date: Mon, 10 May 2021 04:41:58 GMT
Server: gunicorn/19.9.0
{
"args": {},
"data": "{\"name\": \"Mark\", \"age\": \"29\", \"isOnline\": \"false\", \"protocols\": \"[\\\"http\\\", \\\"https\\\"]\"}",
"files": {},
"form": {},
"headers": {
"Accept": "application/json, */*;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "88",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "HTTPie/2.3.0",
"X-Amzn-Trace-Id": "Root=1-6098b996-6e350bd27d924c73774f751a"
},
"json": {
"age": "29",
"isOnline": "false",
"name": "Mark",
"protocols": "[\"http\", \"https\"]"
},
"origin": "1.2.3.4",
"url": "http://httpbin.org/put"
}
However, it's not rocket science to enable that just one character: :
$ http PUT httpbin.org/put name=Mark age:=29 isOnline:=false protocols:='["http", "https"]'
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 660
Content-Type: application/json
Date: Mon, 10 May 2021 04:46:41 GMT
Server: gunicorn/19.9.0
{
"args": {},
"data": "{\"name\": \"Mark\", \"age\": 29, \"isOnline\": false, \"protocols\": [\"http\", \"https\"]}",
"files": {},
"form": {},
"headers": {
"Accept": "application/json, */*;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "78",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "HTTPie/2.3.0",
"X-Amzn-Trace-Id": "Root=1-6098bab1-4bccb0a5664a5e0931bf85ea"
},
"json": {
"age": 29,
"isOnline": false,
"name": "Mark",
"protocols": [
"http",
"https"
]
},
"origin": "1.2.3.4",
"url": "http://httpbin.org/put"
}
You can send JSON via redirect output
$ echo '{"hello": "world"}' | http POST localhost/post
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 458
Content-Type: application/json
Date: Mon, 01 Feb 2021 20:24:51 GMT
Server: gunicorn/19.9.0
{
"args": {},
"data": "{\"hello\": \"world\"}\n",
"files": {},
"form": {},
"headers": {
"Accept": "application/json, */*;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
"Content-Length": "19",
"Content-Type": "application/json",
"Host": "localhost",
"User-Agent": "HTTPie/2.3.0"
},
"json": {
"hello": "world"
},
"origin": "172.17.0.1",
"url": "http://localhost/post"
}
and from file
$ http POST localhost/post < /tmp/tmp.json
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 464
Content-Type: application/json
Date: Mon, 01 Feb 2021 20:26:41 GMT
Server: gunicorn/19.9.0
{
"args": {},
"data": "{\n\t\"key1\": \"value1\"\n}\n",
"files": {},
"form": {},
"headers": {
"Accept": "application/json, */*;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
"Content-Length": "22",
"Content-Type": "application/json",
"Host": "localhost",
"User-Agent": "HTTPie/2.3.0"
},
"json": {
"key1": "value1"
},
"origin": "172.17.0.1",
"url": "http://localhost/post"
}
The ways you can send JSON to the HTTPie is really awsome.
cURL
On the other hand cURL force you to add -H "Content-Type: application/json"
header flag any time you want to end JSON to the endpoint. I know this is not losing control over the communication with endpoints but still it's slightly annoying.
$ curl -H "Content-Type: application/json" -X POST -d '{"key1":"val1"}' http://localhost/post
{
"args": {},
"data": "{\"key1\":\"val1\"}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "15",
"Content-Type": "application/json",
"Host": "localhost",
"User-Agent": "curl/7.58.0"
},
"json": {
"key1": "val1"
},
"origin": "172.17.0.1",
"url": "http://localhost/post"
}
from file
$ curl -H "Content-Type: application/json" -X POST -d @/tmp/tmp.json http://localhost/post
{
"args": {},
"data": "{\t\"key1\": \"value1\"}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "19",
"Content-Type": "application/json",
"Host": "localhost",
"User-Agent": "curl/7.58.0"
},
"json": {
"key1": "value1"
},
"origin": "172.17.0.1",
"url": "http://localhost/post"
}
I haven't find the way how to sedn raw JSON to the server. Maybe I omit something but here wins HTTPie.
Parts
Please continue reading