Skip to content

Commit ee057ba

Browse files
committed
add plain sockets example client
1 parent 48bfc00 commit ee057ba

File tree

3 files changed

+98
-2
lines changed

3 files changed

+98
-2
lines changed

Diff for: docs/source/examples.rst

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ Example Servers
1212
:maxdepth: 2
1313

1414
asyncio-example
15-
twisted-example
15+
curio-example
1616
eventlet-example
1717
gevent-example
18-
curio-example
1918
tornado-example
19+
twisted-example
2020
wsgi-example
2121

2222
Example Clients
@@ -25,5 +25,6 @@ Example Clients
2525
.. toctree::
2626
:maxdepth: 2
2727

28+
plain-sockets-example
2829
twisted-head-example
2930
twisted-post-example

Diff for: docs/source/plain-sockets-example.rst

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Plain Sockets Example Client
2+
============================
3+
4+
This example is a basic HTTP/2 client written using plain Python `sockets`_, and
5+
`ssl`_ TLS/SSL wrapper for socket objects.
6+
7+
This client is *not* a complete production-ready HTTP/2 client and only intended
8+
as a demonstration sample.
9+
10+
This example shows the bare minimum that is needed to send an HTTP/2 request to
11+
a server, and read back a response body.
12+
13+
.. literalinclude:: ../../examples/plain_sockets/plain_sockets_client.py
14+
:language: python
15+
:linenos:
16+
:encoding: utf-8
17+
18+
.. _sockets: https://docs.python.org/3/library/socket.html
19+
.. _ssl: https://docs.python.org/3/library/ssl.html

Diff for: examples/plain_sockets/plain_sockets_client.py

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
plain_sockets_client.py
5+
~~~~~~~~~~~~~~~~~~~~~~~
6+
7+
Just enough code to send a GET request via h2 to an HTTP/2 server and receive a response body.
8+
This is *not* a complete production-ready HTTP/2 client!
9+
"""
10+
11+
import socket
12+
import ssl
13+
import certifi
14+
15+
import h2.connection
16+
import h2.events
17+
18+
19+
SERVER_NAME = 'http2.golang.org'
20+
SERVER_PORT = 443
21+
22+
# generic socket and ssl configuration
23+
socket.setdefaulttimeout(15)
24+
ctx = ssl.create_default_context(cafile=certifi.where())
25+
ctx.set_alpn_protocols(['h2'])
26+
27+
# open a socket to the server and initiate TLS/SSL
28+
s = socket.create_connection((SERVER_NAME, SERVER_PORT))
29+
s = ctx.wrap_socket(s, server_hostname=SERVER_NAME)
30+
31+
c = h2.connection.H2Connection()
32+
c.initiate_connection()
33+
s.sendall(c.data_to_send())
34+
35+
headers = [
36+
(':method', 'GET'),
37+
(':path', '/reqinfo'),
38+
(':authority', SERVER_NAME),
39+
(':scheme', 'https'),
40+
]
41+
c.send_headers(1, headers, end_stream=True)
42+
s.sendall(c.data_to_send())
43+
44+
body = b''
45+
response_stream_ended = False
46+
while not response_stream_ended:
47+
# read raw data from the socket
48+
data = s.recv(65536 * 1024)
49+
if not data:
50+
break
51+
52+
# feed raw data into h2, and process resulting events
53+
events = c.receive_data(data)
54+
for event in events:
55+
print(event)
56+
if isinstance(event, h2.events.DataReceived):
57+
# update flow control so the server doesn't starve us
58+
c.acknowledge_received_data(event.flow_controlled_length, event.stream_id)
59+
# more response body data received
60+
body += event.data
61+
if isinstance(event, h2.events.StreamEnded):
62+
# response body completed, let's exit the loop
63+
response_stream_ended = True
64+
break
65+
# send any pending data to the server
66+
s.sendall(c.data_to_send())
67+
68+
print("Response fully received:")
69+
print(body.decode())
70+
71+
# tell the server we are closing the h2 connection
72+
c.close_connection()
73+
s.sendall(c.data_to_send())
74+
75+
# close the socket
76+
s.close()

0 commit comments

Comments
 (0)