Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal v4.0 b1 (HTTP/2+3, OS Trust Store, Custom DNS, OCSP, ...) #1531

Open
wants to merge 40 commits into
base: master
Choose a base branch
from

Conversation

Ousret
Copy link
Collaborator

@Ousret Ousret commented Oct 3, 2023

This PR showcases how HTTPie could evolve outside of Requests.

Niquests is supposed to be a (mostly) compatible fork of Requests.

Try this preview:

$ pip install "git+https://github.com/Ousret/HTTPie.git@feature-tryout-niquests" -U

Here are the biggest pros of this:

  • OS truststore by default, no more certifi!
  • Object-oriented headers. Could bring additional features!
  • Fully type-annotated!
  • HTTP/3 over QUIC.
  • HTTP/2 by default.
  • Exit Python http.client! in favor of h11.
  • Timeout by default.
  • Inspect peer certificate, HTTP version, TLS version, cipher, and so on via hook/callback before a request is sent.
  • All the features you expose are available in all three protocols.
  • Python 3.7+, no sacrifice needed.
  • Encrypted DNS support. w/ DNS-over-HTTPS, DNS-over-QUIC, DNS-over-TLS and plain DNS-over-UDP.

Obviously, cons:

  • Stricter on emitted requests per RFCs
  • Young project but based on solid bases, knowledge, and experiences.
  • Need to publish new packages to distro. Easy but time-consuming
  • Exit pyopenssl, not supported. more of a pro to me
  • Require major bump? Could be. Should be.

Complete list of changes in the fork: https://github.com/jawah/niquests/blob/main/HISTORY.md

Capture d’écran du 2023-11-27 19-52-12


