ABOUT THIS EPISODE
Working with network protocols is a common need for software projects, particularly in the current age of the internet. As a result, there are a multitude of libraries that provide interfaces to the various protocols. The problem is that implementing a network protocol properly and handling all of the edge cases is hard, and most of the available libraries are bound to a particular I/O paradigm which prevents them from being widely reused. To address this shortcoming there has been a movement towards "sans I/O" implementations that provide the business logic for a given protocol while remaining agnostic to whether you are using async I/O, Twisted, threads, etc. In this episode Aymeric Augustin shares his experience of refactoring his popular websockets library to be I/O agnostic, including the challenges involved in how to design the interfaces, the benefits it provides in simplifying the tests, and the work needed to add back support for async I/O and other runtimes. This is a great conversation about what is involved in making an ideal a reality.
Announcements- Hello and welcome to Podcast.__init__, the podcast about Python and the people who make it great.
- When you’re ready to launch your next app or want to try a project you hear about on the show, you’ll need somewhere to deploy it, so take a look at our friends over at Linode. With the launch of their managed Kubernetes platform it’s easy to get started with the next generation of deployment and scaling, powered by the battle tested Linode platform, including simple pricing, node balancers, 40Gbit networking, dedicated CPU and GPU instances, and worldwide data centers. Go to pythonpodcast.com/linode and get a $100 credit to try out a Kubernetes cluster of your own. And don’t forget to thank them for their continued support of this show!
- Your host as usual is Tobias Macey and today I’m interviewing Aymeric Augustin about his work on the websockets library and the work involved in making it sans I/O
- Introductions
- How did you get introduced to Python?
- Can you start by giving an overview of your work on the websockets library and how the project got started?
- What does "sans I/O" mean and what are the goals associated with it?
- Can you share the history of your work on the websockets project?
- What was your motivation for starting down the path of rearchitecting a project that is already production ready?
- Can you talk through how the websockets library is architected currently?
- How has the design of the project evolved since you first began working on it?
- At a high level, what were the changes required to make it functionally sans i/o?
- What do you see as the primary challenges associated with making network related libraries sans i/o?
- In your experience of porting websockets to be purely protocol oriented, what are the technical and design challenges that you faced?
- One of the goals of the Sans I/O approach is to support reusability and composability of network protocol implementations. What has your experience been as to the viability of those goals in practice?
- What is your current perspective on the cost/benefit of the sans i/o conversion?
- Who are the primary consumers of the websockets library?
- How do you foresee the target audience changing once you have completed extracting the protocol logic?
- What are some of the most interesting, innovative, or unexpected ways that you have seen the websockets project used?
- What are the most interesting, unexpected, or challenging lessons that you have learned while working on the websockets project and sans i/o conversion?
- What do you have planned for the future of the project?
- @aymericaugustin on Twitter
- Website
- Tobias
- Aymeric
- Thank you for listening! Don’t forget to check out our other show, the Data Engineering Podcast for the latest on modern data management.
- Visit the site to subscribe to the show, sign up for the mailing list, and read the show notes.
- If you’ve learned something or tried out a project from the show then tell us about it! Email hosts@podcastinit.com) with your story.
- To help other people find the show please leave a review on iTunes and tell your friends and co-workers
- Join the community in the new Zulip chat workspace at pythonpodcast.com/chat
- Sans I/O: When The Rubber Meets The Road
- Websockets library
- Websockets Protocol
- Qonto
- Tulip
- Asyncio
- CERN Particle Accelerator
- Sans I/O
- Cory Benfield
- HTTP/2
- Twisted
- Curio
- Trio
- Inversion of Control
- ohneio helper library for implementing sans I/O network protocols
- SOCKS Proxy
- Sanic
The intro and outro music is from Requiem for a Fish The Freak Fandango Orchestra / CC BY-SA
IN THIS EPISODE
SHOW NOTES 🔗
TRANSCRIPT 🔗
NOTE
Transcription provided by Podhome.fm
Created: 7/6/2024 4:22:28 PM
Duration: 2284.142
Channels: 1
1
00:00:13.424 --> 00:00:18.164
Hello, and welcome to podcast dot in it, the podcast about Python and the people who make it great.
2
00:00:18.820 --> 00:00:25.640
When you're ready to launch your next app or want to try a project you hear about on the show, you'll need somewhere to deploy it. So take a look at our friends over at Linode.
3
00:00:26.494 --> 00:00:38.460
With the launch of their managed Kubernetes platform, it's easy to get started with the next generation of deployment and scaling powered by the battle tested Linode platform, including simple pricing, node balancers, 40 gigabit networking,
4
00:00:38.920 --> 00:00:42.700
dedicated CPU and GPU instances, and worldwide data centers.
5
00:00:43.415 --> 00:00:44.715
Go to python podcast.com/linode,
6
00:00:46.295 --> 00:01:06.935
that's l I n o d e, today and get a $100 credit to try out a Kubernetes cluster of your own. And don't forget to thank them for their continued support of this show. Your host as usual is Tobias Macy. And today, I'm interviewing Aymeric Augustin about his work on the web Sockets library and the work involved in making it sans. Io. So, Aymeric, can you start by introducing yourself?
7
00:01:07.555 --> 00:01:09.335
Hello. I'm software engineer,
8
00:01:09.780 --> 00:01:12.200
account manager. I'm the CTO of Konta,
9
00:01:12.660 --> 00:01:17.240
startup building a bank for SMEs in Europe. We're based in Paris.
10
00:01:17.665 --> 00:01:21.045
I've been a Python developer since around 2009.
11
00:01:21.585 --> 00:01:24.325
Today, I'm doing a lot more management and a lot less coding.
12
00:01:24.625 --> 00:01:27.365
The I still enjoy doing a bit of Python in my free time.
13
00:01:27.830 --> 00:01:30.010
And do you remember how you first got introduced to Python?
14
00:01:30.550 --> 00:01:41.625
The first contact, I think, was when I was a graduate student and a friend told me about this great and nice and beautiful language called Python. And at the time, pretty much everything I knew was PHP.
15
00:01:42.085 --> 00:01:48.520
And so I did it as, well, I can also open a file in PHP. It's not all that different, just like in other bows.
16
00:01:50.020 --> 00:01:54.185
And so eventually, I came to realize that I could do a bit more thing with Python than with PHP.
17
00:01:54.725 --> 00:01:58.745
And so the reason that we're talking today is because of your work on the WebSockets library
18
00:01:59.125 --> 00:02:10.140
and some of the work that you've been doing to remove the explicit IO dependencies. But before we get into that part of it, I'm wondering if you can just give an overview about the WebSockets library itself and how the project got started.
19
00:02:10.704 --> 00:02:12.724
Yeah. So this project has a fun history.
20
00:02:13.265 --> 00:02:14.484
It was back in 2013.
21
00:02:15.905 --> 00:02:17.204
I was working
22
00:02:17.504 --> 00:02:20.930
at a company where we we did a a lot of things around
23
00:02:21.709 --> 00:02:24.049
electricity storage and usage for transportation.
24
00:02:24.750 --> 00:02:28.049
We had been building the Autolip car sharing service in Paris,
25
00:02:28.485 --> 00:02:30.985
and then we were looking at deploying stationary
26
00:02:31.285 --> 00:02:31.785
batteries,
27
00:02:32.245 --> 00:02:32.905
in Africa
28
00:02:33.285 --> 00:02:35.465
to give power where the grid wasn't reliable.
29
00:02:35.780 --> 00:02:36.599
And so we'd
30
00:02:36.980 --> 00:02:38.840
have as these big power packs
31
00:02:39.140 --> 00:02:41.720
in various location with poor Internet connectivity,
32
00:02:42.099 --> 00:02:51.385
and we were looking for a bidirectional protocol to communicate with them. And we wanted something that that that would be reasonably easy to use on our end, the server side.
33
00:02:51.685 --> 00:03:02.989
And, well, we were mostly doing web stuff with Django at that point, so Python was a natural choice. And Guido van Rossum was just starting working on Tulip, which actually became Async IO.
34
00:03:03.395 --> 00:03:07.975
And so I decided to write a library to implement WebSockets on top of Tulip
35
00:03:08.515 --> 00:03:09.335
because that's interesting
36
00:03:10.115 --> 00:03:14.290
with the plan of using this for this project. And so I left the company shortly thereafter,
37
00:03:14.590 --> 00:03:28.614
but they did end up using WebSockets and I think they still use it. And I continued maintaining it in my free time, even though I never did anything serious with it. Yeah, it's funny how you get an idea for something and then you just can't let it go, even though it's not actually part of your day to day anymore.
38
00:03:29.075 --> 00:03:38.500
We are the best that I still spend so much time there. To be honest, I I had some low points, especially when I realized that 1 of the most popular uses of WebSockets was for interconnecting
39
00:03:38.880 --> 00:03:39.380
to
40
00:03:39.765 --> 00:03:41.385
cryptocurrency exchange trackers.
41
00:03:41.765 --> 00:03:43.705
Many of these provide WebSocket endpoints.
42
00:03:44.325 --> 00:03:46.345
And I take issue with the environmental
43
00:03:47.070 --> 00:03:48.050
footprint of cryptocurrency
44
00:03:48.510 --> 00:03:51.410
at this point in time, and I've been lectured abundantly
45
00:03:52.430 --> 00:03:54.610
about models that could be more energy efficient.
46
00:03:55.065 --> 00:03:59.084
That said, there are still many, many other interesting uses of WebSockets,
47
00:03:59.545 --> 00:04:00.444
and a particularly
48
00:04:00.825 --> 00:04:01.724
interesting 1
49
00:04:02.080 --> 00:04:03.780
is that I know of a user
50
00:04:04.080 --> 00:04:05.300
who relies on WebSockets
51
00:04:05.840 --> 00:04:07.060
to do something
52
00:04:07.760 --> 00:04:12.295
around the particular accelerator of CERN, which is buried somewhere in those Alps.
53
00:04:12.595 --> 00:04:15.655
And so it's kinda amazing that that's WebSockets indirectly
54
00:04:15.955 --> 00:04:16.455
helps
55
00:04:16.915 --> 00:04:17.735
particle physics.
56
00:04:18.100 --> 00:04:23.400
Yeah. It's definitely very cool. Any anytime you can use the term particle physics in a conversation, it's worth it.
57
00:04:23.700 --> 00:04:24.200
Definitely.
58
00:04:25.115 --> 00:04:37.990
Before we get too much into the story of your recent work on the project, I wanna take a sidestep and discuss the whole idea of Sands. Io and what it means and the overall goals associated with that overall strategy.
59
00:04:38.689 --> 00:04:40.789
I think Sands. Io was popularized
60
00:04:41.250 --> 00:04:42.550
around 2016,
61
00:04:44.595 --> 00:04:45.335
and it started
62
00:04:45.955 --> 00:04:46.455
from
63
00:04:46.915 --> 00:04:48.215
noticing some waste.
64
00:04:48.755 --> 00:04:49.735
People repeatedly
65
00:04:50.515 --> 00:04:52.135
write the same things,
66
00:04:52.780 --> 00:04:54.000
typically HTTP,
67
00:04:54.540 --> 00:05:02.000
just because they want to interconnect it to the network in different ways. And so rewriting an entire HTTP parser
68
00:05:02.735 --> 00:05:04.435
or an HTTP 2 implementation,
69
00:05:05.055 --> 00:05:06.354
which is even more complicated
70
00:05:06.815 --> 00:05:09.715
just because you won't talk to a network with asyncio
71
00:05:10.319 --> 00:05:12.819
rather than, say, basic threads and sockets,
72
00:05:13.120 --> 00:05:16.099
like to request those, for example. Well, that's still wasteful.
73
00:05:16.560 --> 00:05:19.620
And so the proposed solution to avoid this,
74
00:05:20.185 --> 00:05:21.405
was to decouple
75
00:05:21.865 --> 00:05:23.004
the protocol implementation
76
00:05:24.025 --> 00:05:24.525
from
77
00:05:24.985 --> 00:05:25.724
2 things.
78
00:05:26.025 --> 00:05:31.639
The first 1 is how you talk to the network, and the second 1 is the concurrency model.
79
00:05:31.940 --> 00:05:33.800
And these things are pretty tied together,
80
00:05:34.180 --> 00:05:36.440
and typically, something like asyncio
81
00:05:36.820 --> 00:05:37.560
or twisted
82
00:05:38.275 --> 00:05:38.855
or curio
83
00:05:39.315 --> 00:05:40.215
or trio,
84
00:05:40.595 --> 00:05:42.215
well, involve both
85
00:05:42.515 --> 00:05:43.735
concurrency primitives,
86
00:05:44.195 --> 00:05:45.095
how you run
87
00:05:45.410 --> 00:05:48.710
coroutines or threads maybe, and how you talk to the network.
88
00:05:49.090 --> 00:05:51.670
So this gives a fairly restrictive baseline,
89
00:05:52.210 --> 00:05:55.455
because you're essentially allowed, bytes and function
90
00:05:55.835 --> 00:05:56.335
calls.
91
00:05:56.715 --> 00:05:58.415
But if you write something
92
00:05:58.955 --> 00:06:00.575
and your public interfaces
93
00:06:01.035 --> 00:06:01.775
are just
94
00:06:02.130 --> 00:06:03.430
bytes and function calls,
95
00:06:03.970 --> 00:06:11.110
then you can hook it to anything. And so this is a theory behind saw. Io is that this allows writing reusable libraries,
96
00:06:11.645 --> 00:06:15.504
and this is how, HTTP 1 1, HTTP 2, and WebSockets
97
00:06:16.044 --> 00:06:17.344
now are working.
98
00:06:17.805 --> 00:06:29.130
So you've been working on the WebSockets library for a while. You had a fully functioning project. It was being used by end users. I'm wondering what your motivation was for starting down the path of doing such a major rearchitecting
99
00:06:29.590 --> 00:06:31.715
of the code base in order to
100
00:06:32.095 --> 00:06:33.475
remove the IO dependencies?
101
00:06:34.655 --> 00:06:38.515
The good news about a Hubby project is that I don't have to have a specific reason
102
00:06:39.150 --> 00:06:42.449
to pursue different goals. And in fact, it's a complete
103
00:06:42.750 --> 00:06:44.210
switch from the original goal.
104
00:06:44.510 --> 00:06:46.050
The original goal of WebSockets
105
00:06:46.350 --> 00:06:48.210
was to feel asynch Ionally.
106
00:06:48.895 --> 00:06:57.395
And over time, I put a lot of energy into making sure the APIs would behave like similar APIs in plain asyncio.
107
00:06:57.889 --> 00:07:01.990
At this point, I think there's been about 1, 000 issues filed in the tracker.
108
00:07:02.530 --> 00:07:08.955
I answered probably several 100 of these with, Hey, how would you do this in plain asyncio?
109
00:07:09.415 --> 00:07:12.235
If you pass the same arguments, it should work in WebSockets.
110
00:07:12.615 --> 00:07:14.235
The original strategy was
111
00:07:14.630 --> 00:07:17.450
let's make the best possible use of asyncio.
112
00:07:18.550 --> 00:07:25.895
So now the design is, hey, let's make the best possible use of Thor. Io and let's see how it's gonna fly. So in a sense, it's a natural continuation
113
00:07:26.435 --> 00:07:36.449
because despite being a good production ready library, WebSockets is also an experimental ground for me as you see how some concept can be applied, how it turns out in real life.
114
00:07:36.750 --> 00:07:38.930
And so I'm just taking the experience further.
115
00:07:39.235 --> 00:07:44.615
Well, it's a pretty big step because at the beginning, I had nothing. So I just started building the implementation.
116
00:07:45.075 --> 00:07:45.815
And now,
117
00:07:46.195 --> 00:07:48.740
well, I know all the bits and the features,
118
00:07:49.199 --> 00:07:56.805
that are useful to make a good complete WebSocket library. So so, of course, I have a bit of a second system effect where I want to do everything right.
119
00:07:57.185 --> 00:07:59.605
But that that makes the experience all the more interesting
120
00:07:59.985 --> 00:08:01.845
because I really get to to compare
121
00:08:02.560 --> 00:08:05.940
the async IO version with the saw. Io version
122
00:08:06.319 --> 00:08:08.180
and, eventually, its declination
123
00:08:08.560 --> 00:08:14.705
with async IO with sockets and strads and perhaps if I want to write it with Curio or Trio.
124
00:08:15.325 --> 00:08:19.105
Digging a bit more into the actual technical implementation of the WebSockets
125
00:08:19.479 --> 00:08:23.259
library, can you talk through a bit about how it was structured
126
00:08:23.560 --> 00:08:25.099
prior to this refactor
127
00:08:25.560 --> 00:08:26.699
and some of the
128
00:08:27.160 --> 00:08:28.300
design and
129
00:08:28.725 --> 00:08:34.505
use case evolution that has happened from when you first started the project to where you are now with this major rewrite?
130
00:08:35.125 --> 00:08:45.775
There's there's an interesting story there. It took me quite a long time to converge to a design that works well. Mostly because I have very little experience to current programming when I started this.
131
00:08:46.255 --> 00:08:50.035
So I have to give a quick primer about the WebSocket protocol.
132
00:08:50.495 --> 00:08:53.155
Essentially, it's a frame oriented protocol
133
00:08:53.630 --> 00:08:55.570
built on top of TCP.
134
00:08:56.190 --> 00:09:04.205
You send the data frames of various types, text and binary, essentially. You can fragment them. So you will send several
135
00:09:04.585 --> 00:09:05.085
frames,
136
00:09:05.465 --> 00:09:07.885
which will be reassembled into a single message.
137
00:09:08.745 --> 00:09:10.365
And you have control frames,
138
00:09:10.890 --> 00:09:16.350
which are ping, pong, and close. Ping and pongs are essentially used as skip lights, and close
139
00:09:16.730 --> 00:09:19.070
provides a more robust closing handshake
140
00:09:19.754 --> 00:09:25.055
versus what you can get with TCP over today's hardware and networks with lots of hardware that interferes.
141
00:09:25.595 --> 00:09:31.589
So the typical challenge if you want to do something that works well is you are receiving a big message in many fragments
142
00:09:32.290 --> 00:09:36.464
and you have a ping in the middle. You want to answer the ping with a pong
143
00:09:36.845 --> 00:09:41.504
before you get to the rest of the message. And so if you have an API
144
00:09:42.089 --> 00:09:49.470
that's like, okay, let's read the next message, which is what you want, you need to have something somewhere that will intercept
145
00:09:50.084 --> 00:09:54.504
the the ping frame and answer it. And so, eventually, when you
146
00:09:54.964 --> 00:09:56.185
investigate the problem,
147
00:09:56.564 --> 00:09:57.064
there's
148
00:09:57.510 --> 00:10:02.490
surely several ways to handle it. But if you're doing it the natural way with asyncio,
149
00:10:03.030 --> 00:10:08.245
which which is just I'm going to run a coroutine that reads frame from the network and processes them.
150
00:10:08.705 --> 00:10:11.445
You end up having a background coroutine
151
00:10:11.985 --> 00:10:15.925
that reads all incoming frames, data frame and control frames,
152
00:10:16.560 --> 00:10:24.580
reassemble messages if they're fragmented and put them in a queue somewhere. This will control frame, ping, pong, close. And then
153
00:10:24.960 --> 00:10:25.780
user code
154
00:10:26.225 --> 00:10:27.925
running in another coroutine
155
00:10:28.305 --> 00:10:30.565
can fetch messages from the queue.
156
00:10:31.025 --> 00:10:31.525
So
157
00:10:32.225 --> 00:10:32.965
right now,
158
00:10:33.320 --> 00:10:35.420
we've gotten into a complicated territory
159
00:10:35.800 --> 00:10:40.700
where we have 2 coroutines running all the time, 1 managed by the library,
160
00:10:41.405 --> 00:10:48.465
and at least 1 other and perhaps many others managed by the user, by the user code who who will call
161
00:10:48.920 --> 00:10:54.060
the receive function of of WebSockets to to receive the next message and will await it in async IO.
162
00:10:54.760 --> 00:10:56.700
Sending is less of a problem.
163
00:10:57.160 --> 00:11:02.605
There's a bit of complication if you if you want to handle back pressure right, and Asyncio
164
00:11:02.985 --> 00:11:04.845
took a few rounds to to stabilize
165
00:11:05.379 --> 00:11:17.095
its implementation of backpressure on Bright. So WebSockets has to had to follow suit and to support multiple Python versions. So it was a bit of a pain. But, eventually, around version 3 or 4 of WebSockets,
166
00:11:17.714 --> 00:11:23.015
this stabilized to a situation where both the receive and the send functions were async,
167
00:11:23.540 --> 00:11:25.880
where a coroutine was running in the background
168
00:11:26.260 --> 00:11:36.824
to process incoming frames, reassemble messages, and put them in a queue, where I could call receive to get the next message or to await it, and you could await send to to send any kind of frame.
169
00:11:37.365 --> 00:11:43.370
And it turns out that mostly for implementation reason, I had the 3rd coroutines that was responsible for eventually
170
00:11:43.750 --> 00:11:46.649
closing down, properly the TCP connection.
171
00:11:47.269 --> 00:11:48.330
It was not
172
00:11:48.709 --> 00:12:01.339
strictly necessary from a theoretical standpoint, and I had long debates, like, Chris Jardanek in the in the ticket tracker about this. He's correct in saying that we don't need a search routine. But to me, it was more practical to ensure
173
00:12:01.800 --> 00:12:03.339
the robustness of the implementation,
174
00:12:03.960 --> 00:12:07.660
which is always a hard point when you have multiple coroutines running.
175
00:12:08.005 --> 00:12:15.145
People keep reporting bugs where clearly, state got out of sync. That was a concurrency issue I wasn't expecting,
176
00:12:15.649 --> 00:12:24.389
and asyncio doesn't really help there because it's very high level. And very often, when there's this kind of concurrency issue where where state
177
00:12:25.045 --> 00:12:33.865
gets in an unexpected state, I end up digging into asyncio internal to to understand what's going on. And so what happened over time
178
00:12:34.170 --> 00:12:34.910
is that
179
00:12:35.610 --> 00:12:37.070
gradually, I started removing
180
00:12:37.610 --> 00:12:38.990
the asyncio abstractions
181
00:12:39.530 --> 00:12:41.470
and getting closer and closer,
182
00:12:41.850 --> 00:12:46.715
to to to the low level APIs. And so asyncio was super useful to get started quickly.
183
00:12:47.095 --> 00:12:49.675
But as a library implementing a protocol,
184
00:12:50.055 --> 00:12:53.900
eventually, I wanted to get rid of the stream readers and the stream writers,
185
00:12:54.540 --> 00:13:02.955
which I choose for abstractions. Well, give me, this many bytes and wait until you add them. This is a super user extraction. You you need this to implement the protocol like WebSockets.
186
00:13:03.415 --> 00:13:13.970
But what asyncio did forced me to go read the implementation, to understand all the fine detail of handling cancellation, for example. Cancellation is a very hard point in, programming.
187
00:13:14.510 --> 00:13:16.130
And so when I saw the opportunity
188
00:13:16.545 --> 00:13:18.084
to get rid of all this
189
00:13:18.385 --> 00:13:23.605
and to rebuild myself without depending on all these abstractions, it felt felt interesting.
190
00:13:24.370 --> 00:13:25.190
Yeah. It's definitely
191
00:13:25.570 --> 00:13:31.190
an interesting challenge to figure out what is the actual core concern of this protocol because
192
00:13:31.574 --> 00:13:39.675
particularly with networking and what has gotten us into this situation is that it just feels natural to handle the IO as part of the protocol implementation.
193
00:13:40.400 --> 00:13:43.620
And so that's why it's had to be redone so many times. And I'm wondering
194
00:13:44.080 --> 00:13:51.375
if you can just give a bit of an overview about sort of the core elements that are necessary for a Sans. Io library
195
00:13:51.755 --> 00:13:52.735
to be functional
196
00:13:53.274 --> 00:13:57.214
and how that changes the overall design and interface
197
00:13:57.595 --> 00:13:58.095
for
198
00:13:58.580 --> 00:13:59.400
the library
199
00:13:59.940 --> 00:14:06.040
where the protocol is the core of the logic, and you're actually adding the IO implementations
200
00:14:06.420 --> 00:14:06.920
as
201
00:14:07.455 --> 00:14:10.995
additional integration layers rather than as the primary concern?
202
00:14:11.775 --> 00:14:13.875
So from a regular user's perspective,
203
00:14:14.529 --> 00:14:16.790
I think it can change nothing.
204
00:14:17.170 --> 00:14:21.029
You can provide exactly the same high level API.
205
00:14:21.475 --> 00:14:24.375
And my plan at this point is to swap
206
00:14:24.755 --> 00:14:25.495
the legacy
207
00:14:25.875 --> 00:14:27.335
asyncio based implementation
208
00:14:27.955 --> 00:14:30.135
with the new Sans IO based implementation.
209
00:14:30.839 --> 00:14:36.620
So every user in some version update will get the new 1 by default, and I think it could work.
210
00:14:37.000 --> 00:14:37.819
So now,
211
00:14:38.199 --> 00:14:40.060
here's where things get complicated.
212
00:14:40.575 --> 00:14:43.475
So the initial implementation, which is based on asyncio,
213
00:14:44.175 --> 00:14:46.675
looks like a read message coroutine
214
00:14:47.375 --> 00:14:49.475
that calls a read frame coroutine
215
00:14:49.855 --> 00:14:52.150
and just some logic for triaging,
216
00:14:52.530 --> 00:14:54.150
control frame data frame, etcetera,
217
00:14:54.610 --> 00:15:18.070
which eventually calls the read exactly method of this asyncio stream reader to get the first 2 bytes. Because to read a WebSocket frame, you you first need to read the the the first 2 bytes and then maybe more header and then the body. It's a type length value protocol, so you always need know how many bytes, you you need to read. And so it's just a chain of coroutines calling other coroutines.
218
00:15:18.385 --> 00:15:24.725
So so it's it's very simple. There's a diagram into documentation that shows the well, every every method to to different networks.
219
00:15:25.480 --> 00:15:28.940
So now once you're doing some IO, you cannot do this anymore
220
00:15:29.320 --> 00:15:30.460
because the basic
221
00:15:30.840 --> 00:15:31.980
data input
222
00:15:32.520 --> 00:15:40.735
is you receive a bunch of bytes from the network and you deal with these bytes. So it's very much the same as the data received,
223
00:15:41.115 --> 00:15:41.615
function
224
00:15:41.930 --> 00:15:43.550
of an async IO protocol.
225
00:15:44.170 --> 00:15:46.430
Just all all the nice teas are gone.
226
00:15:46.810 --> 00:15:48.830
You have some form of inversion of control
227
00:15:49.375 --> 00:15:58.755
in the store. You provide a function that can be called to push some bytes or to push end of file, and then you deal with this. And then the the caller can
228
00:15:59.170 --> 00:16:10.335
check to see if you produced events. So so frames typically in the case of WebSockets. There's a handshake that I'm not mentioning because it's just making things more complicated. You did and it's not strictly necessary for the purposes of the discussion.
229
00:16:11.435 --> 00:16:17.135
So in the song. Io io call, the first thing you need is a stream reader abstraction,
230
00:16:18.140 --> 00:16:22.240
which is just a buffer where you can push the incoming bytes,
231
00:16:22.620 --> 00:16:31.435
and then you can read a fixed number of bytes, from this buffer. And then you need to analyze the cases whether there's enough or not enough. And so, interestingly,
232
00:16:32.455 --> 00:16:35.589
an elegant way to do this in Python is generator
233
00:16:35.890 --> 00:16:36.870
based coroutines,
234
00:16:37.570 --> 00:16:39.430
which we implement with yield from.
235
00:16:39.970 --> 00:16:47.645
And so WebSockets being originally written with yield from because this is all we had prior to Python 3.6, if more reserves,
236
00:16:48.105 --> 00:16:49.005
then switching
237
00:16:49.510 --> 00:16:52.410
to await and async in Python 3.6
238
00:16:52.950 --> 00:16:57.770
has moved back to yield from and to generative based coroutines as opposed to native coroutines
239
00:16:58.645 --> 00:16:59.145
because
240
00:16:59.685 --> 00:17:01.145
I was not allowed
241
00:17:01.525 --> 00:17:02.585
to use asyncio
242
00:17:02.965 --> 00:17:07.625
as a concurrency model because it's some IO. So so I'm only allowed to use functions.
243
00:17:08.210 --> 00:17:15.430
So here, I could certainly take an off the shelf stream reader. I think the whole new. Io project, like, if written by a German person,
244
00:17:16.290 --> 00:17:23.644
does this. And then there's a question of, do do I want to write, something that takes 100 lines and keep what sockets with the dependencies
245
00:17:24.320 --> 00:17:27.380
or understand someone else's stream reader implementation
246
00:17:27.919 --> 00:17:37.195
and take a dependency. And so since this is an experimental project for fun, of course, I'm writing my own. I get to figure out, for example, how to end the end of file.
247
00:17:37.575 --> 00:17:44.240
It's not very hard. There are better ways to do it, but it needs to be done. And I get to figure something harder,
248
00:17:44.620 --> 00:17:46.880
which is how to propagate exceptions.
249
00:17:47.660 --> 00:17:49.840
This is something that you get for free in asyncio
250
00:17:50.335 --> 00:17:51.635
and with Python generators.
251
00:17:52.175 --> 00:17:57.875
And when you start manipulating all the bits by yourself, suddenly, you realize that coroutines
252
00:17:58.255 --> 00:18:02.300
did a lot for you for free. So in this sense, the implementation
253
00:18:02.680 --> 00:18:03.180
suddenly
254
00:18:03.560 --> 00:18:05.900
becomes 1 order of magnitude more complicated
255
00:18:06.440 --> 00:18:07.340
to to
256
00:18:08.174 --> 00:18:09.554
conceptualize and to manipulate
257
00:18:10.015 --> 00:18:12.595
than when I had all the niceties of asyncio.
258
00:18:13.615 --> 00:18:14.115
So
259
00:18:14.815 --> 00:18:15.955
what I did in WebSockets
260
00:18:16.419 --> 00:18:18.440
was build a SOARIO layer and
261
00:18:19.220 --> 00:18:23.880
then try to use it to build a synchronous version of WebSockets,
262
00:18:24.554 --> 00:18:38.179
which is something people have asked for mostly for writing clients. When you write a WebSocket client, very often you have just 1 client. You don't need the fancy, concurrency that allows writing a server with 10, 000 connected clients.
263
00:18:38.480 --> 00:18:42.175
And having to run an async IO, even loop, etcetera, at overhead,
264
00:18:42.635 --> 00:18:47.135
maybe well, you you just have these asyncs and the weights everywhere, which we you don't really need.
265
00:18:47.435 --> 00:18:50.015
So there was value in providing a synchronous client.
266
00:18:50.320 --> 00:18:55.460
And so I have a work in progress version of this, except right now, I'm looking precisely,
267
00:18:55.920 --> 00:19:02.755
at how to end the end of file. So so when you get to the to the end of the TCP stream in the middle of a frame because the connection dropped,
268
00:19:03.135 --> 00:19:08.160
since WebSocket uses long running connections, this happens all the time. You cannot skip this edge case.
269
00:19:08.540 --> 00:19:13.520
And also at how to, handle access and properly. So so I'm at the step of investigating,
270
00:19:13.900 --> 00:19:15.680
and so I don't have the answer yet.
271
00:19:16.745 --> 00:19:24.190
1 of the other goals of the whole Sans. Io approach is the ability to, 1, use the protocol
272
00:19:27.149 --> 00:19:30.850
specifics of their IO requirements, but also for being able to
273
00:19:31.445 --> 00:19:39.304
compose together multiple different protocols. So for instance, if you wanted to be able to use WebSockets with an HTTP proxy or
274
00:19:39.620 --> 00:19:45.800
you wanted to be able to compose together, you know, HTTP 1 versus HTTP 2 compatibility.
275
00:19:46.500 --> 00:19:48.200
And in terms of your experience
276
00:19:48.580 --> 00:19:54.185
of working with WebSockets sockets and going down this road of making it sans. Io, I'm curious if you have
277
00:19:54.565 --> 00:19:55.065
experimented
278
00:19:55.445 --> 00:19:57.385
with the actual real world
279
00:19:57.685 --> 00:19:58.905
utility of
280
00:19:59.670 --> 00:20:02.010
composing together these different protocol implementations
281
00:20:02.790 --> 00:20:06.525
and then layering on your specific IO requirements and just some of the
282
00:20:06.925 --> 00:20:11.345
complexities or challenges or shortcomings that you may have experienced in the process?
283
00:20:12.045 --> 00:20:12.545
Yes.
284
00:20:13.005 --> 00:20:14.065
Pain is real.
285
00:20:14.380 --> 00:20:20.800
WebSockets currently can connect through HTTP proxy, but not all combinations of HTTP well,
286
00:20:21.260 --> 00:20:22.160
scratch that.
287
00:20:22.914 --> 00:20:24.934
Let let let me restart the the entire answer.
288
00:20:25.794 --> 00:20:31.015
While the pain is real, people have been asking for support for HTTP proxies in WebSockets.
289
00:20:31.399 --> 00:20:37.740
I have a pull request that does this, but doesn't necessarily handle well all the HTTP, HTTPS
290
00:20:38.200 --> 00:20:38.700
combinations
291
00:20:39.365 --> 00:20:40.904
or at least it's not tested.
292
00:20:41.205 --> 00:20:46.424
Why? Because asyncio is too high level and doing an TLS handshake
293
00:20:46.725 --> 00:20:48.265
inside a TLS connection.
294
00:20:48.600 --> 00:20:55.419
There's no good public API for doing this. And another example is that people have been asking for SOCKS proxy support.
295
00:20:56.005 --> 00:21:02.424
I'm willing to implement HTTP proxy support in WebSocket. It's just a connect request. I'm not willing to implement SOX.
296
00:21:03.340 --> 00:21:06.559
An interesting fact at this point is that the
297
00:21:07.340 --> 00:21:07.840
transport
298
00:21:08.140 --> 00:21:09.440
and protocol abstractions
299
00:21:09.820 --> 00:21:10.640
of asyncio
300
00:21:11.585 --> 00:21:12.085
were
301
00:21:12.625 --> 00:21:15.284
supposed to provide this kind of composability
302
00:21:15.985 --> 00:21:16.304
and,
303
00:21:16.865 --> 00:21:18.485
were inspired for from Twisted,
304
00:21:19.000 --> 00:21:22.140
which as far as I understand, but I never used it in in real
305
00:21:22.520 --> 00:21:25.100
life. It's designed this way for for this reason.
306
00:21:25.640 --> 00:21:26.884
But to be honest,
307
00:21:27.184 --> 00:21:29.044
I looked at the existing
308
00:21:29.825 --> 00:21:30.325
asyncio
309
00:21:31.024 --> 00:21:32.804
based libraries for SOX,
310
00:21:33.105 --> 00:21:35.365
and I tried to figure out how I could
311
00:21:36.060 --> 00:21:37.120
perform the surgery
312
00:21:37.420 --> 00:21:41.760
in WebSockets to to connect everything together. And it felt complicated,
313
00:21:42.460 --> 00:21:43.760
so I left it there.
314
00:21:44.295 --> 00:21:44.795
It
315
00:21:45.415 --> 00:21:46.875
feels possibly simpler
316
00:21:47.495 --> 00:21:49.355
with a scenario approach,
317
00:21:49.735 --> 00:21:50.235
but
318
00:21:50.615 --> 00:21:52.715
I'm still seeing a possible issue.
319
00:21:53.500 --> 00:21:59.040
Let's say so so I mentioned that in WebSockets, you have handshake, which is an HTTP request and response,
320
00:21:59.500 --> 00:22:04.355
and then you can send frames. So let's assume you are a WebSocket client.
321
00:22:04.895 --> 00:22:06.515
You send a handshake request,
322
00:22:07.135 --> 00:22:10.680
and you get 1 TCP packet, the handshake response,
323
00:22:11.620 --> 00:22:13.080
and a WebSocket frame.
324
00:22:13.620 --> 00:22:15.880
Most implementations will not do this,
325
00:22:16.340 --> 00:22:19.514
but some might. And so suddenly, in your software,
326
00:22:20.455 --> 00:22:23.034
and so in the case of WebSocket, in the stream reader,
327
00:22:23.495 --> 00:22:29.980
you have the response header that you read and possibly other things. And so if you have to hand this off to another library
328
00:22:30.440 --> 00:22:34.380
or, like I said, to to do some surgery in the middle to to reconnect things,
329
00:22:34.865 --> 00:22:43.685
then you start having to pull things out of your buffers and put them back into someone else's buffers. And this this looks, at the same time, entirely doable
330
00:22:44.360 --> 00:22:45.820
and possibly fraught
331
00:22:46.279 --> 00:22:47.100
with risks.
332
00:22:47.559 --> 00:23:04.260
There's a key question for me here is, how do you the limit handovers from 1 protocol to the other. And I think it's hard to find a good software design for this. I'm already struggling with a very basic version of this in WebSocket, which is how to do the handover from the HTTP handshake
333
00:23:04.720 --> 00:23:09.540
to the WebSocket frames. And the code around this, I never found a way to make it elegant.
334
00:23:10.145 --> 00:23:18.325
And so in terms of your experience of going through this conversion from a functioning library that's tied to a specific IO implementation
335
00:23:20.010 --> 00:23:22.510
to extracting the core protocol capabilities
336
00:23:22.970 --> 00:23:32.085
into a Sans. Io implementation and then layering the IO back on and working on trying to compose together multiple Sans. Io protocol implementations.
337
00:23:32.544 --> 00:23:33.284
I'm curious
338
00:23:33.905 --> 00:23:38.320
what your current perspective is on the overall cost benefit analysis
339
00:23:38.780 --> 00:23:44.400
of the conversion and just the overall approach of Sands. Io, and if you think that the
340
00:23:44.725 --> 00:23:47.385
goals as stated still align with the reality.
341
00:23:48.325 --> 00:23:51.865
So if eventual goal was only to have an
342
00:23:52.245 --> 00:23:52.745
asyncio
343
00:23:53.285 --> 00:23:53.785
library,
344
00:23:54.710 --> 00:23:58.090
designing it around the saw your core and the asyncio
345
00:23:58.390 --> 00:23:59.450
integration layer
346
00:24:00.070 --> 00:24:08.785
wouldn't make any sense, in my opinion, because there's a huge overhead in terms of the design, and you do so many other niceties, and you you have to rethink everything from scratch.
347
00:24:09.230 --> 00:24:12.050
Essentially, you you're naked in the jungle with a knife
348
00:24:12.750 --> 00:24:14.850
instead of, flying a jet.
349
00:24:15.870 --> 00:24:16.370
1
350
00:24:17.014 --> 00:24:25.510
clear improvement though is on tests. As there's an absolutely horrific file in WebSockets, which is called test client server dot py,
351
00:24:25.830 --> 00:24:32.890
which essentially performs integration tests between the client and the server and tries tons of permutations of parameters in the protocol.
352
00:24:33.304 --> 00:24:44.100
And this is so slow because every time, you have to create an event loop to isolate things properly and to start a server and to start a client and to make a TCP connection and to exchange a frame and to close everything.
353
00:24:44.480 --> 00:24:47.140
And this is so much slower than necessary.
354
00:24:47.840 --> 00:24:50.260
Once the protocol is extracted out,
355
00:24:50.735 --> 00:24:54.355
you can do all the testing of parameters and their permutations,
356
00:24:55.215 --> 00:24:58.595
purely manipulating bytes in memory, and it's incredibly faster.
357
00:24:59.110 --> 00:25:01.770
So as a promise of better testability
358
00:25:02.230 --> 00:25:03.530
is clearly delivered.
359
00:25:03.910 --> 00:25:06.250
I will have to complete the implementation
360
00:25:06.630 --> 00:25:10.585
of 1 IO integration before I can give the final word on this.
361
00:25:11.045 --> 00:25:12.184
But I think
362
00:25:12.565 --> 00:25:13.065
the
363
00:25:13.445 --> 00:25:13.945
testability
364
00:25:14.405 --> 00:25:16.825
will likely be better on this end,
365
00:25:17.750 --> 00:25:18.890
modulo the fact
366
00:25:19.270 --> 00:25:21.049
that having 2 coroutines,
367
00:25:21.590 --> 00:25:26.410
at least 2 coroutines or 2 threads, for the reason I explained in the beginning is still inevitable.
368
00:25:26.795 --> 00:25:29.375
So there are still all the concurrency pitfalls,
369
00:25:29.995 --> 00:25:38.240
and it's hard to say if there will be large gains on this front in terms of having fewer concurrency bags. I'm afraid
370
00:25:38.620 --> 00:25:44.605
it won't be that different that they will just reopen a big can of bag by by writing a second system.
371
00:25:45.085 --> 00:25:47.345
So so these are my observations at this point.
372
00:25:47.804 --> 00:25:52.145
I'm likely to be more proud with the final result. So with v2,
373
00:25:52.500 --> 00:25:53.000
asyncio
374
00:25:53.460 --> 00:25:54.600
around a core
375
00:25:55.220 --> 00:25:56.440
as on with v 1.
376
00:25:56.900 --> 00:25:58.760
It's likely to be a bit more maintainable.
377
00:25:59.299 --> 00:26:04.765
The only thing that will really make a big difference, in my opinion, is if another major
378
00:26:05.465 --> 00:26:05.965
project,
379
00:26:06.425 --> 00:26:11.960
server product integrates it, and it works well. So in the past, the folks making, Sanic,
380
00:26:12.500 --> 00:26:14.360
have been relying on WebSockets
381
00:26:14.740 --> 00:26:16.200
for their work for the implementation.
382
00:26:17.855 --> 00:26:24.675
But since they didn't have a good API for integrating it, they did what they could. So the result is a bit of a Frankenstein,
383
00:26:25.410 --> 00:26:31.030
and they tend not to get the benefits of APIs or improvements added to WebSockets
384
00:26:31.570 --> 00:26:36.745
unless they do specific work to implement it. So typically, when when WebSockets added compression,
385
00:26:37.125 --> 00:26:47.750
which seemed like a useful feature when you are shuffling a lot of JSON over the wire, the Sanny didn't get it. So 1 of the motivation drivers for, doing this project was making it possible
386
00:26:48.050 --> 00:26:54.804
for people doing this kind of thing to rely on WebSockets. Well, it's not under my control if a product eventually takes advantage of this.
387
00:26:55.505 --> 00:26:58.630
So you mentioned that the potential
388
00:26:59.169 --> 00:27:01.669
use cases and end users of the library
389
00:27:02.210 --> 00:27:14.705
are going to change a little bit where the top level API is going to remain the same, where it's going to be async IO oriented. But it also exposes just this core protocol layer that other people can integrate
390
00:27:15.010 --> 00:27:18.789
more directly or compose together. I'm wondering if you can
391
00:27:19.090 --> 00:27:22.230
just give some perspective on how you foresee
392
00:27:22.835 --> 00:27:34.180
the overall target audience changing and the interaction patterns for you as a maintainer evolving as more people become aware of and are able to take advantage of this peer protocol implementation?
393
00:27:35.200 --> 00:27:39.380
So 1 of the first things that's going to happen is that we will have multiple
394
00:27:40.115 --> 00:27:41.255
versions of WebSockets.
395
00:27:41.875 --> 00:27:43.255
I assume people
396
00:27:43.555 --> 00:27:47.495
writing servers will stick to asyncio for the most part.
397
00:27:47.980 --> 00:27:51.040
I think, few folks who like to tinker or to experiment
398
00:27:51.500 --> 00:28:01.355
may try to write servers with a Qio or Trio if eventually implement this. It's pretty low priority because I think these projects are still mostly experimental
399
00:28:01.895 --> 00:28:04.475
and have nowhere near as much traction as asyncio.
400
00:28:05.000 --> 00:28:09.420
So anyone who uses this is quickly going to run into ecosystem issues.
401
00:28:09.880 --> 00:28:11.260
I can't control my database,
402
00:28:11.960 --> 00:28:13.995
this sort of thing. And the
403
00:28:14.375 --> 00:28:15.915
sockets and STRAD implementation
404
00:28:16.375 --> 00:28:21.820
will be very useful for my clients as as as of well, I have pushed back feature
405
00:28:22.120 --> 00:28:25.020
requests saying, hey, can I have a callback based API?
406
00:28:25.400 --> 00:28:28.620
Because I think coroutines are objectively better than the, callbacks
407
00:28:28.924 --> 00:28:33.184
for this sort of thing and also people for for projects with with callback based APIs.
408
00:28:33.725 --> 00:28:35.825
Now the feature request of
409
00:28:36.360 --> 00:28:43.180
I want a synchronous API is completely legitimate when you just want to write a client to wait for messages
410
00:28:43.595 --> 00:28:49.215
sent by a server and do something with them and move on to the next message. You don't really think how to do this,
411
00:28:49.515 --> 00:28:51.855
and so this will open a new field.
412
00:28:52.190 --> 00:28:57.730
And so now the last part which you had in mind was other projects integrating WebSockets.
413
00:28:58.430 --> 00:29:06.905
In my experience, maintainers of a sub product are very autonomous for doing this. Perhaps this could result in some interesting bug reports.
414
00:29:07.460 --> 00:29:10.760
Historically, I took a very strong stance of quality in WebSockets.
415
00:29:11.140 --> 00:29:13.320
So it's very high result of test coverage.
416
00:29:13.700 --> 00:29:15.720
But while it could expose patterns
417
00:29:16.075 --> 00:29:25.055
that my own implementation is not exposed, some bugs may lurk in there. So we will see, but at this point, I trust the test harness of the style implementation,
418
00:29:25.755 --> 00:29:30.000
and there's no concurrency bugs in there, and concurrency bugs are the only hard ones.
419
00:29:30.700 --> 00:29:43.145
And in terms of your experience of building the project initially and then going through this conversion of extracting the core protocol layers, I'm wondering what are the most interesting or unexpected or challenging lessons that you've learned?
420
00:29:43.840 --> 00:29:47.539
So 1 challenge that I haven't cracked yet
421
00:29:47.919 --> 00:29:49.700
is how to swap
422
00:29:50.320 --> 00:29:53.925
the current implementation, which I'm now calling the legacy implementation
423
00:29:54.785 --> 00:29:56.005
based on I c asyncio
424
00:29:56.545 --> 00:29:57.525
with a new
425
00:29:57.960 --> 00:30:05.260
integration layer built with an asyncio on top of the scenario core. So I spent quite a bit of time exploring
426
00:30:05.895 --> 00:30:08.475
how I could do some clean surgery
427
00:30:09.255 --> 00:30:10.155
to rewire
428
00:30:10.935 --> 00:30:12.315
the current API
429
00:30:13.120 --> 00:30:19.700
and just swap the call so my entire test suite would still pass, and I could have high confidence
430
00:30:20.240 --> 00:30:22.260
that the results contains no regression.
431
00:30:22.985 --> 00:30:26.445
At this point, I have come to believe that it will not happen,
432
00:30:26.825 --> 00:30:30.525
and so I just moved the entire implementation into a legacy
433
00:30:30.905 --> 00:30:31.405
submodel.
434
00:30:32.020 --> 00:30:34.040
I added some compatibility imports.
435
00:30:34.420 --> 00:30:36.360
I think I'm going to build a new implementation
436
00:30:36.660 --> 00:30:39.240
that, well, provides the same API.
437
00:30:39.985 --> 00:30:47.525
Perhaps I release a version where a user who wants to try it can opt in just by importing serve and connect
438
00:30:48.080 --> 00:30:50.419
the primary entry points from a different location.
439
00:30:51.120 --> 00:31:04.265
I'm slightly skeptical on this because I don't think anyone would actually try it. In fact, And at some point I think I will make the jump and, and swap. But the key lesson here is that the Son. Io folks have 1 thing really right. It's that
440
00:31:04.700 --> 00:31:06.880
once you buy into something like asyncio
441
00:31:07.420 --> 00:31:14.080
and the entire code base is predicated around the concurrency model and the IO model and the utilities provided by the library,
442
00:31:14.445 --> 00:31:20.545
Essentially, you can't do anything with it. You cannot refactor it. The only thing you can do is rebuild it, and this is what I end up doing.
443
00:31:21.460 --> 00:31:29.560
Are there any notable features or improvements that you have planned for the future of the project or anything that you're particularly interested in digging into?
444
00:31:30.115 --> 00:31:33.335
1 thing that could come afterwards would be supporting
445
00:31:34.115 --> 00:31:38.935
doing the WebSocket handshake over HTTP 2 versus HTTP 1.
446
00:31:39.460 --> 00:31:40.360
Right now, WebSockets
447
00:31:40.740 --> 00:31:58.400
has its own implementation of HTTP 1 because writing HTTP implementation is fun. So I did it like everyone else. And because I didn't want to take on the dependency if I could avoid it, and also because it wasn't sour. Io, so any dependency would be hard to integrate. So for HTTP 2, it's sufficiently complex
448
00:31:58.780 --> 00:32:01.840
that I'm really not considering doing it by myself.
449
00:32:02.220 --> 00:32:06.505
And so I could try to do this most likely with, H2 library
450
00:32:06.885 --> 00:32:07.945
as a hyper project.
451
00:32:08.645 --> 00:32:12.105
That said, it looks like a lot of work for very minor benefits
452
00:32:12.550 --> 00:32:18.970
because the whole point of WebSockets is to have a long lasting connection, a permanent connection. And the first 200 bytes
453
00:32:19.270 --> 00:32:20.170
really don't matter.
454
00:32:21.265 --> 00:32:24.085
Are there any other aspects of the WebSockets
455
00:32:24.545 --> 00:32:33.190
protocol itself, your implementation of it, or your work on making the library sans. IO that we didn't discuss yet that you'd like to cover before we close out the show?
456
00:32:33.650 --> 00:32:35.670
1 last thing that's interesting, it's authentication.
457
00:32:36.290 --> 00:32:43.755
So right now, if you connect to with WebSockets to a server and there are credentials in the URL, username and password,
458
00:32:44.135 --> 00:32:50.830
WebSockets automatically performs basic auth, which is, well, just adding a header to the to the request.
459
00:32:51.290 --> 00:33:03.934
Is there something similar but with a slightly more complicated API on the server side? Because due to to inversion of control, it's not just a URL you're passing to a function call, but it has to be something you configure when you build the server.
460
00:33:04.320 --> 00:33:07.460
And, of course, people have been asking for digest auth,
461
00:33:07.840 --> 00:33:09.539
which requires 2 HTTP
462
00:33:09.840 --> 00:33:10.340
requests.
463
00:33:10.720 --> 00:33:17.205
So client make a HTTP request, server sends a challenge, A client sends the authentication info, and then we can complete the sequence.
464
00:33:19.024 --> 00:33:24.490
Again, this seems simple, but when I actually try to build this with all the possible permutations
465
00:33:25.030 --> 00:33:27.690
of also following redirect, etcetera, etcetera,
466
00:33:27.995 --> 00:33:28.815
it gets complicated.
467
00:33:29.275 --> 00:33:36.415
And I suspect this could be easier to build inside the song. Io model, because it would it would just be as
468
00:33:36.770 --> 00:33:38.070
a handshake sec sequence
469
00:33:38.450 --> 00:33:43.990
lasting a bit longer. So we're exchanging more bytes, but it's pretty much transparent at the network level.
470
00:33:44.445 --> 00:33:44.945
So
471
00:33:45.405 --> 00:33:50.865
it's not very high priority, and I mentioned earlier the integration is not very elegant in this area.
472
00:33:51.510 --> 00:33:53.610
Essentially, clients and server in WebSockets
473
00:33:54.070 --> 00:33:55.530
share the entire implementation,
474
00:33:56.550 --> 00:33:59.530
except they do something special with the first message
475
00:33:59.885 --> 00:34:09.025
because it's a HTTP request or response that depend on which side you are, and it requires some specific handling. And so now if it has to be the first 2 messages, in some cases,
476
00:34:09.450 --> 00:34:17.230
it requires a bit of rethinking how this part works. Once I do this, perhaps I can have basic auth, which seems like a very valid feature request.
477
00:34:17.575 --> 00:34:21.515
By the way, I realized I didn't talk about something which is handling redirects.
478
00:34:21.895 --> 00:34:22.795
If you're making
479
00:34:23.255 --> 00:34:24.235
client connection
480
00:34:24.600 --> 00:34:25.420
and the server sends
481
00:34:26.120 --> 00:34:26.780
a 3xx
482
00:34:27.080 --> 00:34:30.060
HTTP code, you want to follow the redirect.
483
00:34:30.440 --> 00:34:31.900
And so here, very interestingly,
484
00:34:32.520 --> 00:34:33.545
the protocol
485
00:34:34.085 --> 00:34:35.385
provides an information
486
00:34:36.085 --> 00:34:37.625
that needs to be used
487
00:34:38.085 --> 00:34:41.385
for doing something at the network level, possibly
488
00:34:41.859 --> 00:34:47.960
opening a connection to different host. So this is typically where fire creaks a bit around the edges.
489
00:34:48.420 --> 00:34:48.920
Essentially,
490
00:34:49.220 --> 00:34:55.265
all this logic is pushed to the integration layer. It cannot leave into the core.
491
00:34:55.965 --> 00:35:00.465
And so the integration layer, in the case of a very simple protocol like WebSockets,
492
00:35:01.550 --> 00:35:03.010
stays fairly thick.
493
00:35:03.550 --> 00:35:05.890
The authentication logic I just mentioned,
494
00:35:06.190 --> 00:35:07.650
or the redirect logic
495
00:35:07.950 --> 00:35:16.985
is very likely to leave at least in part in every integration layer. So asyncio, sockets and trial, Choreo, etcetera, etcetera.
496
00:35:17.365 --> 00:35:22.460
So in that sense, even even though there's a good don't repeat yourself effect,
497
00:35:22.920 --> 00:35:30.204
thanks to the saw. Io core, it's not as complete as you might expect if you just read, let's say, the marketing of Square.
498
00:35:31.224 --> 00:35:42.610
Yeah. It's definitely interesting how some of those concerns can kind of creep outside of the core elements of the protocol, and there's no way around implementing it specifically for the each IO implementation.
499
00:35:42.910 --> 00:35:52.744
So it's definitely an interesting complexity to that problem. And I'm curious how many other network protocols have that kind of creeping of concerns into the IO layers.
500
00:35:53.309 --> 00:36:06.215
The way I look at it is just that being user friendly is hard. If you want to do things a simple way, you just push it from 2 users. But if you want to be nice and given anything that looks reasonably like a WebSocket URI
501
00:36:06.835 --> 00:36:37.984
or IRI, by the way, Eventually, we managed to connect to this and provide the send and receive calls. Well, then you have some work to do. And I think this is really what we are seeing here. For anybody who wants to follow along with the work that you're doing or get in touch, I'll have you add your preferred contact information to the show notes. And so with that, I'll move us into the picks. And this week, I'm going to choose taking the time to sit down and do a puzzle every now and then just because they're fun and challenging, and it's a good way to get everybody in the family to work work together on something. So with that, I'll pass it to you, Aymeric. Do you have any picks this week?
502
00:36:38.365 --> 00:36:45.440
I think I'll just link to something completely different, you know, which is my my vision of how we can make engineer proud. In too many companies,
503
00:36:45.900 --> 00:36:54.935
engineers end up suffering and the pressure of deadlines and feeling pushed to improve quality. And I think it's an important problem to solve. So I'll link you to something I've heard about this.
504
00:36:55.474 --> 00:37:07.000
All right. I look forward to reading that. And thank you very much for taking the time today to join me and share your experience of going down the road of implementing a SANDS IO network protocol, particularly 1 as
505
00:37:07.460 --> 00:37:19.470
involved as WebSockets. So it's definitely a very interesting project, and I appreciate all the time and effort you've put into that. And I look forward to seeing where you take the project, and I hope you have a good rest of your day. Thank you for having me on the podcast. Good afternoon.
506
00:37:22.170 --> 00:37:27.375
Thank you for listening. Don't forget to check out our other show, the Data Engineering Podcast at dataengineeringpodcast.com
507
00:37:28.795 --> 00:37:30.655
for the latest on modern data management.
508
00:37:31.435 --> 00:37:32.895
And visit the site at pythonpodcast.com
509
00:37:33.915 --> 00:37:34.550
to subscribe
510
00:37:36.630 --> 00:37:37.370
to the show, sign up for the mailing list, and read the show notes.
511
00:37:37.670 --> 00:37:42.250
And if you've learned something or tried out a project from the show, then tell us about it. Email host@podcastthenit.com
512
00:37:43.510 --> 00:37:44.355
with your story.
513
00:37:45.155 --> 00:37:49.255
To help other people find the show, please leave a review on iTunes and tell your friends and coworkers.