Compare commits

..

30 Commits
3.0.0 ... 3.0.2

Author SHA1 Message Date
559134de0a Release 3.0.2 (#1281) 2022-01-24 21:20:17 +03:00
813e8864a1 Dont apply default options on the httpie command (#1280)
* Mark tests with requires_installation

* Dont apply default options on the httpie command

* lint
2022-01-24 10:13:47 -08:00
45fcd746d7 docs: format the benchmark docs 2022-01-24 18:20:03 +03:00
d5e3611e85 fix lint errors 2022-01-24 18:17:55 +03:00
378a1f513e Document the pyOpenSSL option 2022-01-24 18:17:55 +03:00
df6843b15a docs: add --{local, target}-{repo, branch} / format 2022-01-24 18:17:55 +03:00
640901146f docs: document the --fresh option 2022-01-24 18:17:55 +03:00
6b5d96da72 Describe the usage for benchmarks 2022-01-24 18:17:55 +03:00
97bd9c2a89 docs: add requirements 2022-01-24 18:17:55 +03:00
708608e1d4 docs: mention about the runners 2022-01-24 18:17:55 +03:00
d56a1f216e docs: give a brief description 2022-01-24 18:17:55 +03:00
738a6bea57 docs: fix the title to benchmarking infrastructure 2022-01-24 18:17:55 +03:00
ec521c461b docs: add initial benchmark docs 2022-01-24 18:17:55 +03:00
212000199e docs: fix the nested json example (#1278) 2022-01-24 18:00:54 +03:00
700dbeddb0 Typos 2022-01-24 01:51:53 +01:00
30a4d29f77 Update CHANGELOG.md 2022-01-23 15:15:16 -08:00
aedcad7e2a Update CHANGELOG.md 2022-01-23 15:14:31 -08:00
202f59e04a Tweak nested JSON docs 2022-01-23 18:36:18 +01:00
ba0c1ab258 Tweak auth docs 2022-01-23 17:24:29 +01:00
217cf8ddae Document auto-stream 2022-01-23 17:17:58 +01:00
859e442083 Docs 2022-01-23 16:59:07 +01:00
4e59bbfae6 Docs 2022-01-23 16:52:31 +01:00
caa8fb9058 Tweak response meta docs
- expand response meta section
- add examples
- interlink sections
2022-01-23 14:35:20 +01:00
2797b7244c Update cached brew formula 2022-01-23 14:11:09 +01:00
3b441fa57e 3.0.1 2022-01-23 13:58:01 +01:00
c815e21ef9 Fix time elapsed (#1277)
* Show the actual time elapsed; add docs

* `requests.Response._headers_parsed_at` → `requests.Response._httpie_headers_parsed_at`

* Add `ELAPSED_TIME_LABEL` constant

* Tweak docs

* Tweak docs

* Allow multiple blank lines in Markdown files

* Add rudimentary tests for --meta with different --style’s

* Cleanup tests

* Cleanup tests

* Cleanup tests
2022-01-23 04:52:38 -08:00
8a03b7a824 Update the contributors (#1275) 2022-01-22 17:25:00 -08:00
b3f29c8d1e Display the latest docs (#1274) 2022-01-21 22:15:55 +03:00
a88e44c284 Fix make brew-test
`brew test` is only usable for non-from-source installations.
2022-01-21 19:48:35 +01:00
c97fe64a37 Update brew with 3.0 (#1273) 2022-01-21 21:08:33 +03:00
21 changed files with 617 additions and 119 deletions

View File

@ -3,8 +3,22 @@
This document records all notable changes to [HTTPie](https://httpie.io). This document records all notable changes to [HTTPie](https://httpie.io).
This project adheres to [Semantic Versioning](https://semver.org/). This project adheres to [Semantic Versioning](https://semver.org/).
## [3.0.2](https://github.com/httpie/httpie/compare/3.0.0...3.0.1) (2022-01-23)
[Whats new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
- Fixed usage of `httpie` when there is a presence of a config with `default_options` ([#1280](https://github.com/httpie/httpie/pull/1280)).
## [3.0.1](https://github.com/httpie/httpie/compare/3.0.0...3.0.1) (2022-01-23)
[Whats new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
- Changed the value shown as time elapsed from time-to-read-headers to total exchange time ([#1277](https://github.com/httpie/httpie/issues/1277))
## [3.0.0](https://github.com/httpie/httpie/compare/2.6.0...3.0.0) (2022-01-21) ## [3.0.0](https://github.com/httpie/httpie/compare/2.6.0...3.0.0) (2022-01-21)
[Whats new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
- Dropped support for Python 3.6. ([#1177](https://github.com/httpie/httpie/issues/1177)) - Dropped support for Python 3.6. ([#1177](https://github.com/httpie/httpie/issues/1177))
- Improved startup time by 40%. ([#1211](https://github.com/httpie/httpie/pull/1211)) - Improved startup time by 40%. ([#1211](https://github.com/httpie/httpie/pull/1211))
- Added support for nested JSON syntax. ([#1169](https://github.com/httpie/httpie/issues/1169)) - Added support for nested JSON syntax. ([#1169](https://github.com/httpie/httpie/issues/1169))
@ -28,7 +42,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## [2.6.0](https://github.com/httpie/httpie/compare/2.5.0...2.6.0) (2021-10-14) ## [2.6.0](https://github.com/httpie/httpie/compare/2.5.0...2.6.0) (2021-10-14)
[Whats new in HTTPie 2.6.0 →](https://httpie.io/blog/httpie-2.6.0) [Whats new in HTTPie for Terminal 2.6.0 →](https://httpie.io/blog/httpie-2.6.0)
- Added support for formatting & coloring of JSON bodies preceded by non-JSON data (e.g., an XXSI prefix). ([#1130](https://github.com/httpie/httpie/issues/1130)) - Added support for formatting & coloring of JSON bodies preceded by non-JSON data (e.g., an XXSI prefix). ([#1130](https://github.com/httpie/httpie/issues/1130))
- Added charset auto-detection when `Content-Type` doesnt include it. ([#1110](https://github.com/httpie/httpie/issues/1110), [#1168](https://github.com/httpie/httpie/issues/1168)) - Added charset auto-detection when `Content-Type` doesnt include it. ([#1110](https://github.com/httpie/httpie/issues/1110), [#1168](https://github.com/httpie/httpie/issues/1168))
@ -40,7 +54,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## [2.5.0](https://github.com/httpie/httpie/compare/2.4.0...2.5.0) (2021-09-06) ## [2.5.0](https://github.com/httpie/httpie/compare/2.4.0...2.5.0) (2021-09-06)
[Whats new in HTTPie 2.5.0 →](https://httpie.io/blog/httpie-2.5.0) [Whats new in HTTPie for Terminal 2.5.0 →](https://httpie.io/blog/httpie-2.5.0)
- Added `--raw` to allow specifying the raw request body without extra processing as - Added `--raw` to allow specifying the raw request body without extra processing as
an alternative to `stdin`. ([#534](https://github.com/httpie/httpie/issues/534)) an alternative to `stdin`. ([#534](https://github.com/httpie/httpie/issues/534))

View File

@ -206,7 +206,8 @@ brew-test:
- brew install --build-from-source ./docs/packaging/brew/httpie.rb - brew install --build-from-source ./docs/packaging/brew/httpie.rb
@echo $(H1)Verifying…$(H1END) @echo $(H1)Verifying…$(H1END)
brew test httpie http --version
https --version
@echo $(H1)Auditing…$(H1END) @echo $(H1)Auditing…$(H1END)
brew audit --strict httpie brew audit --strict httpie

View File

@ -538,7 +538,7 @@ and URL parameters. This is a very practical way of constructing
HTTP requests from scratch on the CLI. HTTP requests from scratch on the CLI.
Each *request item* is simply a key/value pair separated with the following Each *request item* is simply a key/value pair separated with the following
characters: `:` (headers), `=` (data field, e.g JSON, Form), `:=` (raw data field) characters: `:` (headers), `=` (data field, e.g., JSON, form), `:=` (raw data field)
`==` (query parameters), `@` (file upload). `==` (query parameters), `@` (file upload).
```bash ```bash
@ -550,9 +550,9 @@ $ http PUT pie.dev/put \
``` ```
| Item Type | Description | | Item Type | Description |
| -----------------------------------------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |-------------------------------------------------------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| HTTP Headers `Name:Value` | Arbitrary HTTP header, e.g. `X-API-Token:123` | | HTTP Headers `Name:Value` | Arbitrary HTTP header, e.g. `X-API-Token:123` |
| URL parameters `name==value` | Appends the given name/value pair as a querystring parameter to the URL. The `==` separator is used. | | URL parameters `name==value` | Appends the given name/value pair as a querystring parameter to the URL. The `==` separator is used. |
| Data Fields `field=value` | Request data fields to be serialized as a JSON object (default), to be form-encoded (with `--form, -f`), or to be serialized as `multipart/form-data` (with `--multipart`) | | Data Fields `field=value` | Request data fields to be serialized as a JSON object (default), to be form-encoded (with `--form, -f`), or to be serialized as `multipart/form-data` (with `--multipart`) |
| Raw JSON fields `field:=json` | Useful when sending JSON and one or more fields need to be a `Boolean`, `Number`, nested `Object`, or an `Array`, e.g., `meals:='["ham","spam"]'` or `pies:=[1,2,3]` (note the quotes) | | Raw JSON fields `field:=json` | Useful when sending JSON and one or more fields need to be a `Boolean`, `Number`, nested `Object`, or an `Array`, e.g., `meals:='["ham","spam"]'` or `pies:=[1,2,3]` (note the quotes) |
| File upload fields `field@/dir/file`, `field@file;type=mime` | Only available with `--form`, `-f` and `--multipart`. For example `screenshot@~/Pictures/img.png`, or `'cv@cv.txt;type=text/markdown'`. With `--form`, the presence of a file field results in a `--multipart` request | | File upload fields `field@/dir/file`, `field@file;type=mime` | Only available with `--form`, `-f` and `--multipart`. For example `screenshot@~/Pictures/img.png`, or `'cv@cv.txt;type=text/markdown'`. With `--form`, the presence of a file field results in a `--multipart` request |
@ -570,7 +570,7 @@ to pass the desired value from a file.
$ http POST pie.dev/post \ $ http POST pie.dev/post \
X-Data:@files/text.txt # Read a header from a file X-Data:@files/text.txt # Read a header from a file
token==@files/text.txt # Read a query parameter from a file token==@files/text.txt # Read a query parameter from a file
name=@files/text.txt # Read a data field's value from a file name=@files/text.txt # Read a data fields value from a file
bookmarks:=@files/data.json # Embed a JSON object from a file bookmarks:=@files/data.json # Embed a JSON object from a file
``` ```
@ -681,11 +681,44 @@ Other JSON types, however, are not allowed with `--form` or `--multipart`.
"about": { "about": {
"mission": "Make APIs simple and intuitive", "mission": "Make APIs simple and intuitive",
"homepage": "httpie.io", "homepage": "httpie.io",
} "stars": 54000
},
"apps": [
"Terminal",
"Desktop",
"Web",
"Mobile"
]
}
}
```
#### Introduction
Lets start with a simple example, and build a simple search query:
```bash
$ http --offline --print=B pie.dev/post \
category=tools \
search[type]=id \
search[id]:=1
```
In the example above, the `search[type]` is an instruction for creating an object called `search`, and setting the `type` field of it to the given value (`"id"`).
Also note that, just as the regular syntax, you can use the `:=` operator to directly pass raw JSON values (e.g, numbers in the case above).
```json
{
"category": "tools",
"search": {
"id": 1,
"type": "id"
}
} }
``` ```
Building arrays is also possible, through `[]` suffix (an append operation). This tells HTTPie to create an array in the given path (if there is not one already), and append the given value to that array. Building arrays is also possible, through `[]` suffix (an append operation). This tells HTTPie to create an array in the given path (if there is not one already), and append the given value to that array.
```bash ```bash
$ http --offline --print=B pie.dev/post \ $ http --offline --print=B pie.dev/post \
@ -696,7 +729,7 @@ $ http --offline --print=B pie.dev/post \
``` ```
```json ```json
{ {
"category": "tools", "category": "tools",
"search": { "search": {
"keywords": [ "keywords": [
@ -739,7 +772,7 @@ $ http --offline --print=B pie.dev/post \
category=tools \ category=tools \
search[type]=platforms \ search[type]=platforms \
search[platforms][]=Terminal \ search[platforms][]=Terminal \
search[platforms][1]=Desktop \ search[platforms][1]=Desktop \
search[platforms][3]=Mobile search[platforms][3]=Mobile
``` ```
@ -807,7 +840,7 @@ $ http --offline --print=B pie.dev/post \
```bash ```bash
$ http --offline --print=B pie.dev/post \ $ http --offline --print=B pie.dev/post \
'foo\[bar\]:=1' \ 'foo\[bar\]:=1' \
'baz[\[]:=2' \ 'baz[\[]:=2' \
'baz[\]]:=3' 'baz[\]]:=3'
``` ```
@ -845,7 +878,7 @@ $ http --offline --print=B pie.dev/post \
'object[\1]=stringified' \ 'object[\1]=stringified' \
'object[\100]=same' \ 'object[\100]=same' \
'array[1]=indexified' 'array[1]=indexified'
``` ```
```json ```json
{ {
@ -903,8 +936,8 @@ You can follow to given instruction (adding a `]`) and repair your expression.
``` ```
```json ```json
{ {
"user": { "user": {
"name": "string" "name": "string"
} }
} }
@ -1010,7 +1043,7 @@ world
```bash ```bash
$ http --form --multipart --offline example.org hello=world Content-Type:multipart/letter $ http --form --multipart --offline example.org hello=world Content-Type:multipart/letter
``` ```
```http ```http
POST / HTTP/1.1 POST / HTTP/1.1
@ -1104,7 +1137,7 @@ To send a header with an empty value, use `Header;`, with a semicolon:
```bash ```bash
http --offline example.org Numbers:one,two http --offline example.org Numbers:one,two
``` ```
```http ```http
GET / HTTP/1.1 GET / HTTP/1.1
@ -1174,7 +1207,7 @@ $ nc pie.dev 80 < request.http
Cookie: sessionid=foo Cookie: sessionid=foo
Host: pie.dev Host: pie.dev
User-Agent: HTTPie/0.9.9 User-Agent: HTTPie/0.9.9
``` ```
Send multiple cookies (note: the header is quoted to prevent the shell from interpreting the `;`): Send multiple cookies (note: the header is quoted to prevent the shell from interpreting the `;`):
@ -1247,12 +1280,18 @@ https -A bearer -a token pie.dev/bearer
For example: For example:
```bash ```bash
$ cat ~/.netrc
machine pie.dev
login httpie login httpie
password test password test
``` ```
```bash
$ http pie.dev/basic-auth/httpie/test
HTTP/1.1 200 OK HTTP/1.1 200 OK
[...] [...]
```
This can be disabled with the `--ignore-netrc` option: This can be disabled with the `--ignore-netrc` option:
```bash ```bash
@ -1300,6 +1339,8 @@ Here are a few picks:
With `307 Temporary Redirect` and `308 Permanent Redirect`, the method and the body of the original request With `307 Temporary Redirect` and `308 Permanent Redirect`, the method and the body of the original request
are reused to perform the redirected request. Otherwise, a body-less `GET` request is performed. are reused to perform the redirected request. Otherwise, a body-less `GET` request is performed.
### Showing intermediary redirect responses
If you wish to see the intermediary requests/responses, If you wish to see the intermediary requests/responses,
then use the `--all` option: then use the `--all` option:
@ -1438,28 +1479,28 @@ By default, HTTPie only outputs the final response and the whole response
| `b` | response body | | `b` | response body |
| `m` | [response meta](#response-meta) | | `m` | [response meta](#response-meta) |
Print request and response headers: Print request and response headers:
```bash ```bash
$ http --print=Hh PUT pie.dev/put hello=world $ http --print=Hh PUT pie.dev/put hello=world
``` ```
#### Response meta #### Response meta
`--verbose` can often be useful for debugging the request and generating documentation examples: The response metadata section currently includes the total time elapsed. Its the number of seconds between opening the network connection and downloading the last byte of response the body.
To _only_ show the response metadata, use `--meta, -m` (analogically to `--headers, -h` and `--body, -b`): To _only_ show the response metadata, use `--meta, -m` (analogically to `--headers, -h` and `--body, -b`):
```bash ```bash
$ http --meta pie.dev/delay/1 $ http --meta pie.dev/delay/1
Content-Type: application/json ```
Host: pie.dev
User-Agent: HTTPie/0.2.7dev ```console
Elapsed time: 1.099171542s
{ ```
"hello": "world"
} The [extra verbose `-vv` output](#extra-verbose-output) includes the meta section by default. You can also show it in combination with other parts of the exchange via [`--print=m`](#what-parts-of-the-http-exchange-should-be-printed). For example, here we print it together with the response headers:
```bash ```bash
$ http --print=hm pie.dev/get $ http --print=hm pie.dev/get
@ -1467,6 +1508,40 @@ Print request and response headers:
```http ```http
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Type: application/json
```
Please note that it also includes time spent on formatting the output, which adds a small penalty. Also, if the body is not part of the output, [we dont spend time downloading it](#conditional-body-download).
If you [use `--style` with one of the Pie themes](#colors-and-formatting), youll see the time information color-coded (green/yellow/orange/red) based on how long the exchange took.
### Verbose output
`--verbose` can often be useful for debugging the request and generating documentation examples:
```bash
$ http --verbose PUT pie.dev/put hello=world
PUT /put HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Host: pie.dev
User-Agent: HTTPie/0.2.7dev
{
"hello": "world"
}
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 477
Content-Type: application/json
Date: Sun, 05 Aug 2012 00:25:23 GMT
Server: gunicorn/0.13.4
{ {
[] []
} }
@ -1496,9 +1571,9 @@ Server: gunicorn/0.13.4
```bash ```bash
# There will be no output, even in case of an unexpected response status code: # There will be no output, even in case of an unexpected response status code:
$ http -qq --check-status pie.dev/post enjoy='the silence without warnings' $ http -qq --check-status pie.dev/post enjoy='the silence without warnings'
$ http -qq --check-status pie.dev/post enjoy='the silence without warnings' ```
### Viewing intermediary requests/responses
To see all the HTTP communication, i.e. the final request/response as well as any possible intermediary requests/responses, use the `--all` option. To see all the HTTP communication, i.e. the final request/response as well as any possible intermediary requests/responses, use the `--all` option.
The intermediary HTTP communication include followed redirects (with `--follow`), the first unauthorized request when HTTP digest authentication is used (`--auth=digest`), etc. The intermediary HTTP communication include followed redirects (with `--follow`), the first unauthorized request when HTTP digest authentication is used (`--auth=digest`), etc.
@ -1628,7 +1703,7 @@ On macOS, you can send the contents of the clipboard with `pbpaste`:
### Request data from a filename ### Request data from a filename
An alternative to redirected `stdin` is specifying a filename (as `@/path/to/file`) whose content is used as if it came from `stdin`.
It has the advantage that the `Content-Type` header is automatically set to the appropriate value based on the filename extension. It has the advantage that the `Content-Type` header is automatically set to the appropriate value based on the filename extension.
For example, the following request sends the verbatim contents of that XML file with `Content-Type: application/xml`: For example, the following request sends the verbatim contents of that XML file with `Content-Type: application/xml`:
@ -1711,21 +1786,21 @@ Syntax highlighting is applied to HTTP headers and bodies (where it makes sense)
```bash ```bash
$ http --response-mime=text/yaml pie.dev/get $ http --response-mime=text/yaml pie.dev/get
$ http --response-mime=text/yaml pie.dev/get ```
```
Formatting has the following effects:
Formatting has the following effects:
- HTTP headers are sorted by name.
- HTTP headers are sorted by name. - JSON data is indented, sorted by keys, and unicode escapes are converted
- JSON data is indented, sorted by keys, and unicode escapes are converted to the characters they represent.
to the characters they represent. - XML and XHTML data is indented.
- XML and XHTML data is indented.
Please note that sometimes there might be changes made by formatters on the actual response body (e.g.,
collapsing empty tags on XML) but the end result will always be semantically indistinguishable. Some of collapsing empty tags on XML) but the end result will always be semantically indistinguishable. Some of
these formatting changes can be configured more granularly through [format options](#format-options). these formatting changes can be configured more granularly through [format options](#format-options).
### Format options ### Format options
### Format options
The `--format-options=opt1:value,opt2:value` option allows you to control how the output should be formatted The `--format-options=opt1:value,opt2:value` option allows you to control how the output should be formatted
when formatting is applied. The following options are available: when formatting is applied. The following options are available:
@ -1744,7 +1819,7 @@ Formatting has the following effects:
```bash ```bash
$ http --format-options headers.sort:false,json.sort_keys:false,json.indent:2 pie.dev/get $ http --format-options headers.sort:false,json.sort_keys:false,json.indent:2 pie.dev/get
``` ```
```
There are also two shortcuts that allow you to quickly disable and re-enable There are also two shortcuts that allow you to quickly disable and re-enable
sorting-related format options (currently it means JSON keys and headers): sorting-related format options (currently it means JSON keys and headers):
`--unsorted` and `--sorted`. `--unsorted` and `--sorted`.
@ -1754,7 +1829,7 @@ The `--format-options=opt1:value,opt2:value` option allows you to control how th
### Redirected output ### Redirected output
HTTPie uses a different set of defaults for redirected output than for [terminal output](#terminal-output). HTTPie uses a different set of defaults for redirected output than for [terminal output](#terminal-output).
HTTPie uses a different set of defaults for redirected output than for [terminal output](#terminal-output). The differences being:
- Formatting and colors arent applied (unless `--pretty` is specified). - Formatting and colors arent applied (unless `--pretty` is specified).
- Only the response body is printed (unless one of the [output options](#output-options) is set). - Only the response body is printed (unless one of the [output options](#output-options) is set).
@ -1903,7 +1978,7 @@ $ http -dco file.zip example.org/file
Streamed output by small chunks à la `tail -f`: Streamed output by small chunks à la `tail -f`:
# Send each new line (JSON object) to another URL as soon as it arrives from a streaming API: ```bash
# Send each new line (JSON object) to another URL as soon as it arrives from a streaming API: # Send each new line (JSON object) to another URL as soon as it arrives from a streaming API:
$ http --stream pie.dev/stream/3 | while read line; do echo "$line" | http pie.dev/post ; done $ http --stream pie.dev/stream/3 | while read line; do echo "$line" | http pie.dev/post ; done
``` ```
@ -1918,6 +1993,8 @@ You can use the `--stream, -S` flag to make two things happen:
```bash ```bash
# Create a new session: # Create a new session:
$ http --session=./session.json pie.dev/headers API-Token:123 $ http --session=./session.json pie.dev/headers API-Token:123
```
```bash ```bash
# Inspect / edit the generated session file: # Inspect / edit the generated session file:
$ cat session.json $ cat session.json
@ -1982,7 +2059,7 @@ $ http --session=user2 -a user2:password pie.dev/get X-Bar:Foo
``` ```
When creating anonymous sessions, please remember to always include at least one `/`, even if the session files is located in the current directory (i.e. `--session=./session.json` instead of just `--session=session.json`), otherwise HTTPie assumes a named session instead. When creating anonymous sessions, please remember to always include at least one `/`, even if the session files is located in the current directory (i.e. `--session=./session.json` instead of just `--session=session.json`), otherwise HTTPie assumes a named session instead.
When creating anonymous sessions, please remember to always include at least one `/`, even if the session files is located in the current directory (i.e. `--session=./session.json` instead of just `--session=session.json`), otherwise HTTPie assumes a named session instead.
### Readonly session ### Readonly session
To use the original session file without updating it from the request/response exchange after it has been created, specify the session name via `--session-read-only=SESSION_NAME_OR_PATH` instead. To use the original session file without updating it from the request/response exchange after it has been created, specify the session name via `--session-read-only=SESSION_NAME_OR_PATH` instead.
@ -2078,7 +2155,7 @@ If the server expires an existing cookie, it will also be removed from the sessi
An `Array` (by default empty) of default options that should be applied to every invocation of HTTPie. An `Array` (by default empty) of default options that should be applied to every invocation of HTTPie.
For instance, you can use this config option to change your default color theme:
```bash ```bash
$ cat ~/.config/httpie/config.json $ cat ~/.config/httpie/config.json
@ -2120,12 +2197,12 @@ $ cat ~/.config/httpie/config.json
2) echo 'Request timed out!' ;; 2) echo 'Request timed out!' ;;
3) echo 'Unexpected HTTP 3xx Redirection!' ;; 3) echo 'Unexpected HTTP 3xx Redirection!' ;;
4) echo 'HTTP 4xx Client Error!' ;; 4) echo 'HTTP 4xx Client Error!' ;;
4) echo 'HTTP 4xx Client Error!' ;; 5) echo 'HTTP 5xx Server Error!' ;;
6) echo 'Exceeded --max-redirects=<n> redirects!' ;; 6) echo 'Exceeded --max-redirects=<n> redirects!' ;;
*) echo 'Other Error!' ;; *) echo 'Other Error!' ;;
esac esac
fi fi
fi ```
### Best practices ### Best practices
@ -2166,9 +2243,10 @@ And since theres neither data nor `EOF`, it will get stuck. So unless your
By default, the plugins (and their missing dependencies) will be stored under the configuration directory, By default, the plugins (and their missing dependencies) will be stored under the configuration directory,
but this can be modified through `plugins_dir` variable on the config. but this can be modified through `plugins_dir` variable on the config.
#### `httpie plugins install` #### `httpie plugins install`
For installing plugins from [PyPI](https://pypi.org/) or from local paths, `httpie plugins install` For installing plugins from [PyPI](https://pypi.org/) or from local paths, `httpie plugins install`
can be used.
```bash ```bash
$ httpie plugins install httpie-plugin $ httpie plugins install httpie-plugin
@ -2185,11 +2263,11 @@ This command is currently in beta.
```bash ```bash
$ httpie plugins list $ httpie plugins list
$ httpie plugins list httpie_plugin (1.0.2)
httpie_plugin (1.0.2) httpie_plugin (httpie.plugins.auth.v1)
httpie_plugin_2 (1.0.6) httpie_plugin_2 (1.0.6)
httpie_plugin_2 (httpie.plugins.auth.v1) httpie_plugin_2 (httpie.plugins.auth.v1)
httpie_plugin_2 (httpie.plugins.auth.v1) httpie_converter (1.0.0)
httpie_iterm_converter (httpie.plugins.converter.v1) httpie_iterm_converter (httpie.plugins.converter.v1)
httpie_konsole_konverter (httpie.plugins.converter.v1) httpie_konsole_konverter (httpie.plugins.converter.v1)
``` ```
@ -2232,7 +2310,7 @@ $ httpie plugins upgrade httpie-plugin
with the HTTPie command that sends it: with the HTTPie command that sends it:
```bash
$ http -f POST pie.dev/post \ $ http -f POST pie.dev/post \
X-API-Key:123 \ X-API-Key:123 \
User-Agent:Bacon/1.0 \ User-Agent:Bacon/1.0 \

View File

@ -1,5 +1,5 @@
{ {
"website": { "website": {
"master_and_released_docs_differ_after": "d40f06687f8cbbd22bf7dba05bee93aea11a169f" "master_and_released_docs_differ_after": null
} }
} }

View File

@ -1,19 +1,45 @@
{ {
"Aaron Miller": {
"committed": [],
"github": "aaronhmiller",
"reported": [
"3.0.0"
],
"twitter": "aaronmiller8"
},
"Alexander Bogdanov": {
"committed": [],
"github": "ab-kily",
"reported": [
"3.0.0"
],
"twitter": null
},
"Almad": { "Almad": {
"committed": [ "committed": [
"2.5.0" "2.5.0"
], ],
"github": "Almad", "github": "Almad",
"reported": [ "reported": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"twitter": "almadcz" "twitter": "almadcz"
}, },
"Andr\u00e1s Czig\u00e1ny": {
"committed": [],
"github": "andrascz",
"reported": [
"3.0.0"
],
"twitter": null
},
"Annette Wilson": { "Annette Wilson": {
"committed": [], "committed": [],
"github": "annettejanewilson", "github": "annettejanewilson",
"reported": [ "reported": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"twitter": null "twitter": null
}, },
@ -25,6 +51,32 @@
"reported": [], "reported": [],
"twitter": null "twitter": null
}, },
"Batuhan Taskaya": {
"committed": [
"3.0.0"
],
"github": "isidentical",
"reported": [
"3.0.0"
],
"twitter": "isidentical"
},
"Brad Crittenden": {
"committed": [],
"github": "bac",
"reported": [
"3.0.0"
],
"twitter": null
},
"Chad": {
"committed": [],
"github": "cythrawll",
"reported": [
"3.0.0"
],
"twitter": null
},
"D8ger": { "D8ger": {
"committed": [], "committed": [],
"github": "caofanCPU", "github": "caofanCPU",
@ -35,7 +87,8 @@
}, },
"Dave": { "Dave": {
"committed": [ "committed": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"github": "davecheney", "github": "davecheney",
"reported": [], "reported": [],
@ -49,6 +102,14 @@
], ],
"twitter": "DawidFerenczy" "twitter": "DawidFerenczy"
}, },
"Ed Rooth": {
"committed": [],
"github": "sym3tri",
"reported": [
"3.0.0"
],
"twitter": null
},
"Elena Lape": { "Elena Lape": {
"committed": [ "committed": [
"2.5.0" "2.5.0"
@ -61,7 +122,8 @@
"committed": [], "committed": [],
"github": "peruzzof", "github": "peruzzof",
"reported": [ "reported": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"twitter": null "twitter": null
}, },
@ -73,6 +135,22 @@
], ],
"twitter": null "twitter": null
}, },
"Gabriel Cruz": {
"committed": [],
"github": "gmelodie",
"reported": [
"3.0.0"
],
"twitter": "gmelodiecruz"
},
"Gaurav": {
"committed": [
"3.0.0"
],
"github": "gkcs",
"reported": [],
"twitter": null
},
"Giampaolo Rodola": { "Giampaolo Rodola": {
"committed": [], "committed": [],
"github": "giampaolo", "github": "giampaolo",
@ -81,6 +159,14 @@
], ],
"twitter": null "twitter": null
}, },
"Greg Myers": {
"committed": [
"3.0.0"
],
"github": "myersg86",
"reported": [],
"twitter": null
},
"Hugh Williams": { "Hugh Williams": {
"committed": [], "committed": [],
"github": "hughpv", "github": "hughpv",
@ -102,21 +188,33 @@
"Jakub Roztocil": { "Jakub Roztocil": {
"committed": [ "committed": [
"2.5.0", "2.5.0",
"2.6.0" "2.6.0",
"3.0.0"
], ],
"github": "jakubroztocil", "github": "jakubroztocil",
"reported": [ "reported": [
"2.5.0", "2.5.0",
"2.6.0" "2.6.0",
"3.0.0"
], ],
"twitter": "jakubroztocil" "twitter": "jakubroztocil"
}, },
"Jan Bra\u0161na": {
"committed": [
"3.0.0"
],
"github": "janbrasna",
"reported": [],
"twitter": "janbrasna"
},
"Jan Verbeek": { "Jan Verbeek": {
"committed": [ "committed": [
"2.5.0" "2.5.0"
], ],
"github": "blyxxyz", "github": "blyxxyz",
"reported": [], "reported": [
"3.0.0"
],
"twitter": null "twitter": null
}, },
"Jannik Vieten": { "Jannik Vieten": {
@ -127,6 +225,22 @@
"reported": [], "reported": [],
"twitter": null "twitter": null
}, },
"Jesper Holmberg": {
"committed": [],
"github": "strindberg",
"reported": [
"3.0.0"
],
"twitter": null
},
"Kirill Krasnov": {
"committed": [],
"github": "Kirill",
"reported": [
"3.0.0"
],
"twitter": null
},
"Marcel St\u00f6r": { "Marcel St\u00f6r": {
"committed": [ "committed": [
"2.5.0" "2.5.0"
@ -135,6 +249,14 @@
"reported": [], "reported": [],
"twitter": "frightanic" "twitter": "frightanic"
}, },
"Marco Seguri": {
"committed": [],
"github": "seguri",
"reported": [
"3.0.0"
],
"twitter": null
},
"Mariano Ruiz": { "Mariano Ruiz": {
"committed": [], "committed": [],
"github": "mrsarm", "github": "mrsarm",
@ -143,22 +265,41 @@
], ],
"twitter": "mrsarm82" "twitter": "mrsarm82"
}, },
"Mark Rosenbaum": {
"committed": [],
"github": "markrosenbaum",
"reported": [
"3.0.0"
],
"twitter": null
},
"Micka\u00ebl Schoentgen": { "Micka\u00ebl Schoentgen": {
"committed": [ "committed": [
"2.5.0", "2.5.0",
"2.6.0" "2.6.0",
"3.0.0"
], ],
"github": "BoboTiG", "github": "BoboTiG",
"reported": [ "reported": [
"2.5.0", "2.5.0",
"2.6.0" "2.6.0",
"3.0.0"
], ],
"twitter": "__tiger222__" "twitter": "__tiger222__"
}, },
"Mike DePalatis": {
"committed": [],
"github": "mivade",
"reported": [
"3.0.0"
],
"twitter": null
},
"Miro Hron\u010dok": { "Miro Hron\u010dok": {
"committed": [ "committed": [
"2.5.0", "2.5.0",
"2.6.0" "2.6.0",
"3.0.0"
], ],
"github": "hroncok", "github": "hroncok",
"reported": [], "reported": [],
@ -172,16 +313,42 @@
], ],
"twitter": null "twitter": null
}, },
"Nanashi.": {
"committed": [],
"github": "sevenc-nanashi",
"reported": [
"3.0.0"
],
"twitter": "sevenc_nanashi"
},
"Omer Akram": { "Omer Akram": {
"committed": [ "committed": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"github": "om26er", "github": "om26er",
"reported": [ "reported": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"twitter": "om26er" "twitter": "om26er"
}, },
"Patrick Taylor": {
"committed": [],
"github": "pmeister",
"reported": [
"3.0.0"
],
"twitter": null
},
"Paul Laffitte": {
"committed": [],
"github": "paullaffitte",
"reported": [
"3.0.0"
],
"twitter": "plaffitt"
},
"Pavel Alexeev aka Pahan-Hubbitus": { "Pavel Alexeev aka Pahan-Hubbitus": {
"committed": [], "committed": [],
"github": "Hubbitus", "github": "Hubbitus",
@ -190,6 +357,14 @@
], ],
"twitter": null "twitter": null
}, },
"Russell Shurts": {
"committed": [],
"github": "rshurts",
"reported": [
"3.0.0"
],
"twitter": null
},
"Samuel Marks": { "Samuel Marks": {
"committed": [], "committed": [],
"github": "SamuelMarks", "github": "SamuelMarks",
@ -198,6 +373,14 @@
], ],
"twitter": null "twitter": null
}, },
"Sebastian Czech": {
"committed": [
"3.0.0"
],
"github": "sebastianczech",
"reported": [],
"twitter": "sebaczech"
},
"Sullivan SENECHAL": { "Sullivan SENECHAL": {
"committed": [], "committed": [],
"github": "soullivaneuh", "github": "soullivaneuh",
@ -218,7 +401,32 @@
"committed": [], "committed": [],
"github": "vovtz", "github": "vovtz",
"reported": [ "reported": [
"2.6.0" "2.6.0",
"3.0.0"
],
"twitter": null
},
"Vivaan Verma": {
"committed": [
"3.0.0"
],
"github": "doublevcodes",
"reported": [],
"twitter": "doublevcodes"
},
"Vladimir Berkutov": {
"committed": [
"3.0.0"
],
"github": "dair-targ",
"reported": [],
"twitter": null
},
"Will Rogers": {
"committed": [],
"github": "wjrogers",
"reported": [
"3.0.0"
], ],
"twitter": null "twitter": null
}, },
@ -238,6 +446,14 @@
"reported": [], "reported": [],
"twitter": null "twitter": null
}, },
"arloan": {
"committed": [],
"github": "arloan",
"reported": [
"3.0.0"
],
"twitter": null
},
"bl-ue": { "bl-ue": {
"committed": [ "committed": [
"2.5.0" "2.5.0"
@ -246,22 +462,48 @@
"reported": [], "reported": [],
"twitter": null "twitter": null
}, },
"blueray453": {
"committed": [],
"github": "blueray453",
"reported": [
"3.0.0"
],
"twitter": null
},
"claudiatd": { "claudiatd": {
"committed": [ "committed": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"github": "claudiatd", "github": "claudiatd",
"reported": [], "reported": [],
"twitter": null "twitter": null
}, },
"coldcoff": {
"committed": [],
"github": "coldcoff",
"reported": [
"3.0.0"
],
"twitter": null
},
"dkreeft": { "dkreeft": {
"committed": [ "committed": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"github": "dkreeft", "github": "dkreeft",
"reported": [], "reported": [],
"twitter": null "twitter": null
}, },
"greg": {
"committed": [
"3.0.0"
],
"github": "gregkh",
"reported": [],
"twitter": null
},
"henryhu712": { "henryhu712": {
"committed": [ "committed": [
"2.5.0" "2.5.0"
@ -270,14 +512,31 @@
"reported": [], "reported": [],
"twitter": null "twitter": null
}, },
"hosseingt": {
"committed": [
"3.0.0"
],
"github": "hosseingt",
"reported": [],
"twitter": null
},
"jakubroztocil": { "jakubroztocil": {
"committed": [ "committed": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"github": "jkbr", "github": "jkbr",
"reported": [], "reported": [],
"twitter": null "twitter": null
}, },
"josephworks": {
"committed": [],
"github": "josephworks",
"reported": [
"3.0.0"
],
"twitter": null
},
"jungle-boogie": { "jungle-boogie": {
"committed": [], "committed": [],
"github": "jungle-boogie", "github": "jungle-boogie",
@ -286,6 +545,14 @@
], ],
"twitter": null "twitter": null
}, },
"luisuimi": {
"committed": [],
"github": "luisuimi",
"reported": [
"3.0.0"
],
"twitter": null
},
"nixbytes": { "nixbytes": {
"committed": [ "committed": [
"2.5.0" "2.5.0"
@ -294,6 +561,14 @@
"reported": [], "reported": [],
"twitter": "linuxbyte3" "twitter": "linuxbyte3"
}, },
"peterpt": {
"committed": [],
"github": "peterpt",
"reported": [
"3.0.0"
],
"twitter": null
},
"qiulang": { "qiulang": {
"committed": [], "committed": [],
"github": "qiulang", "github": "qiulang",
@ -302,6 +577,30 @@
], ],
"twitter": null "twitter": null
}, },
"stonebig": {
"committed": [],
"github": "stonebig",
"reported": [
"3.0.0"
],
"twitter": null
},
"whodidthis": {
"committed": [],
"github": "whodidthis",
"reported": [
"3.0.0"
],
"twitter": null
},
"zoulja": {
"committed": [],
"github": "zoulja",
"reported": [
"3.0.0"
],
"twitter": null
},
"zwx00": { "zwx00": {
"committed": [], "committed": [],
"github": "zwx00", "github": "zwx00",
@ -314,7 +613,8 @@
"committed": [], "committed": [],
"github": "rogerdehe", "github": "rogerdehe",
"reported": [ "reported": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"twitter": null "twitter": null
}, },
@ -322,7 +622,8 @@
"committed": [], "committed": [],
"github": "hh-in-zhuzhou", "github": "hh-in-zhuzhou",
"reported": [ "reported": [
"2.6.0" "2.6.0",
"3.0.0"
], ],
"twitter": null "twitter": null
} }

View File

@ -20,6 +20,9 @@ exclude_rule 'MD014'
# MD028 Blank line inside blockquote # MD028 Blank line inside blockquote
exclude_rule 'MD028' exclude_rule 'MD028'
# MD012 Multiple consecutive blank lines
exclude_rule 'MD012'
# Tell the linter to use ordered lists: # Tell the linter to use ordered lists:
# 1. Foo # 1. Foo
# 2. Bar # 2. Bar

View File

@ -3,19 +3,18 @@ class Httpie < Formula
desc "User-friendly cURL replacement (command-line HTTP client)" desc "User-friendly cURL replacement (command-line HTTP client)"
homepage "https://httpie.io/" homepage "https://httpie.io/"
url "https://files.pythonhosted.org/packages/53/96/cbcfec73c186f076e4443faf3d91cbbc868f18f6323703afd348b1aba46d/httpie-2.6.0.tar.gz" url "https://files.pythonhosted.org/packages/7b/f9/13070f19226b7db3641fb787df36bb715063abe1b8ca03fbaeca0f465d27/httpie-3.0.1.tar.gz"
sha256 "ef929317b239bbf0a5bb7159b4c5d2edbfc55f8a0bcf9cd24ce597daec2afca5" sha256 "0e9bc93ebdcdd2d32ec24b8fa46cf7e4fde9eec7a6bd0c5d0ef224f25d7466b2"
license "BSD-3-Clause" license "BSD-3-Clause"
head "https://github.com/httpie/httpie.git", branch: "master" head "https://github.com/httpie/httpie.git", branch: "master"
bottle do bottle do
sha256 cellar: :any_skip_relocation, arm64_monterey: "83aab05ffbcd4c3baa6de6158d57ebdaa67c148bef8c872527d90bdaebff0504" sha256 cellar: :any_skip_relocation, arm64_monterey: "9d285fcfb55ce8ed787d1b01966d51e6e07f7e77c44a204695a2d6eee9c8698d"
sha256 cellar: :any_skip_relocation, arm64_big_sur: "3c3a5c2458d0658e14b663495e115297c573aa3466d292f12d02c3ec13a24bdf" sha256 cellar: :any_skip_relocation, arm64_big_sur: "743a282b475e87a4eaf11e545f761aef1b8e4bfe49eaee47251d7629a35a8ced"
sha256 cellar: :any_skip_relocation, monterey: "f860e7d3b77dca4928a2c5e10c4cbd50d792330dfb99f7d736ca0da9fb9dd0d0" sha256 cellar: :any_skip_relocation, monterey: "5d63ea4f47b2028b2ba68abe12a4176934193e058edd869270221b41cc946c76"
sha256 cellar: :any_skip_relocation, big_sur: "377b0643aa1f6d310ba4cfc70d66a94cc458213db8d134940d3b10a32defacf1" sha256 cellar: :any_skip_relocation, big_sur: "5a53221a680a35d1aa00cbadde279dbe4f562d22ed207c15bd4221cb8c3180f1"
sha256 cellar: :any_skip_relocation, catalina: "6d306c30f6f1d7a551d88415efe12b7c3f25d0602f3579dc632771a463f78fa5" sha256 cellar: :any_skip_relocation, catalina: "5feadb6d76f55d6f9681682e221008c282dccf0e46ae22a959b4bad2efde204a"
sha256 cellar: :any_skip_relocation, mojave: "f66b8cdff9cb7b44a84197c3e3d81d810f7ff8f2188998b977ccadfc7e2ec893" sha256 cellar: :any_skip_relocation, x86_64_linux: "d530ddbec49588b0d481f156d35f7e5bb7d3b6427d203f04750e55cd3eecc303"
sha256 cellar: :any_skip_relocation, x86_64_linux: "53f036b0114814c28982e8c022dcf494e7024de088641d7076fd73d12a45a0e9"
end end
depends_on "python@3.10" depends_on "python@3.10"
@ -36,8 +35,13 @@ class Httpie < Formula
end end
resource "idna" do resource "idna" do
url "https://files.pythonhosted.org/packages/cb/38/4c4d00ddfa48abe616d7e572e02a04273603db446975ab46bbcd36552005/idna-3.2.tar.gz" url "https://files.pythonhosted.org/packages/62/08/e3fc7c8161090f742f504f40b1bccbfc544d4a4e09eb774bf40aafce5436/idna-3.3.tar.gz"
sha256 "467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3" sha256 "9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
end
resource "multidict" do
url "https://files.pythonhosted.org/packages/8e/7c/e12a69795b7b7d5071614af2c691c97fbf16a2a513c66ec52dd7d0a115bb/multidict-5.2.0.tar.gz"
sha256 "0dd1c93edb444b33ba2274b66f63def8a327d607c6c790772f448a53b6ea59ce"
end end
resource "Pygments" do resource "Pygments" do
@ -65,20 +69,14 @@ class Httpie < Formula
sha256 "0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c" sha256 "0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
end end
resource "multidict" do
url "https://files.pythonhosted.org/packages/8e/7c/e12a69795b7b7d5071614af2c691c97fbf16a2a513c66ec52dd7d0a115bb/multidict-5.2.0.tar.gz"
sha256 "0dd1c93edb444b33ba2274b66f63def8a327d607c6c790772f448a53b6ea59ce"
end
def install def install
virtualenv_install_with_resources virtualenv_install_with_resources
end end
test do test do
# shell_output() already checks the status code assert_match version.to_s, shell_output("#{bin}/httpie --version")
shell_output("#{bin}/httpie -v") assert_match version.to_s, shell_output("#{bin}/https --version")
shell_output("#{bin}/https -v") assert_match version.to_s, shell_output("#{bin}/http --version")
shell_output("#{bin}/http -v")
raw_url = "https://raw.githubusercontent.com/Homebrew/homebrew-core/HEAD/Formula/httpie.rb" raw_url = "https://raw.githubusercontent.com/Homebrew/homebrew-core/HEAD/Formula/httpie.rb"
assert_match "PYTHONPATH", shell_output("#{bin}/http --ignore-stdin #{raw_url}") assert_match "PYTHONPATH", shell_output("#{bin}/http --ignore-stdin #{raw_url}")

View File

@ -0,0 +1,39 @@
# HTTPie Benchmarking Infrastructure
This directory includes the benchmarks we use for testing HTTPie's speed and the
infrastructure to automate this testing accross versions.
## Usage
Ensure the following requirements are satisfied:
- Python 3.7+
- `pyperf`
Then, run the `extras/benchmarks/run.py`:
```console
$ python extras/profiling/run.py
```
Without any options, this command will initially create an isolated environment
and install `httpie` from the latest commit. Then it will create a second
environment with the `master` of the current repository and run the benchmarks
on both of them. It will compare the results and print it as a markdown table:
| Benchmark | master | this_branch |
| -------------------------------------- | :----: | :------------------: |
| `http --version` (startup) | 201 ms | 174 ms: 1.16x faster |
| `http --offline pie.dev/get` (startup) | 200 ms | 174 ms: 1.15x faster |
| Geometric mean | (ref) | 1.10x faster |
If your `master` branch is not up-to-date, you can get a fresh clone by passing
`--fresh` option. This way, the benchmark runner will clone the `httpie/httpie`
repo from `GitHub` and use it as the baseline.
You can customize these branches by passing `--local-repo`/`--target-branch`,
and customize the repos by passing `--local-repo`/`--target-repo` (can either
take a URL or a path).
If you want to run a third enviroment with additional dependencies (such as
`pyOpenSSL`), you can pass `--complex`.

View File

@ -3,6 +3,6 @@ HTTPie: modern, user-friendly command-line HTTP client for the API era.
""" """
__version__ = '3.0.0' __version__ = '3.0.2'
__author__ = 'Jakub Roztocil' __author__ = 'Jakub Roztocil'
__licence__ = 'BSD' __licence__ = 'BSD'

View File

@ -3,6 +3,7 @@ import http.client
import json import json
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
from time import monotonic
from typing import Any, Dict, Callable, Iterable from typing import Any, Dict, Callable, Iterable
from urllib.parse import urlparse, urlunparse from urllib.parse import urlparse, urlunparse
@ -108,7 +109,7 @@ def collect_messages(
**send_kwargs_merged, **send_kwargs_merged,
**send_kwargs, **send_kwargs,
) )
response._httpie_headers_parsed_at = monotonic()
expired_cookies += get_expired_cookies( expired_cookies += get_expired_cookies(
response.headers.get('Set-Cookie', '') response.headers.get('Set-Cookie', '')
) )

View File

@ -30,14 +30,15 @@ def raw_main(
parser: argparse.ArgumentParser, parser: argparse.ArgumentParser,
main_program: Callable[[argparse.Namespace, Environment], ExitStatus], main_program: Callable[[argparse.Namespace, Environment], ExitStatus],
args: List[Union[str, bytes]] = sys.argv, args: List[Union[str, bytes]] = sys.argv,
env: Environment = Environment() env: Environment = Environment(),
use_default_options: bool = True,
) -> ExitStatus: ) -> ExitStatus:
program_name, *args = args program_name, *args = args
env.program_name = os.path.basename(program_name) env.program_name = os.path.basename(program_name)
args = decode_raw_args(args, env.stdin_encoding) args = decode_raw_args(args, env.stdin_encoding)
plugin_manager.load_installed_plugins(env.config.plugins_dir) plugin_manager.load_installed_plugins(env.config.plugins_dir)
if env.config.default_options: if use_default_options and env.config.default_options:
args = env.config.default_options + args args = env.config.default_options + args
include_debug_info = '--debug' in args include_debug_info = '--debug' in args

View File

@ -37,7 +37,8 @@ def main(args: List[Union[str, bytes]] = sys.argv, env: Environment = Environmen
parser=parser, parser=parser,
main_program=main_program, main_program=main_program,
args=args, args=args,
env=env env=env,
use_default_options=False,
) )
except argparse.ArgumentError: except argparse.ArgumentError:
program_args = args[1:] program_args = args[1:]

View File

@ -1,3 +1,5 @@
from time import monotonic
import requests import requests
from enum import Enum, auto from enum import Enum, auto
@ -15,6 +17,9 @@ from .compat import cached_property
from .utils import split_cookies, parse_content_type_header from .utils import split_cookies, parse_content_type_header
ELAPSED_TIME_LABEL = 'Elapsed time'
class HTTPMessage: class HTTPMessage:
"""Abstract class for HTTP messages.""" """Abstract class for HTTP messages."""
@ -96,7 +101,13 @@ class HTTPResponse(HTTPMessage):
@property @property
def metadata(self) -> str: def metadata(self) -> str:
data = {} data = {}
data['Elapsed time'] = str(self._orig.elapsed.total_seconds()) + 's' time_to_parse_headers = self._orig.elapsed.total_seconds()
# noinspection PyProtectedMember
time_since_headers_parsed = monotonic() - self._orig._httpie_headers_parsed_at
time_elapsed = time_to_parse_headers + time_since_headers_parsed
# data['Headers time'] = str(round(time_to_parse_headers, 5)) + 's'
# data['Body time'] = str(round(time_since_headers_parsed, 5)) + 's'
data[ELAPSED_TIME_LABEL] = str(round(time_elapsed, 10)) + 's'
return '\n'.join( return '\n'.join(
f'{key}: {value}' f'{key}: {value}'
for key, value in data.items() for key, value in data.items()

View File

@ -383,4 +383,5 @@ def make_styles():
PIE_STYLES = make_styles() PIE_STYLES = make_styles()
PIE_STYLE_NAMES = list(PIE_STYLES.keys())
BUNDLED_STYLES |= PIE_STYLES.keys() BUNDLED_STYLES |= PIE_STYLES.keys()

View File

@ -1,4 +1,6 @@
import pygments import pygments
from httpie.models import ELAPSED_TIME_LABEL
from httpie.output.lexers.common import precise from httpie.output.lexers.common import precise
SPEED_TOKENS = { SPEED_TOKENS = {
@ -34,7 +36,7 @@ class MetadataLexer(pygments.lexer.RegexLexer):
tokens = { tokens = {
'root': [ 'root': [
( (
r'(Elapsed time)( *)(:)( *)(\d+\.\d+)(s)', pygments.lexer.bygroups( fr'({ELAPSED_TIME_LABEL})( *)(:)( *)(\d+\.\d+)(s)', pygments.lexer.bygroups(
pygments.token.Name.Decorator, # Name pygments.token.Name.Decorator, # Name
pygments.token.Text, pygments.token.Text,
pygments.token.Operator, # Colon pygments.token.Operator, # Colon

View File

@ -1,7 +1,12 @@
# Copy the brand palette
from typing import Optional from typing import Optional
STYLE_PIE = 'pie'
STYLE_PIE_DARK = 'pie-dark'
STYLE_PIE_LIGHT = 'pie-light'
COLOR_PALETTE = { COLOR_PALETTE = {
# Copy the brand palette
'transparent': 'transparent', 'transparent': 'transparent',
'current': 'currentColor', 'current': 'currentColor',
'white': '#F5F5F0', 'white': '#F5F5F0',
@ -138,10 +143,11 @@ COLOR_PALETTE['primary'] = {
COLOR_PALETTE['secondary'] = {'700': '#37523C', '600': '#6c6969', '500': '#6c6969'} COLOR_PALETTE['secondary'] = {'700': '#37523C', '600': '#6c6969', '500': '#6c6969'}
SHADE_NAMES = { SHADE_NAMES = {
'500': 'pie-dark', '500': STYLE_PIE_DARK,
'600': 'pie', '600': STYLE_PIE,
'700': 'pie-light' '700': STYLE_PIE_LIGHT
} }
SHADES = [ SHADES = [

3
pytest.ini Normal file
View File

@ -0,0 +1,3 @@
[pytest]
markers =
requires_installation

View File

@ -1,7 +1,17 @@
from .utils import http import pytest
from httpie.models import ELAPSED_TIME_LABEL
from httpie.output.formatters.colors import PIE_STYLE_NAMES
from .utils import http, MockEnvironment, COLOR
def test_meta_elapsed_time(httpbin, monkeypatch): def test_meta_elapsed_time(httpbin):
r = http('--meta', httpbin + '/get') r = http('--meta', httpbin + '/delay/1')
for line in r.splitlines(): assert f'{ELAPSED_TIME_LABEL}: 1.' in r
assert 'Elapsed time' in r
@pytest.mark.parametrize('style', ['auto', 'fruity', *PIE_STYLE_NAMES])
def test_meta_elapsed_time_colors(httpbin, style):
r = http('--style', style, '--meta', httpbin + '/get', env=MockEnvironment(colors=256))
assert COLOR in r
assert ELAPSED_TIME_LABEL in r

View File

@ -17,7 +17,7 @@ from httpie.cli.argtypes import (
) )
from httpie.cli.definition import parser from httpie.cli.definition import parser
from httpie.encoding import UTF8 from httpie.encoding import UTF8
from httpie.output.formatters.colors import PIE_STYLES, get_lexer from httpie.output.formatters.colors import get_lexer, PIE_STYLE_NAMES
from httpie.status import ExitStatus from httpie.status import ExitStatus
from .fixtures import XML_DATA_RAW, XML_DATA_FORMATTED from .fixtures import XML_DATA_RAW, XML_DATA_FORMATTED
from .utils import COLOR, CRLF, HTTP_OK, MockEnvironment, http, DUMMY_URL from .utils import COLOR, CRLF, HTTP_OK, MockEnvironment, http, DUMMY_URL
@ -227,7 +227,7 @@ def test_ensure_contents_colored(httpbin, endpoint):
assert COLOR in r assert COLOR in r
@pytest.mark.parametrize('style', PIE_STYLES.keys()) @pytest.mark.parametrize('style', PIE_STYLE_NAMES)
def test_ensure_meta_is_colored(httpbin, style): def test_ensure_meta_is_colored(httpbin, style):
env = MockEnvironment(colors=256) env = MockEnvironment(colors=256)
r = http('--meta', '--style', style, 'GET', httpbin + '/get', env=env) r = http('--meta', '--style', style, 'GET', httpbin + '/get', env=env)

View File

@ -5,6 +5,7 @@ from tests.utils import httpie
from tests.utils.plugins_cli import parse_listing from tests.utils.plugins_cli import parse_listing
@pytest.mark.requires_installation
def test_plugins_installation(httpie_plugins_success, interface, dummy_plugin): def test_plugins_installation(httpie_plugins_success, interface, dummy_plugin):
lines = httpie_plugins_success('install', dummy_plugin.path) lines = httpie_plugins_success('install', dummy_plugin.path)
assert lines[0].startswith( assert lines[0].startswith(
@ -14,6 +15,20 @@ def test_plugins_installation(httpie_plugins_success, interface, dummy_plugin):
assert interface.is_installed(dummy_plugin.name) assert interface.is_installed(dummy_plugin.name)
@pytest.mark.requires_installation
def test_plugin_installation_with_custom_config(httpie_plugins_success, interface, dummy_plugin):
interface.environment.config['default_options'] = ['--session-read-only', 'some-path.json', 'other', 'args']
interface.environment.config.save()
lines = httpie_plugins_success('install', dummy_plugin.path)
assert lines[0].startswith(
f'Installing {dummy_plugin.path}'
)
assert f'Successfully installed {dummy_plugin.name}-{dummy_plugin.version}' in lines
assert interface.is_installed(dummy_plugin.name)
@pytest.mark.requires_installation
def test_plugins_listing(httpie_plugins_success, interface, dummy_plugin): def test_plugins_listing(httpie_plugins_success, interface, dummy_plugin):
httpie_plugins_success('install', dummy_plugin.path) httpie_plugins_success('install', dummy_plugin.path)
data = parse_listing(httpie_plugins_success('list')) data = parse_listing(httpie_plugins_success('list'))
@ -23,6 +38,7 @@ def test_plugins_listing(httpie_plugins_success, interface, dummy_plugin):
} }
@pytest.mark.requires_installation
def test_plugins_listing_multiple(interface, httpie_plugins_success, dummy_plugins): def test_plugins_listing_multiple(interface, httpie_plugins_success, dummy_plugins):
paths = [plugin.path for plugin in dummy_plugins] paths = [plugin.path for plugin in dummy_plugins]
httpie_plugins_success('install', *paths) httpie_plugins_success('install', *paths)
@ -34,12 +50,14 @@ def test_plugins_listing_multiple(interface, httpie_plugins_success, dummy_plugi
} }
@pytest.mark.requires_installation
def test_plugins_uninstall(interface, httpie_plugins_success, dummy_plugin): def test_plugins_uninstall(interface, httpie_plugins_success, dummy_plugin):
httpie_plugins_success('install', dummy_plugin.path) httpie_plugins_success('install', dummy_plugin.path)
httpie_plugins_success('uninstall', dummy_plugin.name) httpie_plugins_success('uninstall', dummy_plugin.name)
assert not interface.is_installed(dummy_plugin.name) assert not interface.is_installed(dummy_plugin.name)
@pytest.mark.requires_installation
def test_plugins_listing_after_uninstall(interface, httpie_plugins_success, dummy_plugin): def test_plugins_listing_after_uninstall(interface, httpie_plugins_success, dummy_plugin):
httpie_plugins_success('install', dummy_plugin.path) httpie_plugins_success('install', dummy_plugin.path)
httpie_plugins_success('uninstall', dummy_plugin.name) httpie_plugins_success('uninstall', dummy_plugin.name)
@ -48,6 +66,7 @@ def test_plugins_listing_after_uninstall(interface, httpie_plugins_success, dumm
assert len(data) == 0 assert len(data) == 0
@pytest.mark.requires_installation
def test_plugins_uninstall_specific(interface, httpie_plugins_success): def test_plugins_uninstall_specific(interface, httpie_plugins_success):
new_plugin_1 = interface.make_dummy_plugin() new_plugin_1 = interface.make_dummy_plugin()
new_plugin_2 = interface.make_dummy_plugin() new_plugin_2 = interface.make_dummy_plugin()
@ -61,6 +80,7 @@ def test_plugins_uninstall_specific(interface, httpie_plugins_success):
assert not interface.is_installed(target_plugin.name) assert not interface.is_installed(target_plugin.name)
@pytest.mark.requires_installation
def test_plugins_installation_failed(httpie_plugins, interface): def test_plugins_installation_failed(httpie_plugins, interface):
plugin = interface.make_dummy_plugin(build=False) plugin = interface.make_dummy_plugin(build=False)
result = httpie_plugins('install', plugin.path) result = httpie_plugins('install', plugin.path)
@ -69,6 +89,7 @@ def test_plugins_installation_failed(httpie_plugins, interface):
assert result.stderr.splitlines()[-1].strip().startswith("Can't install") assert result.stderr.splitlines()[-1].strip().startswith("Can't install")
@pytest.mark.requires_installation
def test_plugins_uninstall_non_existent(httpie_plugins, interface): def test_plugins_uninstall_non_existent(httpie_plugins, interface):
plugin = interface.make_dummy_plugin(build=False) plugin = interface.make_dummy_plugin(build=False)
result = httpie_plugins('uninstall', plugin.name) result = httpie_plugins('uninstall', plugin.name)
@ -80,6 +101,7 @@ def test_plugins_uninstall_non_existent(httpie_plugins, interface):
) )
@pytest.mark.requires_installation
def test_plugins_double_uninstall(httpie_plugins, httpie_plugins_success, dummy_plugin): def test_plugins_double_uninstall(httpie_plugins, httpie_plugins_success, dummy_plugin):
httpie_plugins_success("install", dummy_plugin.path) httpie_plugins_success("install", dummy_plugin.path)
httpie_plugins_success("uninstall", dummy_plugin.name) httpie_plugins_success("uninstall", dummy_plugin.name)
@ -93,6 +115,7 @@ def test_plugins_double_uninstall(httpie_plugins, httpie_plugins_success, dummy_
) )
@pytest.mark.requires_installation
def test_plugins_upgrade(httpie_plugins, httpie_plugins_success, dummy_plugin): def test_plugins_upgrade(httpie_plugins, httpie_plugins_success, dummy_plugin):
httpie_plugins_success("install", dummy_plugin.path) httpie_plugins_success("install", dummy_plugin.path)
@ -105,6 +128,7 @@ def test_plugins_upgrade(httpie_plugins, httpie_plugins_success, dummy_plugin):
assert data[dummy_plugin.name]['version'] == '2.0.0' assert data[dummy_plugin.name]['version'] == '2.0.0'
@pytest.mark.requires_installation
def test_broken_plugins(httpie_plugins, httpie_plugins_success, dummy_plugin, broken_plugin): def test_broken_plugins(httpie_plugins, httpie_plugins_success, dummy_plugin, broken_plugin):
httpie_plugins_success("install", dummy_plugin.path, broken_plugin.path) httpie_plugins_success("install", dummy_plugin.path, broken_plugin.path)
@ -127,6 +151,7 @@ def test_broken_plugins(httpie_plugins, httpie_plugins_success, dummy_plugin, br
assert len(data) == 1 assert len(data) == 1
@pytest.mark.requires_installation
def test_plugins_cli_error_message_without_args(): def test_plugins_cli_error_message_without_args():
# No arguments # No arguments
result = httpie(no_debug=True) result = httpie(no_debug=True)
@ -143,6 +168,7 @@ def test_plugins_cli_error_message_without_args():
'POST pie.dev/post header:value a=b header_2:value x:=1' 'POST pie.dev/post header:value a=b header_2:value x:=1'
] ]
) )
@pytest.mark.requires_installation
def test_plugins_cli_error_messages_with_example(example): def test_plugins_cli_error_messages_with_example(example):
result = httpie(*example.split(), no_debug=True) result = httpie(*example.split(), no_debug=True)
assert result.exit_status == ExitStatus.ERROR assert result.exit_status == ExitStatus.ERROR
@ -158,6 +184,7 @@ def test_plugins_cli_error_messages_with_example(example):
'unknown.com UNPARSABLE????SYNTAX', 'unknown.com UNPARSABLE????SYNTAX',
] ]
) )
@pytest.mark.requires_installation
def test_plugins_cli_error_messages_invalid_example(example): def test_plugins_cli_error_messages_invalid_example(example):
result = httpie(*example.split(), no_debug=True) result = httpie(*example.split(), no_debug=True)
assert result.exit_status == ExitStatus.ERROR assert result.exit_status == ExitStatus.ERROR

View File

@ -2,6 +2,7 @@
Here we test our output parsing and matching implementation, not HTTPie itself. Here we test our output parsing and matching implementation, not HTTPie itself.
""" """
from httpie.models import ELAPSED_TIME_LABEL
from httpie.output.writer import MESSAGE_SEPARATOR from httpie.output.writer import MESSAGE_SEPARATOR
from ...utils import CRLF from ...utils import CRLF
from . import assert_output_does_not_match, assert_output_matches, Expect from . import assert_output_does_not_match, assert_output_matches, Expect
@ -111,7 +112,7 @@ def test_assert_output_matches_response_meta():
assert_output_matches( assert_output_matches(
( (
'Key: Value\n' 'Key: Value\n'
'Elapsed Time: 3.3s' f'{ELAPSED_TIME_LABEL}: 3.3s'
), ),
[Expect.RESPONSE_META] [Expect.RESPONSE_META]
) )
@ -124,7 +125,7 @@ def test_assert_output_matches_whole_response():
f'AAA:BBB{CRLF}' f'AAA:BBB{CRLF}'
f'{CRLF}' f'{CRLF}'
f'CCC{MESSAGE_SEPARATOR}' f'CCC{MESSAGE_SEPARATOR}'
'Elapsed Time: 3.3s' f'{ELAPSED_TIME_LABEL}: 3.3s'
), ),
[Expect.RESPONSE_HEADERS, Expect.BODY, Expect.RESPONSE_META] [Expect.RESPONSE_HEADERS, Expect.BODY, Expect.RESPONSE_META]
) )