4.0.0.b1 (unreleased)

  • Make it possible to unset the User-Agent, and Accept-Encoding headers. (#1502)
  • Dependency on requests was changed in favor of compatible niquests. (#1531)
  • Added support for HTTP/2, and HTTP/3 protocols. (#523) (#692) (#1531)
  • Added request metadata for the TLS certificate, negotiated version with cipher, the revocation status and the remote peer IP address. (#1495) (#1023) (#826) (#1531)
  • Added support to load the operating system trust store for the peer certificate validation. (#480) (#1531)
  • Added detailed timings in response metadata with DNS resolution, established, TLS handshake, and request sending delays. (#1023) (#1531)
  • Added support for using alternative DNS resolver using --resolver. DNS over HTTPS, DNS over TLS, DNS over QUIC, and DNS over UDP are accepted. (#99) (#1531)
  • Added support for binding to a specific network adapter with --interface. (#1422) (#1531)
  • Added support for specifying the local port with --local-port. (#1456) (#1531)
  • Added support for forcing either IPv4 or IPv6 to reach the remote HTTP server with -6 or -4. (#94) (#1531)
  • Removed support for pyopenssl. (#1531)
  • Removed support for dead SSL protocols < TLS 1.0 (e.g. sslv3) as per pyopenssl removal. (#1531)
  • Dropped dependency on requests_toolbelt in favor of directly including MultipartEncoder into HTTPie due to its direct dependency to requests. (#1531)
  • Dropped dependency on multidict in favor of implementing an internal one due to often missing pre-built wheels. (#1522) (#1531)
  • Fixed the case when multiple headers where concatenated in the response output. (#1413) (#1531)
  • Fixed an edge case where HTTPie could be lead to believe data was passed in stdin, thus sending a POST by default. (#1551) (#1531)
    This fix has the particularity to consider 0 byte long stdin buffer as absent stdin. Empty stdin buffer will be ignored.
  • Slightly improved performance while downloading by setting chunk size to -1 to retrieve packets as they arrive. (#1531)
  • Added support for using the system trust store to retrieve root CAs for verifying TLS certificates. (#1531)
  • Removed support for keeping the original casing of HTTP headers. This come from an outer constraint by newer protocols, namely HTTP/2+ that normalize header keys by default.
    From the HTTPie user perspective, they are "prettified" on the output by default. e.g. "x-hello-world" is displayed as "X-Hello-World".
  • Fixed multipart form data having filename not rfc2231 compliant when name contain non-ascii characters. (#1401)
  • Fixed issue where the configuration directory was not created at runtime that made the update fetcher run everytime. (#1527)
  • Fixed cookie persistence in HTTPie session when targeting localhost. They were dropped due to the standard library. (#1527)
  • Fixed downloader when trying to fetch compressed content. The process will no longer exit with the "Incomplete download" error. (#1554) (#423) (#1527)
  • Added automated resolution of hosts ending with .localhost to the default loopback address. (#1458) (#1527)

Existing plugins are expected to work without any changes. The only caveat would be that certain plugin explicitly require requests.
Future contributions may be made in order to relax the constraints where applicable.

@Ousret

This comment was marked as resolved.

tests/test_json.py Outdated Show resolved Hide resolved
@Ousret

This comment was marked as resolved.

@Ousret Ousret force-pushed the feature-tryout-niquests branch 3 times, most recently from faf9aef to c12f000 Compare October 13, 2023 16:37
@codecov-commenter
Copy link

codecov-commenter commented Oct 13, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 0.00%. Comparing base (4d7d6b6) to head (7cd6579).
Report is 370 commits behind head on master.

Current head 7cd6579 differs from pull request most recent head e3cbc7f

Please upload reports for the commit e3cbc7f to get more accurate results.

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #1531       +/-   ##
==========================================
- Coverage   97.28%       0   -97.29%     
==========================================
  Files          67       0       -67     
  Lines        4235       0     -4235     
==========================================
- Hits         4120       0     -4120     
+ Misses        115       0      -115     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Ousret

This comment was marked as outdated.

@Ousret

This comment was marked as resolved.

@Ousret Ousret force-pushed the feature-tryout-niquests branch 4 times, most recently from 52653e4 to 247b382 Compare October 16, 2023 05:45
@Ousret Ousret force-pushed the feature-tryout-niquests branch 2 times, most recently from 0b4eebb to 7dd1152 Compare October 23, 2023 10:56
@Ousret
Copy link
Collaborator Author

Ousret commented Oct 23, 2023

We can add the following arguments and document them:

  • --disable-http2
  • --disable-http3
  • --http3, e.g. force trying it.

@Ousret
Copy link
Collaborator Author

Ousret commented Nov 16, 2023

I've added and documented it.

  • --disable-http2
  • --disable-http3
  • --http3, e.g. force trying it.

Ideally, the connection information (TLS, Cipher, Peer/Issuer Certificate, and OCSP) should be in the request metadata and with a lexer instruction.

@Ousret Ousret changed the title ⚗️ Try compatible fork Niquests to supercharge HTTPie ⚗️ Add support for HTTP/2, HTTP/3, System CA, OCSP Revocation, Extended Conn Info Nov 16, 2023
@Ousret
Copy link
Collaborator Author

Ousret commented Nov 27, 2023

Now, the result is pleasant to the eye.
I have implemented the metadata for Request and the result is as follows:

Capture d’écran du 2023-11-27 19-52-12

Niquests now support tracking upload progress, this can also be done here. It should be straightforward enough.

@Ousret Ousret changed the title ⚗️ Add support for HTTP/2, HTTP/3, System CA, OCSP Revocation, Extended Conn Info Add support for HTTP/2, HTTP/3, System CA, OCSP Revocation, Extended Conn Info Dec 10, 2023
@dwt
Copy link

dwt commented Dec 20, 2023

Damn this looks so tantalizing. I'd love for this to get packaged as a tryout / beta. Something installable via e.g. pip install httpie[niquest] perhaps?

@Ousret
Copy link
Collaborator Author

Ousret commented Apr 9, 2024

However I noticed the absence of a --http2 flag to force http2. Could you touch on the rationale behind that omission?

You cannot force HTTP/2 using Niquests. It is negotiated via ALPN/TLS. So if the server misinterpret ALPN or discard it, HTTP/2 won't come. And yes, RFC allows it, see https://datatracker.ietf.org/doc/html/rfc9113#name-starting-http-2-with-prior- but I decided to follow the simplest path forward (ALPN/TLS), as browsers do (https://stackoverflow.com/questions/34076231/why-do-browser-implementations-of-http-2-require-tls). I might reconsider it in the future, but the use cases are rare.

@dwt
Copy link

dwt commented Apr 9, 2024

I may be missing something here, but does that imply that the client has no way to force http1 2 or 3 according to its wishes? When setting up server support for these protocols, I would like to have the ability to force all of these protocols for debugging purposes?

@Ousret
Copy link
Collaborator Author

Ousret commented Apr 9, 2024

I may be missing something here, but does that imply that the client has no way to force http1 2 or 3 according to its wishes?

There's many possibilities, the only missing piece is "force http2" or "disable http1".

  • "--disable-http2" HTTP/1.1 or HTTP/3
  • "--disable-http2 --disable-http3" HTTP/1.1
  • "--disable-http3" HTTP/1.1 or HTTP/2
  • "--http3" HTTP/3

Hope that clarify.

@Ousret
Copy link
Collaborator Author

Ousret commented Apr 24, 2024

Just updated the docs to include previous comment about protocol combo and the availability of HTTP/3 or not.
Since last time Niquests made HTTP/3 support far more enjoyable with less dependencies and a faster execution time.

Finally, we had to downgrade macos-14 (macos-latest) to macos-13 because it's relatively new and some dependencies like brotlicffi aren't ready for it.

@mshahat
Copy link

mshahat commented May 5, 2024

Hello @Ousret , nice work here ... is there an ETA on when to expect this release to become available? Thank you

@Ousret
Copy link
Collaborator Author

Ousret commented May 6, 2024

No precise ETA. We are waiting upon the owner approval.

@markstos
Copy link

Looks like it could be a big step forward if @jkbrzt approves it. Last week I wanted to test resolving over IPv4 vs IPv6. I found that the released version could not help with this task, but it worked perfectly in the fork, with the same -4 and -6 flags that dig uses.

non https server were expected to receive "secure" cookies...? this seems to be a bug that lied in Requests for quite some time.
@ducaale
Copy link

ducaale commented May 23, 2024

Excited to see HTTP/2 and HTTP/3 soon landing in HTTPie 🎉

I may be missing something here, but does that imply that the client has no way to force http1 2 or 3 according to its wishes?

There's many possibilities, the only missing piece is "force http2" or "disable http1".
"--disable-http2" HTTP/1.1 or HTTP/3
"--disable-http2 --disable-http3" HTTP/1.1
"--disable-http3" HTTP/1.1 or HTTP/2
"--http3" HTTP/3

I was wondering if we could have an equivalent to cURL's http-version flags i.e --http1.0,--http1.1,--http2,--http2-prior-knowledge,--http3? Alternatively, it could be a --http-version flag that accepts either 1.0, 1.1, 2, 2-prior-knowledge or 3

@Ousret
Copy link
Collaborator Author

Ousret commented May 23, 2024

Excited to see HTTP/2 and HTTP/3 soon landing in HTTPie 🎉

Likewise!

I was wondering if we could have an equivalent to cURL's http-version flags

I first need to add a feature into Niquests, which isn't top priority right now.
But it is planed, will be in a future minor. Provided everything goes smooth with this.

regards,

docs/README.md Outdated
Comment on lines 1533 to 1541
This will default to SSL v2.3 which will negotiate the highest protocol that both the server and your installation of OpenSSL support.
The available protocols are `ssl2.3`, `ssl3`, `tls1`, `tls1.1`, `tls1.2`, `tls1.3`.
This will default to TLS v1.0 which will negotiate the highest protocol that both the server and your installation of OpenSSL support.
The available protocols are `tls1`, `tls1.1`, `tls1.2`, `tls1.3`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the idea, and without digging any deeper into the changeset to verify my thoughts in implementation, I'm just raising it here for general discussion:

"This is will default to TLS v1.0 which will negotiate the highest protocol that both […] support."

  1. This sounds a bit confusing. It's either "This is will default to TLS v1.0" (which it should not), or "This is will default to a highest TLS version that both […]".
  • Because the values don't mean "use as the lowest", but "use the chosen only" (i.e. not a tls12+ or a range, but tls12 only)
  • I'd actually propose a magic value like "tls" (roughly meaning an alias to PROTOCOL_TLS_CLIENT) that would use the highest protocol available. Not sure if that's the default thou, not sure if the client can have tls13 capability, but still use tls12 as a default? If default == max always, then there's prolly no need for such value, only way to use max protocol then is to omit the param though. Which would mean no "max" magic value would be listed in help/man among the "available protocols", which in turn might seem a bit confusing then. So an explicit value for max protocol available, even when it really doesn't do anything if that's the default, can still have its value for clarity.
  • Do you plan to silently accept the old "ssl2.3" value mapped to default, whatever that ends up to be, not to raise any errors?
  1. Are you sure the "tls1.3" value works? It does not in master, and never did (since the openssl apis are different for TLSv13, and the change in flags structure [see 4. ↓] made it impossible to constrain without all *_OP_NO_* used) — it only selects TLSv13 if that would have been the default anyways…

  2. Adding ranges, i.e. min=tls11 max=tls12, or just one constraint alone (to match the current python sslcontext, instead of selecting one version, or disabling individual one by one) would also be lovely at some point.

Copy link
Collaborator Author

@Ousret Ousret May 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, the "This is will default to ..." is kind of confusing. I've addressed that by rephrasing it.

  1. It is rephrased accordingly with "If not specified, it tries to negotiate the highest protocol that both the server and your installation of OpenSSL support."
  2. "tls1.3" case has been addressed, we are now assuming that "tls1.3" (associated to PROTOCOL_TLS_CLIENT) means strictly do tls 1.3, and appropriate measures have been made.
  3. old ssl2.3 value and alike are gone, completely, accepting them is only going to generate noise in GH issues. tls1.0 is the absolute minimum nowadays. nothing in CPython 3.7+ (+whether OpenSSL 1.1.1+ or 3+) permit to support them. so it will ends up with errors no matter what we do.
  4. The range of accepted tls version should be dealt with separately, I don't want to further increase this PR size, thus delaying it further on. I agree it can be useful, but it's not top priority as we speak.

regards,

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To demonstrate --ssl=tls1.3 is working, run https --ssl=tls1.3 https://tls-v1-2.badssl.com:1012/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment