 OK. So, it looks like I'm going to be talking to you today about designing an HTTP client. It's what it says on the slide. For the purposes of coherency. That's what I'll try to do. There's also a name on the slide. It says Tom Christie. You can call me that if you like for the purposes of coherency. Briefly, me, I work full time developing open source for a living with a sponsorship model for our funding. Some of the software that myself and the collaborators that I work with have designed and built while we've been working with this include the API framework, Django REST framework, the documentation tool, MacDocs, tooling for the ASGI web ecosystem, including Uvicorn and Starlet, and the project that I've been spending most of my attention on lately, which is HTTPS. I don't know why you would say hud up, but it has an exit at the end. I wonder what that's about. So, a little historical context that HTTPX exists within. There was already a very well established HTTP client for Python request, which I expect almost all of you here will know of. But let us not say requests actually are talking about requests. Quite important to think in terms of the context of requests. Request is actually has two main elements to the project, because request is actually a project that's built on top of another existing HTTP client, URLlib3. The big innovation that requests bought was that it bought a level of finesse and design to the API on top of URLlib3, and as well as addressing some functionality aspects that it bought on top of that. But URLlib3 is the project that actually deals with the core networking layer for requests. One of the things that I'd like to mention at this point in time, if you're looking for examples of really well managed open source teams who do a really good job of both the design work but also in terms of how they work together and support their community, I think the URLlib3 team are absolutely excellent and should go and look at how they manage their projects. Also interesting to note at this point, they've spent a lot of design work on improving the UX of URLlib3. So if you're working with requests, you don't necessarily need to do that any longer. You could look at just using URLlib3 directly. Anyway, given that those two things already existed, why HTTPX? So the original motivation for working in this area was addressing two headline features that were missing, still are missing from requests. So there's HTTP2 support and there's async support. We would like a Python HTTP client that's able to take advantage both of HTTP1 and the sometimes performance benefits that HTTP2 provides. We'd also like a Python HTTP client that's able to support usage in the async execution environment. Now there is already an HTTP client that exists for this, which is AIO HTTP. But that only works in the async context. So you can use that, say, if you're working with a fast API web framework. A request only works in the threaded context. So you can use that, say, if you're working with something like Flask and sending API requests. We'd like an API client that is able to work equally well in either context. So those were the two headline features that HTTPX was originally introduced to address. As well as meeting all of the functionality that requests provides. There was also an element of wanting to take on a bit of an intentional design refresh. So where requests really excels is at the high level API. But because it's being built on top of URLLib3 in a way that those project authors hadn't originally intended when you start to get down to some of the really lower level bits of the networking API, it doesn't always fit together quite as well as it might. So being able to have a design refresh that works all the way through the stack. We did spend a chunk of time looking at whether we'd be able to bring this functionality into the requests project directly. Whether we'd be able to work with the URLLib3 team and do something like that. Ended up being too complex, but still essentially what the project is aiming for is an iteration on top of the good design work that requests already provides in that space. That's a picture of the Pompidou Centre in Paris. I didn't take that photograph, so maybe it's not that authentic. I did download it with HTTPX, so maybe it tastes a little bit of HTTPX in this context. Context is important. So these are some of the sorts of ways that I would describe the design intent that we've bought when we've been building HTTPX. I realize that these are just flavorful words. I'm not going to demonstrate in great depth how they apply, but I believe that they're meaningful. When I say things like low complexity stack, the sorts of things that I'm interested in is... Sometimes when you're working with Python packages, you can end up with a bunch of submerged complexity because you have dependencies that sometimes you have a bunch of submerged complexity. I like making sure we don't have submerged complexity and that you've got something with minimal dependencies or that at least has a minimal dependency install available that a single developer ought to be able to come to this code base and comprehensively understand all the different layerings of the stack and feel like there's a unified design that runs all the way through those things. That's a picture of the Bauhaus building in Berlin. It's really easily because it's very definitive about what it would like to be called. Probably other buildings that say Bauhaus on the side, but they wouldn't be the Bauhaus building. This is, or at least it's a photograph of it, but it's also a bit it as well. This is the repository. What's it called? It's the organization repo homepage. Thank you, Marcelo. It's been really chaotic lately, so I'm just having to add Lib. Oh, well. We have the two major components that make up HTTPS here. We have HTTPS, which is the high-level API analogous to requests, and we have HTTP core, which is the core networking package that we rely on, which is, at least in that context, analogous to URL Lib3 and the space that URL Lib3 occupies for requests. Here's our documentation homepage for HTTPS. You can tell that this is the high-level API because it has a pretty picture of a butterfly on it. I like butterflies, and I did take this picture. This is a butterfly in a butterfly house near to where I live, and if you came and visited me, maybe we could go to the butterfly house, and this butterfly will be dead. Is it funny? I don't know. Well, anyway, it's fine. You can tell that this is our homepage for HTTPS core. You can tell that this is the lower-level networking package because it doesn't have a pretty picture of a butterfly, even though I like butterflies. No, this is very seriously-minded documents. Very seriously-minded folks. That's quite exhausting just to look at. You have a really nice requests t-shirt, by the way, over there. I really like that. It's nice. Is it not? It looks so similar. You can tell me, but I'll ask you about that later. On one side, we've got the stack of concerns that HTTPS core needs to deal with. On the other side, we've got the stack of concerns that HTTPS needs to deal with. HTTPS core is designed with the intent of providing the simplest possible API that we can bring to the table that allows for dealing with the... Well, sending an HTTP request and getting an HTTP response and dealing with the connection pooling and the proxy implementations and the networking concerns in order to do that. And HTTPX is everything else over the top. So some of the things which are written down on the other side are, for example, redirects. We don't deal with redirects at all in HTTP core. There's all of the bringing nicer API models over the top of the requests and responses. So whereas HTTP core has a very, very bare bones approach to the API, HTTPS brings nicer object models on top of that. There's also the... So when you make a request and you download a response, the process of going from the bite-wise content that you get back in the response, if you were getting that from HTTP core, things that we're not doing includes content compression. So if you've added in compression... I can support compression headers, that layer is dealt with in HTTPS. And also, how on earth do you go from the bite-wise content that you've gotten a response into, oh, well, here's some actual Unicode text which character encoding are we going to use? Anyway, there's an awful lot of different elements that HTTPX brings over the top. But the helpful thing design-wise is to have a really clear separation of responsibility of these two packages so that when you're looking at and trying to comprehend one of them, you don't need to have in your mind at all any of the design space of the other. And HTTPS actually plugs into a bunch of different possible transports of which HTTP core is only one of them. So the other interesting thing that you can do with HTTPS is you can plug it directly into your whisky app, such as if you have a flask app that you're building, you can plug HTTPS directly into that using it as a test client without having to make actual network requests through it. We have a transport for making ASGI requests. We also have a mock transport that we use lots within our own test suite so that we don't need to send requests in order to prod all of the buttons and make sure that everything fits together coherently. I mostly fit together coherently. And you can also have alternate network implementations. So we have a, I think there's a gist somewhere, it's probably slightly out of date, maybe somebody could update it, which provides a URL lib3-based network implementation for HTTPS. And that's kind of valuable because if you're trying to dig into issues on networking problems, you can switch out to a completely different networking layer and figure out is the problem over here or over here. So, layered design. Oh, yeah. I suppose I also wanted to say about this layered design. We've taken that kind of same approach to layering as well all the way through the stack. So we've been doing a lot of work in HTTB core lately, addressing this kind of thing. So being able to go with, okay, here's the API design when you're working at the level of the connection pool, but also they're making sure that if you want to get down into more bare bones and not many people actually want to do this, but it's still kind of important from the purposes of being able to comprehend what is happening here, you can start using HTTB core and then when you're getting more in depth go, I'd like to get rid of the connection pooling entirely and just starting with working with a direct connection. Or then you can drop down another layer and say, I want to be specific about whether I'm working with an HTTB2 to be one connection and not have the layer over the top of that anyway. This is just our test suite running for HTTBX and it makes me content because of the layered design, we're doing very little actual networking at all and we've got 100% test coverage and 100% type annotations all the way through and everything just runs really nicely and quickly and it makes me happy even though it's only text on a screen. HTTB1 and HTTB2. HTTB1 is a text-based protocol that is capable of handling a single request and response over a TCP stream at any one time whereas HTTB2 bought a whole bunch of performance improvements by allowing multiplexing of multiple HTTP requests and responses over a single TCP stream, so avoiding the need to have to set up multiple TCP streams when you're making lots of concurrent requests. As well as bringing a bunch of other stuff with it being a binary protocol it's able to add new features such as flow control which helps with the network efficiency, header compression which improves the bandwidth efficiency, server push. Can't remember is server push still a good idea or not? Did we find out that that didn't quite work as well as we were expecting possibly? Also that's not what HTTB2 is. It's actually a SIOP designed to entrap overly analytical minds into ever more complex systems design so that they can just all do that stuff busily elsewhere and we can then focus on the more important stuff whilst they're getting too analytical because sometimes it's easy to focus on the wrong things, right? If you're not careful. If you'd want a more conventional explanation of HTTP2 I would recommend the person who developed the curl tool. I always want to say badger but it's not badger. It's a badger. A very comprehensive document on this which I write. What am I saying here? In HTTPX our HTTP support is off by default but you can enable it when you configure the client just by adding a flag. Why have we taken this design decision? HTTB2 adds a whole bunch of clearly demonstrable performance improvements when you're in the context of a browser that don't always necessarily apply in the context of when you're working with a programmatic client because the... I've forgotten the nice word for it, never mind. Crackers. It's not crackers. Because the profile of the kinds of requests that you're making is very different in a browser you're almost always downloading a web page and then downloading a whole slew of resources that are associated with that web page which might not be true when you're making API calls for instance and you're making lots of sequential API calls. Also HTTP2... while it's more network efficient that comes with the cost so it's more computationally complex. It doesn't matter so much in the context of a browser because your browser is probably written in C++ or something else. I don't know what safaris are you sending. No, it's an opera sort of thing. It must be. That would be sad. That was really nice. Yeah, but when you're working with Python of course the computational cost is proportionally higher. Oh look, these are two really really nice songs I own design paradigm libraries that we use. These are the libraries that we use for our HTTP parsing construction and they are designed with... I don't know how many of you in the room have heard the song paradigm but if you're working with message parsing and networking the only sensible approach which is decoupling the actual doing stuff on the network versus the parsing and the message framing and keep all of your IO away from that. HTTP3, we don't support that at the moment. We probably will. I don't know why we will because it probably won't end up being a performance improvement for anybody but maybe it'll be really useful as a debugging tool for some folks so that might be a good reason why we might do it. The performance improvements... HTTP3... Over UDP, not over TCP. Magic happens, wonderful. Evermore complex surface area comes with a cost but I think we'll still do it because something... Oh yeah, sync and async supports. Yeah. So one of the big motivations we're being able to have a client that works with almost exactly the same API regardless of whether you're in the sync context or the async context so here's three different examples of using the client in three different contexts. In the async context we support both using it with async IO. How many people work with async IO, by the way? Okay. And with Trio, how many people use Trio at the moment? Okay, so use Trio. It's flipping wonderful if you can. Trio pioneered structure concurrency and Trio has pioneered the... Here's how you design a set of constraints around which to handle concurrency in a sensible way so that you're not leaking resources which... And it's also developed by one of the smartest people who I know within this sphere. Can't use Trio because... Isn't it frustrating? Being incompatible. There's a great package called NEIO which brings the same design constraints of structured concurrency to async IO that Trio has pioneered. I know that the core devs are also working on bringing some of the same learnings that Trio has bought into async IO but that's going to be a bunch of time and you're just exposed to so many primitives that you really don't want to be using with async at the moment. So this is a little bit of the code base of HTTP core. How are we going to support both async and sync? Well, let's just bundle them all into the same package and have one as the single source of canonical truth which has... We need to be explicit about where our concurrency switches are so that it has async and awake keywords in and then you've got the sync code base which is actually almost exactly the same but doesn't have the explicit here are points of context switching anymore so we have some tooling that we use where we're able to just work on one of these flavours as here's our source of truth and we'll mirror it into the other one and our continuous integration when it runs makes sure that the two are in sync but we then don't really need to think about and that handles the translation of some of the class names some of the import names and the async and awake keywords and if you want to know a little bit about that more I would look at the standalone package on async because our approach to that was based on the URL of three team we're thinking about doing for their work and that's their kind of standalone approach on that oh, we've got a command line client built into HTTPS and it's really nice because there's almost nothing to it you can go and look at the code base for the single module that has that code in it and it brings a tool that you can work with with the client that translates directly on to the stuff that you'll be doing when you're working with the package itself which I always find really satisfying it's, yeah and we're going to be able to do really nice things in there as well like, you know, if you're working with HTTP2 and you need to do, never mind, I haven't got time for that it uses click, click's great, isn't click eight on the pallets team, incredible I mean, you know, flask and all of the other wonderful things I really like click eight, it's really nice we also use rich for making the colours all fancy and nice and if you haven't checked out the work that textualise are doing with their work on consoles their team are doing some really fun stuff if you like retro-console-y things because I think retro-console-y things are fun because we've been tackling this with a complete design refresh we've been able to take a more comprehensive approach to request and response timeouts then you'll get if you're using requests at the moment type annotated all the way through people end up having mixed bags on this I think it's okay when you're working with it from scratch on a project trying to bring it into a project retrospectively less convinced about but for us it's been wonderful being able to, it's just great let's see, separation of concerns I would hope with the way that our codebase has been designed so we've got HTTPX stuff at the top we've got HTTP core stuff at the bottom really would like this to end up being a learning resource so you want to dig into being able to understand what's happening within the HTTP flow that you're able to comprehensively do that all the way through the different layers the request and response models have actually been designed quite intentfully so that we could use it as a basis for a micro-framework and doing stuff on the server side where there's a bunch of things that make that a bit fiddly and complicated and difficult requests but I haven't talked about my team whether they want to do that or not you can also do some things that you're probably not going to want to do but that still might make you feel content that you can with HTTPX and HTTP core I'm not really sure what I want to say about this so for example to be able to look you can ask how do I do any of these ridiculous things if you want and we'll be able to give you really good answers to those and even in the areas like with GRPC where we probably can't quite give you you can do this right away here we can give you here's the very very tiny amount of extra work that we're missing before you'd be able to get to how can I use HTTPX to give me HTTP to bi-directional streaming and support GRPC over the top of that why would you want to do that maybe because GRPC at the moment isn't really so much a protocol in reality but it's actually just a tooling suite and wouldn't it be nice if it was more in practice here at the different implementations oh my goodness me yes I am we are we will it is that's the