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

HTTP/2 bi-directional streaming. #1150

Open
tomchristie opened this issue Aug 8, 2020 · 8 comments
Open

HTTP/2 bi-directional streaming. #1150

tomchristie opened this issue Aug 8, 2020 · 8 comments
Labels
enhancement New feature or request
Milestone

Comments

@tomchristie
Copy link
Member

tomchristie commented Aug 8, 2020

At some point we ought to consider a low-level Transport API for exposing the functionality offered by the HTTP CONNECT method, the HTTP Upgrade header, and HTTP/2's bi-directional streaming. (Eg. as used by gRPC)

All there of these essentially offer the same thing - a means of obtaining a raw connection over HTTP.
The connection itself might either be the actual TCP connection (HTTP/1.1) or just a single stream (HTTP/2), but this should be ~transparent to the user, except for the fact that we might want to expose some functionality such as "now start TLS on this connection" that is only appropriate in the HTTP/1.1 case.

One thing that's potentially a bit fiddly about the API, is that the connect request can either be accepted or rejected, and we've got different kinds of stream interfaces we want to expose in each case. (Ie if it's rejected, then we want to return a regular byte stream representing the HTTP response, otherwise we want to return some kind of Connection interface.)

This issue is related to WebSockets support (which uses the HTTP Upgrade mechanism), but I think we want to provide a dedicated API for that use case, rather than building our websockets support on top of a low level connect mechanism.

Would be useful to hear from any folks with use cases in this area.

Edit, Sept 6 2023: Retitled to reflect the last remaining item here.

@tomchristie tomchristie added the enhancement New feature or request label Aug 8, 2020
@tomchristie tomchristie added this to the someday milestone Aug 8, 2020
@nskalis
Copy link

nskalis commented Jan 29, 2022

First of all, many thanks for your work on httpx and making it available.

A use case would be to use httpx as a grpc client (based on the document you referenced above) and avoid the grpc client code generation. For example, in IP networks (telecom/datacenter industry) the routers are sharing data through telemetry. Every router (3rd party vendor) acts as a grpc server/stub.
Making a HTTP2 POST request is a far simpler and unified method for interacting with a router (for example) than working with (produce and customise) metaclasses the grpc client is based on.

Python dominates the network world, hence this attempt of a low-level transport api in httpx seems to be the best bet  😉

@stale
Copy link

stale bot commented Mar 5, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Mar 5, 2022
@tomchristie
Copy link
Member Author

tomchristie commented Mar 7, 2022

Thanks @Stale, I think we'd like to keep this open. Soz.

It's probably more of a docs issue at this point than an enhancement, since the "network stream" extension does allow us to support this functionality.

@stale
Copy link

stale bot commented Oct 15, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@nskalis
Copy link

nskalis commented Apr 23, 2023

👉 With httpx I can successfully make grpc unary requests to a grpc server. 🚀

Supporting network_stream for http2 will enable a httpx-based client to do

  1. grpc server-streaming
  2. grpc bidirectional streaming
  3. grpc client-streaming
  • use case 👀

The latter would be extremely valuable given that http2 is the transport of choice for grpc, and the main use of http2 to my eyes (?!)
There is whole industry creating a standard https://www.openconfig.net and various specifications for it https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md that are all based on grpc. Having support of grpc server-streaming, grpc bidirectional streaming in httpx would make the management of routers a breeze to work with.


  • examples

If we consider the official example https://grpc.io/docs/languages/python/quickstart/ there is an example of grpc unary (https://github.com/grpc/grpc/blob/v1.54.0/examples/protos/helloworld.proto) and grpc server-streaming (https://github.com/grpc/grpc/blob/v1.54.0/examples/protos/hellostreamingworld.proto).

# using curl (based on https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md)
echo -en '\x00\x00\x00\x00\x10{"name": "niko"}' | curl -ss -k --http2 --http2-prior-knowledge -H "Content-Type: application/grpc+json" -H "TE: trailers" --data-binary @- http://localhost:50051/helloworld.Greeter/SayHello
# using grpcurl
grpcurl -import-path ../../protos -proto hellostreamingworld.proto -plaintext -d '{"name": "niko"}' localhost:50051 hellostreamingworld.MultiGreeter/sayHello 

There is another publicly available example of grpc bidirectional-streaming and grpc server-streaming here https://github.com/bufbuild/connect-demo/blob/3a30d4de07d6ac42110acd4ebf64bb4bf8a62579/proto/buf/connect/demo/eliza/v1/eliza.proto#L25

# using curl (based on https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md)
echo -ne '\x00\x00\x00\x00\x1b{"sentence": "I feel good"}' | curl --http2 -H "Content-Type: application/grpc+json" -H "TE: trailers" --data-binary @- --output - https://demo.connect.build:443/buf.connect.demo.eliza.v1.ElizaService/Say
&{"sentence":"Do you often feel good?"}

Note: You can omit the first 5 bytes of the response (when using curl) as it is the same header the request is using. If you pipe the below output to | od -bc, then it becomes obvious.

echo -ne '\x00\x00\x00\x00\x1b{"sentence": "I feel good"}' | curl -ss -k  --http2 -H "Content-Type: application/grpc+json" -H "TE: trailers" --data-binary @- --output - https://demo.connect.build:443/buf.connect.demo.eliza.v1.ElizaService/Say | od -bc
0000000   000 000 000 000 055 173 042 163 145 156 164 145 156 143 145 042
          \0  \0  \0  \0   -   {   "   s   e   n   t   e   n   c   e   "
0000020   072 042 127 150 145 156 040 144 157 040 171 157 165 040 165 163
           :   "   W   h   e   n       d   o       y   o   u       u   s
0000040   165 141 154 154 171 040 146 145 145 154 040 147 157 157 144 077
           u   a   l   l   y       f   e   e   l       g   o   o   d   ?
0000060   042 175                                                        
           "   }                                                        
0000062

Referencing encode/httpcore#592 here as well for reasons of completeness,
as this feature seems to be important to many users for other protocols too. 🎈

How do you feel about it? Is it a big chunk of work you (and the encode team) have to put in?

@tomchristie
Copy link
Member Author

How do you feel about it?

I like it.
Looks niche from my perspective, but also a valuable niche.

@nskalis
Copy link

nskalis commented Apr 27, 2023

Indeed, because httpx will also be a fully-featured grpc client library (when supporting network_stream for http/2). 🥳


In an effort to save 10min of your time, this is how I construct the byte sequence in question, in other words the data function argument in a POST request.

json_data = json.dumps({"name": "niko"})
compressed_flag = "{:02x}".format(0)
message_length = "{:08x}".format(len(json_data.encode("utf-8")))
message_data = "".join(["{:02x}".format(ord(x)) for x in json_data])
binascii.unhexlify(compressed_flag + message_length + message_data)  # payload

which is the equivalent of

# with open("frame.bin", "wb") as f:
#     f.write(binascii.unhexlify(compressed_flag + message_length + message_data))
# or
# echo -n '00000000107b226e616d65223a20226e696b6f227d' | xxd -r -p - frame.bin
# hexdump -C frame.bin
# echo -en '\x00\x00\x00\x00\x10{"name": "niko"}' | curl -ss -k --http2 --http2-prior-knowledge -H "Content-Type: application/grpc+json" -H "TE: trailers" --data-binary @- http://localhost:50051/helloworld.Greeter/SayHello | od -bc

@nskalis
Copy link

nskalis commented May 2, 2023

is it a good idea to remove the wontfix label from this GitHub issue?

@tomchristie tomchristie changed the title CONNECT, Upgrade, and HTTP/2 bi-directional streaming. HTTP/2 bi-directional streaming. Sep 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants